Modules
Modules API
Modules are catalogue entries in Hutly’s internal authoring organization that
bundle an app site and/or workflow roots as a single
installable unit. See Modules for the concept and the
CLI reference for the intellia modules command group.
chat role or above. Installing a module (every activation step, and the streamed activation) needs an admin or owner. Registering a module (POST) and reading a module’s source bundle are restricted to Hutly’s internal authoring organization.Register a module
Registers a module (upserts by slug). The module’s version goes up only when
its content changed since the last registration: its catalogue fields, roots,
seeds, knowledge base files, or anything in the closure activation installs.
Registering identical content again keeps the version, so dependents are not
asked to re-activate. A registration whose closure cannot be assembled still
bumps the version. Each root must reference an app site or workflow that
already exists in the authoring org and carries its own description.
POST /orgs/{organizationId}/modulesRequest body:
{
"slug": "acme-portal",
"name": "Acme Portal",
"description": "Bundles the Acme storefront site with its nightly sync workflow.",
"visibility": "public",
"category": "e-commerce",
"tags": ["portal", "acme"],
"roots": [
{ "kind": "app_site", "resourceId": "3f9e2b3a-2222-4c11-9b8e-111111111111", "description": "the storefront", "appPaths": ["/", "/orders"] },
{ "kind": "workflow", "resourceId": "7c1d44f0-3333-4d22-8c9f-222222222222", "description": "nightly sync" }
]
}visibility defaults to private; category and tags are optional.
Response: the stored module with its roots (the shape
Get a module returns) plus warnings. Besides the fields above it carries
id, version, and contentDigest: the SHA-256 (64 lowercase hex characters)
of the content the version tracks, or null when the last registration’s
closure could not be assembled or the module has not been registered since
digests were introduced.
An app_site root may carry appPaths (the page paths it ships; an empty list
is allowed, and omitted means every page on the site) and, with appPaths,
attachToModule (the slug of a dependency whose site the pages go on).
Registration answers:
| Status | Code | When |
|---|---|---|
| 400 | invalid_field |
appPaths or attachToModule is malformed (field names which) |
| 400 | module_root_app_path_not_found |
a listed path has no live page on the site |
| 409 | module_app_path_claimed |
another module already ships that path (path, module) |
| 400 | module_attach_target_invalid |
attachToModule is not a dependency that owns the site |
| 400 | module_attach_owner_paths_unrecorded |
the owner has not been re-registered with its paths |
| 409 | module_app_site_owned |
another module already owns the site (siteId, owner); a site has one owner |
| 409 | module_attach_owner_paths_required |
appPaths is omitted on a site another module attaches to or ships paths on (siteId, module) |
List modules
Returns the platform-wide public catalogue — every public module regardless
of authoring org — plus the calling organization’s own modules if it is an
authoring org.
GET /orgs/{organizationId}/modulesEach module also carries installedVersion (the version this organization has
installed, or null) and activationState (active, hidden, or null when
it is not installed).
Get a module
Fetches a single module by id, including its roots.
GET /orgs/{organizationId}/modules/{moduleId}Get a module’s source bundle
Returns the module’s full source bundle — every resource it bundles (app
sites, apps, workflows, tables, agents, knowledge bases) plus a presigned S3
URL per app artifact. This is the payload intellia modules project
projects into a portable YAML tree.
GET /orgs/{organizationId}/modules/{moduleId}/sourceActivation
Bringing a module into a consumer org runs through an activation
session — a stepped resource that moves preview → holes → configure
→ accepted → deploying → done/failed. See Modules
for the concept and the CLI reference for the
intellia modules activation / intellia modules activate commands.
A session has one of two snapshot origins:
registry— the session snapshots the module as currently registered in the catalogue.uploaded— the session snapshots a bundle supplied in thebeginrequest body (an uploaded, possibly Sammy-edited, module tree).
Get an install plan
Returns what installing moduleId would do, without doing any of it. members
is the module’s dependency chain in install order, the module last. Each member
has an action: install when the organization does not have it, upgrade
when it has an older version, and skip when it has the current one. A
dependency that is out of date is always upgraded, because its dependents expect
its current tables. holes lists every secret or connection the members that
run still need, once each, with the slugs of the members that need it.
GET /orgs/{organizationId}/modules/{moduleId}/activation-planBridges. A bridge module joins two optional modules (bridge: true in its
module.yaml). The module list returns it with bridge: true, and the in-app
catalogue leaves it out. The plan adds a bridge, after the chain, when this
install touches a module it joins and every module it depends on is in the plan
or installed. One installed at an older version is upgraded in the same plan,
ahead of the bridge. The bridge carries bridgeFor, the slugs it joins. When a
bridge joins this module to one the organization does not have, that module is returned in suggestions with the bridge and a reason
taken from the bridge’s description; when the bridge joins further modules the
organization lacks, alsoNeeds lists their slugs. Pass the slugs you choose in the include
query parameter (comma-separated, for example ?include=erp-sales) to get the
plan with each one’s chain and the bridges it completes.
Response:
{
"members": [
{ "moduleId": "...", "slug": "erp-base", "name": "ERP Base", "version": 7, "installedVersion": 7, "action": "skip", "derivedSlug": "", "holes": [] },
{ "moduleId": "...", "slug": "erp-sales", "name": "ERP Sales", "version": 6, "installedVersion": null, "action": "install", "derivedSlug": "erp-sales-acme", "holes": [{ "kind": "integration", "ref": "apn_123", "source": "workflow:invoice", "app": "xero" }] }
],
"holes": [
{ "kind": "integration", "ref": "apn_123", "source": "workflow:invoice", "app": "xero", "members": ["erp-sales"] }
],
"suggestions": []
}A dependency resolves as its dependent was published: a public module’s dependency is the public catalogue’s module of that slug, even when the organization has its own module of the same name; an organization’s own module’s dependency is its own module first.
Errors:
| Status | Message | When |
|---|---|---|
| 404 | module_not_found |
The module does not exist or is not visible to the organization |
| 409 | module_dependency_unavailable |
A module in its dependency chain is not available to the organization; slug names it |
| 400 | invalid_field |
field: "include": a slug in include is no longer one the plan offers, or something its chain needs is not available; slug names it. Drop it and ask again |
Begin an activation session
Starts a session for moduleId and returns its id plus a preview (module
summary, unresolved holes, and the derived app_site slug). Registry-origin
activation omits the body; uploaded-origin activation supplies bundle.
While another session is cloning the same module into the organization, or
waiting for the platform to retry a clone that failed, it returns
409 activation_in_progress with that session’s sessionId, and the version
and app-site slug it installs; watch that one instead of starting a second
clone when it is the install you want.
POST /orgs/{organizationId}/modules/{moduleId}/activationsRequest body (optional, uploaded origin only):
{
"bundle": { "module": { "...": "..." }, "workflows": [], "agents": [] }
}Response:
{
"sessionId": "7c1d44f0-3333-4d22-8c9f-222222222222",
"preview": {
"module": { "name": "Acme Portal", "description": "...", "version": 3 },
"holes": [],
"derivedSlug": "acme-portal"
}
}Get an activation session
GET /orgs/{organizationId}/activations/{sessionId}Returns the full ActivationSession, including its current step.
Recompute holes
Recomputes and returns the session’s unresolved holes (secrets/integrations
still required before accept will succeed).
POST /orgs/{organizationId}/activations/{sessionId}/holesConfigure an activation session
Sets the app_site slug and any params for the module being activated. slug
is omitted for workflow-only modules (no app_site to slug).
POST /orgs/{organizationId}/activations/{sessionId}/configureRequest body:
{
"slug": "acme-portal",
"params": { "region": "au" }
}Accept an activation session
Advances the session to accepted. This is a strict gate: it returns 409 activation_holes_unresolved if any holes remain unresolved.
POST /orgs/{organizationId}/activations/{sessionId}/acceptDeploy an activation session
Starts the async clone of the module’s resources into the org, moving the
session to deploying. Returns 409 activation_not_accepted if the session
hasn’t reached accepted yet, and 409 activation_in_progress with the running
session’s sessionId if another session is already cloning the same module
into the organization; this session is then failed, so it can never clone
beside that one. Poll GET .../activations/{sessionId}
to watch progress to done or failed.
POST /orgs/{organizationId}/activations/{sessionId}/deployResponse:
{ "ok": true }Hide / unhide an activated module
Toggle an activated module’s visibility for the org without deactivating it.
POST /orgs/{organizationId}/modules/{moduleId}/hide
POST /orgs/{organizationId}/modules/{moduleId}/unhideBoth return the org’s ModuleActivation record with its updated state
(hidden or active).