Skip to content

Publish and install through the API

The concept behind publications is explained in Sharing and reuse.

A key holding the management capability does everything an administrator does in the console with a bundle: publish, install, sync, and detach a copied member (Manage organisations through the API shows how to switch that capability on). The routes here come in the same two shapes: a path with no organisation in it is your own organisation, and a path naming one (/organisations/{id}/...) is an organisation below you.

For a bundle kept as files in a repository, the artific command calls these routes for you: Release a bundle with the artific command.

Publishing

POST /publications creates the offer — a name, who may see it (visibility), and whether it is listed right away:

curl -s -X POST "$CORE_URL/publications" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"name": "starter-bundle", "visibility": "subtree", "status": "listed"}'

PATCH /publications/{pid} changes the description, and only the description: the name and the visibility stay as they were, and a body naming either is refused. On an app that is already listed in the marketplace the new text shows as pending_description on your own reads, and goes live when Artific next lists a version of the app after its review.

POST /publications/{pid}/versions publishes a version — from rows of your own library, or as a manifest handed over whole:

curl -s -X POST "$CORE_URL/publications/pub_4Kd9.../versions" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"members": [{"kind": "prompt", "id": "prm_9Fb2..."}]}'

GET /publications, GET /publications/{pid} and the covers beside them answer exactly what the console shows of your own offer; GET /publications/{pid}/entitlements shows who is entitled to it. POST .../submit asks Artific to review a catalogue publication; the review itself stays Artific's, as does the review queue of GET /catalogue.

Installing

GET /available-publications shows what your organisation may install; POST /subscriptions installs one:

curl -s -X POST "$CORE_URL/subscriptions" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"publication_id": "pub_4Kd9..."}'

A required parameter with no value leaves the subscription at needs_configuration rather than refusing it; PATCH /subscriptions/{sid} supplies the value afterwards, and PATCH .../secrets a credential on its own. A bundle that signs people in through an OAuth application this deployment has not configured waits at needs_configuration too, with last_error reading unconfigured_client:<name> (the preview lists the name under clients.unconfigured); ask your operator to configure it, then send PATCH /subscriptions/{sid} with the body {} to re-check and start the install. GET /subscriptions, GET .../{sid} and POST .../preview (what installing would mean, writing nothing) work the same way the console's do.

Syncing and forking

POST /subscriptions/{sid}/sync asks directly for the sync a new version, a new config or a new credential would otherwise cause on its own:

curl -s -X POST "$CORE_URL/subscriptions/sub_1Ac7.../sync" \
    -H "Authorization: Bearer $CORE_KEY"

When the subscription is not linked, diverged or stale, this answers 409 with a fixed code (sync_not_now:<status>) rather than a sentence. POST .../members/{member}/fork detaches one member from the publication so your organisation may change it; the name is the member's own in the manifest, never its id. DELETE /subscriptions/{sid} unsubscribes.

Prompts in a bundle

A prompt member's definition is the body of POST /prompts/{id}/versions: template, input_schema, role and install_values, plus the prompt's own metadata. A labels field is accepted and ignored; the copy arrives without labels. Publishing from rows ({"kind": "prompt", "id": "prm_..."} in members) takes the prompt's latest published version, never a draft, and turns its install values into parameters of the bundle, with your own value as the installer's default.

What an installed prompt copy carries

  • Name. The subscription's prefix followed by the member's name.
  • Metadata. The publisher's. Every sync that writes the copy (a new publication version, or a changed config) replaces the copy's metadata as a whole with the member's; a member without metadata leaves {}. While the copy is linked you cannot change it yourself: a new version of a linked copy is 409.
  • Labels. None. Labels never travel: the copy arrives unclassified, and no sync writes its labels.
  • Versions. A sequence of its own. Each sync that writes the copy adds its next version (1, 2, 3), whichever publication version produced it.

A sealed prompt copy

When the publisher withholds the content, the seal hides the template and the variables of each version installed under it, and nothing else. GET /prompts/{id} and the version routes answer such a version with:

Field Value
sealed true
template null
input_schema empty: {"type": "object", "properties": {}, "required": [], "additionalProperties": false}
install_values {}
role, version, state as installed

The prompt's name, metadata and origin stay readable too. A turn renders the version unchanged, so a sealed prompt can be run but not read. A sealed copy cannot be detached, and POST /prompts/{id}/copy refuses it with 403.

Scripts in a bundle

