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): 0 para una transferencia a un cuentahabiente (valor por defecto) o 1 cuando el beneficiario es la propia institución receptora —pago de tarjeta de crédito, de crédito o de servicios a la institución.

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:

statusSignificado
validTransferencia confirmada en el CEP de Banxico.
not_foundNo existe registro de una transacción con esos datos.
cep_unavailableBanxico encontó la transacción pero no pudo proporcionar el CEP.
returnedEl pago se liquidó y la institución beneficiaria lo devolvió después. El CEP, si existe, se sigue entregando.
errorFallo 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.xml

Tambié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"
    ]
  }'
regladetalle
CantidadDe 2 a 3 cuentas. Si se excede, el 422 informa el máximo vigente en meta.max.
FormatoCada 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_beneficiariaSon excluyentes. Enviar las dos responde 422 cuenta_y_candidatas_excluyentes.
Lista inválida422 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 es 429 recheck_rate_limited con Retry-After.
  • Una validación en returned responde su estado actual sin consultar a Banxico, y cualquier otro estado es 422 recheck_not_eligible.
  • Si Banxico no entrega un estado legible, la respuesta es 503 recheck_unavailable y 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).