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

# Artifacts.

> Store a large result once, then query it instead of re-reading it. Artifacts let an agent keep a 40,000-row CSV out of the context window and pull back only the rows it needs.

## The problem artifacts solve

An agent produces something big — a query result, a log dump, a scraped table — and then
needs three rows out of it, twice, four turns later. Pasting the whole thing back into the
prompt burns the context window and the budget on every turn that follows.

An **artifact** is that blob stored once, addressed by id, and readable by *query*: filter,
sort, search, project, paginate. The rows come back; the other 39,997 never enter the
prompt.

Artifacts are scoped to the key that uploaded them. The tenant scope is always taken from
your authenticated key and can never be set from the request body.

## Endpoints

| Endpoint                          | What it does                                           |
| --------------------------------- | ------------------------------------------------------ |
| `POST` `/v1/artifacts`            | Upload content and get a reference back.               |
| `POST` `/v1/artifacts/{id}/query` | Read a slice: filter, sort, search, project, paginate. |
| `GET` `/v1/artifacts/{id}`        | The compact descriptor — never the raw bytes.          |
| `DELETE` `/v1/artifacts/{id}`     | Delete the artifact.                                   |

## Upload

```http theme={null}
POST /v1/artifacts
```

| Field        | Type                | Notes                                                                                                          |
| ------------ | ------------------- | -------------------------------------------------------------------------------------------------------------- |
| `bytes`      | string · required\* | The content as a string.                                                                                       |
| `content`    | any                 | Alternative to `bytes` — a non-string value is serialized as JSON. One of the two is required.                 |
| `mimeType`   | string              | e.g. `text/csv`, `application/json`.                                                                           |
| `schema`     | string              | A hint for the row parser when the shape isn't inferable.                                                      |
| `summary`    | string              | A one-line human description, carried on the descriptor.                                                       |
| `rowCount`   | number              | Row count, when you already know it.                                                                           |
| `source`     | string              | Where it came from. Defaults to `api:upload`.                                                                  |
| `provenance` | object              | `{ "method": "exact" \| "model-generated" \| "heuristic" \| "truncation" \| "unknown", "lossless": boolean }`. |

```sh theme={null}
curl https://api.layerx1.com/v1/artifacts \
  -H "authorization: Bearer $LAYERX1_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "bytes": "[{\"region\":\"eu\",\"errors\":12},{\"region\":\"us\",\"errors\":3}]",
    "mimeType": "application/json",
    "summary": "error counts by region, 2026-08-09",
    "provenance": { "method": "exact", "lossless": true }
  }'
```

```json title="201 Created" theme={null}
{
  "reference": { "artifactId": "art_...", "version": 1, "...": "..." },
  "deduped": false,
  "persisted": true
}
```

Identical content uploaded twice is **deduplicated**: the second call returns `200` with
`"deduped": true` and the same reference. Re-uploading in a retry loop costs nothing.

<Note>
  Provenance defaults to `{ "method": "unknown", "lossless": false }` — the honest default
  for content an agent produced. Only assert `"lossless": true` when the artifact really is
  an exact projection of its source; downstream consumers are entitled to trust it.
</Note>

## Query

```http theme={null}
POST /v1/artifacts/{id}/query
```

The body is the query. Every field is optional — an empty body reads from the top.

| Field              | Type      | Notes                                                           |
| ------------------ | --------- | --------------------------------------------------------------- |
| `filter`           | array     | Predicates, AND-combined: `{ "field", "op", "value" }`.         |
| `sort`             | array     | `{ "field", "dir": "asc" \| "desc" }`, applied after filtering. |
| `search`           | string    | Case-insensitive free-text over each row's serialized form.     |
| `select`           | string\[] | Return only these keys of each row.                             |
| `offset` / `limit` | number    | Pagination window.                                              |
| `range`            | object    | Byte range, and/or `rowStart` / `rowEnd` for row ranges.        |
| `version`          | number    | Pin a version. Defaults to the current head.                    |

Operators for `filter[].op`: `eq`, `ne`, `lt`, `lte`, `gt`, `gte`, `contains`, `in`, `exists`.

```json title="Request" theme={null}
{
  "filter": [{ "field": "errors", "op": "gt", "value": 5 }],
  "sort": [{ "field": "errors", "dir": "desc" }],
  "select": ["region", "errors"],
  "limit": 20
}
```

```json title="Response" theme={null}
{
  "result": {
    "artifactId": "art_...",
    "version": 1,
    "rows": [{ "region": "eu", "errors": 12 }],
    "totalMatched": 1,
    "truncated": false,
    "structured": true
  }
}
```

`totalMatched` counts the rows that matched **before** `offset`/`limit`, and `truncated`
tells you whether your window dropped any — together they are how a client knows to page
rather than guessing from `rows.length`.

Unstructured content (plain text, a log with no parseable rows) comes back as `text`
instead of `rows`, with `"structured": false`. Use `range` to slice it.

## Inspect

```http theme={null}
GET /v1/artifacts/{id}
```

Returns `{ "reference": { ... }, "descriptor": { ... } }` — size, shape, summary, and
provenance. Deliberately **never** the raw bytes: the whole point of an artifact is that
the big thing stays out of the context window, so the read path is `query`, not `get`.

## Delete

```http theme={null}
DELETE /v1/artifacts/{id}
```

Returns `{ "deleted": true, "artifactId": "art_..." }`, or `404` if it does not exist for
your key.

## Errors

Same flat shape as [Agent Runs](/api/agent-runs#errors) — `{ "error": { "type", "message" } }`
with `invalid_request` (400), `unauthorized` (401), `not_found` (404),
`method_not_allowed` (405), and `not_implemented` (501) when the store is not enabled on
the deployment you are calling.

## From the CLI

```sh theme={null}
npx layerx1 artifact upload --file ./errors.json --schema json
npx layerx1 artifact query  art_123 --filter "errors:gt:5" --limit 20
npx layerx1 artifact inspect art_123
npx layerx1 artifact delete  art_123
```
