Esta página cubre todos los eventos de webhook, que abarcan múltiples áreas de la plataforma (actividad de transacciones, depósitos, flujos de widget y vinculación de cuentas por OAuth) en lugar de pertenecer a una sola función.
Cómo funciona
- Registras una URL de webhook en tu aplicación dentro del Portal de Desarrolladores.
- Seleccionas qué tipos de evento escuchar (o te suscribes a todos).
- Cuando ocurre un evento coincidente, Fluz envía un
POSTHTTP a tu URL con un payload JSON firmado. - Tu servidor verifica la firma, responde con un
2xxy procesa el evento.
Webhooks por tipo de app
Cómo se enrutan los eventos a tu app depende de tu modelo de aplicación.Apps privadas — tu propia cuenta
Los webhooks se disparan por actividad en tu propia cuenta. Cualquier transacción, rechazo o depósito en cuentas que posees activa webhooks registrados en tu app. Casos de uso comunes: notificaciones cuando se autoriza o liquida una transacción de tarjeta virtual; alertas en tiempo real de rechazos; monitoreo de depósitos y cambios de balance en tus cuentas de gasto.Apps públicas (OAuth) — actuando en nombre de usuarios
Los webhooks se disparan por actividad en cuentas que han autorizado tu app. Cuando un usuario concede acceso a tu app mediante OAuth, sus eventos se enrutan a tus webhooks registrados, siempre que la concesión OAuth del usuario incluya los scopes requeridos. Esto funciona tanto si el usuario interactúa a través de tu integración directa por API como mediante un widget embebido; el requisito clave es una relación OAuth entre el usuario y tu app. Casos de uso comunes: saber cuándo un usuario ha vinculado (o vuelto a vincular) su cuenta con tu app; monitorear el gasto con tarjeta virtual en cuentas conectadas; notificaciones de rechazos en tiempo real; seguimiento de depósitos completados; recibir actualizaciones de estado KYC y KYB.Apps de widget
Las apps de widget son una app pública especializada. Los eventos se enrutan del mismo modo (basados en la relación OAuth) y, adicionalmente, las apps de widget soportan eventos específicos del widget como finalización de transferencias y compras de tarjetas de regalo.Tipos de eventos
Fluz solo entrega un evento si tu aplicación — y, para apps públicas/OAuth, la concesión OAuth del usuario individual — posee los scopes requeridos. Los eventos sin scopes requeridos (comoOAUTH_USER_LINKED) se entregan a cualquier app pública/OAuth suscrita.
Eventos de transacción
Cubren el ciclo de vida completo de la transacción. Aplican a todos los tipos de app.Eventos de depósito
Eventos KYC y KYB
Aplican a apps públicas/OAuth y de widget. Al igual queOAUTH_USER_LINKED, estos eventos no tienen equivalente en apps privadas: solo se disparan para cuentas conectadas mediante una relación OAuth, y solo cuando la concesión OAuth de tu app (y la del usuario) incluye el scope requerido.
Eventos de OAuth
Aplican a apps públicas/OAuth y de widget.OAUTH_USER_LINKED se entrega solo a tipos de app públicas/OAuth; los suscriptores de apps privadas no lo reciben. Dado que no tiene requisito de scope, lo recibes por cada usuario que vincule o vuelva a vincular su cuenta con tu app, sin importar qué scopes concedan. Úsalo para aprovisionar o actualizar tu registro local del usuario conectado, capturar el conjunto de scopes otorgados y mapear tu propio identificador de usuario mediante externalReferenceId.
Eventos específicos del widget
Aplican a integraciones de widget y OAuth donde los usuarios interactúan a través de flujos embebidos de Fluz.📘 Nota sobre nombres:WIDGET_DEPOSIT_COMPLETEyWIDGET_WITHDRAW_COMPLETEdescriben transferencias cliente↔app; el lenguajeDEPOSIT/WITHDRAWes histórico. Trata “depósito” como “cliente → app” y “retiro” como “app → cliente”.
Configuración de webhooks
1. Abre el Portal de Desarrolladores
Ve al Portal de Desarrolladores y selecciona tu aplicación.2. Abre la sección de Webhooks
- Apps OAuth: pestaña OAuth → Webhook URLs.
- Apps de widget: pestaña Widget → Webhook URLs.
- Apps API / privadas: la sección Webhook URLs en la configuración de tu app.
3. Agrega una URL de webhook
Haz clic en Add new URL e ingresa tu endpoint HTTPS (por ejemplo,https://api.yourapp.com/webhooks/fluz).
4. Selecciona eventos
Elige los tipos de evento que quieres recibir.5. Guarda
Haz clic en Create Webhook. Tu endpoint comienza a recibir eventos de inmediato.Gestión de webhooks
- Múltiples endpoints: puedes registrar más de una URL de webhook por aplicación.
- Cambiar eventos suscritos: elimina el webhook y vuelve a crearlo con la nueva selección de eventos.
- Eliminar un webhook: haz clic en Remove junto a él. El webhook se archiva de inmediato y deja de recibir eventos.
Recepción de webhooks
Formato de la solicitud
Cada webhook se entrega como unPOST HTTP con estos headers:
El cuerpo es un objeto JSON, y cada payload incluye un campo
eventType que identifica el evento.
Requisitos del endpoint
- Solo HTTPS: endpoints HTTP simples se rechazan al momento del registro.
- Públicamente accesible y capaz de aceptar solicitudes
POST. - Responder con un
2xxdentro de 30 segundos. Respuestas no-2xxo timeouts activan reintentos. - Verificar la firma HMAC en cada solicitud.
Verificación de firmas
Cada entrega incluye un headerX-HMAC-Signature: un hash HMAC-SHA256 del JSON en bruto, firmado con la clave API de tu aplicación. Verifícalo siempre antes de confiar en un payload.
⚠️ Verifica contra el cuerpo de la solicitud en bruto. Calcula el HMAC sobre los bytes exactos que envió Fluz; no reserialices el JSON parseado. Volver a convertir a string puede reordenar claves o cambiar espacios en blanco y provocar que firmas válidas fallen. Los ejemplos a continuación capturan el cuerpo en bruto por esta razón.
Node.js (Express)
Python (Flask)
Responder a los webhooks
Tu endpoint debe:- Responder con un estado
2xxdentro de 30 segundos. - Responder rápido: confirma primero y luego procesa de forma asíncrona.
- Ser accesible por HTTPS. Tu endpoint no debe:
- Responder con redirecciones (
3xx). - Responder con
4xx/5xxpara webhooks válidos (esto activa reintentos).
Política de reintentos
Los nuevos eventos reanudan la entrega automáticamente una vez que tu endpoint se recupere. Para reenviar eventos cuyos reintentos ya se agotaron, contacta soporte con el
X-Event-ID correspondiente.
Idempotencia y orden
Los webhooks pueden entregarse más de una vez, y no se garantiza el orden de entrega.- Desduplica usando el header
X-Event-ID. Conserva los IDs procesados (Redis o una base de datos en producción) y omite eventos que ya manejaste. - Ordena por los datos, no por la llegada. Si el orden importa, ordénalo por timestamps del payload (
createdAt,updatedAt,transactionDateTime) y por IDs de evento.
Identificación de la app y el usuario
- Usuario:
userIdes el ID de usuario de Fluz. Para eventos de OAuth/widget,externalReferenceIdse asigna a tu identificador de usuario desde el flujo OAuth. - App: los payloads de transacción incluyen
connectedAppIdyconnectedAppName. Si enrutas múltiples apps a un solo endpoint, ramifica segúnconnectedAppId. ParaOAUTH_USER_LINKED, la app se identifica porappId.
Referencia de payloads
Cada payload incluye uneventType. La disponibilidad de campos puede variar según el evento; los handlers deben ignorar campos no reconocidos para mantener compatibilidad futura.
📘 Una nota sobre los valores destatus. Para transacciones creadas/actualizadas, el campostatustoma uno dePENDING,SETTLEDoFAILED. Las transacciones rechazadas llevan unstatusdeDECLINED(oFAILED). Versiones anteriores de esta página mostrabanCOMPLETEDcomo estado; ese valor no se emite; usaSETTLEDpara detectar una transacción finalizada.
Transacción creada (TRANSACTION_CREATE)
Se dispara para cualquier transacción nueva: compras con tarjeta virtual, depósitos, transferencias y más.
Transacción actualizada (TRANSACTION_UPDATE)
Se dispara cuando cambian el estado o los detalles de una transacción; por ejemplo, cuando una autorización pendiente se liquida.
TRANSACTION_CREATE donde aparecen, más updatedAt (ISO 8601) que marca cuándo ocurrió el cambio. userId, connectedAppId y connectedAppName se mantienen de la misma forma que en TRANSACTION_CREATE, por lo que puedes identificar al usuario y la app de manera consistente a lo largo del ciclo de vida. Una transición de status a SETTLED es la señal de que una transacción previamente pendiente se ha finalizado.
isReversal conserva el mismo significado aquí que en TRANSACTION_CREATE. Ver Reversiones vs reembolsos.
Transacción rechazada (TRANSACTION_DECLINE)
Se dispara cuando se rechaza una transacción. Incluye motivos de rechazo estructurados sobre los que tu app puede actuar.
Campos opcionales adicionales pueden estar presentes según la transacción (p. ej.,
merchantId, merchantCity, merchantState, merchantCountry, cardDisplayName, virtualCardProgram, channel, bankAccountNickname, bankAccountLastFour). Ignora los que no uses.
Reversiones vs reembolsos
Una reversión libera una retención de autorización antes de que se capture dinero. El saldo disponible del tarjetahabiente vuelve a subir, pero nunca salieron fondos y no regresa ningún dinero. Un reembolso ocurre después de una captura: el dinero se movió y el comercio lo devolvió.isReversal diferencia ambos:
Usa este campo en lugar de
transactionType. Los emisores de tarjetas etiquetan estos eventos de forma inconsistente, por lo que el valor de tipo de un emisor para un reembolso de comercio es el de otro para una reversión previa a la captura, y el tipo por sí solo no puede decirte si realmente se movió dinero.
Qué evento lleva una reversión depende de lo que se revirtió:
- Una retención liberada sobre una compra que ya conoces llega como
TRANSACTION_UPDATEen esa transacción. - Una anulación de verificación de tarjeta se registra como su propia transacción, por lo que llega como
TRANSACTION_CREATEcon unamountnegativo y luego se actualiza. Estas son las pequeñas verificaciones que hace un comercio para confirmar que una tarjeta es válida y que anulamos de inmediato.
Depósito completado (DEPOSIT_COMPLETE)
Se dispara cuando se completa un depósito desde una fuente de fondos hacia una cuenta de gasto.
Usuario de OAuth vinculado (OAUTH_USER_LINKED)
Se dispara cuando un usuario completa la vinculación por OAuth con tu aplicación, tanto en el enlace inicial como en actualizaciones posteriores (por ejemplo, cuando el usuario vuelve a autorizar con un conjunto distinto de scopes). Solo apps públicas/OAuth y de widget. No se requiere ningún scope para recibir este evento.
Debido a que
OAUTH_USER_LINKED se dispara nuevamente en re-vinculaciones/actualizaciones, trátalo como un upsert: crea al usuario conectado al recibirlo por primera vez y actualiza el conjunto de scopes almacenado en entregas posteriores.
Acceso a cuentas de gasto
Cuando un usuario vincula su cuenta con tu app, el paso de permisos le permite conceder acceso a todas sus cuentas de gasto o solo a las que elija.spendAccountAccess te indica con cuál de las dos opciones quedó la vinculación, para que puedas empezar a operar sobre una cuenta de gasto — por ejemplo, llamar a getSpendAccountPaymentInstructions para generar instrucciones de depósito — sin una consulta adicional justo después de la vinculación.
- Concesión restringida.
restrictedestrueyuserCashBalanceIdslista las cuentas de gasto que el usuario seleccionó al vincular. Por ahora los IDs se entregan solo en staging; las entregas en producción aún no los incluyen.includesFutureSpendAccountste indica si esa lista es fija:false— la concesión cubre exactamente estas cuentas de gasto. La lista solo cambia si el usuario vuelve a vincular, lo que envía un nuevo eventoOAUTH_USER_LINKED.true— el operador permitió que el usuario incluya las cuentas de gasto que abra después. Cada cuenta de gasto nueva se agrega a la concesión automáticamente, pero abrirla no dispara este evento, así queuserCashBalanceIdssolo es exacto al momento de la vinculación. Llama agetWalletcon el token de acceso del usuario para obtener la lista actual.
- Concesión sin restricción.
restrictedesfalseyuserCashBalanceIdsesnull. La concesión cubre todas las cuentas de gasto que el usuario tiene ahora y cualquiera que abra después. Fluz no enumera las cuentas de gasto actuales en el payload, porque abrir una nueva cuenta de gasto no dispara este evento y la instantánea quedaría desactualizada. Llama agetWalletcon el token de acceso del usuario para obtener la lista actual.
getWallet, cuyo balances.userCashBalances ya viene filtrado a las cuentas de gasto que la vinculación permite.
El evento se dispara de nuevo con el valor actualizado cada vez que cambia la concesión: el usuario vuelve a dar su consentimiento, cambian los scopes concedidos o cambian las cuentas de gasto concedidas aunque los scopes sigan iguales (por ejemplo, el usuario reemplaza una cuenta de gasto por otra). Trata la última entrega como la autoritativa y reemplaza, en lugar de fusionar, la lista de IDs almacenada.
Trata este campo como una conveniencia. Las entregas de webhooks pueden llegar tarde o fuera de orden, así que si necesitas tener certeza de qué cuentas de gasto permite una vinculación, llama a getWallet: es la fuente de verdad.
Solo en staging
spendAccountAccess en OAUTH_USER_LINKED actualmente se entrega solo en el entorno de staging. Las entregas en producción aún no lo incluyen; esta página se actualizará cuando se despliegue.Inicio de KYC (WIDGET_KYC_INITIATION)
Se dispara cuando un usuario inicia la verificación de identidad. Solo apps públicas/de widget.
Transferencia completada — cliente a app (WIDGET_DEPOSIT_COMPLETE)
Se dispara cuando un usuario transfiere fondos a tu aplicación.
Transferencia completada — app a cliente (WIDGET_WITHDRAW_COMPLETE)
Se dispara cuando tu aplicación transfiere fondos a un usuario.
Compra de tarjeta de regalo (WIDGET_PURCHASE_GIFT_CARD)
Se dispara cuando se completa una compra de tarjeta de regalo a través del widget.
userId (ID de usuario de Fluz), accountId (ID de cuenta de Fluz), externalReferenceId (tu identificador de usuario desde el flujo OAuth) y amount cuando corresponda.
Actualización de estado KYC (KYC_STATUS_UPDATE)
Se dispara cuando cambia el estado KYC de un usuario conectado. Solo apps públicas/OAuth y de widget; requiere el scope VERIFY_KYC. Cada payload comparte un sobre común; la forma del objeto verificationData se discrimina por verificationType, mostrado por tipo a continuación.
- SSN
- Documents
- System
Emitido cuando el cambio de estado resultó de una verificación basada en SSN.
legalAddress, cuando está presente (solo tipos SSN y DOCUMENTS), tiene la forma { streetAddressLine1, streetAddressLine2 (optional), city, state, postalCode, country }.
Actualización de estado KYB (KYB_STATUS_UPDATE)
Se dispara cuando cambia el estado KYB de un negocio conectado. Solo apps públicas/OAuth; requiere el scope REGISTER_BUSINESS.
Mejores prácticas
- Responde rápido, procesa después. Devuelve
200de inmediato y maneja el evento de forma asíncrona para evitar timeouts y reintentos innecesarios. - Verifica cada solicitud. Valida
X-HMAC-Signaturecontra el cuerpo en bruto usando tu clave API antes de procesar. - Desduplica con IDs de evento. Rastrea
X-Event-IDpara manejar entregas reintentadas/duplicadas. - Trata los campos tipo enum como cadenas abiertas. Con el tiempo pueden aparecer nuevos valores de
transactionType,channelydeclineCategory; ramifica según los valores que te interesen y tolera los desconocidos. - Upsert en
OAUTH_USER_LINKED. Puede dispararse más de una vez por usuario; actualiza el conjunto de scopes almacenado yspendAccountAccesscada vez en lugar de asumir semántica de solo primer enlace. - Acepta campos desconocidos. Los payloads pueden agregar campos con el tiempo; ignora los no reconocidos en lugar de fallar.
- Monitorea tu endpoint. Alerta sobre respuestas repetidas no-
2xx: después de 5 intentos fallidos seguidos se abandona la entrega de un evento.
Solución de problemas
¿Necesitas ayuda?
- Problemas técnicos: revisa los logs de tu endpoint y contacta soporte con el
X-Event-ID. - Preguntas sobre scopes: consulta Scopes de Aplicación y Códigos de Rechazo.