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

Validate a SPEI transfer (direct)

Audience
public
Auth
API key
Permission
validations:create
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.

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
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.

Request
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.

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
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
Response headers 200
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).
Errors from POST /v1/validate
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

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.