A script can be a member of a bundle like a prompt or a connector. Whoever installs it gets a copy in their own library, offered to their models as script__<prefix><name>.

What a script member holds

A script member's definition is the body of POST /scripts without the name and the labels: a copy arrives unclassified, as every copy does. The script's files do not travel inside the manifest. Each entry of files names a file by path, size_bytes and sha256, and the bytes are held beside the publication, by digest:

curl -s -X PUT "$CORE_URL/publications/pub_4Kd9.../files/$(sha256sum template.pptx | cut -d' ' -f1)" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/octet-stream" \
    --data-binary @template.pptx

The route checks that the bytes have the digest in the path (422 on sha256 otherwise), and holding the same file again writes nothing. GET on the same path answers whether the publication holds it already; 404 means you still have to upload it. The limits:

  • one file at most 32 MiB; a larger upload is 413;
  • a version's script files and carried packages together at most 128 MiB; past that the version is 422 on members;
  • all files of one publication together at most 1 GiB; past that an upload or a publish is 409.

Your operator can change them.

You rarely upload by hand:

  • Publishing from rows ({"kind": "script", "id": "scr_..."} in members) takes the script's latest version and copies its files onto the publication itself.
  • Publishing a manifest needs every file uploaded first. An entry whose digest the publication does not hold is 422 on members.<i>.definition.files.<j>.sha256.

A toolset, an alias or an agent in the same bundle names the script as {"$member": "house_deck", "kind": "script"}, which becomes the subscriber's own copy. A published version's files are readable again at GET /publications/{pid}/versions/{v}/members/script/{member}/files/{path}.

Packages in a bundle

What happens to a package a script names depends on who approved it:

  • A library in the image, or a package your operator approved for the deployment, travels by name only. The subscriber is on the same deployment, so its copy finds the same approval.
  • A package only your organisation approved travels with the bundle: the member names it with its file (ecosystem, name, version, filename, size_bytes, sha256), and the file is held beside the publication like the script's own files. Publishing from rows copies it for you.

The subscriber sees these packages before installing: the preview's packages lists each one with its ecosystem, name, version, file and digest, even when the bundle withholds its content. Installing is the approval. The package is installed for that copied script alone, shown with approval: bundle on the copy's versions, and never added to the subscriber's own list of approved packages. Their administrators still decide what their own scripts may use.

A later version in which a script brings a package it did not bring before (a new name, a new version, or other bytes) waits for the subscriber, as changed terms do: the subscription goes to needs_acceptance and the copy keeps running what it ran. Preview that version first, with the subscription's own prefix and config so the report matches your install, then accept:

curl -s -X POST "$CORE_URL/subscriptions/preview" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"publication_id": "pub_4Kd9...", "follow": "pinned", "pinned_version": 4,
         "prefix": "acme-", "config": {"company": "Acme"}}'

curl -s -X PATCH "$CORE_URL/subscriptions/sub_1Ac7..." \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"accept_terms_version": 4}'

A version that drops a package, or brings only ones already accepted, installs like any other.

Installing a script

A bundle with a script needs tooling and computer in the subscriber's organisation. When the organisation is not permitted computer, the subscription is refused 422: no key there could run the script.

  • Linked. While the copy follows the publication, it is the publication's to change: a new version or a delete of the copy is 409, naming the publication. Each new publication version that changes what the script runs becomes a new version of the copy.
  • Forked. POST .../members/{member}/fork detaches the copy, when the terms allow it. From then on it is an ordinary script of yours and follows no new versions.
  • Sealed. When the publisher withholds the content, the copy can be run but not read: its reads answer the name, the description and the parameters, with entrypoint as null and files empty, and its file route answers 404. A sealed copy cannot be forked or published again. In a turn it runs on a computer of its own, separate from the computer the model's shell tools use, so nothing in the turn can read its files; that second computer is billed as sandbox time like the first.

For an organisation below you

Every route above also exists under /organisations/{id}/..., for an organisation you reach with management:

curl -s -X POST "$CORE_URL/organisations/ten_9Fb2.../subscriptions" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"publication_id": "pub_4Kd9..."}'

This is a management operation over that organisation's library — the same kind of thing creating a key there is — not a way to read its prompts, skills or knowledge directly: that stays a key you register inside it (Work inside a child).

The marketplace

GET /marketplace shows what Artific has approved for the whole deployment — a different path from the console's GET /catalogue, because every route here shares one path space and that path already exists. The review queue itself (?status=submitted in the console) is not there: that stays Artific's alone.