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

# Documents

> Records with filterable attributes and the state judgments are made about.

A document is a record with an `id`, flat filterable `attributes`, and `state`: the JSON content that judgments are made about.

```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
  "id": "t_123",
  "revision": 42,
  "incarnation": 7,
  "attributes": {"plan": "pro", "region": "eu", "tags": ["vip"]},
  "state": {"subject": "...", "body": "...", "messages": [{"role": "customer", "text": "..."}]},
  "created_at": "2026-09-01T09:00:00Z",
  "updated_at": "2026-09-23T12:00:00Z"
}
```

* **`attributes`** is a flat map of strings, numbers, booleans, string arrays and nulls, at most 64 keys. Attributes are filterable and sortable. They are never sent to an engine unless a judgment's context recipe names them.
  * A number reads back as you wrote it. An integer keeps its exact value anywhere in the signed or unsigned 64-bit range, so `1790799482246605123` comes back as `1790799482246605123`, and filters and ranking compare integers exactly. A number written with a fraction or an exponent, such as `2.5` or `3.0`, is a 64-bit float and comes back as one. `3` and `3.0` are equal in a filter.
  * A `null` means no value: an `upsert` leaves that attribute out, and a `patch` removes it. A get does not return it, and `Exists false` matches it.
* **`state`** is any JSON object up to 1 MB. It is never filterable.
* **`revision`** increases every time the document changes. It is the namespace's sequence number at the change, so it is monotonic but not contiguous.
* **`incarnation`** is the sequence at which the document was created. Deleting a document and writing the same id again starts a new incarnation with no answers and no history.
* **`created_at`** is when the document was created: the time of the write that created it, or the `created_at` that write gave, such as a record's original creation time when you [import existing data](/guides/import-existing-data). A given `created_at` may be at most 5 minutes in the future. A later write never changes it; a new incarnation gets a new one. [Relations](/concepts/relations) read related documents newest created first.

## Writing

`POST /namespaces/{ns}` takes up to 1,000 documents or 64 MB, and applies its operations in this order: `upsert`, `patch`, `append`, `delete`. They commit atomically, and a request is never split across two commits.

| Operation | Effect |
| - | - |
| `upsert` | Replaces the whole document. An optional `created_at` sets its creation time if this write creates it, and an optional `if_revision` makes it [conditional](#conditional-upserts). |
| `patch` | Merges the top-level keys of `attributes` and `state`. A key set to `null` is removed. Patching a missing document creates it, and an optional `created_at` sets its creation time then. |
| `append` | Pushes `values` onto the array at `path` inside `state`, creating it if needed. A value identical to an element already anywhere in the array, or to an earlier value in `values`, is skipped, so a retried append adds nothing. To append two identical elements on purpose, give each a unique `id` field. |
| `delete` | Removes the document. Its answers and evaluations stay in history until the namespace is deleted. |

Every write is idempotent by construction, so retrying a request is always safe; [system behavior](/behavior#your-data) says exactly what a late retry does. A write returns once its batch is durable, in roughly 100 to 200 ms in the service's region ([measured](/tradeoffs)). With `wait_for`, it also waits until the named judgments have answers for this revision, up to `wait_timeout_ms`: 5,000 by default, and a value above 10,000 is lowered to 10,000. An empty `wait_for` is the same as leaving it out. On timeout the write is still committed, and those answers come back `pending`, which here means only that they were not ready within the wait. A large write waits on its first 16 documents at interactive priority; the rest are judged with ordinary catch-up and usually come back `pending`. A get works [freshness](/concepts/freshness) out from the stored answer instead, so the same answer can read `unavailable` there if the document was never judged in its current incarnation, or `stale` under a policy that will not judge the new revision on its own.

### Conditional upserts

An `upsert` may carry `if_revision`: the write commits only if the document's current revision is exactly that, and `0` means only if the document does not exist (it was never written, or it was deleted). Use it to write back a document you read without overwriting a change someone made since:

```json POST /v1/namespaces/acme%2Fprod theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{"upsert": [{"id": "rulebook", "state": {"rules": ["..."]}, "if_revision": 20488}]}
```

If any document's revision differs, the whole request is refused with `conflict` and nothing in it is written. `details.revisions` gives each mismatched id's current revision, or `null` when it does not exist or has a write that has not committed yet:

```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{"error": {"code": "conflict", "message": "...", "details": {"revisions": {"rulebook": 20511}}}}
```

Read the document again and decide what to write. A retry of a conditional write whose first attempt did commit is `conflict` too, since the revision moved on; read the document to tell. `if_revision` is for `upsert` only: a `patch` or `append` always applies. A document written by a request that has not committed yet has no revision to match, so a conditional write racing it is `conflict`, with `null` for that document, until it lands.

Writes to one document are applied in the order they commit, so concurrent `patch` and `append` writes from different clients all stand. During a failover a write can be refused with `rate_limited`; retry it, as the SDKs do. See [system behavior](/behavior#your-data).

## Reading

`GET /namespaces/{ns}/documents/{id}` returns the document with its answers. Add `include=history` for its evaluations, newest first, and `include=history,context,raw` for the compiled context and raw engine response of each. `POST /namespaces/{ns}/query` filters, ranks and pages. See [answers](/concepts/answers#querying-answers).


## Related topics

- [Judge a document with its related documents](/guides/related-documents.md)
- [Judge a document with the document it points at](/guides/referenced-document.md)
- [Roll up answers from related documents](/guides/roll-ups.md)
- [Report on groups of documents](/guides/groups.md)
- [Stop publishing tenant documents](/api-reference/groups/stop-publishing-tenant-documents.md)


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