Venflow API

Introducción

Webhooks

Los webhooks de Venflow te permiten recibir notificaciones HTTP en tiempo real cuando ocurren eventos importantes en tu organización, como pagos exitosos, creación de suscripciones o fallas en cobros.

¿Cómo Funcionan?

Cuando un evento ocurre (por ejemplo, un pago exitoso), Venflow envía una solicitud POST con el payload del evento en formato JSON a cada URL de webhook configurada en tu organización.

Tu Servidor ← POST (JSON payload) ← Venflow

Configuración

Los webhooks se configuran desde el panel, en Desarrolladores → Webhooks. No hay un endpoint público para darlos de alta: la API de Venflow no expone este recurso, así que se configuran una vez desde el panel y tu integración solo se ocupa de recibir los eventos.

Cada webhook incluye:

CampoDescripción
urlURL de tu endpoint que recibirá los eventos
eventsArray de tipos de eventos a recibir (vacío = todos)
secretSecreto para verificar la autenticidad de los eventos
enabledHabilitar o deshabilitar el webhook

Filtrado de Eventos

Puedes configurar qué eventos recibe cada webhook usando el campo events:

  • Array vacío o ["*"]: recibe todos los eventos
  • Eventos específicos: solo recibe los tipos indicados
{
  "url": "https://tu-api.com/webhooks/venflow",
  "events": ["PAYMENT_SUCCESS_EVENT", "PAYMENT_FAILED_EVENT"],
  "enabled": true
}

Seguridad

Si configuras un secret en tu webhook, Venflow usa ese valor para acreditar cada entrega. Hoy conviven dos headers mientras completamos la migración:

HeaderEstadoQué contiene
X-Webhook-SignatureNuevo estándarFirma HMAC-SHA256 del cuerpo junto con la hora de envío: t=<unix_segundos>,v1=<hex>
X-Webhook-SecretObsoleto (deprecado)El secreto en texto plano. Se dejará de enviar al cerrar la ventana de deprecación

Si el webhook no tiene secret configurado, no se envía ninguno de los dos headers.

X-Webhook-Signature (recomendado)

La firma prueba dos cosas a la vez: que el evento salió de Venflow y que el cuerpo no fue alterado en el camino. Además incluye la hora de envío, así puedes rechazar una solicitud capturada y reenviada más tarde.

X-Webhook-Signature: t=1789415801,v1=b03900efcfc5b467a9192c1e448a82bd77187fb70cda36cefffc810d7564170c

Esta firma se está habilitando organización por organización (despliegue progresivo). Mientras dure el despliegue, tu endpoint puede recibir entregas con el header y otras sin él. Programa a la defensiva: si el header X-Webhook-Signature está presente, verifícalo y rechaza la solicitud cuando no coincida; si no está presente, procesa el evento como hasta ahora. Cuando el despliegue termine, la firma llegará siempre y podrás exigirla.

El paso a paso y el código de verificación están en Verificar la firma.

X-Webhook-Secret (obsoleto)

Este header lleva tu secreto en texto plano, de modo que cualquier proxy o registro de logs intermedio puede verlo. Se sigue enviando junto con la firma durante una ventana de deprecación, para no romper las integraciones que hoy lo comparan, y luego se eliminará.

  • No escribas integraciones nuevas contra este header. Usa X-Webhook-Signature.
  • Si ya lo usas, migra a la verificación de firma antes de que cierre la ventana. El cambio se anunciará en la documentación antes de aplicarse.
// Forma obsoleta — solo para integraciones existentes.
const secret = req.headers['x-webhook-secret'];

if (secret !== process.env.VENFLOW_WEBHOOK_SECRET) {
  return res.status(401).json({ error: 'Unauthorized' });
}

Recomendaciones de Seguridad

  • Usa HTTPS para tu endpoint de webhook
  • Verifica X-Webhook-Signature siempre que venga en la solicitud
  • Responde con un código 2xx para confirmar la recepción
  • Responde rápido: hay un tiempo límite de 5 segundos por intento. Si tu procesamiento es lento, encola el evento y responde de inmediato
  • No expongas tu secreto de webhook en código del lado del cliente

Comportamiento de Entrega

AspectoDetalle
Método HTTPPOST
Content-Typeapplication/json
EnvíoA todos los webhooks habilitados que estén suscritos al evento
Tiempo límite5 segundos por intento (conectar y responder)
ReintentosHasta 3 intentos, separados por 5 minutos
ÉxitoCualquier respuesta 2xx. El primer intento exitoso detiene los reintentos
Tras agotar los intentosVenflow envía un correo de aviso a la organización y deja de intentar ese evento

Reintentos

Si tu endpoint no responde con un código 2xx dentro de los 5 segundos, Venflow reintenta la entrega hasta dos veces más, con 5 minutos de espera entre intentos. Agotados los tres, te enviamos un correo avisando que el endpoint está fallando.

Tu endpoint tiene que ser idempotente. Procesar el mismo evento dos veces no puede cobrar dos veces, ni duplicar un registro. Y ojo con un caso que sorprende: si tu servidor tarda 6 segundos en responder 200, para nosotros el intento falló — vas a recibir el evento de nuevo aunque ya lo hayas procesado correctamente. Responde 200 primero y haz el trabajo pesado después, de forma asíncrona.

Hoy el payload no trae un identificador de evento, así que para deduplicar tienes que apoyarte en los datos del propio evento (por ejemplo, el id de la factura o de la transacción junto con el eventType).

Los eventos de prueba enviados desde el panel son de un solo intento: no se reintentan ni generan el correo de aviso. Ver Probar Webhooks.

Headers Enviados

POST /tu-endpoint HTTP/1.1
Content-Type: application/json
X-Webhook-Signature: t=1789415801,v1=b03900efcfc5b467a9192c1e448a82bd77187fb70cda36cefffc810d7564170c
X-Webhook-Secret: tu_secreto_configurado

Campos Comunes

Los eventos a nivel de cliente (todos menos los GATEWAY_*) incluyen estos campos base:

{
  "eventType": "NOMBRE_DEL_EVENTO",
  "client": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "cliente@email.com",
    "name": "Juan",
    "lastName": "Pérez"
  }
}
CampoTipoDescripción
eventTypestringIdentificador del tipo de evento
client.idstring (UUID)ID único del cliente
client.emailstringEmail del cliente
client.namestringNombre del cliente
client.lastNamestringApellido del cliente

Los dos eventos GATEWAY_API_KEY_* son la excepción: corresponden a la organización, no a un cliente, y no traen el objeto client. En su lugar traen organizationId, configId y bankId.

Consulta la referencia de eventos para ver los campos específicos de cada tipo de evento.

Qué asumir del payload y qué no

Estas cuatro cosas son las que más rompen una integración de webhooks. Ninguna es obvia leyendo un ejemplo:

  1. Los objetos opcionales pueden faltar. subscription, invoice, payment y session no vienen en todos los eventos, ni siquiera en todos los casos del mismo evento. Comprueba antes de leer adentro.
  2. session solo llega en pagos que nacen de un checkout. Los cobros recurrentes de una suscripción no lo traen.
  3. subscription.status no es una lista cerrada. Deja siempre una rama por defecto.
  4. Agregamos campos nuevos sin avisar. El payload no tiene versión, así que tu parser tiene que ignorar lo que no conoce en vez de fallar. Nunca quitamos ni renombramos un campo existente.