 and Clairic (field cards). Designed.)
What OCR outputs
OCR outputs the characters and lines it finds in an image. It doesn't decide what a string means, and it doesn't turn "12.50" into a number or "Oct 3" into a date.
So after OCR, you still have to work out which line is the order number and which number is the tax.
What Clairic outputs
Clairic returns one typed value per field you define. Scalar fields can carry confidence and pNull, and a value the input can't support comes back as null. When you send text as state, text answers can include a span pointing back into it. Clairic doesn't generate text.
Same receipt, two outputs (illustrative):
| OCR | Clairic | |
|---|---|---|
| Output | Several lines of raw text | { order_number, purchase_time, subtotal, tax, tip, discount: null } |

See invoice & receipt extraction
Pipeline comparison
A typical OCR pipeline is image → OCR → regex, rules or templates → fields. Those rules are tied to a layout, so they need upkeep whenever a layout changes.
With Clairic, the rules step becomes one extract() call with field definitions and instructions.
 → fields. Designed.)
| OCR + rules | Clairic extract() | |
|---|---|---|
| Output | Text | Typed fields |
| Finding fields | Write your own rules or templates | Field definitions + instructions |
| Missing values | Handle it yourself when a rule doesn't match | null when pNull is above the threshold |
| Confidence signal | Depends on the engine | Field-level confidence and pNull (scalar fields) |
| Computed values (e.g. due date) | Write them yourself | Can be worked out via instructions |
| Billing | Depends on your setup | Once per request, by input size |
Combine them
If you already run OCR, pass its text to Clairic as state. Keeping the source text means text answers can carry a span you can highlight. You can also send text and images together: non-empty state plus up to 4 images.
tsimport {
createSystemOne, defineFields, textField, numberField, dateTimeField,
} from '@system-one-ai/core';
import { systemOneAdapter } from '@system-one-ai/adapter-system-one';
import { createFetchTransport } from '@system-one-ai/transport-fetch';
const apiKey = process.env.SYSTEM_ONE_API_KEY;
if (!apiKey) throw new Error('Set SYSTEM_ONE_API_KEY.');
const client = createSystemOne({
adapter: systemOneAdapter,
transport: createFetchTransport(),
apiKey,
timeoutMs: 30_000,
maxRetries: 0,
});
const ocrText = `<text returned by your OCR step>`;
const result = await client.extract({
model: 'clairic',
state: ocrText,
fields: defineFields({
order_number: textField(),
purchase_time: dateTimeField(),
subtotal: numberField({ decimals: 2 }),
tax: numberField({ decimals: 2 }),
discount: numberField({ decimals: 2, instructions: 'Only if the receipt shows a discount.' }),
}),
options: { returnStateText: true },
});
const order = result.answers.order_number;
console.log(order.value, order.span); // span = [start, end] character positions in ocrText
Install with npm install @system-one-ai/core@0.8.0 @system-one-ai/adapter-system-one@0.7.0 @system-one-ai/transport-fetch@0.8.0, set SYSTEM_ONE_API_KEY in .env, and run with node --env-file=.env. Every run creates a new billable request. options.returnStateText returns the text that spans index into, which makes highlighting easy.
To see Clairic on invoice text without writing code, try the invoice extraction demo (sign-in required).
When OCR alone is enough
If you need full-text search or a full-text archive, and you don't need specific fields, OCR alone does the job.
If you do need fields, see LLM extraction vs Clairic and screenshot to JSON, or check rates on the Clairic model page.
Related reading
- Invoice & receipt extraction API
- LLM extraction vs Clairic
- Screenshot to JSON for AI agents
- Clairic model page
Last verified: 2026-10-22