Overview

Artifacts v2 is the one store every Allternit surface reads and writes: Desktop, ai.allternit.com, the phone layout, the m.allternit.com PWA, cloud computers and gizzi. An artifact is something the assistant (or a person) made that you would show someone. Each one has a kind, an immutable list of versions, and sharing settings. The design contract is docs/design/artifacts-v2.md in allternit-platform.
This is a different API from the sectioned Artifacts API under /api/v1/artifacts, which lives on your data-plane node. Artifacts v2 is served by the hosted Cloud API under the separate /api/v2 prefix.

Base URL

Authentication

Every route except the public link route takes one of:
  • a Clerk session JWT (Authorization: Bearer <jwt>). This is the only caller that carries an organization, a role and an email;
  • an allternit_* API token with the compute scope (or *). A token without it gets 403;
  • a paired device’s token (allternit_runtime_…, what gizzi login stores). It acts as the device’s owner, with no organization.
See Authentication.

Kinds

When a create leaves out body_format, the kind’s default is used. The server also accepts well-formed kinds it doesn’t know (lower-case, at most 32 characters) so newer clients keep working. Apps show an unknown kind as a page when the body is HTML and as code otherwise. runtime_version is 2 for current artifacts. 1 marks legacy artifacts imported from the old local stores. A create with runtime_version: 1 must set origin.legacy_source.

Access

Your access to an artifact is the highest of: If you have no access, every route answers 404, so it doesn’t reveal that the artifact exists. If you can see it but lack the level an action needs, you get 403. Viewers and commenters see shared_version, or the latest version when it is null (“always share latest”). Editors and the owner see every version. An outside email invite counts as accepted the first time its invitee opens the artifact while signed in with that email. From then on it no longer expires.

Objects

ArtifactSummary

Artifact

An ArtifactSummary plus:

List artifacts

Returns { "items": [ArtifactSummary], "next_cursor": string | null }, newest updated_at first.

Create an artifact

Returns 201 with the Artifact (version 1). Optional fields: id, icon, template_id, origin, capabilities, body_format, meta, runtime_version.
  • Idempotent create. If you send an id you already own, you get that artifact back unchanged with 200. If someone else owns the id, you get 409 id_conflict. Client ids are art_ followed by letters, digits, _ or -, at most 128 characters. Legacy imports use art_legacy_<source>_<localId>.
  • Body limit. A body can be at most 16 MiB. A larger one gets 413 body_too_large.
  • Organization. The artifact belongs to the creator’s active Clerk organization, and its sharing follows that organization. If the org has turned artifacts off, the create gets 403 artifacts_disabled. If the org has turned connectors off, capabilities.connectors gets 422 connectors_disabled.

Get, update and delete

PATCH returns the Artifact. title and icon need edit. shared_version (from 1 to current_version, or null) and capabilities need owner. Turning on ai or connectors for an artifact shared by link gets 422 link_not_allowed. DELETE is owner-only and permanent. There is no trash. Versions, shares and comments are deleted with the artifact. It returns 204.

Dashboard body

A dashboard artifact’s body is JSON with body_format application/vnd.allternit.dashboard+json. The server stores it like any other body; the apps parse and validate it. A tile that fails validation is skipped and the rest of the dashboard still draws.
A tool result is read as a table: an array of objects, { "columns": [], "rows": [[]] }, or JSON or CSV text. Dashboards run as the viewer: each call goes through the viewer’s own connectors, and a tool that is not read-only is refused (a SQL tool is allowed for a single read-only statement). The owner’s client sets capabilities.connectors to the connectors and tools the tiles call, so visibility: "link" is refused for a dashboard (see Sharing). Dashboards saved before this format, with application/vnd.allternit.openui, still open as cards.

Versions

Appending needs edit and returns 201 with the Artifact. body_format defaults to the previous version’s. If base_version is not the current version, the server answers:
Fetch version 4, merge your change onto it, and append again with base_version: 4. Viewers only see the shared version, so asking them for any other version gets 404.

Sharing

Both routes are owner-only. PUT replaces the whole share list. Rows you leave out are removed, and rows you keep only change level. Both return a Sharing object:
link_url appears only when visibility is link. policy.reason explains why links are not allowed. The server sets the link base from ARTIFACTS_PUBLIC_BASE_URL (default https://ai.allternit.com/a).

Sharing rules

All of these failures return 422 with the code shown. The owner always has access, so a share naming the owner is dropped. If the same principal is listed twice, the higher level is kept.
This route returns {id, kind, title, icon, body, body_format, meta, version, updated_at, runtime_version, owner_name}. It works only while visibility is link and the policy still allows links. If an org turns off external sharing or artifacts, its links stop working at once. Viewers get shared_version, or the latest version when it is null. Every other case answers 404. Responses carry Cache-Control: private, no-store and X-Robots-Tag: noindex.

Organization settings

GET returns the settings for your active organization, plus org_id, updated_by, updated_at and can_edit. Personal accounts get the defaults with org_id: null. PUT changes only the fields you send. It needs the Clerk organization role admin (or org:admin). Everyone else gets 403. Admins can list what the org has shared outside with GET /api/v2/org/artifact-shared-outside. It returns {items: [{id, title, kind, owner_id, owner_name, by_link, outside_invites, allowed, updated_at}]} for artifacts that are public by link or have email invites, and needs the same admin role. allowed is whether the artifact is in allowed_external.

Page runtime

Pages that store data, call AI or use connectors have their own routes under /api/v2/artifact-runtime/{id}. See the Page runtime API.

Comments

Comments are threaded: a root comment plus replies one level deep. They are stored in artifact_comments. A comment has id, artifact_id, version, anchor, parent_id, author_id, author_name, author_image_url, body, to_assistant, resolved_at and created_at. body is 1 to 4000 characters. anchor is an opaque object up to 16 KB; the app writes { kind: "text" | "cell" | "slide" | "element", ... }. A comment that mentions @gizzi or @allternit is stored with to_assistant: true. Docs have no Commenter level, so a viewer of a doc can’t comment.

Presence

Presence is shared by every Cloud API instance. Each heartbeat upserts one row per artifact and user in artifact_presence (migration 086_artifact_presence.sql); the table holds only the current state and is not a viewing history. Send a heartbeat about every 15 seconds. GET returns the entries seen in the last 45 seconds, up to 200, ordered by user_id. left deletes the caller’s entry. Entries older than 10 minutes are deleted as a side effect of about one request in 50. When the owner’s organization turned presence off, GET returns { enabled: false, users: [] } and POST does nothing.

Errors

Errors use the standard Cloud API shape { "error", "message", "code" }. Artifact-specific codes are listed above. 401 means you are not signed in. 404 means the artifact doesn’t exist or you have no access.

Storage

The schema is cmd/allternit-cloud-api/migrations_pg/085_artifacts_v2.sql. It creates artifacts, artifact_versions, artifact_shares and org_artifact_settings, plus artifact_storage and artifact_consents (used by the page runtime) and artifact_comments for comments. The migration is idempotent and is applied to production by hand. The shared client uses src/styles/artifact-navigation.css for the Artifacts toolbar and inline mode selector. ArtifactsView keeps its title outside a nonwrapping, horizontally scrollable controls group; ModeDock uses nonshrinking buttons in the same single-row pattern. Both use --font-ui. Selection uses neutral translucent gradients and inset highlights rather than mode accent tokens, with solid surfaces for reduced transparency or increased contrast. This CSS is imported by the shared components used on Desktop, web and the phone PWA.