OCR a capture-session document photo
/v1/vision/document-scanAuthenticates with client credentials — no bearer token required.
Runs self-hosted OCR (tesseract.js) and/or Azure Document Intelligence over a document image looking for MRZ text, then redeems the capture session against identity-kyc's internal redeem endpoint — which persists the image (and whatever was read from it) against the case the session was minted for. `document_type` declares which of `passport` / `national_id` / `residence_permit` / `driving_licence` this scan claims to be (defaults to `passport`) and drives a class-aware flow (RIGID-785): a valid MRZ of ANY supported format redeems the same way regardless of class (`scan_quality: di_extracted`). Failing that, `passport` (the one class the self-hosted tesseract TD3 engine covers) falls back to it exactly as before — `di_fallback_tess` on a tesseract hit — and only once BOTH DI and tesseract have missed does a DI read that still returned usable visual-zone fields redeem as a fields-only read (`scan_quality: 'di_fields_extracted'`, `mrz_extracted: false`); a miss on all three reports tesseract's own outcome, exactly as before RIGID-785. Every other class has no OCR fallback at all — tesseract only reads TD3, so running it against a card would spend ~15s to say nothing — so a DI read with usable visual-zone fields redeems immediately as a fields-only read (`scan_quality: 'di_fields_extracted'`); failing that, it reports `scan_quality: 'di_unavailable'` when Document Intelligence itself errored, or `'no_mrz_found'` when it ran cleanly but found nothing usable. OCR is validated at capture time (RIGID-691): a read only reports `mrz_extracted: true` after ICAO 9303 check digits verify, with classic character confusions (O/0, I/1, S/5, …) repaired under check-digit constraint before forwarding — the stored MRZ is the repaired read (TD1/TD2 formats validate but do not repair). A completed tesseract read that cannot be made check-digit-valid reports `scan_quality: decode_error` with `mrz_extracted: false`: the image was decodable but the text is untrustworthy, so widgets should prompt a retake. A miss of any kind still redeems the session and stores the image for review; identity-kyc re-validates the stored MRZ independently (defense in depth). `scan_quality` reports the outcome taxonomy (`extracted` / `low_confidence` / `no_mrz_found` / `timeout` / `decode_error` / `client_extracted` / `di_extracted` / `di_fallback_tess` / `di_fields_extracted` / `di_unavailable`) alongside `mrz_extracted`, which only tells the caller whether MRZ text was forwarded, not why it was withheld or what else (if anything) was. A request may also include `document_mrz` already extracted client-side, in any of three shapes — two 44-char TD3 lines (passport), two 36-char TD2 lines, or three 30-char TD1 lines — all '\n'-joined; when it independently validates it is forwarded (repaired, if needed — TD3 only) and OCR is skipped entirely (`scan_quality: 'client_extracted'`), while an invalid `document_mrz` is silently ignored and the image is scanned exactly as if the field had been omitted. `side` (`front`/`back`) is an optional telemetry/provenance dimension for a card-format capture — this endpoint has no per-side branching logic of its own. Served by the vision host, not the primary API host (see the second `servers` entry) — temporary pending the unified API edge (ADR-0043). No OAuth bearer token: `capture_session_token` in the body is the entire authorization, verified downstream when this handler calls identity-kyc's redeem endpoint. Rate-limited per RIGID-591 (per-source-IP and per-session-token, both fixed 60s windows) ahead of a per-instance OCR concurrency cap; any of the three returns 429, with a `Retry-After` header on the rate-limit refusals.
Request body
Example
curl -X POST https://vision.dev.rigid.fi/v1/vision/document-scan \
-H "Authorization: Bearer $RIGID_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"capture_session_token": "string",
"image_base64": "string"
}'Responses
200The capture session was redeemed; case_id identifies the KYC case.
400Validation error
404Capture session not found or expired
410The capture session scan budget is exhausted (`code: scan_budget_exhausted`) — the stored image will still be reviewed, so the widget should land on its done/review terminal rather than treating this like an expired session
429Too many document scans — instance busy, or a rate limit was exceeded (rate-limit refusals carry a Retry-After header)
500Internal server error
502identity-kyc redeem call failed