POST https://api.veriko.mx/v1/validate-ocr

Validate a SPEI transfer (OCR)

Audience
public
Auth
API key
Permission
validations_ocr:create
How-to guide →

Uploads a receipt image, extracts fields via OCR, and validates against Banxico CEP in a single call. Accepts image as base64 (image) or remote URL (image_url). The cuenta_beneficiaria field is optional: if the image shows a masked account, the system resolves it from accounts registered at /v1/beneficiaries. Use ?async=1 to enqueue the OCR pipeline and receive an immediate 202; the image is sanitized and persisted synchronously before enqueuing. Poll GET /v1/validations/{id} until a terminal status. Accepts optional Idempotency-Key with the same semantics as POST /v1/validate — use that endpoint when the receipt fields are already known. After the verdict, full details (including ocr_result, image_path, and retry_state) are available via GET /v1/validations/{id}.

Parameters
Parameter In Type Required Description
async query string optional

When `1`/`true`/`yes`, the validation is queued and the server responds 202 immediately with a `validation_id`; the client polls `GET /v1/validations/{id}` until the terminal state. Without the flag or with `0`, the response is synchronous and returns the final result in the same POST.

Default: 0

Idempotency-Key header string optional

Optional client-generated key (Stripe-style) that guarantees the request is processed **exactly once** within a 24-hour TTL. The scope is `(user_id, endpoint, key)`. Retries with the same key and the same body return the byte-for-byte cached response with the `Idempotent-Replayed: true` header, without consuming rate-limit quota, without re-firing webhooks, and without creating a new `validations` row. Same key with a different body → 422 `idempotency_key_reused`. Same key with an in-flight request → 409 `idempotency_key_in_progress`. 5xx responses are not cached (retries with the same key are processed for real). Format: 1–255 characters, alphanumeric + `_` + `-`.

Parameters
Parameter Type Required Description
image string (byte) Required unless image_url is sent

Receipt image encoded in base64 (JPEG, PNG, or WebP). Maximum size: 12 MB. Maximum dimensions: 12,000 px per side. Mutually exclusive with `image_url`; if both are provided, `image` takes precedence.

image_url string (uri) Required unless image is sent

Public HTTPS URL of the receipt image. The server downloads the image on receipt and persists it the same way as the `image` flow. Same format and size limits apply (JPEG, PNG, or WebP, maximum 12 MB).

e.g. https://storage.example.com/receipts/comprobante-2025-03.jpg
cuenta_beneficiaria string (pattern) optional

Optional hint for the beneficiary account: 18-digit CLABE, 13-19 digit card, or 10-digit DiMo phone, auto-detected by length. Used to disambiguate when OCR extracts a partial or masked number from the receipt.

e.g. 012180004412345678
banco_emisor string (pattern) optional

Optional sender bank override. Accepted only when the value is a 4-5 digit numeric Banxico participant code. Textual values are ignored — OCR-extracted text is trusted over the client hint.

e.g. 40012
banco_receptor string (pattern) optional

Optional receiver bank override. Accepted only when the value is a 4-5 digit numeric Banxico participant code. Textual values are ignored. Useful when OCR could not derive the receiving bank or the client knows the exact Banxico code.

e.g. 40002
retry_policy object optional

Automatic retry policy for this validation. If omitted, the policy configured at `PUT /v1/users/me/retry-policy` is used. Retries do not consume validation quota.

Request
curl -X POST 'https://api.veriko.mx/v1/validate-ocr' \
  -H 'Authorization: Bearer mxcep_••••' \
  -H 'Content-Type: application/json' \
  -d '{
    "image": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
  }'

Python example — coming soon.

JavaScript example — coming soon.

PHP example — coming soon.

Response 2xx Validation
Field Type Description
id * string (uuid)

Unique identifier of the validation (UUID v4).

type * string

JSON:API resource type. Always `validation`.

attributes * object

Canonical validation data.

validation_type string

`direct` for text-parameter requests; `ocr` for receipt image requests.

is_playground boolean

Whether the validation ran in playground mode. Playground executions still query Banxico but do not consume quota, emit webhooks, or fire notifications.

status string

Lifecycle state: `queued` — enqueued for worker; `processing` — worker handling; `valid` — CEP found and data matches; `not_found` — Banxico queried, transfer not found; `cep_unavailable` — Banxico unreachable; `invalid` — payload rejected post-enqueue; `failed` — terminal failure; `error` — retriable error (Banxico HTTP 5xx).

banxico_status string | null nullable

Banxico-reported status after query. `null` before query.

processing_time_ms integer | null nullable

Milliseconds from enqueue to terminal resolution.

request_data object

Literal snapshot of the original request fields.

created_at string (date-time)

UTC timestamp of enqueue.

completed_at string | null nullable

UTC timestamp of terminal resolution. `null` while `status` is `queued`/`processing`.

enqueued_at string | null nullable

Timestamp of bus enqueue.

processing_started_at string | null nullable

Timestamp of the first worker XCLAIM.

expires_at string | null nullable

Expiration timestamp for queued validations. After this, the job moves to `failed`.

etag_version integer | null nullable

Incremental version used for `If-None-Match` polling.

image_path string | null nullable

Relative path of the receipt image. OCR only.

ocr_result object | null nullable

Raw OCR result. OCR only.

ocr_confidence number | null nullable

OCR confidence 0–1. OCR only; `null` for `direct`.

normalized_data object | null nullable

Normalized post-OCR fields used to query Banxico.

normalization_warnings array | null nullable

Warnings emitted by the normalization pipeline.

is_masked boolean | null nullable

Whether the receipt has a masked PAN.

banxico_result object | null nullable

Literal payload returned by Banxico CEP.

error_message string | null nullable

Human-readable error message when terminal.

