Skip to content

Extract Invoice and Receipt Fields with One API Call

See sample results

![Invoice text and receipt photo turned into typed fields by Clairic](TODO-IMG: Cover. A mock invoice (text) and a mock receipt photo → field cards (invoice_number, total, tax…). Designed.)

Fields to extract

FieldType / factoryNotes
invoice_number / order_numbertext / textField()Document number, can carry a span
issue date, due_datedate / dateField()Calendar date
purchase_timedatetime / dateTimeField()Date and time
total, subtotal, tax, tip, unit_pricenumber / numberField({ decimals: 2 })decimals is 0–6
quantitynumber / numberField({ integer: true })Whole number
contacts / line item nameslist / listField({ items: 'text' })items is required: text, number or date
days_to_payestimate / estimateField({ min, max })Returns median, p10, p90 and more

Text invoice example (INV-2291)

This is the invoice example from the Clairic SDK docs. Install the SDK:

bashnpm 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

Save as extract-invoice.mts and set SYSTEM_ONE_API_KEY in .env:

tsimport {
  createSystemOne, defineFields, textField, numberField, dateField, listField, estimateField,
} 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 fields = defineFields({
  invoice_number: textField({ instructions: 'The invoice id printed on the document.' }),
  due_date: dateField({ instructions: 'Issue date plus the net payment days.' }),
  total: numberField({ decimals: 2, instructions: 'The total amount due.' }),
  contacts: listField({ items: 'text' }),
  days_to_pay: estimateField({ min: 0, max: 120, step: 1 }),
});

const result = await client.extract({
  model: 'clairic',
  state: [
    'INVOICE INV-2291',
    'From: Northwind Supply',
    'To: Contoso Ltd',
    'Issued: 2026-09-02, net 30',
    'Total due: 1080.00 including shipping',
    'Contact: Dana Ruiz, Priya Shah',
  ].join('\n'),
  fields,
  referenceDate: '2026-10-19',
  options: { includeQuantiles: false },
});

const invoice: string | null = result.answers.invoice_number.value;
console.log({
  model: result.model,
  invoice,
  dueDate: result.answers.due_date.value,
  total: result.answers.total.value,
  contacts: result.answers.contacts.value,
  daysToPay: result.answers.days_to_pay.median,
  truncated: result.truncated,
  usage: result.usage,
});

Run it with node --env-file=.env extract-invoice.mts. Every run creates a new billable request.

contacts is a list, so its value is an array of names. days_to_pay is an estimate field, so read its median; p10 and p90 come back too.

![Clairic output for invoice INV-2291 with invoice number, due date and total](TODO-IMG: Real terminal run of extract-invoice.mts, API key masked as sk-****.)

Try the invoice demo

Skewed, blurry receipt photos

In an example from the official blog, Clairic reads a receipt photo taken at an angle with hard-to-read text and pulls the order number, purchase time, quantity, unit price, subtotal, tax and tip. The receipt shows no discount, so the discount field returns null.

Here's the photo version, saved as extract-receipt.mts. You can send up to 4 images per request:

tsimport { readFile } from 'node:fs/promises';
import {
  createSystemOne, defineFields, textField, numberField, dateTimeField,
  type ImageInput,
} 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;
const imagePath = process.argv[2];
if (!apiKey || !imagePath) throw new Error('Set SYSTEM_ONE_API_KEY and supply a JPEG path.');

const images: readonly ImageInput[] = [{
  mediaType: 'image/jpeg',
  base64: (await readFile(imagePath)).toString('base64'),
}];

const client = createSystemOne({
  adapter: systemOneAdapter,
  transport: createFetchTransport(),
  apiKey,
  timeoutMs: 30_000,
  maxRetries: 0,
});

const fields = defineFields({
  order_number: textField({ instructions: 'The order or receipt number.' }),
  purchase_time: dateTimeField({ instructions: 'Date and time of purchase.' }),
  quantity: numberField({ integer: true }),
  unit_price: numberField({ decimals: 2 }),
  subtotal: numberField({ decimals: 2 }),
  tax: numberField({ decimals: 2 }),
  tip: numberField({ decimals: 2 }),
  discount: numberField({ decimals: 2, instructions: 'Discount amount, only if the receipt shows one.' }),
});

const result = await client.extract({ model: 'clairic', state: '', images, fields });

for (const [name, answer] of Object.entries(result.answers)) {
  console.log(name, answer.value, answer.confidence, answer.pNull);
}

When the receipt has no discount, discount comes back null. Before production, test with your own photos and set thresholds on confidence and pNull.

![Skewed receipt photo and Clairic fields, with discount returned as null](TODO-IMG: Left: mock skewed, blurry receipt photo with no discount line. Right: real run output of extract-receipt.mts showing discount null; API key masked.)

referenceDate and due dates

referenceDate (reference_date over HTTP) sets the year for dates written without one, such as a receipt that only prints the day and month.

Due dates can be computed. In the invoice example, the due_date field's instructions say "Issue date plus the net payment days.", so Clairic works out the due date from 2026-09-02 and net 30.

Missing fields return null

When pNull is above the null threshold (0.5 by default), the value is null, and guess may still hold something. Adjust the threshold for the whole request with options.nullThreshold, or per field.

The invoice extraction demo shows this: when the total due says "to be confirmed", the amount comes back null and the invoice number is still extracted.

Pricing in one line

Clairic is billed once per request based on input size, not per field; see the Clairic model page. New accounts get 100 free credits on sign-up.

Try the demo

Try the same invoice task in the invoice extraction demo (sign-in required). To call the API directly, this is the HTTP body for POST https://system-one.dev/v1/systemone:

json{
  "model": "clairic",
  "state": "INVOICE INV-2291\nIssued: 2026-09-02, net 30\nTotal due: 1080.00",
  "reference_date": "2026-10-19",
  "fields": {
    "invoice_number": { "type": "text" },
    "total": { "type": "number", "decimals": 2 }
  }
}

See the API docs for authentication, idempotency and headers. Retrying the same operation with the same key doesn't create a second charge while the replay record remains.

![Clairic invoice extraction demo on system-one.dev](TODO-IMG: Real screenshot of /en/demo/invoice-extraction, fields and result area.)

Get an API key

Get an API key

Last verified: 2026-10-22

Frequently asked questions

Can Clairic read a skewed or blurry receipt photo?

It can extract fields from photos taken at an angle or with hard-to-read text; the official example pulls order number, purchase time, quantity, unit price, subtotal, tax and tip from one. Test with your own photos and check confidence and pNull.

Which inputs can I send?

Text, JSON, and up to 4 images (PNG, JPEG or WebP) embedded as base64.

What if a field isn't on the invoice?

It comes back as null when pNull is above the null threshold, so a missing discount stays null rather than being filled in.

How is it billed?

Once per request, based on input size: each 4KB block costs 2 credits, no matter how many fields you extract. See the Clairic model page.

What does referenceDate do?

It sets the year for dates written without one, for example on a receipt that only shows day and month.