---
title: "Descripción general de los webhooks"
description: "Cómo ThunderPhone entrega eventos en tiempo real, cómo verificar firmas y cómo se comparan los modelos de entrega heredado y basado en endpoints."
---

ThunderPhone envía solicitudes HTTP `POST` a tu servidor cuando ocurren eventos durante una llamada: inicia una llamada entrante, termina una llamada, finaliza una ejecución de evaluación, se activa una alerta, entre otros. Hay **dos modelos de entrega**:

<CardGroup cols={2}>
  <Card title="Endpoints de webhook (recomendado)" icon="bolt" href="/es/webhooks/endpoints">
    Varias URL, secretos por endpoint, filtros de eventos por endpoint
    y reintentos automáticos.
    Adminístralos mediante `GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints`.
  </Card>
  <Card title="Webhook heredado de una sola URL" icon="link" href="/api-reference/organizations#legacy-single-url-webhook">
    Una URL por organización. Incluye los eventos del ciclo de vida de las llamadas, incluidos los intercambios de configuración
    **bloqueantes**. Se administra en `GET/PUT /v1/webhook`.
  </Card>
</CardGroup>

Todos los tipos de eventos enumerados en el [catálogo de eventos](/es/webhooks/events) se entregan mediante endpoints de webhook. Los seis eventos del ciclo de vida de las llamadas
(`telephony.incoming`, `telephony.complete`, `telephony.tool`,
`web.incoming`, `web.complete`, `web.tool`) **también** se envían al
webhook heredado de una sola URL; si tienes tanto una URL heredada como un endpoint coincidente, recibes el evento en **ambas** rutas. El comportamiento bloqueante (el [intercambio de configuración de
`telephony.incoming` / `web.incoming`](/es/webhooks/call-incoming) y el
[despacho de herramientas](/es/tools/overview) en modo webhook) existe exclusivamente en la ruta heredada; cada entrega a un endpoint es una notificación sin esperar respuesta.

## Formato de la carga útil

Las entregas a endpoints son un objeto JSON con `data`, `event_id` y
`type`:

```json
{
  "data": {
    "call_id": 987654321,
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  },
  "event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
  "type": "telephony.incoming"
}
```

`event_id` es único para cada evento emitido. Es idéntico en todos los reintentos
**y** en cada endpoint que recibe el evento; úsalo para deduplicar.

El webhook heredado de una sola URL envía el mismo `type` y `data`, pero
**sin** `event_id`:

```json
{
  "type": "telephony.incoming",
  "data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}
```

En la transmisión, cada cuerpo se serializa de forma canónica: claves ordenadas
alfabéticamente, sin espacios en blanco, UTF-8. Los ejemplos con formato
legible de esta documentación son solo para facilitar la lectura.

Consulta el [catálogo de eventos](/es/webhooks/events) para ver la lista completa de tipos de eventos y campos de carga útil.

## Verificación de firma

Cada solicitud incluye dos firmas, ambas HMAC-SHA256 con la `secret` del
endpoint como clave (o la `secret` de webhook de tu organización para entregas
heredadas):

- `X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex>` firma la
  marca de tiempo, el método, la ruta y consulta, y el cuerpo sin procesar. Verifica
  esta firma y rechaza las solicitudes cuyo `t` difiera en más de **5 minutos**
  de tu reloj para evitar que una solicitud capturada pueda reproducirse más tarde. Los reintentos
  se firman nuevamente cuando se envían.
- `X-ThunderPhone-Signature` firma únicamente el cuerpo sin procesar. Se sigue enviando,
  sin cambios, para que los verificadores existentes sigan funcionando.

### Pasos

1. Lee el encabezado, el método, la ruta y la consulta sin procesar, y el
   cuerpo sin procesar **antes** de realizar cualquier análisis.
2. Calcula el HMAC-SHA256 hexadecimal de
   `<t>\n<METHOD>\n<path and query>\n<body>` con tu secreto.
3. Rechaza la solicitud si `t` difiere en más de 300 segundos o si el
   resultado no coincide con `v1` en una comparación de tiempo constante.

Los cuerpos son la serialización JSON canónica (claves ordenadas, separadores
compactos), y firmamos exactamente los bytes que transmitimos. La
[guía de verificación](/es/guides/verify-webhook-signatures) incluye la cadena
firmada exacta, un ejemplo práctico, código sin el SDK y el encabezado
heredado.

<CodeGroup>
```python Python (Flask)
from flask import Flask, abort, request
from thunderphone import verify_signature

app = Flask(__name__)

@app.post("/thunderphone-webhook")
def handle():
    query = request.query_string.decode()
    if not verify_signature(
        request.headers.get("X-ThunderPhone-Signature-V2"),
        secret=WEBHOOK_SECRET,
        method=request.method,
        path=request.path + (f"?{query}" if query else ""),
        body=request.get_data(),  # raw bytes
    ):
        abort(401)
    event = request.get_json()
    # dispatch on event["type"] …
    return "", 204
```

```javascript Node.js (Express)
import express from "express";
import { verifySignature } from "@thunderphone/sdk";

const app = express();
app.post(
  "/thunderphone-webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const ok = verifySignature({
      header: req.header("X-ThunderPhone-Signature-V2"),
      secret: process.env.WEBHOOK_SECRET,
      method: req.method,
      path: req.originalUrl,
      body: req.body,
    });
    if (!ok) return res.sendStatus(401);
    const event = JSON.parse(req.body.toString("utf8"));
    // dispatch on event.type …
    res.sendStatus(204);
  },
);
```
</CodeGroup>

