Validar una transferencia SPEI (directa)
Objetivo
Enviar los campos de una transferencia SPEI a Banxico y recibir un veredicto legible (valid, not_found, cep_unavailable, returned o error). Cuando la transferencia está confirmada puedes descargar el certificado Comprobante Electrónico de Pago (CEP) en PDF o XML.
Requisitos previos
- Una API key activa (
veriko_…). Si no tienes una genérala desde la consola de Veriko, en la sección API y abre el panel API Key. - Los datos de la transferencia:
- fecha de envío (
fecha): el día en que se hizo la transferencia, no el día de operación que imprime el CEP. Ver fechas y zona horaria. - monto (
monto). - clave de rastreo (
clave_rastreo) o referencia numérica (referencia_numerica) — Al menos uno. - banco emisor (
emisor). - banco receptor (
receptor) — ambos admiten el código o el nombre del banco; los tienes en la lista de bancos. - cuenta beneficiaria (
cuenta_beneficiaria— CLABE, Tarjeta o Celular), o una lista de cuentas candidatas (cuentas_candidatas) cuando no sabes cuál de varias recibió el pago: ver el paso 7. - «Pago a Banco» (
receptor_participante, opcional):0para una transferencia a un cuentahabiente (valor por defecto) o1cuando el beneficiario es la propia institución receptora —pago de tarjeta de crédito, de crédito o de servicios a la institución.
- fecha de envío (
Pasos
1. Enviar la validación
Manda los campos de la transferencia a POST /v1/validate.
Proporciona clave_rastreo, referencia_numerica o ambos — más identificadores aumentan la precisión.
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.50,
"clave_rastreo": "MXBA20250315001234",
"emisor": "40014",
"receptor": "40012",
"cuenta_beneficiaria": "012180004412345678"
}'Respuesta síncrona (200) — la consulta al CEP se completó en el momento:
{
"data": {
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"type": "validation",
"attributes": {
"status": "valid",
"processing_time_ms": 1320,
"created_at": "2025-03-15T14:22:10Z",
"completed_at": "2025-03-15T14:22:11Z"
},
"links": {
"self": "/v1/validations/f47ac10b-58cc-4372-a567-0e02b2c3d479",
"cep_xml": "/v1/validations/f47ac10b-58cc-4372-a567-0e02b2c3d479/cep?format=xml",
"cep_pdf": "/v1/validations/f47ac10b-58cc-4372-a567-0e02b2c3d479/cep?format=pdf"
}
}
}Valores de estado:
| status | Significado |
|---|---|
valid | Transferencia confirmada en el CEP de Banxico. |
not_found | No existe registro de una transacción con esos datos. |
cep_unavailable | Banxico encontó la transacción pero no pudo proporcionar el CEP. |
returned | El pago se liquidó y la institución beneficiaria lo devolvió después. El CEP, si existe, se sigue entregando. |
error | Fallo inesperado en el sistema. |
2. Manejar el modo asíncrono (status:"async")
Cuando añades ?async=1 a la URL del POST, la API responde 202 y la validación se procesa en segundo plano:
curl -X POST 'https://api.veriko.mx/v1/validate?async=1' \
-H 'Authorization: Bearer veriko_••••' \
-H 'Content-Type: application/json' \
-d '{ "fecha": "...", "monto": ..., "clave_rastreo": "...", ... }'Respuesta 202:
{
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "validation",
"attributes": { "status": "queued" }
}
}Para sondear el estado de la validacion: GET /v1/validations/{id} cada 2–5 segundos hasta que status alcance un valor terminal (valid, not_found, cep_unavailable, invalid, returned, failed, error):
curl 'https://api.veriko.mx/v1/validations/a1b2c3d4-e5f6-7890-abcd-ef1234567890' \
-H 'Authorization: Bearer veriko_••••'3. Descargar el comprobante oficial (CEP)
Cuando status es valid, la respuesta trae links.cep_pdf y links.cep_xml, y con ellos puedes descargar el comprobante oficial expedido por el Banco de México (Banxico):
# PDF
curl 'https://api.veriko.mx/v1/validations/f47ac10b-.../cep?format=pdf' \
-H 'Authorization: Bearer veriko_••••' \
--output cep.pdf
# XML
curl 'https://api.veriko.mx/v1/validations/f47ac10b-.../cep?format=xml' \
-H 'Authorization: Bearer veriko_••••' \
--output cep.xmlTambién puedes hacer que el comprobante llegue al chat de Telegram vinculado a la cuenta, sin descargarlo tú:
curl -X POST 'https://api.veriko.mx/v1/validations/f47ac10b-.../cep/send-telegram' \
-H 'Authorization: Bearer veriko_••••'La respuesta es un 202 inmediato con queued: true: confirma que se encoló, no que se entregó. El documento aparece en el chat unos segundos después, y es el mismo PDF que devuelve la descarga de arriba.
Hacen falta dos condiciones, y cada una falla con su propio código: que el CEP exista —si no, 409 cep_not_available— y que la cuenta tenga Telegram vinculado —si no, 409 telegram_not_linked—. Lo segundo se comprueba en telegram_linked de GET /v1/users/me, y se configura en Gestionar notificaciones.
4. Activar reintentos automáticos en resultados no satisfactorios
Para los resultados not_found o cep_unavailable, puedes activar reintentos automáticos para que la plataforma consulte Banxico nuevamente sin llamadas adicionales de tu parte:
curl -X PUT 'https://api.veriko.mx/v1/validations/a1b2c3d4-.../retry-policy' \
-H 'Authorization: Bearer veriko_••••' \
-H 'Content-Type: application/json' \
-d '{
"retry_policy": {
"enabled": true,
"max_retries": 3,
"interval_seconds": 600,
"outcomes": ["not_found", "cep_unavailable"]
}
}'También puedes incluir retry_policy directamente en el cuerpo para configurar reintentos en una sola llamada. Consulta el historial de reintentos de la validación con GET /v1/validations/{id}/retry-attempts.
Para más información de los reintentos, consulta Políticas de reintentos
5. Evitar envíos duplicados
Usa el encabezado Idempotency-Key para deduplicar reintentos de red. Cualquier petición con la misma clave y body idéntico dentro de 24 horas devuelve la respuesta en caché:
curl -X POST 'https://api.veriko.mx/v1/validate' \
-H 'Authorization: Bearer veriko_••••' \
-H 'Idempotency-Key: mi-sistema-txn-id-12345' \
-H 'Content-Type: application/json' \
-d '{ ... }'Para más información sobre idempotencia, consulta Idempotencia
6. Relacionar la validación con tu pedido
Para ligar cada validación a un pedido propio, envía client_ref en el cuerpo de POST /v1/validate o de POST /v1/validate-ocr, también con ?async=1. Es un texto de 1 a 64 caracteres, sin saltos de línea ni emoji, y no debe contener datos personales (nombres, correos electrónicos ni cuentas).
curl -X POST 'https://api.veriko.mx/v1/validate' \
-H 'Authorization: Bearer veriko_••••' \
-H 'Content-Type: application/json' \
-d '{ "client_ref": "orden-4812", ... }'La referencia vuelve tal cual en la respuesta, en GET /v1/validations/{id}, en la lista y en el payload de los webhooks de validación (data.attributes.client_ref). Para encontrar la validación de un pedido, GET /v1/validations?client_ref=orden-4812 devuelve solo las que llevan esa referencia exacta. Un valor que no cumple las reglas responde 422 invalid_client_ref sin consumir cuota.
7. Probar varias cuentas en una sola validación
Cuando sabes que el pago fue a una de varias cuentas, pero no a cuál (por ejemplo, un colegio con más de una cuenta), envía cuentas_candidatas en lugar de cuenta_beneficiaria. Es una sola validación y una sola unidad de cuota, lleve las cuentas que lleve.
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.50,
"clave_rastreo": "MXBA20250315001234",
"emisor": "40014",
"cuentas_candidatas": [
"012180004412345678",
"002010077777777771"
]
}'| regla | detalle |
|---|---|
| Cantidad | De 2 a 3 cuentas. Si se excede, el 422 informa el máximo vigente en meta.max. |
| Formato | Cada cuenta es una CLABE (18 dígitos con dígito verificador válido), una tarjeta (16, con Luhn válido) o un celular DiMo (10). Se envían como texto y sin repetirse. |
Con cuenta_beneficiaria | Son excluyentes. Enviar las dos responde 422 cuenta_y_candidatas_excluyentes. |
| Lista inválida | 422 cuentas_candidatas_invalidas, con la posición que falló en field_errors. |
Los dos 422 ocurren antes de consumir cuota. También valen en POST /v1/validate-ocr y con ?async=1.
La consulta recorre las cuentas en el orden enviado y adopta la primera que coincide con la transferencia. La respuesta trae la ganadora completa en normalized_data.cuenta_beneficiaria y su posición en candidate_match: index (desde 0) y account_last4.
{
"data": {
"attributes": {
"status": "valid",
"normalized_data": { "cuenta_beneficiaria": "002010077777777771" },
"candidate_match": { "index": 1, "account_last4": "7771" }
}
}
}En el payload de los webhooks de validación candidate_match también viaja (data.attributes.candidate_match), sin la cuenta completa. Si ninguna cuenta coincide, la respuesta es un 422 con el motivo en code (cuenta_unresolvable_after_probes cuando no hay un diagnóstico más preciso), y la unidad de cuota se consume igual: las consultas a Banxico ya se hicieron. La excepción es el rechazo de la petición antes de consultar (preflight_failed), que no consume cuota.
8. Volver a consultar un valid
Un valid es el veredicto del momento de la consulta: el CEP acredita que la transferencia se liquidó, y la institución receptora puede devolver el dinero después. Para confirmar que sigue ahí antes de dar por cerrado un pago, POST /v1/validations/{id}/recheck vuelve a preguntar a Banxico sólo por el estado del pago, con los datos que la validación ya guardó:
curl -X POST 'https://api.veriko.mx/v1/validations/f47ac10b-58cc-4372-a567-0e02b2c3d479/recheck' -H 'Authorization: Bearer veriko_••••'La respuesta es la misma validación, y meta.recheck dice cuándo se consultó (checked_at), si cambió (changed) y el veredicto que tenía antes (previous_status):
{
"data": {
"attributes": {
"status": "returned",
"banxico_status": "returned",
"banxico_result": { "_payment_status": { "code": "devuelto", "reversed": true } }
}
},
"meta": {
"recheck": { "checked_at": "2026-10-02T09:15:44Z", "changed": true, "previous_status": "valid" }
}
}- La operación no consume cuota ni cuenta como una validación nueva, y el CEP se conserva.
- Sólo responde para una validación de las últimas 72 horas, con una consulta cada 10 minutos por validación: pasado el plazo es
422 recheck_window_expired, y una consulta repetida antes de tiempo es429 recheck_rate_limitedconRetry-After. - Una validación en
returnedresponde su estado actual sin consultar a Banxico, y cualquier otro estado es422 recheck_not_eligible. - Si Banxico no entrega un estado legible, la respuesta es
503 recheck_unavailabley se puede reintentar de inmediato.
Sin pedirla, el servicio también revisa las validaciones valid de las últimas 72 horas, y avisa con el webhook validation.returned (arquitectura de webhooks).
Siguientes pasos
- Guarda cuentas en las que recibes transferencias y agiliza futuras validaciones: Crear beneficiario.
- Valida a partir de una imagen de comprobante en lugar de campos manuales: ver Validar vía OCR.
- Consulta el historial completo de validaciones:
GET /v1/validations(filtra por estado, rango de fechas, tipo de validación).