# Records

Workflow memory for your tenant. Send raw text, get back a typed record.

Create classifies the text and stores the result. Reads and deletes never bill.

## Auth

Create needs a session cookie on every call. The billed tenant comes from the bearer key when one is present. Otherwise it comes from the session tenant. A bearer key alone cannot create: without a session the call answers 401.

List, read, and delete use only the session tenant. A bearer key alone cannot read.

## Endpoints

### Create a record

```http
POST /records/create
```

```json
{"text": "ERROR [auth]: token expired", "thread_id": "deploy-42"}
```

`text` is required. It may be empty. An empty or unclassifiable text stores a fallback record with 200. `thread_id` is optional. `classifier_id` is optional: set it to route the call through one of your [classifiers](/docs/classifiers) instead of the built-in types. A routed create answers 404 for an unknown id, 410 for an archived or unpublished one, and stores your label as the record type. The 413 limit measures the `text` field UTF-8 bytes against 64 KiB. `schema` is optional on create: it names a built-in type and is stored in the record metadata. An unknown name answers 422.

The answer is the stored record:

```json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "log_event",
  "thread_id": "deploy-42",
  "content": {},
  "metadata": {},
  "raw": "ERROR [auth]: token expired",
  "created_at": "2026-10-13T00:00:00Z"
}
```

The answer echoes the input `text` back as `raw`, so the stored
row always shows what was submitted.

### List records

```http
GET /records/get?type=log_event&thread_id=deploy-42&cursor=20&limit=20
```

Every filter is optional. The answer is a page:

```json
{"records": [], "next_cursor": null}
```

`next_cursor` is null on the last page. The default limit is 20. The max is 100. Values below 1 become 1. A bad or negative cursor answers 400.

### Read one record

```http
GET /records/get/{id}
```

Answers the record. A missing or foreign id answers 404. A malformed id answers 422.

### Delete a record

```http
DELETE /records/delete/{id}
```

Answers 204 with no body. A missing id answers 404. A foreign id answers 404. A row you may not delete answers 404. Nothing leaks across tenants.

## Record types

Nine types exist. The classifier picks one per create.

`issue`, `log_event`, `commit`, `pull_request`, `tool_call`,
`diagnostic`, `diff`, `review_comment`, `text`.

A create routed through your classifier stores your label as the type, so routed tenants can see their own labels here.

## Metadata

A clean classification stores:

| key | meaning |
|---|---|
| `router` | classifier that routed the text |
| `valid` | the answer passed validation |
| `retries` | classifier attempts used |
| `latency_ms` | classifier time in milliseconds |
| `schema` | built-in type name passed on create |

A fallback stores:

| key | meaning |
|---|---|
| `unclassified` | always true on this path |
| `reason` | why the classifier answer was unusable |
| `upstream_status` | classifier status code, when one exists |
| `retries` | classifier attempts used |
| `latency_ms` | classifier time in milliseconds |

## Deletes

The owner can delete. A tenant admin can delete. A system owner can delete, including cross-tenant rows. Everyone else gets 404.

## Pricing

Create bills through the shared tenant pool in both paths: built-in extract and routed classifier. Each create consumes one included call; past the pool the prepaid wallet pays per call. A fallback store debits then refunds, so it nets zero. There is no records-specific quota: a routed create draws the same pool as every other call, and an empty balance answers 429 with a top-up link. Records has no default rate bucket, so a rate 429 only fires when one is configured. A billed create carries `x-mivia-debit` and `x-mivia-free-remaining` headers. Lists, reads, and deletes are free. See [pricing](/docs/pricing).

## No MCP tools

Records have no MCP tools. Use REST, or browse and create from the dashboard at `/records`.
