Skip to content

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.

Roles: Listing the catalogue, reading a module and reading its install plan are available to organization members with the 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}/modules

Request 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}/modules

Each 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}/source

Activation

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 the begin request 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-plan

Bridges. 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}/activations

Request 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}/holes

Configure 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}/configure

Request 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}/accept

Deploy 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}/deploy

Response:

{ "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}/unhide

Both return the org’s ModuleActivation record with its updated state (hidden or active).