# Artifacts (/docs/api/artifacts)



An artifact is a durable, indexed output copied from a sandbox workspace
to object storage. Export is always explicit — nothing is saved
automatically — and artifacts survive after the sandbox is destroyed.

## The Artifact object [#the-artifact-object]

| Field         | Type    | Description                                      |
| ------------- | ------- | ------------------------------------------------ |
| `id`          | string  | Artifact ID, prefixed `art_`                     |
| `orgId`       | string  | Owning organization, prefixed `org_`             |
| `projectId`   | string  | Owning project, prefixed `prj_`                  |
| `sandboxId`   | string  | Source sandbox, prefixed `sbx_`                  |
| `name`        | string  | Display name                                     |
| `sourcePath`  | string  | Workspace path it was copied from, when recorded |
| `objectKey`   | string  | Object storage key                               |
| `sizeBytes`   | integer | Size in bytes                                    |
| `contentType` | string  | MIME type, when detected                         |
| `createdAt`   | string  | ISO 8601 creation timestamp                      |
| `deletedAt`   | string  | ISO 8601 deletion timestamp, once deleted        |

## Create an artifact [#create-an-artifact]

`POST /v1/sandboxes/{sandboxId}/artifacts` — requires scopes
`artifact:create` and `file:read`. Accepts an
[`Idempotency-Key`](/docs/api/pagination-and-idempotency) header.

Copies a file from the live sandbox workspace into object storage. The
sandbox must still be alive — export anything you need before killing it.

| Field  | Type   | Required | Constraints                          |
| ------ | ------ | -------- | ------------------------------------ |
| `path` | string | Yes      | Must resolve inside `/workspace`     |
| `name` | string | No       | Display name; defaults from the path |

```bash title="Terminal"
curl -X POST https://api.crownest.dev/v1/sandboxes/sbx_abc123/artifacts \
  -H "Authorization: Bearer $CROWNEST_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 4c8a1d7e-export-01" \
  -d '{ "path": "/workspace/model.bin", "name": "model" }'
```

Returns `201` with the artifact:

```json
{
  "artifact": {
    "id": "art_abc123",
    "orgId": "org_abc123",
    "projectId": "prj_abc123",
    "sandboxId": "sbx_abc123",
    "name": "model",
    "sourcePath": "/workspace/model.bin",
    "objectKey": "orgs/org_abc123/projects/prj_abc123/objects/obj_abc123",
    "sizeBytes": 5242880,
    "contentType": "application/octet-stream",
    "createdAt": "2026-06-11T13:00:00.000Z"
  }
}
```

Errors: `invalid_request`, `forbidden`, `not_found`, `file_not_found`,
`path_outside_workspace`, `sandbox_destroyed`.

<Callout type="info" title="Tip">
  You can also export artifacts automatically when a command finishes by passing
  `collect` to the run endpoint. See [Commands](/docs/api/commands#run-a-command).
</Callout>

## List artifacts [#list-artifacts]

`GET /v1/sandboxes/{sandboxId}/artifacts` — requires scope
`artifact:read`.

Returns a [paginated](/docs/api/pagination-and-idempotency) list of the
sandbox's artifacts, newest first. Standard `limit` and `cursor`
parameters apply.

```bash title="Terminal"
curl https://api.crownest.dev/v1/sandboxes/sbx_abc123/artifacts \
  -H "Authorization: Bearer $CROWNEST_API_KEY"
```

```json
{
  "data": [
    {
      "id": "art_abc123",
      "name": "model",
      "sizeBytes": 5242880,
      "createdAt": "2026-06-11T13:00:00.000Z"
    }
  ],
  "hasMore": false
}
```

## Get an artifact [#get-an-artifact]

`GET /v1/artifacts/{artifactId}` — requires scope `artifact:read`.

Returns one artifact by ID. This works after the source sandbox is
destroyed.

```bash title="Terminal"
curl https://api.crownest.dev/v1/artifacts/art_abc123 \
  -H "Authorization: Bearer $CROWNEST_API_KEY"
```

Returns `200` with `{ "artifact": { ... } }`. Errors: `forbidden`,
`not_found`.

## Get a download URL [#get-a-download-url]

`POST /v1/artifacts/{artifactId}/download-url` — requires scope
`artifact:read`.

Returns instructions for downloading the artifact's content.

```bash title="Terminal"
curl -X POST https://api.crownest.dev/v1/artifacts/art_abc123/download-url \
  -H "Authorization: Bearer $CROWNEST_API_KEY"
```

```json
{
  "url": "https://api.crownest.dev/v1/artifacts/art_abc123/download",
  "method": "GET",
  "headers": {
    "content-disposition": "attachment; filename=\"model\"",
    "x-content-type-options": "nosniff"
  },
  "authMode": "api_key"
}
```

With `authMode` of `api_key`, send your API key in the `Authorization`
header on the GET request to the URL, the same as any other call. The
returned `headers` are content hints (for example `content-disposition`)
that the download response sets.

## Download artifact content [#download-artifact-content]

`GET /v1/artifacts/{artifactId}/download` — requires scope
`artifact:read`.

Returns the artifact bytes directly with the stored content type. This is
the same URL returned by the download-url endpoint when `authMode` is
`api_key`.

```bash title="Terminal"
curl https://api.crownest.dev/v1/artifacts/art_abc123/download \
  -H "Authorization: Bearer $CROWNEST_API_KEY" \
  --output model.bin
```

Errors: `forbidden`, `not_found`.

## Delete an artifact [#delete-an-artifact]

`DELETE /v1/artifacts/{artifactId}` — requires scope `artifact:delete`.

Deletes the artifact's content and marks the record deleted. The operation
is idempotent by state — deleting an already-deleted artifact returns the
deleted record.

```bash title="Terminal"
curl -X DELETE https://api.crownest.dev/v1/artifacts/art_abc123 \
  -H "Authorization: Bearer $CROWNEST_API_KEY"
```

Returns `200` with `{ "artifact": { ... } }` including `deletedAt`.
Errors: `forbidden`, `not_found`.

## Next steps [#next-steps]

* Produce outputs to export with [Commands](/docs/api/commands).
* Manage workspace files in [Files](/docs/api/files).
