payable-receipt-ocr
Reference

Result Schema v1

Every field in the payable-receipt-ocr/1 JSON contract, warning codes, and migration from the old alpha schema.

The schema identifier is "payable-receipt-ocr/1" (no v). Both the Python result.to_dict() method and the CLI produce this schema. The normative source is docs/schema/v1.md in the repository.

Full example

A typical result from a Blinkit / Swiggy / Zepto checkout screenshot:

{
  "schema_version": "payable-receipt-ocr/1",
  "source": {
    "filename": "zepto-checkout.png",
    "dimensions": [1080, 1920]
  },
  "processing": {
    "passes_planned": 12,
    "passes_completed": 10,
    "passes_failed": 0,
    "degraded": false,
    "deadline_exceeded": false,
    "duration_ms": 2843
  },
  "runtime": {
    "baseline_id": "linux-noble-amd64-2026-08",
    "conformant": false,
    "tesseract_version": "tesseract 5.5.0",
    "model_sha256": {
      "eng": "7d4322bd2a7749724879683fc3912cb542f19906c83bcc1a52132556427170b2",
      "Devanagari": "3bbb87c1de2a6a2ef0a97dc041e6eea2723a1c22d638f5e38157a5cd441c12b7"
    }
  },
  "result": {
    "total": "289.86",
    "currency": "INR",
    "evidence_grade": "strong",
    "requires_confirmation": true,
    "authorizes_persistence": false,
    "matched_label": "to pay",
    "label_kind": "payment"
  },
  "warnings": []
}

A "none" evidence result (no total found):

{
  "schema_version": "payable-receipt-ocr/1",
  "result": {
    "total": null,
    "currency": null,
    "evidence_grade": "none",
    "requires_confirmation": true,
    "authorizes_persistence": false,
    "matched_label": null,
    "label_kind": null
  },
  "warnings": [
    {"code": "no_candidate", "message": "No payment or fallback total label with a monetary value was extracted."}
  ]
}

Top-level fields

FieldTypeDescription
schema_versionstringAlways "payable-receipt-ocr/1"
sourceobjectSource image metadata
processingobjectPipeline execution metrics
runtimeobjectOCR runtime identity
resultobjectThe recognition suggestion
warningsarrayZero or more warning objects
diagnosticsobjectPresent only when diagnostics=True; contains raw OCR text

source

FieldTypeDescription
filenamestringBasename of the input image path
dimensions[int, int][width, height] of decoded source image in pixels

filename reflects the basename of whatever path was passed to recognize(). If the path contains personal information, scrub it before logging or transmitting the result.

processing

FieldTypeDescription
passes_plannedintOCR passes scheduled
passes_completedintPasses that returned a result
passes_failedintPasses that timed out or raised an error
degradedbooltrue if any pass failed or the deadline was reached
deadline_exceededbooltrue if the total deadline was reached
duration_msintTotal wall time for the recognize() call in milliseconds

runtime

FieldTypeDescription
baseline_idstringBaseline identifier from runtime-baseline.toml
conformantbooltrue only when OS, arch, and Tesseract version all match the baseline tuple
tesseract_versionstringFirst line of tesseract --version output
model_sha256object{"eng": "<hex>", "Devanagari": "<hex>"} — checksums of verified model files

result

FieldTypeDescription
totalstring | nullDecimal string (e.g. "289.86"); null when evidence_grade="none"
currencystring | nullISO currency code; null when evidence_grade="none"
evidence_gradestringOne of "strong", "review", "none"
requires_confirmationtrueAlways true — never changes
authorizes_persistencefalseAlways false — never changes
matched_labelstring | nullLabel text that anchored the suggestion
label_kind"payment" | "fallback" | nullWhether the label was a payment label or a fallback total

Warnings

Each warning is an object with two fields:

{"code": "weak_evidence", "message": "Independent OCR evidence did not meet the strong-suggestion gate."}
FieldNotes
codeStable identifier — safe to branch on in code
messageHuman-readable — may change across versions; do not parse

Stable warning codes

CodeMeaning
no_candidateNo payment or fallback total was extracted
weak_evidenceGrade is not "strong"
currency_conflictConflicting explicit currency markers in OCR output
competing_totalA competing total has equal or greater cross-pass support
currency_context_mismatchBest-supported total conflicts with the supplied currency context
ranking_warningPossible digit corruption in a candidate
degraded_processingOne or more passes failed or the deadline was reached
pass_timeoutAn individual OCR pass timed out
pass_failedAn individual OCR pass raised an error
deadline_exceededThe total deadline was reached

New warning codes may be added in additive updates without a schema version bump. Consumers must not treat an unrecognized warning code as an error.

Evidence grade semantics

GradetotalcurrencyMeaning
"strong"non-nullnon-nullPayment-labelled, INR-confirmed, multi-pass agreement, no conflicts, not degraded
"review"non-nullnon-nullA total was found but not all corroboration criteria were met
"none"nullnullNo supported total extracted

"strong" still requires human confirmation. See Evidence grades.

Diagnostic opt-in

result = recognize("receipt.png", currency="INR", diagnostics=True)
payload = result.to_dict(include_diagnostics=True)
# payload["diagnostics"]["ocr"]["passes"][0]["raw_text"]

diagnostics is absent from the output by default. Calling to_dict(include_diagnostics=True) on a result produced without diagnostics=True raises ValueError.

Python-only properties (not in JSON)

PropertyTypeValue
needs_reviewboolevidence_grade != "strong"
pass_countintAlias for passes_completed

Migration from the old alpha schema

If you parsed the old alpha JSON ("payable-receipt-ocr/v1"), update the following:

1. schema_version string changed:

-"schema_version": "payable-receipt-ocr/v1"
+"schema_version": "payable-receipt-ocr/1"

2. result.needs_review removed from JSON — use evidence_grade != "strong" instead.

3. result.matched_line removed — no replacement.

4. warnings changed from string array to object array:

-"warnings": ["weak_evidence"]
+"warnings": [{"code": "weak_evidence", "message": "..."}]

5. pass_count moved out of result:

-"result": { ..., "pass_count": 6 }
+"processing": { ..., "passes_completed": 6 }

Compatibility policy

After v1, additive changes (new optional fields, new warning codes) will not bump the schema version. Consumers must ignore unknown fields to remain forward-compatible.

On this page