Extraction
POST /v1/extract returns typed values from one input: a text span with its
offsets, a number read or computed, a date, a duration, a list, an estimate
with an interval, or a short forecast. Each value comes with a confidence and
the probability that the input does not determine it. It runs Cloudflare's
Clairic, an extraction model that answers in one forward pass and generates no
tokens. Use it to pull fields out of invoices, receipts and emails, compute a
total or a day count, price a listing from comparables, or forecast a daily
series, without running a conversation.
The call is metered like any model op. Nothing is stored.
Models
| Model | Input | Key |
|---|---|---|
cloudflare.clairic | text and up to 4 images | your project's Cloudflare key only |
model is required and must be the namespaced id. Clairic is a preview
checkpoint on Cloudflare Workers AI; Cloudflare's
model page
documents the body field for field, and this endpoint takes it unchanged.
Extract values
# Authorization: tenant-admin token (server-side only)
curl https://api.cloud.ingram.tech/v1/extract \
-H "Authorization: Bearer $IC_TOKEN" \
-H "IC-Api-Version: 2026-05-01" \
-H "Content-Type: application/json" \
-d '{
"model": "cloudflare.clairic",
"state": "Invoice INV-2291 from Northwind Supply, issued 2026-09-02, net 30. 4 pallets at $250.00 each, shipping $80.00. Contact: Dana Ruiz, Priya Shah.",
"fields": {
"invoice_number": { "type": "text" },
"due_date": { "type": "date", "instructions": "Issue date plus the net payment days" },
"total": { "type": "number", "decimals": 2, "unit": "USD", "instructions": "Amount due including shipping" },
"contacts": { "type": "list", "items": "text" },
"po_number": { "type": "text", "instructions": "Purchase order number" },
"days_to_pay": { "type": "estimate", "min": 0, "max": 120, "step": 1, "instructions": "Days after issue until this invoice is paid" }
}
}'
With the TypeScript SDK:
import { IngramCloud } from "@ingram-cloud/sdk/client";
const ic = new IngramCloud({ token: process.env.IC_TOKEN! });
const { answers } = await ic.extract.create({ model: "cloudflare.clairic", state, fields });
fields maps a name (^[A-Za-z_][A-Za-z0-9_.\-]{0,63}$) to a typed field.
Every field takes instructions and null_threshold, the p_null above
which value is null (default 0.5).
type | Returns | Options |
|---|---|---|
text | a span of state, with span offsets | |
number | a number read or computed | unit, integer, decimals 0 to 6 |
date, time, datetime | an ISO value | reference_date on the request anchors dates without a year |
duration | a length of time | unit: seconds, minutes, hours, days, weeks, months, years |
list | matching items in input order, with spans | items: text, number or date; decimals |
estimate | a value and its distribution inside [min, max] | step (a number, "month", "year"), scale linear or log, unit |
forecast | one estimate per step | the estimate options plus horizon 1 to 64 and interval: step, minute, hour, day, week, month, quarter, year |
Fields in one request are answered independently against the same input. Clairic does one or two arithmetic steps; it does not chain long derivations or aggregate many table rows. Have it read the operands and let your code do the math.
Images
images takes up to 4 PNG, JPEG or WebP images, each a data URL or
{ "content_type", "base64" }, 4 MiB each. They are placed before state,
which may then be empty. Remote URLs are refused.
"images": ["data:image/jpeg;base64,…"],
"fields": { "total": { "type": "number", "decimals": 2 }, "merchant": { "type": "text" } }
Read the answers
{
"model": "cloudflare.clairic",
"answers": {
"invoice_number": { "type": "text", "value": "INV-2291", "span": [8, 16], "guess": "INV-2291", "confidence": 1, "p_null": 3.8e-10 },
"due_date": { "type": "date", "value": "2026-10-02", "guess": "2026-10-02", "confidence": 0.9999, "p_null": 1.9e-08 },
"total": { "type": "number", "value": 1080, "guess": 1080, "confidence": 0.9999, "p_null": 2.3e-08 },
"contacts": { "type": "list", "value": ["Dana Ruiz", "Priya Shah"], "spans": [[120, 129], [131, 141]], "confidence": 0.9999 },
"po_number": { "type": "text", "value": null, "guess": "INV-2291", "confidence": 0.9995, "p_null": 1 },
"days_to_pay": { "type": "estimate", "value": 30, "mean": 29.535, "median": 30, "p10": 21, "p90": 38, "p_below": 5.9e-06, "p_above": 1.2e-05 }
},
"truncated": false,
"usage": { "state_tokens": 61, "image_tokens": 0, "schema_tokens": 472, "input_tokens": 655, "output_tokens": 0, "passes": 3 }
}
value is the answer, or null when p_null passed the field's threshold.
guess is the best value regardless of null_threshold, so a null answer still
says what the model would have read. confidence is the model's confidence in
guess. Estimates and forecasts carry median, p10 and p90; options: { "include_quantiles": true } adds 99 percentiles. Every answer is well formed;
none is guaranteed correct. Route on confidence, p_null and the width of the
interval, and send the low-confidence cases to a person or a conversation.
input_tokens includes schema_tokens, the tokens your field definitions cost.
passes is how many parallel passes the fields were split across.
Limits & errors
The input is 65,536 tokens at most. One pass holds 8,192 tokens of state plus fields; fields that do not fit run in parallel passes that each see the whole input. The request body is 16 MiB at most, images 8 MiB decoded in total.
| Status | Code | When |
|---|---|---|
422 | invalid_request | The body breaks the shape above. param names the field. |
422 | model_kind_mismatch | model is not an extraction model. A bare clairic is one. |
422 | model_key_missing | Your project has no Cloudflare key. |
400 | model_request_failed | Cloudflare refused the request: a bad token, a limit above. The message carries Cloudflare's reason. |
402 | budget_exceeded, card_required, insufficient_credits | As for runs; only a project-wide budget applies. See Usage and Billing. |
429 | rate_limit_exceeded | Cloudflare is rate-limiting the key. Retry after the Retry-After seconds. |
500 | extraction_failed | Any other failure. 502 when Clairic answered in a shape this API does not know. |
Keys
Clairic runs on your project's Cloudflare key only. Create a Workers AI token in
the Cloudflare dashboard (Workers AI Read is enough) and note the account id.
Add both in Settings → Models, or over the API, where the account id is
workspace_id:
# Authorization: tenant-admin token (server-side only)
curl -X PUT https://api.cloud.ingram.tech/v1/tenant/model_keys/cloudflare \
-H "Authorization: Bearer $IC_ADMIN_TOKEN" \
-H "IC-Api-Version: 2026-05-01" \
-H "Content-Type: application/json" \
-d '{ "api_key": "<workers-ai-token>", "workspace_id": "<account-id>" }'
# → { "provider": "cloudflare", "configured": true, "base_url": null, "workspace_id": "<account-id>" }
A Cloudflare key without workspace_id is 422 workspace_id_required.
Cloudflare bills the input tokens to your account; the platform fee is debited
from your wallet at the Clairic price.
In the console
Settings → Models lists Cloudflare with the other providers; its row takes the Workers AI token and the account id.