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

# Extractions

> Pull typed values out of text and images — strings, numbers, dates, durations, lists, estimates and forecasts — with a calibrated confidence per field, using Cloudflare's Clairic extraction model on your AI Reserve key and wallet, billed per input token.

The gateway exposes a typed extraction model under `/v1/extractions`, with the same API key, rate
limits, wallet, and spend caps as every other route. Behind it sits Cloudflare's
[Clairic](https://developers.cloudflare.com/workers-ai/models/clairic/) model: you send a
**state** (text, optionally with up to four images) and a schema of typed **fields**, and get back
a value for every field in one forward pass — a `text` field is a character span of your input, a
`number` is computed, a `date` is ISO, a `list` is the matching items in input order, an
`estimate` is a distribution with a median and an 80% interval. Every answer carries a calibrated
`confidence` and a `p_null`: a field the input does not determine comes back `null` rather than a
guess, and the guess is still there under `guess` if you want it.

Use it for the parts of a pipeline that are reading rather than writing: invoice and receipt
fields, dates and amounts out of contracts, structured attributes out of listings or support
tickets, numbers out of screenshots. There is no prompt to engineer and no JSON to repair — the
model can only answer in the shape you declared. An extraction is a few hundred milliseconds and a
fraction of a cent, so it fits in front of every row.

You never create a Cloudflare account or hold a Cloudflare token. AI Reserve holds one account,
each extraction is billed to your wallet at the published per-token rate, and every call appears in
your usage analytics under the model `clairic`.

> [!NOTE]
>
> **Sibling of [Decisions](/capabilities/decisions).** Clef answers typed *questions* (yes/no,
> choice, score) with probabilities; Clairic fills typed *fields* with values. Both are one forward
> pass over the same kind of `state`, so an agent can ask "is this an invoice?" and "what is the
> total?" against the same text with two cheap calls.

## Quickstart

```bash theme={"dark"}
curl -s https://api.aireserve.com/v1/extractions \
  -H "Authorization: Bearer $AIRESERVE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "clairic",
    "state": "Invoice #4421 from Acme Corp, issued March 3 2026, due in 30 days. Total due: $1,250.00. Items: widgets, gaskets, bolts.",
    "fields": {
      "vendor": { "type": "text",   "instructions": "The vendor name" },
      "total":  { "type": "number", "instructions": "Total amount due", "decimals": 2, "unit": "USD" },
      "issued": { "type": "date",   "instructions": "Invoice issue date" },
      "due":    { "type": "date",   "instructions": "Invoice due date" },
      "items":  { "type": "list",   "items": "text", "instructions": "Line items listed" }
    }
  }'
```

```json theme={"dark"}
{
  "model": "clairic",
  "answers": {
    "vendor": { "type": "text", "value": "Acme Corp", "span": [19, 28], "confidence": 0.995, "p_null": 0.002, "guess": "Acme Corp" },
    "total":  { "type": "number", "value": 1250, "confidence": 0.9995, "p_null": 0.00007, "guess": 1250 },
    "issued": { "type": "date", "value": "2026-03-03", "confidence": 0.996, "p_null": 0.0017, "guess": "2026-03-03" },
    "due":    { "type": "date", "value": "2026-04-02", "confidence": 0.90, "p_null": 0.0009, "guess": "2026-04-02" },
    "items":  { "type": "list", "value": ["widgets", "gaskets", "bolts"], "spans": [[96, 103], [105, 112], [114, 119]], "confidence": 0.995 }
  },
  "truncated": false,
  "usage": { "state_tokens": 61, "image_tokens": 0, "schema_tokens": 407, "input_tokens": 468, "output_tokens": 0, "passes": 2 },
  "aireserve": { "request_id": "6b1d…", "model": "clairic" }
}
```

Every response carries an `aireserve` envelope with the request id to quote to support and the
model the extraction billed under. Note the `due` date: the input said "due in 30 days", and the
model computed it from the issue date — with a lower confidence than the dates it could read
directly.

## Request

| Field | Type | Notes |
| - | - | - |
| `model` | enum | Required. `clairic` (64K context, text + images). |
| `state` | string | The text to read. Required unless `images` is sent. `text` answers are character spans of this string. Long text is truncated upstream to fit the context (`truncated: true` in the response). |
| `fields` | object | Required, 1–256 entries. Keys are your field names (letter or `_` first, then letters, digits, `_`, `.`, `-`; max 64 chars); answers come back under the same names. |
| `images` | array | Optional, up to 4 embedded PNG / JPEG / WebP images placed before the state, as `data:` URLs or `{ "content_type", "base64" }` objects (4 MiB each, 8 MiB total decoded). Remote URLs are not accepted. |
| `reference_date` | string | Optional ISO date that anchors dates written without a year ("March 3" → which year). |
| `options` | object | Optional. `include_quantiles: true` adds 99 percentiles to every estimate; `return_state_text: true` returns the text that spans index into. |

### Field types

Every field takes a `type` and `instructions` — what the field should contain, plus any rule the
model should apply. The other knobs depend on the type:

| `type` | Returns | Knobs |
| - | - | - |
| `text` | A span of the state: `value` plus `[start, end]` character `span`. | — |
| `number` | A computed number. | `integer`, `decimals` (0–6), `unit` (label only). |
| `date` | `YYYY-MM-DD`. | `reference_date` (request-level) for dates without a year. |
| `time` | `HH:MM:SS`. | — |
| `datetime` | ISO 8601 date-time. | — |
| `duration` | A number in `unit`. | `unit`: `seconds` (default), `minutes`, `hours`, `days`, `weeks`, `months`, `years`; `integer`, `decimals`. |
| `list` | Matching items in input order (`value` array plus per-item `spans`). | `items`: `text`, `number` or `date` (required); `decimals` for number lists. |
| `estimate` | A distribution: `value` (point estimate), `mean`, `median`, `p10`, `p90`, `p_below` / `p_above` the range. | `min`, `max` (required; numbers, or ISO dates for date estimates), `scale`: `linear` or `log`, `step`. |
| `forecast` | One estimate per future step under `steps`, plus `value` / `median` / `p10` / `p90` arrays. | `min`, `max`, `horizon` (1–64 steps), `interval`: `step`, `minute`, `hour`, `day`, `week`, `month`, `quarter`, `year`. |

Every answer also carries `confidence` and, for nullable types, `p_null` and `guess`. A field is
`null` when `p_null` exceeds its `null_threshold` (default 0.5) — set the threshold per field to
trade recall for precision.

Fields are answered independently against the same state. Fields that do not fit next to the
state in one 8,192-token pass are split across parallel passes upstream (`usage.passes`), so a
large schema still returns in one round trip.

> [!TIP]
>
> Extractions are **calibrated**: a `confidence` of `0.6` means "probably right, not certainly".
> Accept the confident fields automatically and route the uncertain band — or any `null` — to a
> person or a larger model. Keep arithmetic in your code: the model computes simple derived values
> (a due date, a total) but multi-step math belongs in a `number` field per operand, not one field
> per result.

## Images

Send a scanned receipt, a screenshot or a chart as an embedded image; `state` may then be empty.
Text fields read from an image carry `"source": "image"` and no `span`.

```python theme={"dark"}
import base64, httpx, os

API_KEY = os.environ["AIRESERVE_API_KEY"]

with open("receipt.jpg", "rb") as f:
    receipt = base64.b64encode(f.read()).decode()

r = httpx.post(
    "https://api.aireserve.com/v1/extractions",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "model": "clairic",
        "images": [{"content_type": "image/jpeg", "base64": receipt}],
        "reference_date": "2026-10-20",
        "fields": {
            "merchant": {"type": "text", "instructions": "Merchant name"},
            "total": {"type": "number", "instructions": "Total charged", "decimals": 2, "unit": "USD"},
            "paid_on": {"type": "date", "instructions": "Transaction date"},
            "card_last4": {"type": "text", "instructions": "Last four digits of the card"},
        },
    },
    timeout=30,
)
r.raise_for_status()
answers = r.json()["answers"]
if answers["total"]["value"] is None or answers["total"]["confidence"] < 0.8:
    review_queue.put(r.json()["aireserve"]["request_id"])
```

## Billing

| Model | Context | Billed |
| - | - | - |
| `clairic` | 64K | \$0.10 per 1M input tokens |

Rates match Cloudflare's published Workers AI list price. Extractions have no output tokens, so a
request costs its input alone: the `state` and the serialized `fields` (each field adds its own
template tokens — `usage.schema_tokens` shows the count), plus images, which are tokenized at one
token per 32×32 pixel block and capped at 4,096 tokens each. The wallet holds an in-process
estimate before the call and settles on the token count Cloudflare reports; an extraction that
fails — at the provider, on a timeout, or because the surface is not configured — is never charged.
Current rates are always on the [pricing page](https://aireserve.com/pricing).

## Data handling

Request bodies are sent to Cloudflare, Inc. (United States; served from Cloudflare's global
network). Under Cloudflare's published Workers AI data-usage terms, inputs and outputs are
Customer Content that Cloudflare does not use to train models or improve its services, does not
make available to other customers, and stores only when paired with a Cloudflare storage product
— which this route does not use — so it carries **no data-retention consent gate**. Still, send
the minimum state the extraction needs. Details on the
[data-handling page](https://privacy.aireserve.com/data-handling).

## Errors

| Status | Meaning |
| - | - |
| `400` | Malformed request — an unknown model, no fields, a bad field type, an empty state with no images, or Cloudflare's validation message relayed (for example a `list` without `items`, an `estimate` without `min`/`max`, or an image whose dimensions are too large). |
| `402` | Spend cap exceeded or insufficient wallet balance. |
| `403` | Not a client-scoped key, or model not on the key's allowlist. |
| `429` | Rate limit exceeded, here or upstream. Retry after the `Retry-After` header. |
| `503` | Extractions are not configured on this deployment, or the provider is unavailable. Retry. |
| `504` | The provider timed out. Retry. |


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