Skip to main content
Los webhooks te notifican los eventos de tu cuenta en un callback HTTPS propio, firmados criptográficamente.

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.
La suscripción recibe los eventos de tu cuenta. Puedes listar las suscripciones activas en cualquier momento:

Desactivar y reactivar una suscripción

Cuando un callback deja de usarse, desactívalo en vez de borrarlo (las suscripciones nunca se borran: quedan disabled y puedes reactivarlas cuando quieras):
Para reactivarla, el mismo llamado con { "status": "active" }.
  • El toggle rige lo futuro: una suscripción disabled deja de recibir eventos nuevos, pero las entregas ya encoladas siguen saliendo.
  • Es idempotente: repetir el estado vigente responde 200 sin 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

En 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 con status: "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 un POST 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.
Calcula el HMAC sobre el body crudo (bytes tal como llegan), no sobre el JSON re-serializado. Rechaza timestamps muy antiguos (> 5 minutos) para prevenir replay.

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-ID para 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 GET del 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 una idempotency_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_changed y wallet_send_status_changed. transfer_received no la lleva.
  • El recurso payin también la devuelve en GET /v1/payins/{id}.

Buenas prácticas

  • Responde 200 de inmediato y procesa en background.
  • Registra el X-Webhook-Delivery-ID para 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:
El evento solo se entrega a la cuenta seleccionada por la campaña. Puede recibirse mediante una suscripción webhook de cuenta o el event stream; deduplica las entregas por 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.
Última modificación el 3 de octubre de 2026