error_code string | null nullable

Machine-readable error code when terminal.

batch_id integer | null nullable

Bulk import batch identifier if applicable.

batch_position integer | null nullable

Position within the batch (1-indexed).

retry_state object

Full retry cycle state. Always present; if retries are not active, `enabled=false` and policy fields are `null`. Bulk import rows always have `enabled=false`.

enabled boolean

Whether the retry cycle is active for this validation.

max_retries integer | null nullable

Configured maximum number of retries. The upper bound depends on the plan (`retry_max_retries`) or the global default `max_retries_cap` (typically 5–10). `null` if `enabled=false`.

interval_seconds integer | null nullable

Interval between retries in seconds (300–86400). `null` if `enabled=false`.

outcomes array | null nullable

Validation outcomes that trigger a retry (`not_found`, `cep_unavailable`, `error`). `null` if `enabled=false`.

attempts_completed integer

Number of retries completed so far.

next_attempt_at union

Timestamp of the next scheduled retry. `null` if the cycle is in a terminal state or if no retries are active.

resolved_at union

Timestamp when a retry resolved the validation to `valid`. `null` if the cycle has not ended by resolution.

exhausted_at union

Timestamp when retries were exhausted without resolution. `null` if the cycle has not ended by exhaustion.

cancelled_at union

Timestamp when the cycle was explicitly cancelled. `null` if not cancelled.

terminal_state string | null nullable

Terminal state of the cycle: `pending` — active, no final result yet; `resolved` — a retry obtained `valid`; `exhausted` — all attempts used; `cancelled` — cancelled by the user.

links object

Related links (JSON:API `links`).

self string

URL of the validation.

cep_xml string | null nullable

URL of the CEP in XML format. `null` if `status` is not `valid`.

cep_pdf string | null nullable

URL of the CEP in PDF format. `null` if `status` is not `valid`.

Response status codes POST /v1/validate-ocr
Status Class Description Body
200 2xx Validation verdict with fields extracted by OCR. The `ocr_confidence` field (0–1) indicates extraction confidence. No body
202 2xx Validation enqueued. Poll `GET /v1/validations/{id}` until one of the terminal statuses (`valid`, `not_found`, `cep_unavailable`, `invalid`, `failed`, `error`). `meta.next_poll_after_seconds` hints the recommended first-poll interval. ValidationQueued
400 4xx The `Idempotency-Key` header value does not meet the allowed format (alphanumeric + `_` + `-`, 1–255 characters). ErrorResponse
401 4xx Authentication is required or the provided credentials are invalid. ErrorResponse
409 4xx A request with the same `Idempotency-Key` is still being processed. The client must retry after the number of seconds indicated by the `Retry-After` header. In-flight rows older than `idempotency.in_flight_timeout_seconds` (300 by default) are automatically treated as zombies and cleaned up on the next attempt. ErrorResponse
422 4xx Request validation failed. Body-level codes: `image_or_image_url_required`, `invalid_image`, `invalid_image_format`, `image_too_large`, `invalid_url`, `invalid_url_scheme`, `url_ssrf_blocked`, `invalid_clabe_checksum`. Post-decode sanitizer codes (both sync path and async pre-dispatch): `image_too_small`, `image_mime_mismatch`, `image_dimensions_too_large`, `image_decompression_bomb`, `image_polyglot_detected`, `image_url_unreachable`, `image_url_too_large`, `image_url_too_many_redirects`. In async mode generic sanitization failure is reported as `image_invalid`. Reused `Idempotency-Key` with different body → `idempotency_key_reused`. ErrorResponse
429 4xx Rate limit exceeded ErrorResponse
503 5xx OCR not configured on this deploy (`ocr_not_configured`), Banxico upstream failure (`banxico_rate_limit_exhausted`), or async dispatch failure (Redis down → `dispatch_failed`). Wait a few minutes and retry. ErrorResponse
Response headers 200
Header Type Description
Idempotent-Replayed string Present only when the client sent `Idempotency-Key`. `false` for fresh responses; `true` when replayed from cache.
Errors from POST /v1/validate-ocr
Status Code Detail
400 invalid_idempotency_key

Invalid Idempotency-Key format. Allowed: A-Z a-z 0-9 _ - (1-255 chars).

Envelope
meta.request_id
3c4d5e6f7a8b
401 unauthorized

Invalid or missing authentication credentials.

Envelope
meta.request_id
c4d5e6f7a8b9
409 idempotency_key_in_progress

A request with this Idempotency-Key is still being processed.

Envelope
meta.request_id
2b3c4d5e6f7a
Response headers
  • Retry-After : integer — Seconds to wait before retrying
429 rate_limit_exceeded

Rate limit exceeded. Try again in 45 seconds.

Envelope
meta.request_id
f7a8b9c0d1e2
Response headers
  • Retry-After : integer — Seconds to wait before retrying. Matches the endpoint's rate-limit window (typically 60s for list endpoints, 1-5s for in-flight idempotent operations).
  • X-RateLimit-Limit : integer — Configured request cap for this bucket (emitted only on 429).
  • X-RateLimit-Remaining : integer — Requests remaining in the current window — always 0 at the moment of the 429 (emitted only on 429).
  • X-RateLimit-Reset : integer — Absolute Unix epoch (seconds) when the window resets. Emitted only on 429, alongside Retry-After. Per-endpoint overrides exist (e.g. `rate_limited_login`).

Retry policy

POST /v1/validate-ocr

Automatic retry policy. Configures when and how many times the system retries a validation that returned an eligible outcome (`not_found`, `cep_unavailable` or `error` by default).

Attempts
Interval
5 min – 1 h × 24
Eligible outcomes
not_found , cep_unavailable , error

This operation accepts an optional retry policy.