## Semántica de entrega

Estas semánticas se aplican a las entregas de **puntos de conexión**. El
webhook heredado de una sola URL realiza un único intento síncrono sin reintentos, excepto el
intercambio de configuración de llamada telefónica
[`telephony.incoming`](/es/webhooks/call-incoming),
que se reintenta una vez.

<AccordionGroup>
  <Accordion title="Reintentos">
    Cada evento se intenta una vez de inmediato. Cualquier respuesta `2xx`
    confirma la entrega. Ante cualquier otro resultado (que no sea 2xx,
    error de conexión o tiempo de espera agotado), reintentamos a los **1 m, 5 m, 30 m, 2 h, 6 h,
    12 h y 24 h después del primer intento** — 8 intentos durante
    24 horas. Si todos los intentos fallan, la entrega se detiene y el punto de conexión
    se marca como `status="failing"` en
    [puntos de conexión de webhooks](/es/webhooks/endpoints); enviamos un correo electrónico a los
    propietarios y administradores de tu organización y lo mostramos en el panel. Devuelve `2xx` tan pronto como
    la carga útil se acepte de forma duradera; procésala de forma asíncrona.
    Los envíos de prueba desde la API de prueba del punto de conexión reciben un reintento aproximadamente un minuto después
    del primer intento y nunca cambian el estado del punto de conexión.
  </Accordion>

  <Accordion title="Orden">
    El orden de entrega se realiza según el mejor esfuerzo. En la práctica entregamos los eventos
    en el orden en que se emiten, pero los reintentos pueden cambiar el orden cuando hay fallos.
    Siempre elimina duplicados y reconcilia mediante `call_id` / id de objeto.
  </Accordion>

  <Accordion title="Duplicados">
    La entrega es **al menos una vez**: un reintento después de una respuesta que nunca
    recibimos puede duplicar un evento. Cada reintento incluye el mismo
    `event_id`, así que almacena los ids procesados y omite las repeticiones. `event_id` también
    se comparte entre endpoints: dos endpoints suscritos al
    mismo evento reciben el mismo `event_id`.
  </Accordion>

  <Accordion title="Tiempos de espera">
    Las entregas de puntos de conexión tienen un tiempo de espera de **30 s** por intento. En la
    ruta heredada, las solicitudes bloqueantes que controlan el comportamiento de llamadas en vivo — el
    intercambio de configuración de
    [`telephony.incoming` / `web.incoming`](/es/webhooks/call-incoming) —
    están limitadas a **10 s** para el primer intento de una llamada telefónica y a **13 s**
    para todo el intercambio, incluido su único reintento, y a **10 s** para una sesión de widget. Una respuesta lenta retrasa
    la atención de la llamada, así que intenta responder en un par de segundos. El [despacho de herramientas](/es/tools/overview) en modo webhook permite 20 s
    de forma predeterminada, y las declaraciones de herramientas pueden establecer un `timeout` de nivel superior.
  </Accordion>

  <Accordion title="IPs de origen">
    Los webhooks salientes se originan en el rango de IP de la nube de ThunderPhone.
    Si tu firewall requiere una lista de permitidos, contacta a soporte y
    compartiremos los rangos actuales.
  </Accordion>
</AccordionGroup>

## Elegir entre webhooks heredados y basados en endpoints

| Característica | Heredado (`/v1/webhook`) | Endpoints (`/v1/developer/webhook-endpoints`) |
|---------|------------------------|----------------------------------------------|
| Número de URL | 1 por organización | Muchas por organización |
| Cobertura de eventos | Solo `telephony.*` / `web.*` | Los 10 tipos de eventos |
| Filtro de eventos | — | Por endpoint |
| Reintentos | Ninguno, excepto uno para el intercambio de configuración de llamadas telefónicas | 8 intentos durante 24 h |
| Envoltorio | `type` + `data` | `type` + `data` + `event_id` |
| Rotación de secretos | Reemplaza un único secreto | Secreto por endpoint |
| Desactivar sin eliminar | `PUT /v1/webhook` con `{"url": ""}` | `status=disabled` |
| Visibilidad del estado | — | `active` / `disabled` / `failing` |
| Intercambio de configuración bloqueante | Sí ([`telephony.incoming` / `web.incoming`](/es/webhooks/call-incoming)) | Nunca: solo notificaciones |
| Ideal para | Configuración dinámica de llamadas | Consumo de eventos en producción |

Las nuevas integraciones deben consumir eventos mediante webhooks
basados en endpoints. Conserva (o agrega) una URL heredada solo si configuras las llamadas
dinámicamente al momento de atenderlas o usas el despacho de herramientas en modo webhook: esos
intercambios de solicitud/respuesta solo se ejecutan en la ruta heredada.

---

## Relacionado

<CardGroup cols={2}>
  <Card title="Catálogo de eventos" icon="list" href="/es/webhooks/events">
    Todos los tipos de eventos y sus cargas útiles.
  </Card>
  <Card title="Endpoints de webhook" icon="bolt" href="/es/webhooks/endpoints">
    Administra múltiples endpoints, filtros de eventos y secretos.
  </Card>
  <Card title="telephony.incoming / web.incoming" icon="phone" href="/es/webhooks/call-incoming">
    La solicitud bloqueante que tu servidor debe responder para configurar llamadas.
  </Card>
  <Card title="telephony.complete / web.complete" icon="phone" href="/es/webhooks/call-complete">
    Carga útil posterior a la llamada con transcripción, grabación y métricas.
  </Card>
</CardGroup>
