Skip to Content
ConceptsRequests & documents

Requests & documents

A Request is one OCR job — a batch of files uploaded into a single session, processed against one document type or custom schema. Each file/page in the request becomes a Document with its own status, confidence, and extracted JSON.

You key downstream pipelines against request_id (for the whole batch) or document_id (for the page). Both IDs are stable and idempotent.

Anatomy of a request

{ "id": "req_3fa1c4e2b9d1", // request_id — stable "merchant_id": "merchant_abc123", "name": "AP batch 04/2026", // human label, freeform "session_id": "f47ac10b-58cc-4372", "document_type": "trade_invoice_jp", "status": "processing", // queued | processing | success | failed | partial "total_images": 240, "success_images": 217, "failed_images": 3, "created_at": "2026-04-30T03:18:02Z", "completed_at": null }

Anatomy of a document

{ "id": "doc_8a7c2e0fa912", // document_id — stable "request_id": "req_3fa1c4e2b9d1", "status": "completed", // queued | llm_processing | completed | failed | rejected | confirmed "document_type": "trade_invoice_jp", "source_url": "https://.../sakura_t1234.pdf", "fields": { "vendor": "株式会社サクラ商事", "t_number": "T1234567890123", "t_number_valid": true, // verified live against NTA "issue_date": "2026-04-30", "total": "¥495,800", "tax_10_amount": "¥45,000", "tax_8_amount": "¥8,800" }, "confidence": { "vendor": 0.99, "t_number": 0.99, "total": 0.99, "tax_10_amount": 0.98, "tax_8_amount": 0.97 }, "created_at": "2026-04-30T03:18:02Z", "completed_at": "2026-04-30T03:18:06Z" }

State machine

┌──────────────┐ upload │ │ LLM fail / timeout ─────────▶│ queued ├────────────────────┐ │ │ │ └──────┬───────┘ │ │ worker picks up ▼ ▼ ┌──────────┐ ┌──────────────┐ │ failed │ │llm_processing│ │ (retry) │ └──────┬───────┘ └────┬─────┘ │ extracted │ ▼ │ ┌──────────────┐ reject by LLM │ │ completed │◀───────────────────┘ └──────┬───────┘ (low confidence, │ wrong doc type) human review (optional) ┌──────────────┐ │ confirmed │ └──────────────┘
  • queuedllm_processingcompleted is the happy path
  • failed is automatically retried (default 3 attempts with exponential backoff)
  • rejected is what the LLM returns when it can’t extract — never billed
  • confirmed is set when an operator clicks Approve in the correction UI (or via the confirmation API)

Retry semantics

You can re-run a single document or the whole request:

EndpointEffect
POST /api/v1/ocr/documents/{id}/retryRe-run one page (idempotent)
POST /api/v1/ocr/requests/{id}/retryReplay every failed/rejected page in batch

Retries are billed exactly once per successful extraction — failed retries don’t accrue.

For the canonical event names emitted as a request moves through these states, see Webhooks → Overview.