Skip to main content
Los webhooks permiten que tu aplicación reciba notificaciones en tiempo real cuando ocurren eventos en la plataforma de Fluz. En lugar de hacer polling para detectar cambios, registras un endpoint HTTPS y Fluz te envía los datos del evento en el momento en que sucede algo: una transacción con tarjeta virtual, una compra rechazada, un depósito completado, un usuario vinculando su cuenta mediante OAuth y más.
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.
Los webhooks funcionan en todos los tipos de aplicaciones en la plataforma de Fluz: apps privadas que operan en tu propia cuenta, apps públicas OAuth que actúan en nombre de otros usuarios vía API y apps de widgets embebidos.

Cómo funciona

  1. Registras una URL de webhook en tu aplicación dentro del Portal de Desarrolladores.
  2. Seleccionas qué tipos de evento escuchar (o te suscribes a todos).
  3. Cuando ocurre un evento coincidente, Fluz envía un POST HTTP a tu URL con un payload JSON firmado.
  4. Tu servidor verifica la firma, responde con un 2xx y procesa el evento.
Si tu endpoint no está disponible o devuelve un error, Fluz reintenta hasta 5 veces con backoff exponencial antes de desistir de esa entrega.

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 (como OAUTH_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 que OAUTH_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_COMPLETE y WIDGET_WITHDRAW_COMPLETE describen transferencias cliente↔app; el lenguaje DEPOSIT/WITHDRAW es 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 un POST 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 2xx dentro de 30 segundos. Respuestas no-2xx o timeouts activan reintentos.
  • Verificar la firma HMAC en cada solicitud.

Verificación de firmas

Cada entrega incluye un header X-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 2xx dentro 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/5xx para 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: userId es el ID de usuario de Fluz. Para eventos de OAuth/widget, externalReferenceId se asigna a tu identificador de usuario desde el flujo OAuth.
  • App: los payloads de transacción incluyen connectedAppId y connectedAppName. Si enrutas múltiples apps a un solo endpoint, ramifica según connectedAppId. Para OAUTH_USER_LINKED, la app se identifica por appId.

Referencia de payloads

Cada payload incluye un eventType. 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 de status. Para transacciones creadas/actualizadas, el campo status toma uno de PENDING, SETTLED o FAILED. Las transacciones rechazadas llevan un status de DECLINED (o FAILED). Versiones anteriores de esta página mostraban COMPLETED como estado; ese valor no se emite; usa SETTLED para 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.
Los campos coinciden con 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_UPDATE en esa transacción.
  • Una anulación de verificación de tarjeta se registra como su propia transacción, por lo que llega como TRANSACTION_CREATE con un amount negativo 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.
Una reversión no es una falla, pero requiere manejo: libera la retención que registraste para la autorización correspondiente, o permanecerá abierta de tu lado para siempre. No la registres como una nueva salida, ya que no se movió dinero.

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. restricted es true y userCashBalanceIds lista 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. includesFutureSpendAccounts te 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 evento OAUTH_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í que userCashBalanceIds solo es exacto al momento de la vinculación. Llama a getWallet con el token de acceso del usuario para obtener la lista actual.
  • Concesión sin restricción. restricted es false y userCashBalanceIds es null. 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 a getWallet con el token de acceso del usuario para obtener la lista actual.
Solo se incluyen IDs. No se incluyen apodos ni saldos, porque pueden cambiar sin disparar este evento; léelos desde 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 stagingspendAccountAccess 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.
Campos comunes de payload 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.
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 200 de inmediato y maneja el evento de forma asíncrona para evitar timeouts y reintentos innecesarios.
  • Verifica cada solicitud. Valida X-HMAC-Signature contra el cuerpo en bruto usando tu clave API antes de procesar.
  • Desduplica con IDs de evento. Rastrea X-Event-ID para manejar entregas reintentadas/duplicadas.
  • Trata los campos tipo enum como cadenas abiertas. Con el tiempo pueden aparecer nuevos valores de transactionType, channel y declineCategory; 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 y spendAccountAccess cada 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?