En esta sección
Guías / Webhooks entrantes

Webhooks entrantes

Registra endpoints que disparan tareas o emiten eventos, con firma HMAC.

Actualizada el Ver .md

Un webhook entrante es un endpoint que Meteor expone para que un tercero le haga POST. Cuando llega un evento, Meteor puede disparar una tarea o emitir un evento interno. Necesita el scope webhooks:manage.

Estos son webhooks entrantes (tercero → Meteor). Si lo que quieres es que Meteor te avise cuando pasa algo (run.completed, etc.), salta a Eventos salientes más abajo.

Crear un webhook

const wh = await met.webhooks.create({
  name: 'Entrada de leads',
  binding_type: 'generic',        // 'task' ejecuta una tarea; 'generic' solo emite evento
  verifier_type: 'hmac_sha256',   // 'none' para sin firma
});

console.log(wh.url);      // la URL a la que apunta el tercero
console.log(wh.secret);   // ⚠️ se muestra UNA sola vez

Guarda el secret apenas lo recibes: no se vuelve a mostrar. Con él verificas que cada entrega vino realmente de tu integración.

Verificar la firma

El tercero firma el cuerpo con el secret (HMAC-SHA256) y manda la firma en un header. Del lado de Meteor, la verificación es automática si el webhook es hmac_sha256.

Rotar el secret

Si sospechas que se filtró:

const rotated = await met.webhooks.rotateSecret(wh.id);
console.log(rotated.secret);   // nuevo secret; invalida el anterior

Ver entregas

const events = await met.webhooks.events(wh.id, { limit: 20 });
const sample = await met.webhooks.sample(wh.id);   // último payload recibido

sample es útil para configurar el mapeo de campos del payload.

Conectar un flujo

Un webhook generic puede disparar un flujo cuando recibe un evento (connectFlow en la API). Así, un POST externo arranca una automatización completa en Meteor.

Eventos salientes (Meteor → tu servidor)

Un evento saliente es Meteor haciéndote POST a *ti* cuando algo pasa en tu workspace — por ejemplo, cuando un Run termina. Registras un endpoint, eliges qué eventos quieres, y Meteor entrega cada uno firmado con HMAC para que puedas verificar que vino de Meteor.

Registrar un endpoint

Registras el endpoint desde tu panel: Desarrolladores → Actividad → Webhooks. Eliges la URL de tu servidor y qué eventos quieres (o * para todos). Al crearlo, Meteor te muestra el secreto de firma (whsec_...) una sola vez — guárdalo, lo necesitas para verificar cada entrega.

Cambiar los eventos de un endpoint

Para sumar o quitar eventos, cambiar la URL o la descripción, usa PATCH /webhook-endpoints/{id}. El secreto de firma no cambia: tu verificación sigue funcionando igual, sin redesplegar nada. enabled_events reemplaza la lista completa, así que para sumar un evento manda la lista actual más el nuevo:

const [ep] = await met.webhooks.subscriptions.list();
await met.webhooks.subscriptions.update(ep.id, {
  enabled_events: [...ep.enabled_events, 'contact.message.failed'],
});

Si te suscribiste con *, no tienes que hacer nada: los eventos nuevos del catálogo te llegan solos.

Catálogo de eventos

Son 30. La columna scope es el permiso que la key debe portar para recibir ese evento: el mismo con el que leerías ese recurso por la API.

