Webhooks

Recibe cada llamada
en tu servidor

Cuando termina una llamada, se apunta una cita o avanza una campaña, enviamos un POST con JSON a la dirección que elijas. Aquí tienes cómo recibirlo y cómo comprobar que viene de nosotros.

Todavía no hay una API pública. Hoy los datos salen de CallHalla por webhooks y, para tu asistente de IA, por MCP.

Cómo se da de alta

  1. 1En tu panel, entra en Webhooks y crea uno con la URL de tu servidor.
  2. 2Elige los eventos que quieres recibir.
  3. 3Copia el secreto de firma que aparece en la tarjeta del webhook.
  4. 4Pulsa Probar. Te mandamos un envío de ejemplo, firmado igual que los de verdad.

Qué recibes

Cada envío es un POST con JSON y siempre tiene la misma forma: el evento, la hora y los datos. Este es el de una llamada terminada:

call.completed
{
  "event": "call.completed",
  "timestamp": "2026-10-11T10:30:00.000Z",
  "data": {
    "contact": { "phone": "+34600000000" },
    "call": {
      "id": "…",
      "status": "completed",
      "duration": 94,
      "phoneNumber": "+34960000000",
      "fromNumber": "+34600000000",
      "endedAt": "2026-10-11T10:30:00.000Z",
      "transcript": [ … ]
    }
  }
}

Lo que va en data depende del evento. Para ver el de cada uno, mira el registro de envíos del panel.

Eventos que puedes recibir

Llamadas

  • call.startedSale la llamada de prueba de una campaña. Las llamadas que contesta Halla no lo mandan.
  • call.completedHalla termina una llamada. Incluye la transcripción.
  • call.failedUna llamada no llega a completarse.

Campañas

  • campaign.startedEmpieza una campaña, o se reanuda tras una pausa.
  • campaign.pausedSe pausa una campaña, a mano o al salir de su horario.
  • campaign.completedTermina una campaña. Incluye cuántas llamadas salieron bien y cuántas no.
  • campaign.failedUna campaña se detiene por un error.
  • campaign.cancelledSe cancela una campaña.

Flujos de conversación

  • flow.completedTermina bien una llamada atendida con un flujo de conversación.
  • flow.failedFalla una llamada atendida con un flujo de conversación.

Citas

  • appointment.bookedHalla apunta una cita en firme, o aceptas una solicitud en el panel.
  • appointment.confirmedSe confirma una cita: la marcas en el panel, o la persona lo dice en la campaña de confirmar citas.
  • appointment.cancelledSe anula una cita.
  • appointment.rescheduledSe cambia una cita de día u hora.
  • appointment.completedMarcas una cita como hecha en el panel.
  • appointment.no_showMarcas en el panel que la persona no vino.
  • appointment.reminder_sentSe anota en una cita que se le ha mandado un recordatorio. CallHalla no los manda por su cuenta.

Formularios

  • form.submittedUn agente con flujo de conversación rellena un formulario durante una llamada.

Firma

Cada envío lleva la cabecera X-Webhook-Signature. Su valor es sha256= seguido del HMAC-SHA256, en hexadecimal, del cuerpo tal cual llega. Se calcula con el secreto de tu webhook.

También enviamos X-Webhook-Event, con el nombre del evento, y X-Webhook-Delivery, que identifica el envío y es el mismo en todos sus reintentos. Si te llega dos veces, puedes descartar el duplicado.

Reintentos

Esperamos hasta 30 segundos una respuesta 2xx. Si no llega, lo intentamos cinco veces más, esperando 1, 5, 15, 30 y 60 minutos entre un intento y el siguiente: seis intentos en unas dos horas.

Los reintentos pendientes se guardan en nuestra base de datos, así que un reinicio del servidor no los pierde. En el registro del panel ves cada intento y puedes reenviarlo.

Verificación de la firma en Node
const crypto = require("crypto");

function verifyWebhook(rawBody, signatureHeader, secret) {
  // signatureHeader = "sha256=<hex>"
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret)
      .update(rawBody)
      .digest("hex");
  return (
    typeof signatureHeader === "string" &&
    signatureHeader.length === expected.length &&
    crypto.timingSafeEqual(
      Buffer.from(signatureHeader),
      Buffer.from(expected)
    )
  );
}

// rawBody: el cuerpo tal cual llega, antes de JSON.parse
app.post("/webhooks/callhalla", (req, res) => {
  const ok = verifyWebhook(
    req.rawBody,
    req.headers["x-webhook-signature"],
    process.env.CALLHALLA_WEBHOOK_SECRET
  );
  if (!ok) return res.status(401).end();
  res.status(200).end();
});
MCP

Conecta tu asistente de IA

CallHalla tiene un servidor MCP en https://callhalla.io/mcp. Claude, ChatGPT o cualquier asistente compatible se conecta a tu cuenta con OAuth: entras en CallHalla y pulsas «Permitir». Está en todos los planes.

Cómo se conecta

  1. 1En claude.ai o en Claude Desktop: Ajustes → Conectores → «Añadir conector personalizado», con la dirección https://callhalla.io/mcp.
  2. 2En ChatGPT: crea un conector con la misma dirección.
  3. 3Se abre CallHalla: entras con tu cuenta y pulsas «Permitir». Si quieres, quitas los cambios y le dejas solo leer.
En Claude Code
claude mcp add --transport http callhalla https://callhalla.io/mcp
claude mcp login callhalla

Qué puede hacer

  • Leer el resumen del mes, tus llamadas con su resumen y su transcripción, tu agenda, tus contactos con sus notas y lo que sabe tu recepcionista.
  • Si se lo permites: apuntar, mover, confirmar y anular citas, con las mismas reglas de horario y solapes que el teléfono; añadir notas a un contacto y enseñar textos nuevos a tu recepcionista.
  • Nunca: hacer llamadas, lanzar campañas ni tocar tus números, tu facturación o los ajustes de tu cuenta.
Herramientas
get_monthly_summary
list_calls
get_call
list_appointments
create_appointment
update_appointment
search_contacts
get_contact
add_contact_note
list_knowledge
add_knowledge

El permiso dura una hora y se renueva solo, y solo sirve para el MCP: no abre el resto de CallHalla. Lo ves y lo desconectas cuando quieras en Ajustes → Perfil → Apps conectadas.

Empieza con Halla

Crea tu cuenta y da de alta tu primer webhook desde el panel.