Cómo usar
Agrega el header X-Sandbox-Scenario: <escenario> a cualquier llamada de cash-in o cash-out. Sin el header, el sandbox usa el escenario success por defecto.
curl -X POST https://sandbox.mx.ntxpay.com/api/spei/cash-out \
-H "Authorization: Bearer $TOKEN" \
-H "X-Sandbox-Scenario: error:insufficient-funds" \
-H "Content-Type: application/json" \
-d '{
"amountCentavos": 15000,
"destinationClabe": "012180001234567890",
"beneficiaryName": "Maria Lopez",
"externalId": "test-error-001"
}'
El header solo controla el webhook asíncrono. La respuesta HTTP siempre es 201 Created con status: PENDING, sin importar el escenario. El resultado final (CONFIRMED, FAILED o EXPIRED) llega en el webhook ~1 segundo después.
Escenarios disponibles
Escenarios de éxito
| Header Value | Comportamiento síncrono | Webhook |
|---|
success (default) | 201 PENDING | CONFIRMED en ~1s |
| (sin header) | 201 PENDING | CONFIRMED en ~1s |
Escenarios de error
| Header Value | Comportamiento síncrono | Webhook |
|---|
error:insufficient-funds | 201 PENDING | FAILED con errorCode: INSUFFICIENT_FUNDS |
error:invalid-clabe | 201 PENDING | FAILED con errorCode: INVALID_CLABE |
error:account-not-found | 201 PENDING | FAILED con errorCode: ACCOUNT_NOT_FOUND |
error:account-blocked | 201 PENDING | FAILED con errorCode: ACCOUNT_BLOCKED |
error:duplicate-external-id | 201 PENDING | FAILED con errorCode: DUPLICATE_EXTERNAL_ID |
error:bank-rejected | 201 PENDING | FAILED con errorCode: BANK_REJECTED |
Escenarios de atraso
| Header Value | Comportamiento síncrono | Webhook |
|---|
delayed:5s | 201 PENDING | CONFIRMED tras +5s |
delayed:30s | 201 PENDING | CONFIRMED tras +30s |
delayed:60s | 201 PENDING | CONFIRMED tras +60s |
El delay máximo permitido es de 120 segundos — valores mayores se truncan automáticamente.
Ejemplo: webhook de éxito
{
"event": "cash_out",
"deliveryId": "8e2c5b6f-3a12-4b9c-9a18-77a2b3c4d5e6",
"createdAt": "2026-03-26T10:00:00.000Z",
"transaction": {
"id": 12345,
"externalId": "test-success-001",
"paymentMethod": "SPEI",
"direction": "out",
"type": "cash_out",
"status": "CONFIRMED",
"provider": "sandbox",
"amountCentavos": 15000,
"clabe": "012180001234567890",
"referenceNumerical": "9876543",
"createdAt": "2026-03-26T09:59:59.000Z",
"confirmedAt": "2026-03-26T10:00:00.000Z",
"counterpart": {
"name": "Maria Lopez",
"taxId": null,
"bank": {}
}
},
"errorCode": null,
"errorMessage": null,
"metadata": {}
}
Ejemplo: webhook de falla
{
"event": "cash_out",
"deliveryId": "1a3f9e8d-2c47-4b9c-aa18-77a2b3c4d5e6",
"createdAt": "2026-03-26T10:01:00.000Z",
"transaction": {
"id": 12346,
"externalId": "test-error-001",
"paymentMethod": "SPEI",
"direction": "out",
"type": "cash_out",
"status": "FAILED",
"provider": "sandbox",
"amountCentavos": 15000,
"clabe": "012180001234567890",
"referenceNumerical": null,
"confirmedAt": null
},
"errorCode": "INSUFFICIENT_FUNDS",
"errorMessage": "Cuenta sin saldo suficiente",
"metadata": {}
}
Notas:
- En
status: FAILED, referenceNumerical y confirmedAt son null — la red SPEI nunca confirmó la transacción.
errorCode y errorMessage describen el motivo de la falla.
Restricciones
- El header
X-Sandbox-Scenario funciona exclusivamente en cuentas sandbox.
- Las cuentas de producción que envíen el header reciben:
{
"statusCode": 400,
"message": "X-Sandbox-Scenario header is only supported in sandbox mode."
}
Buenas prácticas
- Prueba todos los escenarios antes de ir a producción — implementa el manejo de
CONFIRMED, PENDING, FAILED y EXPIRED.
- Valida los campos de error — usa
errorCode para decisiones automáticas; reserva errorMessage para logs/usuarios.
- Prueba con delay — verifica que tu sistema maneja bien la entrega lenta del webhook.
- Idempotencia — usa
transaction.id como clave de idempotencia; el mismo webhook puede ser re-entregado.