Extractions
Send a document, choose a mode, and read every part of the result.
On this page
An extraction reads one document with one document type and returns its values, each with the evidence for it. It is created by POST /v1/extractions and has an id that starts rv_req_, which is also the request's id.
Send a document#
The request is multipart/form-data. It needs documents.extract, and documents.upload when it carries a file.
RECEVIZ_API="https://accounthouse-backend-793493499887.me-central1.run.app/api/receviz/v1"
curl "$RECEVIZ_API/extractions" \
-H "Authorization: Bearer $RECEVIZ_SECRET_KEY" \
-F "file=@receipt.jpg" \
-F "document_type=payment_receipt" \
-F "mode=standard"filefileThe document: JPEG, PNG, WebP, TIFF, HEIC/HEIF or PDF. Its type is decided by its bytes, never by its name or declared content type. Optional when
client_ocris sent.document_typestringThe document type's key, such as
payment_receipt. Optional only when the application has a default document type.schema_versionintegerA published version to use. By default, the application's pinned version or the latest.
modestringfast,standardorverified. By default, the application's default mode, orstandard.asyncbooleantrueto process in the background and answer202at once. The default isfalse.optionsJSON object, as a stringSee Options below.
metadataJSON object, as a stringYour own keys, returned on the extraction and in its webhooks: a flat object of strings, numbers, booleans or null, at most 2048 bytes. Only the first 50 keys are kept, and longer names and texts are cut to 40 and 500 characters. Use it for your ids, not for document content.
client_ocrJSON object, as a stringText read on the device, instead of or as well as the file. See Device text.
Headers:
AuthorizationBearerrequiredA secret key, or a client token in an app or a page.
Idempotency-KeystringMakes a retry safe. See Retry safely.
X-ReceViz-SDKstringWhich SDK sent the request, as
platform/version(web/0.1.0). The SDKs set it; it is shown in your request log.
Wait for it, or come back for it#
A small document is read while you wait and answered with 200 and the full result. A request with async=true is queued and answered at once with 202, the extraction's id, status queued and a Location header pointing at it.
Large documents are queued even when you asked to wait: a file over 10 MB, or a PDF of more than three pages. The 202 response then has no warnings list, but the finished result carries the warning processed_async.
{
"id": "rv_req_8fK2aQ0zT3mN1pL5vB7xY9",
"object": "extraction",
"status": "queued",
"livemode": false,
"application": "app_Q3fK8sLm2VxT9pNa4RwZ1c",
"document_type": "invoice",
"schema_version": 3,
"mode": "standard",
"created_at": "2026-09-27T09:14:05Z",
"completed_at": null,
"metadata": {}
}Collect the result with GET /v1/extractions/{extraction_id} until its status is succeeded or failed, or subscribe to the extraction.completed and extraction.failed webhooks. The SDKs poll for you, starting at one second and backing off to eight.
RECEVIZ_API="https://accounthouse-backend-793493499887.me-central1.run.app/api/receviz/v1"
# 202 Accepted: the body has the extraction's id and status "queued",
# and the Location header points at it.
curl -i "$RECEVIZ_API/extractions" \
-H "Authorization: Bearer $RECEVIZ_SECRET_KEY" \
-H "Idempotency-Key: invoice-2026-09-000731" \
-F "file=@statement.pdf" \
-F "document_type=invoice" \
-F "async=true"
# Then fetch it until status is "succeeded" or "failed".
curl "$RECEVIZ_API/extractions/rv_req_8fK2aQ0zT3mN1pL5vB7xY9" \
-H "Authorization: Bearer $RECEVIZ_SECRET_KEY"- A request that waits has 90 seconds to be read. When reading takes longer, the answer is
504 processing_timeout: send that document withasync=true. - A queued document has ten minutes. An extraction left stuck is picked up again, up to three attempts, and then fails with
processing_timeout. - The file of a queued document is held in private storage only until it is processed, and deleted as soon as the extraction succeeds or fails.
Retry safely#
Send an Idempotency-Key header (up to 128 characters, such as a UUID or your own order id) and a retry of the same request within 24 hours returns the first extraction instead of making a second one. The replayed response carries Idempotent-Replayed: true, and is 200 if the first extraction has finished or 202 if it is still running.
“The same request” means the same file bytes, document_type, schema_version, mode and options, and whether client_ocr was sent. Reusing a key for anything else within the 24 hours answers 409 idempotency_key_reused. Keys are remembered per credential; a client token shares its key's.
Modes#
The three modes are profiles over the same stages, not different engines.
| Mode | What runs | When to use it |
|---|---|---|
fast | OCR and deterministic extraction. Values are typed and field thresholds apply, but no validation, no document rules, no arithmetic verdict and no language model: validation.performed is false. | The quickest answer, when you check the values yourself |
standard | Everything in FAST, plus field validation, the document type's rules, the document's own arithmetic, confidence thresholds and review. This is the default. | Most integrations |
verified | Everything in STANDARD, plus a language model as a second reader for exactly the fields that are uncertain, grounded in the document's own lines. | When a wrong value costs more than a slower answer |
VERIFIED mode needs the documents.verify capability and a data policy that allows language models; otherwise it is refused (403 missing_capability or 403 verification_disabled) rather than quietly run as STANDARD. See Confidence and verification for what the second reader may and may not do.
Options#
options is a JSON object sent as one form field, for example options={"include_ocr":"text","reference_date":"2026-09-27"}. An application can set defaults for some of them in the console; a request overrides those, except that evidence can only be made stricter.
include_ocrnone | text | linesReturn the recognised text as well:
textneedsocr.basic,lines(every line with its box and confidence) needsocr.structured. When the evidence level isnone, the text is withheld and the warningocr_output_withheldsays so. Defaultnone.evidencefull | boxes_only | noneHow much of the document travels back with each value. It can only tighten the level set by the data policy and the application. See Evidence levels.
tilingauto | off | forceAdaptive Document Tiling.
forceneedsadaptive_tiling; without that capability, tiling is off. Defaultauto.date_orderDMY | MDYHow to read 03/04/2026. By default, the application's setting, then the document type's, which is
DMYunless set otherwise.reference_dateYYYY-MM-DDThe day to treat as today, for
not_future,max_age_daysand judging whether a date is plausible. By default, the current date (UTC).known_merchantsarray of stringsMerchant names you already know: up to 250 names of up to 60 characters. A printed name close to one of them (a garbled logo, a missing letter) is read as that name. The list is only matched against what is printed: it never puts a merchant on a document that does not show one.
default_currencystringA three-letter currency code to assume when the document prints none. A field with the
currencysemantic that was not found gets this value, marked as not read: statusdefault, sourcedefault, confidence 0. By default, the application's setting, then the document type's.ocr_providerauto | document_ai | gemini | clientWhich recogniser reads the document. Leave it at
auto, which prefers the document OCR service and falls back to model transcription where the data policy allows it.
Device text#
An app that already read the document on the device can send that text as client_ocr: one page as {"width", "height", "lines": [{"text", "x", "y", "w", "h", "confidence"}]}, or several as {"pages": [...]}. Coordinates are fractions of the page from 0 to 1, with the origin at the top-left; confidence is optional. Up to 2,500 lines a page and 30 pages.
RECEVIZ_API="https://accounthouse-backend-793493499887.me-central1.run.app/api/receviz/v1"
curl "$RECEVIZ_API/extractions" \
-H "Authorization: Bearer $RECEVIZ_SECRET_KEY" \
-F "document_type=payment_receipt" \
-F 'client_ocr={"width": 800, "height": 880, "lines": [ {"text": "DATE: 26/09/2026", "x": 0.05, "y": 0.3136, "w": 0.4, "h": 0.0409}, {"text": "AED 150.00", "x": 0.65, "y": 0.7136, "w": 0.3, "h": 0.05} ]}'- FAST and STANDARD read the device's text and never call a cloud recogniser;
processing.ocr_providerisclient. - VERIFIED still reads the image when one is sent, because a verifier that sees only the device's reading would be checking the device against itself.
- Without a file, the request needs
documents.extractbut notdocuments.upload.
The response#
A finished extraction. This one was made from the receipt on the ReceViz home page, sent as device text, so no cloud recogniser ran and there is no picture to measure:
{
"id": "rv_req_8fK2aQ0zT3mN1pL5vB7xY9",
"object": "extraction",
"status": "succeeded",
"livemode": false,
"application": "app_Q3fK8sLm2VxT9pNa4RwZ1c",
"document_type": "payment_receipt",
"schema_version": 1,
"schema_id": "sch_7HdK2mQpX9sLw4VbN8cT1e",
"mode": "standard",
"created_at": "2026-09-27T09:14:05Z",
"completed_at": "2026-09-27T09:14:06Z",
"data": {
"amount": 150,
"currency": "AED",
"transaction_date": "2026-09-26",
"transaction_time": "14:05",
"receipt_number": "000102",
"rrn": "626914123456",
"auth_code": "A1B2C3",
"terminal_id": "12345678",
"payment_method": "card",
"card_scheme": "visa",
"card_last_four": "4242",
"approved": true,
"merchant": "BLUE DHOW CAFE",
"merchant_address": "DUBAI MARINA WALK"
},
"fields": {
"amount": {
"value": 150,
"type": "currency",
"status": "ok",
"confidence": 0.906,
"verified": false,
"checks": [],
"source": "rules",
"page": 1,
"bounding_box": {
"x": 0.77,
"y": 0.7136,
"width": 0.18,
"height": 0.05
},
"raw_text": "AED 150.00"
},
"currency": {
"value": "AED",
"type": "string",
"status": "ok",
"confidence": 0.941,
"verified": false,
"checks": [],
"source": "rules",
"page": 1,
"bounding_box": null,
"raw_text": null
},
"transaction_date": {
"value": "2026-09-26",
"type": "date",
"status": "ok",
"confidence": 0.935,
"verified": false,
"checks": [],
"source": "rules",
"page": 1,
"bounding_box": {
"x": 0.2,
"y": 0.3136,
"width": 0.25,
"height": 0.0409
},
"raw_text": "DATE: 26/09/2026"
},
"transaction_time": {
"value": "14:05",
"type": "string",
"status": "ok",
"confidence": 0.941,
"verified": false,
"checks": [],
"source": "rules",
"page": 1,
"bounding_box": {
"x": 0.825,
"y": 0.3136,
"width": 0.125,
"height": 0.0409
},
"raw_text": "TIME: 14:05"
},
"receipt_number": {
"value": "000102",
"type": "string",
"status": "ok",
"confidence": 0.931,
"verified": false,
"checks": [],
"source": "rules",
"page": 1,
"bounding_box": {
"x": 0.8,
"y": 0.3773,
"width": 0.15,
"height": 0.0409
},
"raw_text": "INVOICE NO: 000102"
},
"rrn": {
"value": "626914123456",
"type": "string",
"status": "ok",
"confidence": 0.941,
"verified": false,
"checks": [],
"source": "rules",
"page": 1,
"bounding_box": {
"x": 0.175,
"y": 0.4409,
"width": 0.3,
"height": 0.0409
},
"raw_text": "RRN: 626914123456"
},
"auth_code": {
"value": "A1B2C3",
"type": "string",
"status": "ok",
"confidence": 0.931,
"verified": false,
"checks": [],
"source": "rules",
"page": 1,
"bounding_box": {
"x": 0.325,
"y": 0.5046,
"width": 0.15,
"height": 0.0409
},
"raw_text": "AUTH CODE: A1B2C3"
},
"terminal_id": {
"value": "12345678",
"type": "string",
"status": "ok",
"confidence": 0.931,
"verified": false,
"checks": [],
"source": "rules",
"page": 1,
"bounding_box": {
"x": 0.175,
"y": 0.25,
"width": 0.2,
"height": 0.0409
},
"raw_text": "TID: 12345678"
},
"payment_method": {
"value": "card",
"type": "enum",
"status": "ok",
"confidence": 0.931,
"verified": false,
"checks": [],
"source": "rules",
"page": 1,
"bounding_box": null,
"raw_text": null
},
"card_scheme": {
"value": "visa",
"type": "enum",
"status": "ok",
"confidence": 0.931,
"verified": false,
"checks": [],
"source": "rules",
"page": 1,
"bounding_box": {
"x": 0.05,
"y": 0.5682,
"width": 0.1,
"height": 0.0409
},
"raw_text": "VISA"
},
"card_last_four": {
"value": "4242",
"type": "string",
"status": "ok",
"confidence": 0.941,
"verified": false,
"checks": [],
"source": "rules",
"page": 1,
"bounding_box": {
"x": 0.85,
"y": 0.5682,
"width": 0.1,
"height": 0.0409
},
"raw_text": "************4242"
},
"approved": {
"value": true,
"type": "boolean",
"status": "ok",
"confidence": 0.931,
"verified": false,
"checks": [],
"source": "rules",
"page": 1,
"bounding_box": {
"x": 0.4,
"y": 0.8,
"width": 0.2,
"height": 0.0409
},
"raw_text": "APPROVED"
},
"merchant": {
"value": "BLUE DHOW CAFE",
"type": "string",
"status": "ok",
"confidence": 0.912,
"verified": false,
"checks": [],
"source": "rules",
"page": 1,
"bounding_box": {
"x": 0.29,
"y": 0.0818,
"width": 0.42,
"height": 0.05
},
"raw_text": "BLUE DHOW CAFE"
},
"merchant_address": {
"value": "DUBAI MARINA WALK",
"type": "string",
"status": "ok",
"confidence": 0.865,
"verified": false,
"checks": [],
"source": "rules",
"page": 1,
"bounding_box": {
"x": 0.2875,
"y": 0.15,
"width": 0.425,
"height": 0.0409
},
"raw_text": "DUBAI MARINA WALK"
}
},
"review": {
"required": false,
"reasons": []
},
"validation": {
"performed": true,
"rules": [],
"arithmetic": {
"status": "unknown",
"amount": "150.0"
}
},
"verification": {
"performed": false
},
"warnings": [],
"quality": {
"score": null,
"issues": [],
"pages": []
},
"enrichment": {
"merchant": null
},
"processing": {
"engine": "receviz-engine/1.0.0",
"mode": "standard",
"ocr_provider": "client",
"pages": 1,
"tiling": {
"applied": false,
"pages": {}
},
"timings_ms": {
"quality": 0,
"ocr": 0,
"layout": 0,
"extraction": 1,
"validation": 0,
"verification": 0,
"total": 2
},
"source": "api"
},
"metadata": {
"order": "A-17"
}
}idstringrv_req_…. The same id is the request id inX-Request-Id, in the request log and in webhooks.statusstringqueued,processing,succeededorfailed.livemodebooleanTrue for live keys.
document_type, schema_version, schema_idstring, integer, stringExactly which published version read the document.
modestringThe mode that ran.
dataobjectThe values alone, shaped like the document type; every field present,
nullwhen not found.fieldsobjectEach value with its evidence. See below.
reviewobjectrequired, and thereasonsa person should look.validation, verificationobjectWhat was checked, and what the second reader did.
warningsarrayThings about the request or the document worth knowing.
qualityobjectHow good the picture was.
enrichmentobjectWhat ReceViz adds from its own knowledge rather than from the page, such as the known brand.
processingobjectHow the document was read, and how long each stage took.
ocrobjectOnly with
options.include_ocr.metadataobjectYours, as sent.
Fields: each value with its evidence#
fields has one entry per field of the document type. A scalar field looks like fields.amount above:
valueanyThe typed value, the same as in data. Null when not found.
typestringThe field's type from the definition.
statusstringok,missing,invalid(it broke a validation check),needs_review(below its threshold, a rule failed, the arithmetic does not add up, or the verifier disagreed) ordefault(its fallback value).confidencenumber or nullFrom 0 to 1; null when there is no value.
verified, checksboolean, arraycheckslists what independently backed the value:arithmetic(the document's own sums) andverifier(the second reader agreed, or found it).verifiedis true when there is at least one.sourcestringWhat produced the value:
rules(tuned rules),label,type,pattern,verifier,inferred,defaultornone.pageintegerWhich page it was printed on, counting from 1.
bounding_boxobjectWhere it was printed, as
{x, y, width, height}in fractions of the page from the top-left corner. Null for a value that is not printed in one place, such as a currency taken from a price.raw_textstringThe printed line or lines the value came from.
issuesarray{code, message, severity}: for examplepattern_mismatch,ambiguousorrule_failed. Severity iswarningorerror.verificationobjectIn VERIFIED mode, what the second reader concluded about this field, and why.
alternativesarrayOther values the document supported, each with its score, best first: what a person would choose between when reviewing.
An object field is {"type": "object", "status", "confidence", "fields": {…}} with a result per sub-field. An array field is {"type": "array", "status", "confidence", "items": [{…}]}, one object of field results per item; its status is ok, empty, or missing when it is required.
Evidence levels#
The evidence level decides how much of the document comes back. It is set by the data policy, can be made stricter by the application, and stricter again by a request.
| Level | Each value comes with |
|---|---|
full | Page, bounding box, printed text and alternatives; the verifier's alternative value; the arithmetic's figures |
boxes_only | Page and bounding box, but no printed text |
none | Values, statuses, confidences and checks only |
The same amount, under boxes_only and then none:
{
"value": 150,
"type": "currency",
"status": "ok",
"confidence": 0.906,
"verified": false,
"checks": [],
"source": "rules",
"page": 1,
"bounding_box": {
"x": 0.77,
"y": 0.7136,
"width": 0.18,
"height": 0.05
}
}
{
"value": 150,
"type": "currency",
"status": "ok",
"confidence": 0.906,
"verified": false,
"checks": [],
"source": "rules"
}Review#
review.required is true when a person should look before the values are used, and review.reasons lists why, each as {field, code, message}. Reasons about the document as a whole have a field of null.
| Code | Meaning |
|---|---|
missing_required | A required field, or one whose fallback is error, was not found. |
pattern_mismatch, below_minimum, date_in_future… | The value broke one of its field's checks; the code is the first failed check. |
low_confidence | The confidence is below the field's threshold. |
ambiguous | Another value on the document is almost as likely. |
rule_failed | A rule of the document type failed. |
arithmetic_inconsistent | The document's own sums do not add up with this value. |
verification_disagreed | The second reader read the field differently. |
Accept automatically only what does not need review
A value can be well read and still wrong for your purpose. Store data directly when review.required is false; otherwise show the reasons, the printed text and the alternatives to a person.
Validation, verification, warnings, quality and processing#
validationobjectperformed(false in FAST mode);rules, one entry per rule as{rule, type, status, fields, message}with statuspassed,failedorskipped; andarithmetic, whosestatusisconsistent,inconsistentorunknown.verificationobjectperformed; how manyfieldsneeded it; when it did not run, areason(nothing_uncertain,verifier_unavailable,verifier_failed); when it ran, the provider, the model, the time it took and theoutcomes(agreed, disagreed, filled, rejected, unanswered).warningsarray{code, message}, such asprocessed_async,ocr_output_withheld,declared_type_mismatch(the file was not what its content type said; its bytes were used),no_text,document_contains_instructions,verification_unavailable,verification_failed,rule_failedandarithmetic_inconsistent.qualityobjectMeasured on each page before it is read: a
scorefrom 0 to 1, namedissues(too_dark,overexposed,low_contrast,blurry,glare,low_resolution), and the brightness, contrast, sharpness and glare figures; per page inpagesfor a document of several. Quality never refuses a document: whether a poor photo still read is what the confidences say. The score is null when there was no picture.processingobjectengine(the engine's version),mode,ocr_provider,pages,tiling(see Adaptive Document Tiling),timings_msper stage (quality, ocr, layout, extraction, validation, verification and total) andsource:api,sdkorplayground.ocrobjectWith
include_ocr=text:{text, pages, provider}. Withlines:{provider, pages: [{page, width, height, lines: [{id, text, bounding_box, confidence}]}]}.
Known brands#
When the document type reads a merchant name, ReceViz checks it against the brands it knows: a few thousand merchants and banks, matched only when the match is certain. A wrong brand is worse than none, so an unknown shop gives merchant: null.
brandstringThe brand's stable key, such as
carrefour.namestringThe brand's own name. The printed name stays in
data.kindmerchant | bankWhat kind of brand it is.
logo_urlstring | nullA 128 px WebP of the brand's logo, from
GET /v1/brands/{brand}/logo, when logos are enabled for integrations; otherwise null. The link needs no key, and ReceViz does not log who loads it.
Other companies' trademarks
Brand names and logos belong to their owners. Show them only to identify the merchant on a document, never in a way that suggests the brand endorses your product. A logo can be withdrawn at any time; the link then answers 404, so fall back to the name.
Failed and purged extractions#
An asynchronous extraction that could not be processed has status failed and an error with the same codes as the API's errors. (A synchronous one answers with the error itself.)
{
"id": "rv_req_8fK2aQ0zT3mN1pL5vB7xY9",
"object": "extraction",
"status": "failed",
"livemode": false,
"application": "app_Q3fK8sLm2VxT9pNa4RwZ1c",
"document_type": "invoice",
"schema_version": 3,
"mode": "standard",
"created_at": "2026-09-27T09:14:05Z",
"completed_at": "2026-09-27T09:14:06Z",
"metadata": {},
"error": {
"code": "pdf_encrypted",
"message": "The PDF is password-protected. Send it without a password."
}
}Results are kept only as long as the data policy says. After that, the extraction still exists, but its values, evidence and metadata are gone:
{
"id": "rv_req_8fK2aQ0zT3mN1pL5vB7xY9",
"object": "extraction",
"status": "succeeded",
"livemode": false,
"application": "app_Q3fK8sLm2VxT9pNa4RwZ1c",
"document_type": "invoice",
"schema_version": 3,
"mode": "standard",
"created_at": "2026-09-27T09:14:05Z",
"completed_at": "2026-09-27T09:14:06Z",
"metadata": {},
"result_purged": true,
"purged_at": "2026-09-28T09:14:06Z",
"review_required": false,
"message": "The result is no longer held: this application's data policy limits how long results are kept."
}With the zero retention policy, a synchronous result is never stored, and an asynchronous one is handed over once, to the first fetch or webhook delivery, and then removed. See Data and privacy.
List recent extractions#
GET /v1/extractions lists the calling application's extractions, newest first, with metadata only and never a value: id, status, document type and version, mode, livemode, source, whether review was required, fields found and in total, pages, error code, total time, whether the result is still held, and when it was created and completed.
limit is 1 to 100 (default 25). To page back, pass the last id you have as starting_after; has_more says whether there are older ones.