EventoCuándo disparaScope
run.completedUn Run terminó con éxito.runs:read
run.failedUn Run falló.runs:read
channel.run.completedEl Met respondió un mensaje de canal.runs:read
channel.run.failedEl Met falló al responder un mensaje de canal.runs:read
contact.createdSe creó un contacto.contacts:read
contact.updatedSe actualizó un contacto.contacts:read
contact.message.receivedEntró un mensaje de un contacto por un canal (WhatsApp, etc.).conversations:read
conversation.handoffUna conversación pasó a operador humano.conversations:read
task.completedDeprecado: no se emite desde el 2026-08-31, cuando se retiró la ejecución de tareas. Sale del catálogo el 2027-04-07.tasks:read
billing.thresholdEl gasto de Energía de una API key cruzó un umbral de su presupuesto (50/80/100%).billing:read
app.authorizedUn workspace autorizó tu app OAuth (nuevo grant).integrations:read
app.revokedUn workspace revocó el acceso de tu app OAuth (grant revocado).integrations:read
snapshot.publishedUna plantilla propia fue aprobada y publicada al marketplace.snapshots:read
snapshot.install.completedLa instalación de una plantilla terminó.snapshots:read
snapshot.install.failedLa instalación de una plantilla falló.snapshots:read
conversion.sentUna conversión se emitió con éxito a Meta (CAPI).conversions:read
conversion.discardedUna conversión propuesta fue descartada por la compuerta (incluye la razón).conversions:read
automation.run.failedUna automatización falló al ejecutarse.automations:read
contact.message.sentSalió un mensaje hacia un contacto, lo haya escrito el Met o una persona.conversations:read
contact.message.failedUn mensaje hacia un contacto no se pudo entregar. Trae el código de error del proveedor.conversations:read
contact.deletedSe borró un contacto.contacts:read
broadcast.completedUna difusión terminó de enviarse (trae cuántos salieron y cuántos fallaron).channels:read
channel.template.approvedMeta aprobó una plantilla de WhatsApp.channels:read
channel.template.rejectedMeta rechazó una plantilla de WhatsApp (trae el motivo).channels:read
channel.template.status_updatedUna plantilla de WhatsApp cambió de estado por otra razón (pausada, deshabilitada, en apelación…): trae el estado de Meta.channels:read
call.missedUna llamada quedó sin contestar.conversations:read
billing.energy.lowEl saldo de Energía cruzó hacia abajo el umbral bajo. Dispara en el cruce, no en cada ejecución.billing:read
billing.energy.depletedEl saldo de Energía se agotó.billing:read
billing.energy.rechargedEntró una recarga de Energía (compra, recarga automática o abono del partner).billing:read
billing.subscription.deactivatedLa suscripción del workspace quedó desactivada.billing:read

* no es un evento: es el comodín «todos los del catálogo», y cubre también los que agreguemos después. Si prefieres enterarte de un evento nuevo antes de empezar a recibirlo, suscríbete a la lista explícita.

Cinco cosas que ahorran una depuración:

Esta misma tabla la devuelve la API, siempre al día:

const { data } = await met.webhooks.subscriptions.eventTypes();
// [{ type: 'run.completed', description: '…', scope: 'runs:read' }, …]

Verificar la firma

Cada entrega trae el header X-Met-Signature: t=<ts>,v1=<hmac>. Nunca proceses un evento sin verificarlo — usa el helper del SDK, que hace la verificación timing-safe y chequea el timestamp (anti-replay):

import Met from '@meteor.ia/sdk';
const met = new Met(process.env.MET_API_KEY!);

app.post('/webhooks/met', (req, res) => {
  let event;
  try {
    event = met.webhooks.constructEvent(req.rawBody, req.headers['x-met-signature'], WHSEC);
  } catch {
    return res.status(400).send('firma inválida');
  }
  if (event.type === 'run.completed') { /* ... */ }
  res.sendStatus(200);   // responde 2xx rápido; Meteor reintenta si no.
});

Necesitas el cuerpo crudo (raw body) para verificar la firma — configura tu framework para no re-serializar el JSON antes de constructEvent.

Reintentos

Si tu endpoint no responde 2xx en 15 segundos, Meteor reintenta con espera creciente: el primer intento y luego a 1 min → 5 min → 30 min → 2 h → 6 h, 6 intentos en total (unas 8,6 horas desde el primero). Después marca la entrega como failed; la puedes volver a mandar con POST /webhook-endpoints/{id}/deliveries/{deliveryId}/retry o POST /events/{id}/replay. Cada reintento lleva el mismo id de evento, también en el header X-Met-Event-Id: úsalo para no procesar dos veces lo mismo. Las entregas salen en paralelo, así que el orden no está garantizado: ordena por created. El log de intentos por endpoint está en tu Workbench (sección Desarrolladores).