https://api.veriko.mx/v1/validateManually validate a SPEI transfer
How-to guide →Validates a SPEI transfer against the Banxico CEP from the fields you send.
The following data is required:
- Date.
- Amount.
- Tracking key / Numeric reference.
- Sending bank.
- Receiving bank.
- Beneficiary account, or a list of candidate accounts.
The beneficiary account (cuenta_beneficiaria) is required on this operation, unless cuentas_candidatas is sent. When both are missing, the request is rejected with 422 preflight_failed and the field error cuenta_required: the service does not look it up among the saved beneficiaries.
When it is not known which of several accounts the payment went to, cuentas_candidatas replaces cuenta_beneficiaria: from 2 accounts up to the maximum the service accepts (3 by default) in a single validation and with a single quota unit. The query goes through the candidates in the order sent and adopts the first one that matches the transfer; that account comes back in full in normalized_data.cuenta_beneficiaria and its position in candidate_match. If none matches, the response is an HTTP status 422 with the reason in code. The two forms cannot be sent together (422 cuenta_y_candidatas_excluyentes) and a list that does not comply is rejected with 422 cuentas_candidatas_invalidas, both before any quota is consumed.
The final verdict (terminal state) can be one of the following:
valid: The transfer CEP was found and validated.not_found: The transfer CEP was NOT found.cep_unavailable: Banxico recognized the transfer, but the CEP is not currently available.returned: the transfer was settled and the beneficiary institution later sent it back. The CEP, when there is one, is still delivered.error: Internal service error.
A not_found, cep_unavailable, or error verdict is not necessarily final: the CEP may take time to be published. In that case, automatic retries can be used by sending retry_policy in the request body, according to the retry policy.
Each call consumes plan quota, deducted when the request is accepted. If the quota is exhausted, the endpoint returns HTTP status 429 without attempting the validation.
| 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 |
|---|---|---|---|
fecha | string (date) (pattern, 10–10) | required | Sending date of the transfer in ISO 8601 format (YYYY-MM-DD). Banxico locates the CEP with it, and it can differ from the operation day the CEP prints ( 2025-03-15 |
monto | number (double) (> 0) | required | Transfer amount in Mexican pesos (MXN), greater than zero and with up to two decimal places. Scientific notation (e.g. 15000.5 |
clave_rastreo | string (1–30) | Required unless referencia_numerica is sent | Transfer tracking key, between 1 and 30 characters. Accepts letters, numbers, spaces, and the punctuation marks Banxico allows (such as hyphens, slashes, periods, commas, MXBA20250315001234 |
referencia_numerica | string (pattern, 1–?) | Required unless clave_rastreo is sent | Numeric transfer reference, between 1 and 7 digits. Accepts only numbers and spaces. Required when 1234567 |
emisor | string (?–255) | optional | SPEI name or code of the sending bank. When it is missing or incorrect, the tracking key may provide it. If it still cannot be resolved, the response is an HTTP status BANCO NACIONAL DE MEXICO |
receptor | string (?–255) | optional | SPEI name / code of the receiving bank. Optional when the bank can be identified from BBVA MEXICO |
cuenta_beneficiaria | string (pattern) | optional | Receiving bank account for the transfer. It is required when 012180004412345678 |
cuentas_candidatas | array<string> (items: 2–10, unique items) | optional | Accounts among which the receiving one is, for when it is not known which. It replaces ["012180004412345678","002010077777777771"] |
receptor_participante | integer (enum) | optional | Whether the transfer beneficiary is the receiving institution itself («Pago a Banco» in the Banxico CEP) rather than one of its account holders. Default: 0 |
retry_policy | object | optional | Automatic retry policy for this validation. If omitted, the general policy configured for the user is applied. Retries do NOT consume validation quota. |
enabled | boolean | optional |
true |
max_retries | integer (≥ 1) | optional | Maximum number of automatic retries. Once reached, the cycle enters the 3 |
interval_seconds | integer (≥ 300, ≤ 86400) | optional | Seconds to wait between each retry (300–86400, i.e., 5 minutes to 24 hours). e.g.600 |
outcomes | array<string> (items: 1–?, unique items) | optional | Outcomes that trigger a retry. By default, they are ["not_found","cep_unavailable","error"] |
client_ref | string (pattern, 1–64) | optional | Reference of your own that is returned as sent in the validation, in the validation webhooks, and as an exact filter of orden-4812 |
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.
Retry policy
POST /v1/validate
Validation retry policy: activation, limit, interval, and eligible outcomes.
- Attempts
- —
- Interval
- 5 min – 1 h × 24
- Eligible outcomes
not_found,cep_unavailable,error
This operation accepts an optional retry policy.
| 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 | Validation mode — direct |
is_playground | boolean |
false |
status | string | Lifecycle state — valid |
banxico_status | string | nullnullable | Verdict reported by Banxico. It can be valid |
processing_time_ms | integer | nullnullable | Milliseconds spent processing the validation. e.g.1320 |
request_data | object | Copy of the original request fields. |
client_ref | string | Reference of your own sent in the request ( orden-4812 |
created_at | string (date-time) | Validation creation timestamp in ISO 8601 UTC. e.g.2025-03-15T14:22:10Z |
completed_at | string | nullnullable | Date and time of completion, in ISO 8601 UTC. 2025-03-15T14:22:11Z |
enqueued_at | string | nullnullable | Queue-entry timestamp in ISO 8601 UTC. e.g.2026-04-30T10:15:00Z |
processing_started_at | string | nullnullable | Processing start timestamp in ISO 8601 UTC. e.g.2026-04-30T10:15:00Z |
expires_at | string | nullnullable | Expiration timestamp for queued validations in ISO 8601 UTC. After this, the validation moves to 2026-04-30T10:15:00Z |
etag_version | integer | nullnullable | Incremental version used with 1 |
image_path | string | nullnullable | Relative reference to the receipt, image or PDF, only when comprobantes/3fa85f64-5717-4562-b3fc-2c963f66afa6.jpg |
image_retained | boolean |
true |
purged_at | string (date-time) | Date and time, in ISO 8601 UTC, when the validation was purged with 2026-10-02T09:30:00Z |
ocr_result | object | nullnullable | Fields extracted from the receipt, only when |
ocr_confidence | number | nullnullable | OCR score from 0 to 1, only when 0.94 |
normalized_data | object | nullnullable | Fields normalized after OCR (when |
normalization_warnings | array | nullnullable | Normalization warnings present. They can explain an unexpected |
duplicate_of | object | Earlier validation that had already confirmed this same transfer in the same account. It only appears when a |
id* | string (uuid) | Identifier of the earlier validation. e.g.3fa85f64-5717-4562-b3fc-2c963f66afa6 |
created_at* | string (date-time) | Creation date and time of the earlier validation, in ISO 8601 UTC. e.g.2026-10-01T15:04:05Z |
account_conflict | object | Conflict between the account sent and the one shown in the image. It only appears on |
sent_last4* | string | Last 4 digits of the account sent in 5678 |
read_last4* | string | Last 4 digits of the account shown in the image (the visible ones, 3 or 4, if it is masked). e.g.9012 |
candidate_match | object | Which of the |
index* | integer | Position (from 0) of the winning account in the 1 |
account_last4* | string | Last 4 digits of the winning account. e.g.7771 |
is_masked | boolean | nullnullable | Whether the beneficiary account is masked in the receipt, be it a CLABE, a card or a phone number. It is included only when it is |
banxico_result | object | nullnullable | Data returned by the Banxico CEP. The typed properties below are the 20 declared by the official SPEI complement schema.
|
operationDate | string | SPEI business day that Banxico assigned to the operation, as reported by the CEP ( 2025-03-15 |
processingTime | string | Operation time as reported by the CEP ( 14:22:10 |
speiKey | string | Operation key within the SPEI system (the CEP's |
trackingKey | string | SPEI tracking key of the operation — the same one sent in the validation request. e.g.MBAN01002503151422ABCDEF |
digitalSignature | string | Digital seal of the CEP ( |
certificateNumber | string | Serial number of the digital certificate Banxico used to seal the CEP. |
cadenaCda | string | Original data string of the operation ( |
senderBank | string | Sending bank of the operation, as identified by the CEP. e.g.BBVA |
senderName | string | Name of the operation's sender, as reported by the CEP. |
senderAccountType | string | Sender's account type (e.g. CLABE or card), as reported by the CEP. |
senderAccount | string | Sender's account number, as reported by the CEP — unmasked in this resource (a private lookup, owned by the validation's account). |
senderRfc | string | Sender's RFC (Mexican tax ID), as reported by the CEP. |
receiverBank | string | Receiving bank of the operation, as identified by the CEP. e.g.STP |
beneficiaryName | string | Name of the operation's beneficiary, as reported by the CEP. |
beneficiaryAccountType | string | Beneficiary's account type (e.g. CLABE or card), as reported by the CEP. |
beneficiaryAccount | string | Beneficiary's account number, as reported by the CEP. Unmasked in this resource (a private lookup, owned by the validation's account). Outgoing webhooks DO mask it to the last 4 digits in |
beneficiaryRfc | string | Beneficiary's RFC (Mexican tax ID), as reported by the CEP. |
amount | number (float) | Operation amount Banxico confirmed via the CEP, in pesos. e.g.1500 |
iva | number (float) | VAT (IVA) of the operation reported by the CEP, in pesos. |
paymentConcept | string | Payment concept of the operation, as reported by the CEP. |
error_message | string | nullnullable | Human-readable error message when terminal. e.g.Banxico no respondió tras tres intentos. |
error_code | ValidationErrorCode | null | Stable code of the terminal error, when there is one.
After |
batch_id | integer | nullnullable | Bulk import job identifier, when applicable. e.g.318 |
batch_position | integer | nullnullable | Position within the job, starting at 1. e.g.12 |
retry_state | object | Full retry cycle state. Always present; if retries are not active, |
enabled | boolean |
true |
max_retries | integer | nullnullable | Configured maximum number of retries; 3 |
interval_seconds | integer | nullnullable | Interval between retries in seconds, from 600 |
outcomes | array | nullnullable | 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 | Date of the next retry; |
resolved_at | TimestampUTC | null | Date when a retry produced |
exhausted_at | TimestampUTC | null | Date when attempts were exhausted; |
cancelled_at | TimestampUTC | null | Cycle cancellation date; |
terminal_state | string | nullnullable | Cycle state: pending |
links | object | Related links (JSON:API |
self | string | Canonical link to the validation details. e.g./v1/validations/3fa85f64-5717-4562-b3fc-2c963f66afa6 |
cep_xml | string | nullnullable | URL of the CEP in XML format. It only appears when /v1/validations/a1b2c3d4-e5f6-4890-abcd-ef0123456789/cep?format=xml |
cep_pdf | string | nullnullable | URL of the CEP in PDF format. It only appears when /v1/validations/a1b2c3d4-e5f6-4890-abcd-ef0123456789/cep?format=pdf |
| Status | Class | Description | Body |
|---|---|---|---|
| 200 | 2xx | Final validation verdict. The status field can be valid, not_found, cep_unavailable, returned, or error. When the response carries links.cep_pdf and links.cep_xml, the CEP certificate is available through GET /v1/validations/{id}/cep. | Validation |
| 202 | 2xx | Validation enqueued. Poll GET /v1/validations/{id} until one of the terminal statuses (valid, not_found, cep_unavailable, invalid, returned, 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. 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, invalid_bank_code (emisor/receptor not recognized), intra_bank_no_cep (sender and receiver are the same bank), invalid_field_type (a field that must be text arrived as an array or object — e.g. emisor, receptor, or referencia_numerica), invalid_receptor_participante, retry_policy_invalid, retry_pending_cap_exceeded, cuenta_y_candidatas_excluyentes (cuenta_beneficiaria and cuentas_candidatas together), cuentas_candidatas_invalidas (the list does not hold from 2 up to the current maximum of valid, distinct accounts) and cuenta_unresolvable_after_probes (no candidate matched the transfer). Also emitted when an Idempotency-Key is reused with a different body (idempotency_key_reused). When the submitted data contradicts itself, the request is rejected before Banxico is queried with preflight_failed and per-field detail in errors (clabe_receptor_mismatch and tarjeta_receptor_mismatch: the account belongs to a bank other than receptor; cuenta_invalid_luhn; clave_fecha_incoherente; clave_longitud_invalida). It is also rejected with preflight_failed when cuenta_beneficiaria is missing (cuenta_required). No preflight_failed rejection on this route consumes quota, whether the data contradicts itself, a field is malformed or the account is absent. | ErrorResponse |
| 429 | 4xx | Rate limit exceeded | ErrorResponse |
| 503 | 5xx | Banxico was unavailable (banxico_rate_limit_exhausted) or the asynchronous validation could not be started (dispatch_failed). The request can be repeated after a few minutes. | ErrorResponse |
| Header | Type | Description |
|---|---|---|
Idempotent-Replayed | string | Only present when the request included the Idempotency-Key header. false marks a fresh response; true marks a response reused from the idempotency cache, which remains valid for 24 hours per account, endpoint, and key. |
| 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
|