> ## Documentation Index
> Fetch the complete documentation index at: https://docs.crewship.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Artifacts Commands

> Create, version, share, comment on and export Artifacts from the CLI, including dashboard data, Slides and the workspace artifact policy.

# Artifacts from the CLI

An artifact is a private, multi-file HTML project with an immutable version
history. Every save stores a complete file manifest as a new numbered
version; nothing is edited in place. You can pin one version as the shared
version, attach comments to an element of an exact version, keep JSON data
for a dashboard next to the code, and read or export a deck that follows the
Slides profile.

The commands live under `crewship page artifact`, because an artifact is
stored on a Page record. The full flag reference stays on
[Page Commands](https://docs.crewship.ai/cli/page.md); this page is the task guide, with output recorded
from a real workspace. Design canvases (`crewship page design`) and Craft
documents (`crewship craft`) have their own commands and are not covered here.

```bash theme={null}
crewship page artifact <subcommand> [flags]
crewship workspace artifact-policy show|set [flags]
```

## Before you start

Each group of commands needs a workspace feature flag, which an OWNER or
ADMIN turns on with [`crewship feature-flag enable`](https://docs.crewship.ai/cli/feature-flag), or an
instance switch set to `true` in the server environment. `artifact_projects`
is on by default; `artifact_dashboard_data` and `artifact_slides` are off:

| Commands | Workspace flag | Instance switch |
| - | - | - |
| All `page artifact` commands except `archive`/`unarchive` | `artifact_projects` | `CREWSHIP_ARTIFACT_PROJECTS_ENABLED` |
| `page artifact data …` | `artifact_projects` and `artifact_dashboard_data` | `CREWSHIP_ARTIFACT_DASHBOARD_DATA_ENABLED` |
| `page artifact slides`, `page artifact export-pdf` | `artifact_projects` and `artifact_slides` | `CREWSHIP_ARTIFACT_SLIDES_ENABLED` |

The `CREWSHIP_ARTIFACT_DASHBOARD_DATA_ENABLED` and
`CREWSHIP_ARTIFACT_SLIDES_ENABLED` switches replace only the
`artifact_dashboard_data` and `artifact_slides` flags. Data and Slides still
need `artifact_projects` (or `CREWSHIP_ARTIFACT_PROJECTS_ENABLED`) to be on.

```bash theme={null}
crewship feature-flag enable artifact_dashboard_data
crewship feature-flag enable artifact_slides
crewship feature-flag disable artifact_projects   # turn Projects off for this workspace
```

When `artifact_projects` is off and its bypass is not set, `availability` returns
`{"enabled": false}`. It does not report the Dashboard or Slides sub-flags:
it can still return true when those are off. A command whose required flag
is off fails with `404` (`artifact projects are not enabled` or
`dashboard data is disabled`). The CLI itself needs no web build-time
variable; the `url` that commands return, `/artifacts/<page-id>?workspace_id=<workspace-id>`,
opens the artifact in the web app. Older `/pages/<slug>?artifact=1` links
still work: with the Artifacts library in the build they redirect to
`/artifacts/<page-id>` with the same workspace and version; without it they
open the Pages project viewer. `crewship doc create` returns the same
canonical `url` for a new document.

Credentials and roles:

* A human CLI token with the `pages:read` scope can read; saving, commenting,
  restoring, sharing and data writes need `pages:write`.
* Creating an artifact needs the OWNER, ADMIN or MANAGER role, or a MEMBER
  with the page-create capability.
* Restore, share, replies, retained history and archive belong to the artifact owner
  (or an authorized workspace admin for archive).
* Saving a version and data writes are open to the owner and to anyone with an
  Edit share, always against the latest version. Read and Comment shares are read-only.
* `workspace artifact-policy` needs OWNER or ADMIN and a token with the
  `workspace:admin` scope.
* Someone who has no grant on a private artifact receives `404`, not `403`,
  so the artifact's existence is not revealed.

## Output and errors

Artifact commands always print the server's JSON document. The default
`table` format and `quiet` both print indented JSON; `-f yaml` and
`-f ndjson` re-encode the same document. There is no separate `--json` flag.
`archive` and `unarchive` are the exception: `table` prints the raw compact
JSON line and `quiet` prints nothing.

Errors go to standard error. With `-f json` the error is a JSON envelope
whose `extensions.code` carries the machine-readable reason:

```json theme={null}
{
  "error": {
    "message": "API error (409): This project changed. Read the current version and compare your changes before publishing again. Your draft has not been discarded.",
    "status": 409,
    "detail": "This project changed. …",
    "exit_code": 5,
    "extensions": {
      "code": "artifact_version_conflict",
      "error": "This project changed. …"
    }
  }
}
```

| HTTP | Exit | `code` | When |
| - | - | - | - |
| 400 / 422 | 2 | `artifact_slides_unsupported` (422) | Invalid request; a version outside the Slides profile |
| 401 / 403 | 4 | | Missing scope or role, for example a non-admin reading the policy |
| 404 | 3 | | Unknown artifact, version, comment or undeclared collection, or a feature flag is off |
| 409 | 5 | `artifact_version_conflict` | `--base-version` is no longer the latest version, an owner comment targets an older version, or a `pin set` review is stale |
| 409 | 5 | `artifact_library_pin_corrupt` | The stored pin no longer matches its SHA-256; pin the Library again |
| 409 | 5 | `artifact_slug_taken` | `create` with a slug that is already used |
| 409 | 5 | `artifact_anchor_mismatch` | The comment quote does not match the selected element |
| 409 | 5 | `artifact_data_conflict` | A document's `ifVersion` is stale |
| 409 | 5 | `stale_policy` | `artifact-policy set` with an old `--base-version` |
| 413 | 2 | `artifact_history_quota`, `artifact_storage_quota` | Version history or workspace storage is full; nothing was saved |
| 413 | 1 | `artifact_data_quota`, `artifact_slides_limit` | Dataset or Slides limits are full |
| 422 | 2 | `artifact_slides_overflow` | Slide content does not fit its frame |

A conflict or quota error never discards anything: earlier versions and data
are kept, and the CLI never retries with a newer base. Keep your local file,
read the current state and decide what to save. Validation that happens in the
CLI before any request (a missing, unknown or invalid flag, a manifest that
cannot be read, is not valid or is over 9 MiB) exits with `2`. One exception:
the checks `page artifact data` and the Slides commands run after parsing their
flags, such as a data batch without `--file`, exit with `1`. It prints the
message only, or with `-f json` an envelope without `status` or `extensions`:

```json theme={null}
{"error": {"message": "--base-version must be the exact positive latest version; use create for a new project", "exit_code": 2}}
```

## Create and save versions

### `crewship page artifact availability`

Read whether artifact projects are enabled in the current workspace.

```bash theme={null}
crewship page artifact availability -f json
```

```json theme={null}
{"enabled": true}
```

| Flag | Description |
| - | - |
| `--agent <agent-id>` | Also report whether you may create an artifact project for this agent. |

With `--agent`, the reply adds `can_create`. It is `true` only when artifact
projects are enabled, the agent's crew is allowed by the
[agent publishing policy](#agent-publishing-policy) and you may create Pages
in the workspace. When the crew is not allowed, or the agent is not in this
workspace, the reply also carries `"code": "artifact_crew_not_allowed"`:

```bash theme={null}
crewship page artifact availability --agent <agent-id> -f json
```

```json theme={null}
{"enabled": true, "can_create": false, "code": "artifact_crew_not_allowed"}
```

### `crewship page artifact storage`

Read the workspace's aggregate artifact storage: bytes used by every artifact
family, bytes reserved by uploads in flight, the limit, and warning levels.
Only workspace owners and admins can run it; other members get `403`.

```bash theme={null}
crewship page artifact storage -f json
```

```json theme={null}
{"used_bytes": 734003200, "reserved_bytes": 0, "limit_bytes": 34359738368, "level": "none", "instance_level": "none", "retention": "keep-all"}
```

When `level` reaches `full`, saves fail with `artifact_storage_quota`: nothing
is saved, your `--file` input is unchanged and every existing version is kept.
History is never deleted automatically.

### `crewship page artifact create <slug>`

Create a new artifact and save its first version. `--file` is a JSON manifest
with `entrypoint`, `files` and an optional `message`; each file has `path`,
`media_type` and UTF-8 `content`. Use `--file -` to read standard input. The
manifest is limited to 9 MiB, and other top-level keys are rejected.

| Flag | Description |
| - | - |
| `--name <title>` | Artifact title (required). |
| `--file <path>` | Complete manifest; `-` reads stdin (required). |
| `--test-item` | Mark the new artifact as a test item. Human credentials only. |

```json theme={null}
{"entrypoint":"index.html","message":"First version","files":[
  {"path":"index.html","media_type":"text/html","content":"<h1 id=\"hero\">Quarterly review</h1><a href=\"details.html\">Details</a>"},
  {"path":"details.html","media_type":"text/html","content":"<h1 id=\"details\">Details</h1><a href=\"index.html\">Home</a>"}]}
```

```bash theme={null}
crewship page artifact create quarterly-review --name "Quarterly review" --file manifest.json
```

```json theme={null}
{
  "page_id": "<page-id>",
  "slug": "quarterly-review",
  "url": "/artifacts/<page-id>?workspace_id=<workspace-id>",
  "version": 1
}
```

Creating the same slug again fails with `409 artifact_slug_taken`.

### `crewship page artifact publish <slug>`

Save a complete new manifest as the next version. `--base-version` must be the
latest version you read; there is no partial update.

| Flag | Description |
| - | - |
| `--base-version <n>` | Exact latest version read before editing (required, at least 1). |
| `--file <path>` | Complete manifest; `-` reads stdin (required). |

```bash theme={null}
crewship page artifact publish quarterly-review --base-version 1 --file manifest.json
```

```json theme={null}
{
  "actor": "<user-id>",
  "page_id": "<page-id>",
  "slug": "quarterly-review",
  "url": "/artifacts/<page-id>?workspace_id=<workspace-id>",
  "version": 2
}
```

Running the same command again, still with `--base-version 1`, returns
`409 artifact_version_conflict` and exit code `5`.

As an **alternative to the publish command above**, read the current version,
build a manifest from it and publish with `.latest_version` as the base. Run
only one of the two publish examples after creating version 1. The remaining
examples assume this read/edit/publish recipe produced version 2 with the
message "Shorter title"; running both recipes would create version 3 and
require newer base/version numbers below. The `manifest` object that `get` returns
also carries `created_at`, `restored_from` and `version`, which the CLI
rejects (`unknown manifest field "restored_from"`), so copy only `entrypoint`
and `files` and add your own `message`:

```bash theme={null}
crewship page artifact get quarterly-review -f json > snapshot.json
jq '{entrypoint: .manifest.entrypoint, files: .manifest.files, message: "Shorter title"}' \
  snapshot.json > manifest.json
# edit the content of the files in manifest.json
crewship page artifact publish quarterly-review \
  --base-version "$(jq -r '.latest_version' snapshot.json)" --file manifest.json
```

A user with an explicit Edit grant can publish under their own identity;
owner history stays private.

### `crewship page artifact get <slug>`

Read the artifact snapshot: the manifest of the latest version (or of
`--version`), the version list, comments, your permissions and storage usage.

| Flag | Description |
| - | - |
| `--version <n>` | Exact retained version; `0` (default) reads the latest. |

```bash theme={null}
crewship page artifact get quarterly-review -f json
```

```json theme={null}
{
  "can_comment": true, "can_restore": true, "can_send_to_agent": false,
  "can_share": true, "can_write": true,
  "comments": [],
  "enabled": true,
  "latest_version": 2,
  "manifest": {
    "created_at": "2026-10-30T00:21:29Z",
    "entrypoint": "index.html",
    "files": [{"path": "index.html", "media_type": "text/html", "content": "…"}],
    "message": "Shorter title",
    "restored_from": 0,
    "version": 2
  },
  "name": "Quarterly review",
  "origin": null,
  "page_id": "<page-id>",
  "quota": {"limit_bytes": 268435456, "retention": "keep-all", "used_bytes": 1139, "warning": false},
  "shared_version": 0,
  "slug": "quarterly-review",
  "versions": [
    {"actor": "<user-id>", "created_at": "…", "entrypoint": "index.html", "message": "Shorter title", "restored_from": 0, "version": 2},
    {"actor": "<user-id>", "created_at": "…", "entrypoint": "index.html", "message": "First version", "restored_from": 0, "version": 1}
  ]
}
```

`quota` shows history usage. Every version is kept (`keep-all`); when
`used_bytes` reaches `limit_bytes`, new versions fail with
`413 artifact_history_quota` and existing versions stay. No CLI command changes
the quota or prunes versions.

Readers with a Read or Comment grant see only the shared version.

### `crewship page artifact versions <slug>`

Print the same snapshot as `get` for the latest version. Use the `versions`
array in it; pass `get --version <n>` to read an older manifest.

```bash theme={null}
crewship page artifact versions quarterly-review -f yaml
```

## Restore and share

### `crewship page artifact restore <slug>`

Copy a retained version into a new latest version. History is never rewritten.
Owner only.

| Flag | Description |
| - | - |
| `--base-version <n>` | Exact latest version (required). |
| `--version <n>` | Retained version to restore (required). |

```bash theme={null}
crewship page artifact restore quarterly-review --base-version 2 --version 1
```

```json theme={null}
{"actor": "<user-id>", "page_id": "<page-id>", "slug": "quarterly-review", "url": "…", "version": 3}
```

The new version has `"restored_from": 1` and the message `Restore version 1`.
An unknown `--version` returns `404`.

### `crewship page artifact share <slug>`

Pin an existing version as the shared version. People with a Read or Comment
grant see only this version; the latest version is unchanged. Owner only.

| Flag | Description |
| - | - |
| `--base-version <n>` | Exact latest version (required). |
| `--version <n>` | Version to share (required). |

```bash theme={null}
crewship page artifact share quarterly-review --base-version 3 --version 2
```

```json theme={null}
{
  "shared_version": 2,
  "url": "/artifacts/<page-id>?version=2&workspace_id=<workspace-id>"
}
```

The share `url` opens the shared version: a reader of that link sees version 2,
never the latest one.

A stale `--base-version` returns `409 artifact_version_conflict`.

## Library publication and pins

A Library is an artifact project with a `design-system.json`. Its owner
publishes one version; other projects in the workspace pin a self-contained
copy of that version's tokens. Editing, restoring or deleting the Library, or
losing access to it, never changes a pinned project.

### `crewship page artifact library show <slug>`

Read the published version. Any reader of the Library may call it.

```json theme={null}
{"published_version": 2, "published_by": "<user-id>", "published_at": "2026-11-09T01:00:00.000Z", "can_publish": true}
```

`published_version` is `0` while nothing is published. `can_publish` is true
only for the Library's owner.

### `crewship page artifact library publish <slug>`

Publish a retained version. **Owner only**: editors save draft versions but
cannot publish, workspace owners and admins keep their audited read access but
cannot publish someone else's Library, and agent runs cannot publish. The
version must contain `design-system.json`, a JSON object of at most 32 KiB.

| Flag | Description |
| - | - |
| `--version <n>` | Retained version to publish (required). |

```bash theme={null}
crewship page artifact library publish brand --version 2
```

Publishing the version already published is a no-op. A non-owner gets `403`.

### `crewship page artifact pin set <slug>`

Pin the Library's published version into project `<slug>`, or update the pin
to a newer publication. You need Edit on the project, and you must be able to
read the published version through your own access: the owner reads any
version, an editor of the Library only its latest version, everyone else only
its shared version. The workspace owner and admin read path does **not** count:
an administrator can read a private Library (with an audit record), but cannot
turn that read into a copy. Agent runs cannot pin.

Every flag below describes the state you reviewed. If anything differs when the
request arrives (another published version, another pinned Library or pinned
version), nothing changes and you get `409 artifact_version_conflict`. The
same applies when `--if-pinned-library` names a slug that no longer resolves
(the Library was renamed or deleted): pass `@<library_page_id>` from `pin show`
instead.

| Flag | Description |
| - | - |
| `--library <slug or @id>` | Library in this workspace (required). |
| `--version <n>` | Published Library version you reviewed (required), as `pin check` or `library show` reported it. |
| `--if-pinned-library <slug or @id>` | Library currently pinned in the project, empty when nothing is pinned. Required exactly when `--if-pinned-version` is not `0`. |
| `--if-pinned-version <n>` | Library version currently pinned, `0` when nothing is pinned (required). |

```bash theme={null}
crewship page artifact pin set pitch-deck --library brand --version 1 --if-pinned-version 0
crewship page artifact pin set pitch-deck --library brand --version 2 --if-pinned-library brand --if-pinned-version 1
```

```json theme={null}
{"library_page_id": "<page-id>", "library_version": 1, "library_name": "Brand", "sha256": "…", "copied_by": "<user-id>", "copied_at": "…", "tokens": {"colors": {"primary": "#1d4ed8"}},
 "files": [{"path": "fonts/Inter.woff2", "media_type": "font/woff2", "status": "copied", "size": 48212, "sha256": "…", "family": "Inter", "weight": 400, "style": "normal", "licence": "OFL-1.1"}], "changed": true}
```

Pinning the version already pinned returns `"changed": false`. A Library you
cannot read, one with nothing published and one that does not exist all return
the same `404`.

**What a pin shares.** The copy (the tokens, the Library's name at the time,
and the font and licence files the pin carries) belongs to the project. Everyone who can read the project can read it with
`pin show`, including people who cannot read the Library. The copy stays when
the Library is unshared, made private or deleted. Pin only a Library whose
tokens every reader of the project may see. There is no unpin command yet.

### `crewship page artifact pin show <slug>`

Read the pinned copy. Any reader of the project may call it; the Library is not
read, so the copy stays available after the Library changes or your access to
it ends. The SHA-256 is of the stored source bytes and is checked on every read. The response carries tokens as JSON, whose whitespace and escaping may differ. A copy that no longer
matches is never served and returns `409 artifact_library_pin_corrupt` ("The
pinned copy is damaged; pin the library again."). `pin check` reports the same
error. An editor who still has read access to the reviewed publication can run
`pin set` with that publication and the previous pin’s Library and version.
The server replaces the damaged copy only when that reviewed pin still matches.

Project previews render from this copy: see
[Previews render from the pin](https://docs.crewship.ai/guides/artifacts/library.md#previews-render-from-the-pin).

### `crewship page artifact pin files <slug>`

Read the font and licence files the pin carries, bytes in base64. Any reader of
the project may call it; the Library is not read. A font left behind is listed
with `status` `licence` (no redistributable licence) or `too_large` (over
1 MiB, or past 4 MiB for the pin, licence files included) and empty `content`. Each copy is checked
against its SHA-256; a damaged one returns `409 artifact_library_pin_corrupt`.

```json theme={null}
{"files": [
 {"path": "fonts/Inter.woff2", "media_type": "font/woff2", "status": "copied", "size": 48212, "sha256": "…", "family": "Inter", "weight": 400, "style": "normal", "licence": "OFL-1.1", "content": "d09GMg…"},
 {"path": "fonts/OFL.txt", "media_type": "text/plain", "status": "copied", "size": 4400, "sha256": "…", "content": "Q29weXJp…"},
 {"path": "fonts/Brand.woff2", "media_type": "font/woff2", "status": "licence", "size": 0, "sha256": "", "family": "Brand", "content": ""}]}
```

How a Library stores fonts and which licences count:
[What a pin carries](https://docs.crewship.ai/guides/artifacts/library.md#what-a-pin-carries).

### `crewship page artifact pin check <slug>`

Review an update before applying it.

```json theme={null}
{"pinned_version": 1, "published_version": 2, "library_available": true, "update_available": true,
 "changes": {"added": ["colors.accent"], "removed": [], "changed": ["colors.primary"]},
 "file_changes": {"added": ["fonts/Inter.woff2", "fonts/OFL.txt"], "removed": [], "changed": []},
 "not_copied": ["fonts/Brand.woff2:licence"], "dependencies_missing": false}
```

`update_available` is true when the Library's published version differs from
the pinned one, including when the owner republished an older version. Paths
are dot-joined token keys; each list holds at most 200 entries and
`truncated` says when one was cut. `file_changes` compares the carried font
and licence files by path and content, and `not_copied` names the published
version's fonts an update would leave behind, as `path:reason`.
`dependencies_missing` is true for a pin made before fonts travelled: it holds
the published version but none of its files, `update_available` is true too,
and `pin set --version <pinned_version>` copies the fonts without changing the
tokens. When you can no longer read the Library,
`library_available` is false and nothing about it is reported. Apply a reviewed
update with `pin set --version <published_version> --if-pinned-library <library> --if-pinned-version <pinned_version>`.

## Comments

### `crewship page artifact comment <slug>`

Add a comment anchored to an element of an exact version. The owner comments
on the latest version; a comment on an older version returns
`409 artifact_version_conflict`. `--quote` must match the element's text, or
the server returns `409 artifact_anchor_mismatch`.

| Flag | Description |
| - | - |
| `--version <n>` | Version the comment refers to (required). |
| `--path <file>` | File in the manifest, for example `index.html` (required). |
| `--selector <css>` | Selector of the element, preferably an `id` (required). |
| `--quote <text>` | Current text of the element (required). |
| `--text <text>` | Comment text (required). |
| `--send-to-agent` | Also queue the comment as a task for the agent. |
| `--wait` | With `--send-to-agent`: deliver the task to the artifact's source chat, then wait for the agent's answer. |
| `--wait-timeout <duration>` | With `--wait`: stop waiting after this long, for example `5m` (default `15m`). The task stays queued. |

A comment has no idempotency key. If the reply is lost, run
`crewship page artifact comments <slug>` before sending it again: a retry after
a commit stores a second comment.

```bash theme={null}
crewship page artifact comment quarterly-review --version 3 --path index.html \
  --selector '#hero' --quote 'Quarterly review' --text 'Add the year to the title'
```

```json theme={null}
{
  "author": "<user-id>", "author_kind": "human",
  "created_at": "2026-10-30T00:21:50Z",
  "delivery_retryable": false,
  "id": "<comment-id>",
  "path": "index.html", "quote": "Quarterly review", "selector": "#hero",
  "replies": [], "status": "comment",
  "text": "Add the year to the title",
  "version": 3
}
```

Queuing with `--send-to-agent` does not confirm that an agent received the
task.

`--wait` does what the viewer does after you send a comment to the agent. It
posts the task message into the chat the artifact was created from, confirms
that the chat saved it, then reads the comment every two seconds. Progress goes
to stderr. When the comment becomes `answered` or `needs_clarification`, the
command prints the comment with its replies. Run it as a person: an agent run
cannot dispatch artifact tasks.

```bash theme={null}
crewship page artifact comment quarterly-review --version 3 --path index.html \
  --selector '#hero' --quote 'Quarterly review' --text 'Add the year to the title' \
  --send-to-agent --wait --wait-timeout 10m
```

The comment is saved and queued before the wait starts, so a failure later on
leaves it queued:

* The artifact has no source chat: exit code 2. It was not created from an
  agent chat.
* The task message could not be delivered, for example because the agent is
  busy: the command prints a `crewship run … --artifact-target` command that
  retries the delivery.
* The timeout passes first: the command says so and exits non-zero. Read the
  result later with `crewship page artifact comments`.

### `crewship page artifact comments <slug>`

Read comments with the artifact snapshot. The output has the same shape as
`get`, with the comments of the selected version in `comments`.

| Flag | Description |
| - | - |
| `--version <n>` | Version whose comments to read; `0` (default) reads the latest. |

```bash theme={null}
crewship page artifact comments quarterly-review --version 3
```

### `crewship page artifact reply <slug> <comment-id>`

Answer a comment. A normal reply describes a change, so `--version` must be a
version newer than the comment's version. With `--needs-clarification` you
ask the author to select the element again, and `--version` may be the
comment's own version. Any other combination returns `404`.

| Flag | Description |
| - | - |
| `--text <text>` | Reply text (required). |
| `--version <n>` | Version the reply addresses (required). |
| `--needs-clarification` | Ask for a fresh selection instead of claiming a change. |

```bash theme={null}
crewship page artifact reply quarterly-review <comment-id> --version 3 \
  --text "Which year format?" --needs-clarification
crewship page artifact publish quarterly-review --base-version 3 --file manifest.json
crewship page artifact reply quarterly-review <comment-id> --version 4 \
  --text "Title now includes the year"
```

```json theme={null}
{"author": "<user-id>", "author_kind": "human", "created_at": "…", "text": "Title now includes the year", "version": 4}
```

## Dashboard data

Requires `artifact_dashboard_data` as well as `artifact_projects`. A version
declares its collections in a `dashboard.json` file in the manifest, with the
media type `application/json` and the content
`{"schema_version":1,"collections":["sales"]}`. The examples below use an
artifact created from this manifest:

```json theme={null}
{"entrypoint":"index.html","message":"Dashboard","files":[
  {"path":"index.html","media_type":"text/html","content":"<h1 id=\"title\">Sales</h1>"},
  {"path":"dashboard.json","media_type":"application/json","content":"{\"schema_version\":1,\"collections\":[\"sales\"]}"}]}
```

```bash theme={null}
crewship page artifact create sales-dashboard --name "Sales dashboard" --file dashboard-manifest.json
```

Data is versioned per document, separately from code versions. Writing data
does not create a code version, and restoring code does not rewind data.
Every read names an exact code version, and only collections declared by that
version are reachable. The owner and Edit-share holders read and write at the
latest version; a batch whose `base_version` is not the latest version returns
`409` and writes nothing. Shared readers (Read or Comment) can read the
collections of the shared version and cannot write (`403`).

Limits: 256 KiB per document, 50 writes and 1 MiB per batch, 4096 documents
and 8 MiB per collection, 32 collections per artifact. Collection and document
IDs are 1–128 ASCII letters, digits, `_` or `-`, starting with a letter or
digit. Values are JSON objects.

### `crewship page artifact data batch <slug> <collection>`

Apply up to 50 document writes atomically. `ifVersion: 0` creates a document;
an update gives the document's current version. `base_version` is the current
code version.

| Flag | Description |
| - | - |
| `--file <path>` | JSON batch `{base_version, documents:[{id, ifVersion, value}]}`; `-` reads stdin (required). |

```json theme={null}
{"base_version":1,"documents":[{"id":"north","ifVersion":0,"value":{"revenue":1250}}]}
```

```bash theme={null}
crewship page artifact data batch sales-dashboard sales --file batch.json
```

```json theme={null}
{
  "collection": "sales",
  "code_version": 1,
  "documents": [
    {"id": "north", "version": 1, "value": {"revenue": 1250},
     "updated_at": "2026-10-30T00:22:07Z", "source_label": "You", "source_kind": "human"}
  ]
}
```

Sending the same batch again returns `409 artifact_data_conflict` and nothing
is written. A document over 256 KiB is refused by the CLI before sending
(`artifact dataset quota exceeded`); the server returns
`413 artifact_data_quota` for the same request.

### `crewship page artifact data get <slug> <collection>`

Read every document of one collection at an exact code version.

| Flag | Description |
| - | - |
| `--version <n>` | Exact code version that declares the collection (required). |

```bash theme={null}
crewship page artifact data get sales-dashboard sales --version 1
```

The output has the same shape as `batch`. A collection that the version does
not declare returns `404`.

### `crewship page artifact data collections <slug>`

List the declared collections that already hold documents, with their size.
A declared collection with no documents is not listed.

| Flag | Description |
| - | - |
| `--version <n>` | Exact code version (required). |

```bash theme={null}
crewship page artifact data collections sales-dashboard --version 1 -f ndjson
```

```json theme={null}
{"code_version":1,"collections":[{"name":"sales","documents":1,"bytes":16}]}
```

## Slides

Requires `artifact_slides` as well as `artifact_projects`. A deck is an
ordinary artifact version that follows a closed Slides profile. Content
outside the profile returns `422 artifact_slides_unsupported`. Both commands
need an exact `--version` (at most 9007199254740991); owners can read any
retained version, Edit grants the latest version, and readers only the shared
version.

This manifest creates a two-slide deck with every supported element:

```json theme={null}
{"entrypoint":"slides/intro.html","message":"Deck","files":[
  {"path":"deck.json","media_type":"application/json","content":"{\"schema_version\":1,\"order\":[\"intro\",\"summary\"]}"},
  {"path":"slides/intro.html","media_type":"text/html","content":"<section id=\"intro\"><p id=\"intro-title\" style=\"left:80px;top:80px;width:1600px;height:180px;font-size:48px;color:#112233;text-align:left\">Q3 review</p><div id=\"intro-bar\" data-shape=\"rectangle\" style=\"left:80px;top:280px;width:400px;height:12px;background-color:#2255AA;border-color:#2255AA;border-width:0px\"></div><aside id=\"intro-notes\">Open with the headline number.</aside></section>"},
  {"path":"slides/summary.html","media_type":"text/html","content":"<section id=\"summary\"><table id=\"summary-table\" style=\"left:80px;top:80px;width:1200px;height:240px;font-size:32px;color:#111111;background-color:#FFFFFF;border-color:#CCCCCC\"><tbody><tr><td>Region</td><td>Revenue</td></tr><tr><td>North</td><td>1250</td></tr></tbody></table></section>"}]}
```

```bash theme={null}
crewship page artifact create quarterly-deck --name "Quarterly deck" --file deck-manifest.json
```

The profile rules:

* The manifest holds exactly `deck.json` (`application/json`) and one
  `slides/<id>.html` file (`text/html`) for each ID in `order`, and no other
  files. `deck.json` has only `schema_version: 1` and `order` (1–100 slide
  IDs).
* A slide file is a single `<section id="<id>">` whose ID matches its file
  name. Its children are `p`, `div`, `table` and at most one `aside`, with no
  further nesting; headings, images, `span` and other tags are rejected.
* Every element has a unique `id` (a letter, then up to 63 letters, digits,
  `_` or `-`) and a `style` attribute with exactly the properties below; any
  other attribute or property, including `position`, is rejected.

| Element | Becomes | Required `style` properties |
| - | - | - |
| `<p>` with plain text | Text | `left`, `top`, `width`, `height`, `font-size`, `color`, `text-align` |
| Empty `<div>` with `data-shape` set to `rectangle` or `ellipse` | Shape | `left`, `top`, `width`, `height`, `background-color`, `border-color`, `border-width` |
| `<table><tbody><tr><td>` with plain-text cells | Table | `left`, `top`, `width`, `height`, `font-size`, `color`, `background-color`, `border-color` |
| `<aside id="…">`, last in the section | Speaker notes | none (`id` only) |

Sizes are whole pixels (`80px`), colors are six-digit hex (`#112233`),
`text-align` is `left`, `center` or `right`, `font-size` is 1–160 pixels and
`border-width` 0–16 pixels. `font-family` is optional on text and tables and
accepts only `Go Regular` or `sans-serif`. The canvas is 1920 × 1080.

#### Rich profile (optional)

A deck may also use the additions below. A deck that uses any of them is
returned with `schema_version: 3`; a deck that uses none keeps the
`schema_version: 1` projection byte for byte. Each addition has a fixed syntax
and anything else is refused with `422 artifact_slides_unsupported`.

| Where | Addition | Accepted values |
| - | - | - |
| `<p>`, `<table>` | `font-family` | Exactly one of `Go Regular`, `IBM Plex Sans`, `JetBrains Mono` (bare, or in single or double quotes). Fallback lists such as `'IBM Plex Sans', Arial` are refused. |
| `<p>` | `font-weight` | `400`, `600` or `700` where the face has it: IBM Plex Sans 400/600/700, JetBrains Mono 400/600, Go Regular 400. |
| `<p>` | `letter-spacing` | Whole pixels from `-20px` to `100px`, and no less than minus half the font size; added after every character. |
| `<p>` | `line-height` | A unitless number with up to two decimals from `0.8` to `3` (default `1.2`). |
| `<p>`, `<table>` | `font-size` | Up to 320 pixels (160 without the rich profile). |
| `<div data-shape="rectangle">` | `border-radius` | Whole pixels, at most half the shorter side. |
| `<div data-shape>` | `background-image` | `linear-gradient(<0–359>deg, <#hex> <0–100>%, …)` with 2–8 stops in increasing order. `background-color` stays required and is not painted under the gradient. |
| `<div data-shape="line" data-line="x1 y1 x2 y2">` | Line | Whole-pixel end points inside the element box; style has only `left`, `top`, `width`, `height`, `border-color` and `border-width` (1–16). Optional `data-arrow-start` and `data-arrow-end` take `triangle`. |
| `<div data-icon="<name>">` | Icon | An outline icon from the fixed list below, drawn in `color` and centred in the box; style has only `left`, `top`, `width`, `height` and `color`. |
| `<section>` | `data-transition` | `fade` or `push`, played by **Present** when the slide is entered. |

Icon names: `arrow-down`, `arrow-left`, `arrow-right`, `arrow-up`, `bell`,
`bot`, `briefcase`, `building`, `calendar`, `chart-column`, `chart-line`,
`chart-pie`, `check`, `circle-check`, `clock`, `cloud`, `code`, `database`,
`download`, `file-text`, `flag`, `folder`, `globe`, `heart`, `house`, `info`,
`layers`, `lightbulb`, `link`, `lock`, `mail`, `map-pin`, `message-circle`,
`message-square`, `phone`, `play`, `presentation`, `rocket`, `search`, `send`,
`settings`, `shield`, `sparkles`, `star`, `target`, `terminal`, `trending-up`,
`triangle-alert`, `upload`, `user`, `users`, `x`, `zap`. They are Lucide icons
(ISC) bundled with Crewship; a deck can name one but never supply SVG.

In the projection these become `font_weight`, `letter_spacing` (pixels) and
`line_height` (percent of the font size, so `1.05` is `105`) on `text`;
`corner_radius`, `gradient` (`{"angle", "stops": [{"color", "position"}]}`) and,
for `kind: "line"`, `line` (`{"x1", "y1", "x2", "y2", "start", "end"}`) on
`shape`; an element of `type: "icon"` with `icon: {"name", "color"}`; and
`transition` on the slide. Each is omitted when it holds its default.

### `crewship page artifact slides <slug-or-id>`

Read the projected deck and the anchors that comments can use. Use `@<id>` to
select by Page ID. The example output below is abridged: it omits the intro
shape, the summary slide and the summary table anchor.

| Flag | Description |
| - | - |
| `--version <n>` | Exact code version (required). |

```bash theme={null}
crewship page artifact slides quarterly-deck --version 1 -f json
```

```json theme={null}
{
  "anchors": {
    "intro-title": {"path": "slides/intro.html", "quote": "Q3 review", "selector": "#intro-title", "slide_id": "intro"}
  },
  "deck": {
    "deck_id": "<page-id>", "schema_version": 1, "width": 1920, "height": 1080,
    "slide_order": ["intro", "summary"],
    "slides": [
      {"id": "intro", "notes": "Open with the headline number.", "elements": [
        {"id": "intro-title", "type": "text", "x": 80, "y": 80, "width": 1600, "height": 180,
         "text": {"value": "Q3 review", "align": "left", "color": "#112233", "font_family": "Go Regular", "font_size": 48}}
      ]}
    ],
    "theme": {"background": "#FFFFFF", "color": "#111111", "font_family": "Go Regular", "font_size": 32}
  }
}
```

### `crewship page artifact export-pdf <slug-or-id>`

Export the deck of an exact version to a local PDF file. The file is written
atomically and the command prints a receipt, not PDF bytes. `--out -` is
rejected.

| Flag | Description |
| - | - |
| `--version <n>` | Exact code version (required). |
| `--out <file>` | Local PDF path (required); never sent to the server. |

```bash theme={null}
crewship page artifact export-pdf quarterly-deck --version 1 --out deck.pdf
```

```json theme={null}
{
  "byte_length": 7934,
  "code_version": 1,
  "media_type": "application/pdf",
  "path": "deck.pdf",
  "sha256": "<sha256>"
}
```

The CLI checks the media type, the `%PDF-` header and an 8 MiB limit before
writing.

## Archive

### `crewship page artifact archive <page>`

Remove an artifact from default lists without deleting content or changing
access. `page artifact archive` and `page artifact unarchive` are the same
commands as [`page archive` and `page unarchive`](https://docs.crewship.ai/cli/page). Run a dry run
first, then confirm with the returned token.

| Flag | Description |
| - | - |
| `--dry-run` | Preview only; returns a `review_token`. |
| `--pages-version <n>` | Exact `pages_version` of the Page (required); see `crewship page get <page> -f json`. |
| `--review-token <token>` | Token from the matching dry run. |
| `--yes` | Confirm the reviewed change. |

```bash theme={null}
crewship page artifact archive quarterly-review --dry-run --pages-version 0 -f json
```

```json theme={null}
{
  "after": {"archived": true}, "before": {"archived": false},
  "changed": false, "dry_run": true,
  "impact": {"access_changed": false, "content_deleted": false, "removed_from_default_lists": true},
  "page": "quarterly-review", "page_id": "<page-id>",
  "pages_version": 0,
  "review_token": "<review-token>"
}
```

```bash theme={null}
crewship page artifact archive quarterly-review --yes --pages-version 0 --review-token <review-token>
```

The confirmation returns `"changed": true` and the new `pages_version`.
Reusing the token afterwards returns `409 pages_version changed; preview again`.

### `crewship page artifact unarchive <page>`

Return an archived artifact to default lists. Flags and flow are the same as
for `archive`.

```bash theme={null}
crewship page artifact unarchive quarterly-review --dry-run --pages-version 1
```

## Agent guides per kind

Each artifact kind ships a versioned guide that agents follow when they work
on an artifact of that kind: the exact data shapes and size limits, the write
order (on create the index first; on revise the data first and the index
last), the read-before-write version check, the conflict rule (stop after the
third refusal and report), the agent modes (Ask, Comment, Propose, Edit; only
the Owner delegates Edit, and a comment is never a proposal) and when not to
verify. The guides are built into the server; they describe supported product paths and required policy and grant nothing.

### `crewship artifacts kind-guide [kind]`

Without a kind, list the guides with their version, shared-rules version and
SHA-256 digest. With a kind (`docs`, `slides`, `design`, `library`,
`dashboard`, `app`; `design-system` and `project` are accepted as aliases),
print that guide. Word and Sheet are Craft documents and have no artifact
guide (`404`).

```bash theme={null}
crewship artifacts kind-guide -f json
crewship artifacts kind-guide docs
crewship artifacts kind-guide design-system -f json
```

```json theme={null}
{"kind": "library", "title": "Library (design system)", "version": "1.0.0", "rules_version": "1.1.0", "sha256": "…", "bytes": 4210, "content": "# Library agent guide\n…"}
```

The digest is over `content`, so a changed guide is visible even when only the
shared rules moved.

Agents get the same text. When a chat message carries an artifact as
context, the server-built Page context block ends with a line such as
`Kind: docs; agent guide 1.2.0 (artifact_kind_guide)`, and the agent reads the
guide with the `artifact_kind_guide` tool of its sidecar. The kind comes from
the version the sender may read; Word, Sheet, panel and board Pages carry no
Kind line.

Each guide also declares `modes` (the agent modes the kind has a path for:
`ask`, `comment`, `propose`, `edit`) and `capabilities` with `supported` and
`unsupported` names. A client hides an unsupported feature instead of
imitating it; neither list grants access. Docs declare Propose for schema 4–6
Documents (`propose.schema4`, `propose.schema5`, `propose.schema6` supported, `propose.schema1` to
`propose.schema3` unsupported), Design has no proposal path (`propose` is
unsupported), and an App's scripts reach only
`data.get`, `data.list`, `data.batch` and `viewer`: user profiles, presence,
model calls, connectors, uploads, downloads, in-page comments and publishing
are declared unsupported.

On a Dashboard, a data write counts as Edit, not Propose: it changes what
readers see at once, so it needs the Owner's authorization for that run. A
code change is a Propose: a new retained version that does not move the
shared version.

## Agent publishing policy

The workspace artifact policy is the administrative ceiling for agent work on
artifacts: an agent can create or change an artifact on a person's behalf
only when the policy is enabled and the agent's crew is on the allowlist.
`--enabled=true` with an empty allowlist admits no crew. The policy does not
limit what people do themselves.

### `crewship workspace artifact-policy show`

```bash theme={null}
crewship workspace artifact-policy show -f json
```

```json theme={null}
{"crew_ids": [], "enabled": false, "version": 0}
```

`version: 0` means no policy has been saved yet. A non-admin receives
`403 active trusted workspace administrator required`.

### `crewship workspace artifact-policy set`

Replace the whole policy. Pass the `version` from `show` as `--base-version`.

| Flag | Description |
| - | - |
| `--base-version <n>` | Version returned by `show` (required). |
| `--enabled` | Pass `--enabled=true` or `--enabled=false` explicitly (required). |
| `--crew-id <id>` | Complete allowlist; repeat or comma-separate. |
| `--clear-crews` | Replace the allowlist with an empty list. |

Use exactly one of `--crew-id` and `--clear-crews`.

```bash theme={null}
crewship workspace artifact-policy set --base-version 0 --enabled=false --clear-crews -f json
```

```json theme={null}
{"crew_ids": [], "enabled": false, "version": 1}
```

A stale `--base-version` returns `409 stale_policy`; a crew from another
workspace or a deleted crew returns `400`. Each change is recorded in the
workspace audit log. See also [Workspace Commands](https://docs.crewship.ai/cli/workspace.md).

## Inside an agent run

When `CREWSHIP_AGENT_TOKEN` holds a run capability (`agtv2.…`), the artifact
commands do not use a saved profile. They call the run's scoped MCP tools
instead, on behalf of the person who authorized the run:

| Command | Agent tool |
| - | - |
| `create` | `artifact_create` |
| `publish` | `artifact_publish` |
| `get`, `versions` | `artifact_versions` |
| `restore` | `artifact_restore` |
| `comments` | `artifact_comments` |
| `reply` | `artifact_reply` |
| `data collections`, `data get`, `data batch` | `artifact_data_collections`, `artifact_data_read`, `artifact_data_batch` |
| `slides`, `export-pdf` | `artifact_slides_read`, `artifact_export_pdf` |

`share`, `comment` and `availability` are not available to an agent and fail
with `artifact or Design operation unavailable to an agent run`. `archive` and
`unarchive` do not use the run capability: they send the saved profile's
request with the normal CLI credentials, and the server refuses an agent or
run caller with `403`. In this
mode the `--server`, `--profile`, `--workspace` and `--server-allow-mismatch`
flags are rejected, and a token that is not a run capability fails with
`original live run capability required; no human profile fallback`. A refused
tool call prints `artifact tool refused: {…}`. If the outcome is unknown, the
CLI prints `artifact outcome unavailable; preserve candidate and inspect before
retrying`: keep your manifest and read the artifact before saving again.

These tools also need the [agent publishing policy](#agent-publishing-policy)
to be enabled with the agent's crew on the allowlist.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.