Crear una suscripción
event_type: uno de los eventos de la tabla siguiente, o*para todos.callback_url: HTTPS obligatorio; se rechazan localhost e IPs privadas — para desarrollo local usa un túnel HTTPS.secret: mínimo 16 caracteres; se usa para firmar cada entrega. Se almacena cifrado y no puede recuperarse.
Desactivar y reactivar una suscripción
Cuando un callback deja de usarse, desactívalo en vez de borrarlo (las suscripciones nunca se borran: quedandisabled y puedes reactivarlas
cuando quieras):
{ "status": "active" }.
- El toggle rige lo futuro: una suscripción
disableddeja de recibir eventos nuevos, pero las entregas ya encoladas siguen saliendo. - Es idempotente: repetir el estado vigente responde
200sin cambiar nada. - Solo puedes tocar las suscripciones de tu cuenta: una suscripción de
otra cuenta responde
404(es indistinguible de una inexistente).
Eventos
Payload de cada evento
payout_status_changed, una actualización de screening técnico puede ser
pending antes de cualquier débito. Las actualizaciones finales son
completed o failed (con failed el débito ya fue reembolsado cuando
recibes el evento). crypto_withdrawal_status_changed usa completed o
failed.
En payout_status_changed, status_code y status_message son el resultado
normalizado que reporta el core. status_message puede venir vacío cuando el
riel no entrega detalle adicional; se depura antes de la entrega y nunca
contiene la identidad de un proveedor upstream.
Fallos terminales antes del débito
Si el screening falla antes del camino de débito, el evento puede ser terminal constatus: "failed", predebit_failure: true y
funds_debited: false; se omite receipt_url. Un fallo posterior al débito
es distinto: el débito exacto se reembolsa antes del evento terminal. La
plataforma también puede persistir revisiones de transición no terminales,
por lo que un payout puede producir más de una entrega
payout_status_changed; deduplica por X-Webhook-Event-ID y no crees otro
payout.
Formato de entrega
Cada entrega es unPOST JSON con estos headers:
Verificar las firmas
CBPay envía ambos headers de firma en cada entrega de webhook de plataforma. V1 sigue siendo válida y se continúa enviando, por lo que adoptar V2 no rompe la compatibilidad. Las integraciones nuevas deben verificar V2 primero y mantener el fallback a V1 mientras migran los receptores existentes.
Ambas entradas usan los valores de
X-Webhook-Timestamp, X-Webhook-Event
y X-Webhook-Event-ID de la misma solicitud cuando corresponde. El resultado
es un HMAC-SHA256 hexadecimal en minúsculas usando el secreto de la
suscripción. V2 vincula el tipo y el ID del evento a la firma: no tomes esos
valores desde un payload JSON re-serializado. X-Webhook-Delivery-ID sirve
para trazabilidad de la entrega; deduplica por X-Webhook-Event-ID.
Reintentos e idempotencia
- Tu endpoint debe responder 2xx dentro del timeout; cualquier otra respuesta se reintenta.
- Hasta 5 intentos con backoff incremental:
- Usa
X-Webhook-Event-IDpara deduplicar: el mismo evento puede llegar más de una vez (entregas at-least-once). - Si los 5 intentos fallan, el evento no se reenvía — recupera el estado
con el
GETdel recurso (por eso ningún flujo debe depender SOLO del webhook).
Tu idempotency key vuelve en el payload
Cuando el evento viene de una operación que creaste con unaidempotency_key (un payout, un payin con tarjeta, un retiro crypto, un
envío desde wallet segregada, un refund…), el payload la trae de vuelta
como idempotency_key — concilia la notificación contra tu propia
referencia sin un GET extra.
- Es opcional: presente solo cuando el request original traía una key no vacía, y omitida en caso contrario. Las operaciones creadas por la propia plataforma jamás exponen una key interna.
- Eventos cubiertos:
payout_status_changed,payin_credited,payin_settlement_scheduled,payin_expired,payin_refunded,crypto_withdrawal_status_changedywallet_send_status_changed.transfer_receivedno la lleva. - El recurso payin también la devuelve en
GET /v1/payins/{id}.
Buenas prácticas
- Responde
200de inmediato y procesa en background. - Registra el
X-Webhook-Delivery-IDpara trazabilidad. - No dependas solo de webhooks para estados críticos: puedes consultar el
objeto por API en cualquier momento (
GET /v1/payouts/{id}, etc.).
banking_virtual_iban_inbound
This event reports an inbound EUR transfer received by an owned virtual IBAN.
The event is normalized before delivery; consumers should deduplicate by the
event ID and fetch the banking operation or virtual account when they need the
latest state. The event does not expose an upstream provider identity.
admin_announcement
Este evento account-scoped se emite cuando el broadcast aprobado por un
administrador de la organización llega al canal de notificaciones de la
cuenta. Es texto plano localizado, nunca HTML:
X-Webhook-Event-ID.
dispute_status_changed: consulta la guía de disputas para payload y transiciones firmadas. Payins fiat locales: payin_credited lleva credit_asset/fiat_credited; payin_refunded lleva debited_asset/debited_amount.