DocExtract

Authentication

Send your key as a bearer token on every request to /v1/…. Requests without a valid key get HTTP 401.

Authorization: Bearer YOUR_KEY

Extract a statement

POST /v1/statements/extract with a JSON body. Send images (one base64 PNG or JPEG per page, in page order), text, or both.

curl -X POST https://YOUR_HOST/v1/statements/extract \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"images": ["<base64 page 1>", "<base64 page 2>"]}'
FieldTypeNotes
imagesarray of stringsBase64 PNG or JPEG page images, up to 50. The API does not accept PDFs: the web app converts PDF pages to images in the browser, and API users should render pages to images (about 1,500 px wide works well) before sending.
textstringStatement text, if you already have it.

Response

{
  "request_id": "9f2c…",
  "backend": "openai",
  "timing_ms": 61234,
  "statement": {
    "bank": "Northline Savings",
    "account_last4": "3548",
    "period_start": "2026-03-01",
    "period_end": "2026-03-31",
    "opening_balance": "6468.53",
    "closing_balance": "8992.00",
    "total_credits": "3777.12",
    "total_debits": "1253.65",
    "transactions": [
      { "date": "2026-03-03", "description": "RENT PAYMENT",
        "amount": "-430.02", "balance": "6038.51" }
    ]
  },
  "validation": {
    "status": "pass",
    "risk_score": 0,
    "flags": []
  }
}

Money values are strings in dollars with two decimals. Amounts are negative for money leaving the account. A field the model could not find is null.

validation.status is pass or review. risk_score runs from 0 to 100. Each flag has a code, a severity (error or warn), a readable message, and a zero-based row index when it points at one transaction.

Check codes

Any error makes the status review. Warnings alone do not.

CodeSeverityMeaning
RECONCILIATION_MISMATCHerrorOpening balance plus transactions does not equal the closing balance.
RUNNING_BALANCE_BREAKerrorA row's balance does not follow from the previous balance and its amount. Includes the row.
SUMMARY_MISMATCHerrorPrinted total deposits or withdrawals differ from the sum of the rows.
MISSING_BALANCE, NO_TRANSACTIONSerrorOpening or closing balance, or all transactions, were not found.
DATE_OUT_OF_PERIOD, DATE_ORDER, DATE_UNREADABLEwarnA date falls outside the period, goes backwards, or could not be read.
MODEL_OUTPUT_REPAIREDwarnThe model's reply ended early (often a missing closing brace) and was closed automatically. If rows were lost, the reconciliation check fails too.
DUPLICATE_TRANSACTIONwarnIdentical rows. Often legitimate, so it is only a warning.

A review result means "a person should look". The checks cannot tell a misread digit from a deliberate edit, and an edit that keeps every balance consistent passes them.

Errors

Errors return JSON: {"error": "message"}.

StatusMeaning
400The body is not valid JSON.
401Missing or wrong API key.
413Body larger than 25 MB.
422Bad input, for example no images or text, or invalid base64.
502The model server failed or returned something unusable. Safe to retry.

Limits

  • Up to 50 page images per request and 25 MB per request.
  • On a CPU-only server expect about a minute per page. Set a long client timeout (15 minutes is reasonable).
  • Requests are processed one at a time by the model, so concurrent requests queue.

Health check

GET /health returns {"status": "ok", "backend": "…"} and needs no key.