https://api.veriko.mx/v1/validate-ocrValidate a SPEI transfer (OCR)
How-to guide →Takes a receipt image, extracts the fields from it, and validates against the Banxico CEP in a single call. The image travels as base64 in image or as a remote address in image_url. cuenta_beneficiaria is optional here, unlike in the direct validation: when the receipt shows the account masked, it is resolved against the accounts registered at /v1/beneficiaries. That is the practical reason to have beneficiaries registered before using this endpoint. Asynchronous mode. With ?async=1 the response is an immediate 202 and the verdict is collected by polling GET /v1/validations/{id}. The image is processed before responding even in this mode, so this 202 takes longer than the direct validation's. Every call consumes plan quota, just as the direct validation does and with the same deduction on acceptance. Exhausting the quota returns 429; the detail is in quotas and plans. The Idempotency-Key header behaves exactly as in POST /v1/validate, which is the preferable route when the receipt fields are already known: it skips the extraction step and its margin of error. After the verdict, the full detail — with ocr_result, image_path, and retry_state — is at GET /v1/validations/{id}.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
async | query | string | optional | When Default: |
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 11111111-2222-3333-4444-555555555555 |
| 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 iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg== |
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 https://storage.example.com/receipts/comprobante-2025-03.jpg |
cuenta_beneficiaria | string (pattern) | optional | Optional hint for the beneficiary account: 18-digit CLABE, 16-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 |
retry_policy | object | optional | Automatic retry policy for this validation. If omitted, the policy configured at |
curl -X POST 'https://api.veriko.mx/v1/validate-ocr' \
-H 'Authorization: Bearer veriko_••••' \
-H 'Content-Type: application/json' \
-d '{
"image": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
}'Python example — coming soon.
JavaScript example — coming soon.
PHP example — coming soon.
| Field | Type | Description |
|---|---|---|
type* | string | JSON:API resource type. Always validation |
id* | string (uuid) | Unique identifier of the validation (UUID v4). e.g.3fa85f64-5717-4562-b3fc-2c963f66afa6 |
attributes* | object | Canonical validation data. |
validation_type | string |
direct |
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. e.g.false |
status | string | Lifecycle state: valid |
banxico_status | string | nullnullable | Banxico-reported status after query. valid |
processing_time_ms | integer | nullnullable | Milliseconds from enqueue to terminal resolution. e.g.1320 |
request_data | object | Literal snapshot of the original request fields. |
created_at | string (date-time) | UTC timestamp of enqueue. e.g.2025-03-15T14:22:10Z |
completed_at | string | nullnullable | UTC timestamp of terminal resolution. 2025-03-15T14:22:11Z |
enqueued_at | string | nullnullable | Timestamp of bus enqueue. e.g.2026-04-30T10:15:00Z |
processing_started_at | string | nullnullable | Timestamp of the first worker XCLAIM. e.g.2026-04-30T10:15:00Z |
expires_at | string | nullnullable | Expiration timestamp for queued validations. After this, the job moves to 2026-04-30T10:15:00Z |
etag_version | integer | nullnullable | Incremental version used for 1 |
image_path | string | nullnullable | Relative path of the receipt image. OCR only. e.g.ocr/2026/04/a1b2c3d4.jpg |
ocr_result | object | nullnullable | Raw OCR result. OCR only. |
ocr_confidence | number | nullnullable | OCR confidence 0–1. OCR only; 0.94 |
normalized_data | object | nullnullable | Normalized post-OCR fields used to query Banxico. |
normalization_warnings | array | nullnullable | Warnings emitted by the normalization pipeline. |
is_masked | boolean | nullnullable | Whether the receipt has a masked PAN. |
banxico_result | object | nullnullable | Literal payload returned by Banxico CEP. |
error_message | string | nullnullable | Human-readable error message when terminal. e.g.Banxico no respondió tras tres intentos. |
error_code | ValidationErrorCode | null | Machine-readable error code when terminal. |
batch_id | integer | nullnullable | Bulk import batch identifier if applicable. e.g.318 |
batch_position | integer | nullnullable | Position within the batch (1-indexed). e.g.12 |
retry_state | object | Full retry cycle state. Always present; if retries are not active, |
enabled | boolean | Whether the retry cycle is active for this validation. e.g.true |
max_retries | integer | nullnullable | Configured maximum number of retries. The upper bound depends on the plan ( 3 |
interval_seconds | integer | nullnullable | Interval between retries in seconds (300–86400). 600 |
outcomes | array | nullnullable | Validation outcomes that trigger a retry ( ["not_found","cep_unavailable","error"] |
attempts_completed | integer | Number of retries completed so far. e.g.1 |
next_attempt_at | TimestampUTC | null | Timestamp of the next scheduled retry. |
resolved_at | TimestampUTC | null | Timestamp when a retry resolved the validation to |
exhausted_at | TimestampUTC | null | Timestamp when retries were exhausted without resolution. |
cancelled_at | TimestampUTC | null | Timestamp when the cycle was explicitly cancelled. |
terminal_state | string | nullnullable | Terminal state of the cycle: pending |
links | object | Related links (JSON:API |
self | string | URL of the validation. e.g./v1/validations/3fa85f64-5717-4562-b3fc-2c963f66afa6 |
cep_xml | string | nullnullable | URL of the CEP in XML format. /v1/validations/a1b2c3d4-e5f6-7890-abcd-ef0123456789/cep.xml |
cep_pdf | string | nullnullable | URL of the CEP in PDF format. /v1/validations/a1b2c3d4-e5f6-7890-abcd-ef0123456789/cep.pdf |
| 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 |
| 413 | 4xx | The request body exceeds the maximum accepted size (body_too_large). | 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 |
| Header | Type | Description |
|---|---|---|
Idempotent-Replayed | string | Present only when the client sent Idempotency-Key. false for fresh responses; true when replayed from cache. |
| Status | Code | Example |
|---|---|---|
| 400 | invalid_idempotency_key | Invalid Idempotency-Key format. Allowed: A-Z a-z 0-9 _ - (1-255 chars). Envelope
|
| 401 | unauthorized | Invalid or missing authentication credentials. Envelope
|
| 409 | idempotency_key_in_progress | A request with this Idempotency-Key is still being processed. Envelope
Response headers
|
| 413 | body_too_large | The request body is too large. Envelope
|
| 429 | rate_limit_exceeded | Rate limit exceeded. Try again in 45 seconds. Envelope
Response headers
|
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.