POSThttps://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 from the fields sent:

  • date
  • amount
  • tracking key
  • numeric reference
  • issuing bank
  • receiving bank
  • beneficiary account

The response is a verdict: valid, not_found, cep_unavailable, or error. This is the explicit route. The alternative, POST /v1/validate-ocr, takes the receipt image and infers those same fields from it; the verdict and the rest of the contract are identical. Asynchronous mode. With ?async=1 the operation is enqueued and the response is an immediate 202 carrying the identifier. The verdict is collected by polling GET /v1/validations/{id} until a terminal state; the cadence, and the use of ETag to avoid downloading the same thing twice, are covered in asynchronous operations. Every call consumes plan quota, in synchronous and asynchronous mode alike, and it is deducted when the request is accepted, not when it resolves. Exhausting the quota returns 429; how it is counted, and the caps, are covered in quotas and plans. Idempotency. The Idempotency-Key header is optional and prevents a duplicate validation when a network retry repeats the request. Reusing the key with a different body returns 422 idempotency_key_reused; doing so while the previous operation is still in flight returns 409 idempotency_key_in_progress. A 5xx response is not stored, so a server failure does not leave the key spent. A not_found, cep_unavailable, or error verdict is not necessarily final: the CEP can take time to publish. Automatic retries are enabled with PUT /v1/validations/{id}/retry-policy, or by sending retry_policy in this same body, and how they work is covered in the retry policy. The full history is available at GET /v1/validations.

Parameters
ParameterInTypeRequiredDescription
asyncquerystringoptional

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

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

e.g. 11111111-2222-3333-4444-555555555555
Parameters
ParameterTypeRequiredDescription
fechastring (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
montonumber (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_rastreostring (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_numericastring (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
emisorstring (?–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
receptorstring (?–255)optional

Name of the receiving bank. For CLABE (18 digits) and card (16 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_beneficiariastring (pattern)optional

SPEI destination account: 18-digit CLABE, 16-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_policyobjectoptional

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 veriko_••••' \
  -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 2xxValidation
FieldTypeDescription
type*string

JSON:API resource type. Always validation.

e.g. validation
id*string (uuid)

Unique identifier of the validation (UUID v4).

e.g. 3fa85f64-5717-4562-b3fc-2c963f66afa6
attributes*object

Canonical validation data.

validation_typestring

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

e.g. direct
is_playgroundboolean

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
statusstring

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

e.g. valid
banxico_statusstring | nullnullable

Banxico-reported status after query. null before query.

e.g. valid
processing_time_msinteger | nullnullable

Milliseconds from enqueue to terminal resolution.

e.g. 1320
request_dataobject

Literal snapshot of the original request fields.

created_atstring (date-time)

UTC timestamp of enqueue.

e.g. 2025-03-15T14:22:10Z
completed_atstring | nullnullable

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

e.g. 2025-03-15T14:22:11Z
enqueued_atstring | nullnullable

Timestamp of bus enqueue.

e.g. 2026-04-30T10:15:00Z
processing_started_atstring | nullnullable

Timestamp of the first worker XCLAIM.

e.g. 2026-04-30T10:15:00Z
expires_atstring | nullnullable

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

e.g. 2026-04-30T10:15:00Z
etag_versioninteger | nullnullable

Incremental version used for If-None-Match polling.

e.g. 1
image_pathstring | nullnullable

Relative path of the receipt image. OCR only.

e.g. ocr/2026/04/a1b2c3d4.jpg
ocr_resultobject | nullnullable

Raw OCR result. OCR only.

ocr_confidencenumber | nullnullable

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

e.g. 0.94
normalized_dataobject | nullnullable

Normalized post-OCR fields used to query Banxico.

normalization_warningsarray | nullnullable

Warnings emitted by the normalization pipeline.

is_maskedboolean | nullnullable

Whether the receipt has a masked PAN.

banxico_resultobject | nullnullable

Literal payload returned by Banxico CEP.

error_messagestring | nullnullable

Human-readable error message when terminal.

e.g. Banxico no respondió tras tres intentos.
error_codeValidationErrorCode | null

Machine-readable error code when terminal.

batch_idinteger | nullnullable

Bulk import batch identifier if applicable.

e.g. 318
batch_positioninteger | nullnullable

Position within the batch (1-indexed).

e.g. 12
retry_stateobject

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.

enabledboolean

Whether the retry cycle is active for this validation.

e.g. true
max_retriesinteger | nullnullable

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.

e.g. 3
interval_secondsinteger | nullnullable

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

e.g. 600
outcomesarray | nullnullable

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

e.g. ["not_found","cep_unavailable","error"]
attempts_completedinteger

Number of retries completed so far.

e.g. 1
next_attempt_atTimestampUTC | null

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

resolved_atTimestampUTC | null

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

exhausted_atTimestampUTC | null

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

cancelled_atTimestampUTC | null

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

terminal_statestring | nullnullable

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. null if the validation has no retry cycle.

e.g. pending
linksobject

Related links (JSON:API links).

selfstring

URL of the validation.

e.g. /v1/validations/3fa85f64-5717-4562-b3fc-2c963f66afa6
cep_xmlstring | nullnullable

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

e.g. /v1/validations/a1b2c3d4-e5f6-7890-abcd-ef0123456789/cep.xml
cep_pdfstring | nullnullable

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

e.g. /v1/validations/a1b2c3d4-e5f6-7890-abcd-ef0123456789/cep.pdf
Response status codesPOST /v1/validate
StatusClassDescriptionBody
2002xxSynchronous 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
2022xxValidation 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
4004xxThe Idempotency-Key header value does not meet the allowed format (alphanumeric + _ + -, 1–255 characters). ErrorResponse
4014xxAuthentication is required or the provided credentials are invalid.ErrorResponse
4094xxA 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
4134xxThe request body exceeds the maximum accepted size (body_too_large).ErrorResponse
4224xxRequest 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
4294xxRate limit exceededErrorResponse
5035xxBanxico 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 headers200
HeaderTypeDescription
Idempotent-ReplayedstringOnly 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
StatusCodeExample
400invalid_idempotency_key

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

Envelope
meta.request_id
3c4d5e6f7a8b
401unauthorized

Invalid or missing authentication credentials.

Envelope
meta.request_id
c4d5e6f7a8b9
409idempotency_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
413body_too_large

The request body is too large.

Envelope
meta.request_id
1a2b3c4d5e6f
429rate_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.