Documentación
Volver a confirio.app
Secciones de la documentación

Integración · Webhooks

Recibí eventos en tu servidor #

Los webhooks avisan a tu aplicación cuando cambia una sala, una sesión, una grabación o una cuota. Configurá la URL, los eventos y el secreto desde el Panel.

Elegí eventos #

La API publica la lista vigente en GET https://api.confirio.app/api/v1/webhooks/event-types. Los tipos disponibles son:

Consultá el endpoint para ver la descripción vigente de cada evento.

El secreto se muestra una sola vez al crear o rotar el webhook. Guardalo como variable de entorno en tu servidor. También podés configurar encabezados propios desde el Panel.

Verificá la firma antes de procesar #

Cada envío incluye X-Webhook-Timestamp (segundos Unix) y X-Webhook-Signature (HMAC-SHA256 codificado en Base64). La firma se calcula sobre el texto timestamp.cuerpo, usando el cuerpo JSON original sin volver a serializarlo.

El ejemplo del SDK toma el cuerpo crudo, comprueba que el timestamp no tenga más de cinco minutos y compara la firma en tiempo constante. Instalá Express y configurá el secreto en la terminal:

Terminal · instalar y configurar
npm install express
export CONFIRIO_WEBHOOK_SECRET=TU_SECRETO_DEL_PANEL

Guardá el código como webhook.mjs y ejecutalo con node webhook.mjs:

Receptor Express · webhook.mjs
import express from 'express';
import { createHmac, timingSafeEqual } from 'node:crypto';

const secret = process.env.CONFIRIO_WEBHOOK_SECRET;
if (!secret) {
  console.error('Falta CONFIRIO_WEBHOOK_SECRET en el entorno.');
  process.exit(1);
}

const toleranceSeconds = 5 * 60;
const processed = new Set(); // En producción, una tabla con event_id como clave única.
const app = express();

app.post('/webhooks/confirio', express.raw({ type: 'application/json' }), (req, res) => {
  const timestamp = req.get('X-Webhook-Timestamp');
  const signature = req.get('X-Webhook-Signature');
  if (!timestamp || !signature || Math.abs(Date.now() / 1000 - Number(timestamp)) > toleranceSeconds) {
    return res.status(401).end();
  }

  const expected = createHmac('sha256', secret)
    .update(`${timestamp}.${req.body.toString('utf8')}`)
    .digest();
  const received = Buffer.from(signature, 'base64');
  if (received.length !== expected.length || !timingSafeEqual(received, expected)) {
    return res.status(401).end();
  }

  const event = JSON.parse(req.body.toString('utf8'));
  if (processed.has(event.event_id)) {
    return res.status(200).end(); // reintento o repetición de algo ya procesado
  }
  processed.add(event.event_id);

  if (event.event_type === 'session.ended') {
    const data = event.data;
    console.log(`Sala ${data.room_id}: ${data.total_participant_seconds} s, ${data.connection_count} conexiones, datos ${data.data_status}`);
  } else {
    console.log(`Evento ${event.event_type}`, event.data);
  }

  // Responder rápido: si el proceso tarda, encolalo y contestá 2xx igual.
  res.status(200).end();
});

app.listen(3001, () => console.log('Receptor de webhooks en http://localhost:3001/webhooks/confirio'));

En producción, reemplazá el Set en memoria por un registro persistente con una restricción única sobre event_id, y procesá el evento una sola vez.

Identificadores y cuerpo #

Los headers incluyen X-Webhook-Event, X-Webhook-Event-Id y X-Webhook-Delivery-Id. El event_id identifica el evento y sirve para deduplicar; un reintento puede volver a entregar ese mismo evento. En session.ended, los datos incluyen segundos de participante, cantidad de conexiones y estado de los datos.

Reintentos y respuestas #

El servicio hace hasta cinco intentos en total. Reintenta errores de red, respuestas 5xx, 408 y 429; otros errores 4xx terminan esa entrega. Las esperas base que se usan con este límite son 30 segundos, 2 minutos, 10 minutos y 30 minutos, con una variación aleatoria.

Respondé con un código 2xx después de aceptar el evento. Si el procesamiento puede demorar, guardalo en una cola y contestá rápido. Revisá entregas y errores en el historial de webhooks del Panel.