https://api.veriko.mx/v1/validate Validate a SPEI transfer (direct)
How-to guide →Validates a SPEI transfer against the Banxico CEP using provided fields: date, amount, tracking key, numeric reference, issuing bank, receiving bank, and beneficiary account. Returns a verdict with status valid, not_found, cep_unavailable, or error. Use ?async=1 to enqueue the operation and receive an immediate 202; poll GET /v1/validations/{id} until a terminal status. Key difference from POST /v1/validate-ocr: this endpoint accepts explicit fields; the OCR endpoint accepts a receipt image and infers the fields automatically. Accepts optional Idempotency-Key for network retry de-dup. Reusing the same key with a different body returns 422 idempotency_key_reused; reusing it while the prior operation is in flight returns 409 idempotency_key_in_progress. 5xx responses are not cached. After a not_found / cep_unavailable / error you can enable automatic retries with PUT /v1/validations/{id}/retry-policy or pass retry_policy directly in the body. The full history is available at GET /v1/validations.
| 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: |
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 + `_` + `-`. |
| Parameter | Type | Required | Description |
|---|---|---|---|
fecha | string (date) (pattern, 10–10) | required | Transfer date in ISO 8601 format (YYYY-MM-DD). Must match the date registered in the CEP; differences of ±1 day may result in `not_found`. e.g.2025-03-15 |
monto | number (double) (> 0) | required | Transfer amount in Mexican pesos (MXN), greater than zero and up to two decimal places. Must match exactly the amount registered in the CEP; any difference may result in `not_found`. e.g.15000.5 |
clave_rastreo | string (1–30) | Required unless referencia_numerica is sent | SPEI tracking key generated by the sending bank: alphanumeric string of 18 to 30 characters. Required if `referencia_numerica` is absent; both can be sent simultaneously for more precise matching. e.g.MXBA20250315001234 |
referencia_numerica | string (pattern, 1–7) | Required unless clave_rastreo is sent | Numeric transfer reference: between 1 and 7 digits. Required if `clave_rastreo` is absent; both can be sent simultaneously. e.g.1234567 |
emisor | string (?–255) | optional | Name of the sending bank. Free text; normalized internally against the Banxico catalog. Ignored for CLABE and card numbers, where the sender is derived from the account prefix. For phone/DiMo (10 digits), providing this field improves bank resolution. e.g.BANCO NACIONAL DE MEXICO |
receptor | string (?–255) | optional | Name of the receiving bank. For CLABE (18 digits) and card (13-19 digits) the bank is derived automatically from the prefix/BIN and this field is ignored. Only relevant when `cuenta_beneficiaria` is 10 digits (phone/DiMo) and cannot be resolved by other means. e.g.BBVA MEXICO |
cuenta_beneficiaria | string (pattern) | optional | SPEI destination account: 18-digit CLABE, 13-19 digit card number, or 10-digit DiMo phone number. The type is auto-detected by length and the appropriate checksum is validated (CLABE: standard check digit; card: Luhn). For phone numbers, the receiving bank is resolved from the user's whitelist then global catalog; if unresolvable, the validation result is `error` with code `bank_code_unresolvable_for_phone`. e.g.012180004412345678 |
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. |
curl -X POST 'https://api.veriko.mx/v1/validate' \
-H 'Authorization: Bearer mxcep_••••' \
-H 'Content-Type: application/json' \
-d '{
"fecha": "2025-03-15",
"monto": 15000.5,
"clave_rastreo": "MXBA20250315001234",
"referencia_numerica": "1234567",
"emisor": "BANCO NACIONAL DE MEXICO",
"receptor": "BBVA MEXICO",
"cuenta_beneficiaria": "012180004412345678"
}'
Python example — coming soon.
JavaScript example — coming soon.
PHP example — coming soon.
| 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`. |
| Status | Class | Description | Body |
|---|---|---|---|
| 200 | 2xx | Synchronous validation verdict. The `status` field takes one of: `valid` (transfer confirmed in the CEP), `not_found` (no record for the provided fields), `cep_unavailable` (Banxico did not respond in time), or `error` (unexpected pipeline failure). When `has_cep: true` the CEP certificate is available via `GET /v1/validations/{id}/cep`. | 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. Typical codes: `clave_or_ref_required`, `required` (fecha/monto), `invalid_date`, `invalid_amount`, `invalid_account_format`, `invalid_account_length`, `invalid_clabe_checksum`, `invalid_card_luhn`, `retry_policy_invalid`, `retry_pending_cap_exceeded`. Also emitted when an `Idempotency-Key` is reused with a different body (`idempotency_key_reused`). | ErrorResponse |
| 429 | 4xx | Rate limit exceeded | ErrorResponse |
| 503 | 5xx | Banxico upstream failure OR async dispatch failure (Redis down → `dispatch_failed`). Returns `banxico_rate_limit_exhausted` when Banxico is throttling and the platform exhausted its automatic retries. Wait a few minutes and retry. | ErrorResponse |
| Header | Type | Description |
|---|---|---|
Idempotent-Replayed | string | Only present when the client sent the `Idempotency-Key` header. `false` for fresh responses; `true` when the response is a replay from the idempotency cache (24h TTL keyed by user+endpoint+key). |
| Status | Code | Detail |
|---|---|---|
| 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
|
| 429 | rate_limit_exceeded | Rate limit exceeded. Try again in 45 seconds. Envelope
Response headers
|
Retry policy
POST /v1/validate
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.