---
title: "telephony.incoming / web.incoming"
description: "Webhook bloqueante que define a configuração de uma chamada recebida em tempo real."
---

Quando uma chamada telefônica recebida chega a um número **sem um
agente atribuído**, ou uma sessão de widget web é iniciada em uma chave publicável no
`mode="webhook"`, o ThunderPhone envia uma solicitação **bloqueante**
`telephony.incoming` / `web.incoming` para sua
[URL de webhook legada](/api-reference/organizations#legacy-single-url-webhook)
e aguarda uma resposta de configuração: até **13 segundos** para uma
chamada telefônica, incluindo uma tentativa adicional, e até **10 segundos** para uma sessão
de widget (consulte [Tempos limite e novas tentativas](#timeouts-and-retries)). Use este
intercâmbio para escolher dinamicamente um prompt, uma voz e ferramentas por chamada —
consulte o [guia de configuração dinâmica de chamadas](/pt/guides/dynamic-call-config)
para conhecer o padrão completo.

<Note>
  Os [endpoints de webhook](/pt/webhooks/endpoints) inscritos também recebem
  `telephony.incoming` / `web.incoming` — para **todas** as chamadas recebidas
  e sessões web, com agente configurado ou não — mas essas entregas são
  notificações fire-and-forget com um `event_id`, nunca bloqueantes.
  Somente o webhook legado de URL única contém a troca de configuração
  desta página. Os formatos das notificações de endpoint estão no
  [catálogo de eventos](/pt/webhooks/events).
</Note>

## Tempos limite e novas tentativas

Responda rápido: quem liga está aguardando na linha enquanto você decide.

Para uma chamada telefônica (`telephony.incoming`):

- A primeira tentativa pode levar até **10 segundos**.
- Se exceder o tempo limite, não conseguir conectar ou receber uma resposta
  `5xx`, `408` ou `429`, o ThunderPhone tenta mais uma vez. A nova tentativa recebe o tempo
  restante de um orçamento de **13 segundos** para toda a troca: cerca de 3 segundos após
  uma primeira tentativa que excedeu o tempo limite, quase todos os 13 após uma falha rápida.
  Não há nova tentativa quando restariam menos de 2 segundos.
- Qualquer outra resposta `4xx`, uma resposta acima do
  [limite de tamanho](#response-size-limit) ou uma resposta `2xx` cujo corpo não seja uma
  configuração válida é final e não gera nova tentativa.
- Ambas as tentativas enviam o mesmo corpo, a mesma `X-ThunderPhone-Signature`
  e o mesmo cabeçalho `X-ThunderPhone-Idempotency-Key`
  (`telephony-incoming-<call_id>`), para que um manipulador com efeitos colaterais
  possa reconhecer a nova tentativa.

Não há agente de fallback. Se nenhuma configuração utilizável chegar, a
chamada é encerrada imediatamente, em vez de deixar quem liga em uma linha
silenciosa. Ela aparece no histórico de chamadas como uma chamada com falha e
não faturada, com `end_reason` `inbound_config_webhook_failed`, e o evento
usual [`telephony.complete`](/pt/webhooks/call-complete) é enviado.

Uma sessão de widget (`web.incoming`) recebe uma única tentativa de até 10
segundos. Se ela falhar, a solicitação de sessão falha com `502` ou `422`.

<Warning>
  **Chamadas configuradas por webhook não incluem um anúncio de consentimento do ThunderPhone.** As chamadas configuradas por meio dessa troca ignoram o anúncio no início da chamada no nível do agente e são expressamente excluídas da estrutura de anúncios de consentimento do ThunderPhone (Termos de Serviço, seção "Gravação e consentimento"). Sua organização é a única responsável por todos os avisos e consentimentos de gravação, monitoramento, participação de IA e identificação de quem liga exigidos nessas chamadas — as chamadas ainda podem ser gravadas, transcritas, analisadas e atendidas por IA. Inclua as divulgações necessárias em seu próprio fluxo de chamadas antes de ativar este caminho.
</Warning>

## Payload da solicitação

Para chamadas telefônicas (`telephony.incoming`):

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

| Campo | Tipo | Descrição |
|-------|------|-------------|
| `call_id` | inteiro | ID da chamada — estável em todos os eventos desta chamada |
| `from_number` | string | Número E.164 de quem liga |
| `to_number` | string | Destino E.164 (um dos seus números ThunderPhone) |

Para sessões de widget web (`web.incoming`), o `data` identifica a
página de incorporação em vez de números de telefone:

```json
{
  "type": "web.incoming",
  "data": {
    "call_id": 987654322,
    "origin_domain": "https://example.com",
    "publishable_key_prefix": "pk_live_a1b2"
  }
}
```

| Campo | Tipo | Descrição |
|-------|------|-------------|
| `call_id` | inteiro | ID da chamada |
| `origin_domain` | string | A origem da página que hospeda o widget, conforme enviada pelo navegador. Um cliente que não seja um navegador pode enviar qualquer valor, portanto não o trate como prova da origem da solicitação |
| `publishable_key_prefix` | string | Primeiros caracteres da chave publicável que abriu a sessão |
| `language`, `primary_language` | string | Presente quando a sessão do widget solicitou uma substituição de idioma |
| `voice` | string | Presente quando a sessão do widget solicitou uma substituição de voz |
| `website_context` | string | Presente quando o widget transmitiu contexto da página por sessão |

Os campos de substituição (`language`, `voice`, `website_context` e quaisquer
`variables` do navegador) chegam somente quando a chave publicável os permite em
suas [substituições do navegador](/pt/guides/embed-web-widget). Uma
solicitação de sessão que contenha uma substituição não permitida pela chave é rejeitada antes que
este webhook seja chamado.

<Note>
  Widgets no modo webhook entregam esta solicitação ao próprio
  `webhook_url` da chave publicável quando um estiver definido, usando como
  alternativa a URL de webhook no nível da organização. Em ambos os casos,
  ela é assinada com o `secret` de webhook da organização.
</Note>

---

## Esquema de resposta

Retorne um objeto JSON que descreva a configuração do agente para esta chamada.
`prompt` e `voice` são obrigatórios; todos os demais campos são opcionais.

```json
{
  "prompt":  "You are a helpful booking assistant for Acme Restaurant.",
  "voice":   "john",
  "product": "spark",
  "background_track": null,
  "tools":   []
}
```

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|----------|-------------|
| `prompt` | string | sim | Prompt de sistema que orienta o agente |
| `voice` | string | sim | ID da voz de [`GET /v1/voices`](/api-reference/agents#voices), por exemplo, `john`. `voice_name` é aceito como um alias. Vozes desconhecidas falham na validação e rejeitam a chamada |
| `product` | string | não | O padrão é `spark`. Permitidos: `spark`, `bolt`, `storm-base`, `storm-base-with-ack`, `storm-extra`, `storm-extra-with-ack` |
| `thinking_level` | string | não | `minimal`, `base` (padrão) ou `extra`. Substituído para produtos Storm: `storm-extra*` força `extra`; outros `storm-*` forçam `base` |
| `audio_context_mode` | string | não | `full` (padrão) ou `reduced` |
| `watchdog_enabled` | booleano | não | Ativa a supervisão desta chamada. O padrão é `false` |
| `additional_audio_context` | booleano \| null | não | Inclui os últimos turnos do áudio de quem liga, em vez de apenas o turno mais recente, melhorando correções e a coleta de dados com muita soletração ou números, com pequeno impacto na latência e no custo. É ativado por padrão para sessões de entrada e desativado para chamadas telefônicas de saída; `null` mantém o padrão |
| `storm_feedback_mode` | string | não | `none`, `acknowledgement` (padrão) ou `tick` |
| `language` | string | não | Abreviação de `primary_language` |
| `primary_language` | string | não | Código de idioma normalizado (o padrão é `en`). Códigos que não podem ser resolvidos rejeitam a chamada |
| `has_additional_languages` | booleano | não | O padrão é `false` |
| `additional_languages` | array de strings | não | Idiomas adicionais para os quais o agente pode alternar |
| `native_voice_switching` | booleano | não | O padrão é `false`. Quando a chamada alterna para outro idioma, troca para uma voz nativa desse idioma (correspondente ao gênero) em vez de manter a voz configurada |
| `language_switching` | string \| null | não | `ask_first` (padrão) ou `follow_caller`; `null` é igual a `ask_first`. Define como um agente multilíngue lida com a outra pessoa falando outro de seus idiomas. `ask_first` alterna imediatamente quando a pessoa pede o idioma ou diz uma frase completa nele e pergunta se deve continuar nesse idioma após algo curto, como um "sim". `follow_caller` alterna para qualquer idioma configurado que a pessoa fale, mesmo que seja uma única palavra. Aplica-se apenas quando `additional_languages` está definido |
| `background_track` | string \| null | não | ID de áudio ambiente ou `null` |
| `acknowledgement_prompt_mode` | string | não | `auto` (padrão) ou `manual` (produtos Storm-with-ack) |
| `acknowledgement_prompt` | string | não | Usado quando `acknowledgement_prompt_mode="manual"` |
| `silence_interval_seconds` | inteiro \| null | não | 5–120. Segundos de silêncio de quem liga antes de uma verificação |
| `silence_max_checkins` | inteiro \| null | não | 1–10 |
| `silence_checkins_enabled` | booleano | não | O padrão é `true` |
| `connect_tone_enabled` | booleano | não | O padrão é `false` |
| `pronunciation_dicts` | booleano \| null | não | O padrão é `false`; `null` é igual a `false`. Quando `true`, a fala da Cartesia na chamada usa o dicionário de pronúncia do ThunderPhone para o idioma da chamada, quando houver um disponível. Funciona para chamadas telefônicas e de widget. Aplica-se apenas às vozes Cartesia do próprio ThunderPhone, não às vozes na sua própria chave de API da Cartesia. Não é usado com `agent_id`, em que as configurações salvas do agente se aplicam |
| `voicemail_action` | string | não | `prompt` (padrão), `hangup` ou `message` |
| `voicemail_message` | string | não | Usado quando `voicemail_action="message"` |
| `agent_name` | string | não | Nome de exibição informado aos painéis e ao widget |
| `org_name` | string | não | Nome de exibição da organização para a persona do agente |
| `tools` | array | não | Esquemas embutidos de ferramentas de função (consulte [Ferramentas de função](/pt/tools/overview)) |
| `transfer_destinations` | array de objetos \| null | não | Até 50 entradas `{name, number}` para as quais esta chamada pode ser transferida, validadas como os [números de transferência](/api-reference/agents#transfer-numbers) de um agente. O agente transfere pelo nome e somente para esses destinos; `[]` significa nenhuma transferência. Omita esse campo ou envie `null` para permitir que o agente transfira para um número que escolher |
| `timezone` | string | não | Fuso horário IANA para esta chamada, como `America/Chicago`. O agente informa a data e a hora nesse fuso, em vez de usar o próprio fuso horário. Também permitido ao lado de `agent_id`. Valores que não são fusos IANA são ignorados. Consulte [Definir o fuso horário por chamada](/pt/guides/dynamic-call-config) |
| `call_id` | inteiro | não | Eco opcional do ID da chamada da solicitação; ignorado |

<Note>
  Chaves desconhecidas no nível superior são **ignoradas** silenciosamente — um nome
  de campo com erro de digitação não rejeita a configuração, apenas não é aplicado. A ordem de fala
  e `max_hold_seconds` não são aceitos aqui; eles são configuráveis apenas no próprio
  [Agente](/api-reference/agents).
</Note>

Como `prompt` e `voice` são obrigatórios, retornar `{}` ou qualquer
resposta que falhe na validação rejeita a chamada: uma chamada telefônica é encerrada
conforme descrito em [Tempos limite e novas tentativas](#timeouts-and-retries), e uma
solicitação de sessão do widget falha com `422`. Não há alternativa para agente estático
neste caminho (um número ou uma chave no modo webhook não tem agente atribuído).

---

## Limite de tamanho da resposta

<Warning>
  As respostas de configuração são limitadas a **5 MiB**. Se um manipulador
  retornar uma resposta maior, inclusive com um status `2xx`,
  o ThunderPhone informará que a resposta excedeu o limite e
  rejeitará a chamada ou sessão do widget. Mantenha na resposta apenas os campos
  necessários para configurar a chamada; hospede dados grandes por meio de ferramentas de função ou
  outro serviço em vez de incorporá-los à configuração.
</Warning>

---

## Exemplo de manipulador

<CodeGroup>
```python Python (FastAPI)
import hashlib
import hmac
import json
import os

from fastapi import FastAPI, HTTPException, Request

app = FastAPI()
WEBHOOK_SECRET = os.environ["THUNDERPHONE_WEBHOOK_SECRET"]

def verify(body: bytes, signature: str) -> bool:
    expected = hmac.new(WEBHOOK_SECRET.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature or "")

@app.post("/thunderphone-webhook")
async def webhook(request: Request):
    body = await request.body()
    if not verify(body, request.headers.get("X-ThunderPhone-Signature", "")):
        raise HTTPException(status_code=401)

    event = json.loads(body)
    if event["type"] == "telephony.incoming":
        caller = event["data"]["from_number"]
        prompt = (
            "Greet the caller as a San Francisco local…"
            if caller.startswith("+1415")
            else "You are a friendly customer support agent…"
        )
        return {
            "prompt": prompt,
            "voice": "john",
            "product": "spark",
        }
    if event["type"] == "web.incoming":
        return {
            "prompt": "You are the website's helpful voice assistant…",
            "voice": "john",
            "product": "spark",
        }
    return {}
```

```javascript Node.js (Express)
import crypto from "node:crypto";
import express from "express";

const app = express();
const SECRET = process.env.THUNDERPHONE_WEBHOOK_SECRET;

function verify(body, signature) {
  const expected = crypto
    .createHmac("sha256", SECRET)
    .update(body)
    .digest("hex");
  return signature &&
    crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

app.post(
  "/thunderphone-webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    if (!verify(req.body, req.header("X-ThunderPhone-Signature"))) {
      return res.sendStatus(401);
    }
    const event = JSON.parse(req.body.toString("utf8"));

    if (event.type === "telephony.incoming" || event.type === "web.incoming") {
      const caller = event.data.from_number || "web";
      const prompt = caller.startsWith("+1415")
        ? "Greet the caller as a San Francisco local…"
        : "You are a friendly customer support agent…";
      return res.json({
        prompt,
        voice: "john",
        product: "spark",
      });
    }
    res.json({});
  },
);
```
</CodeGroup>

---

## Resposta com ferramentas de função

Anexe ferramentas para que a IA possa chamar suas APIs durante a conversa:

```json
{
  "prompt":  "You are a booking assistant. Use the available tools to help customers schedule appointments.",
  "voice":   "john",
  "product": "spark",
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "search_appointments",
        "description": "Find available appointment slots",
        "parameters": {
          "type": "object",
          "properties": {
            "date": { "type": "string", "description": "YYYY-MM-DD" },
            "service": { "type": "string" }
          },
          "required": ["date"]
        }
      },
      "endpoint": {
        "url": "https://api.example.com/appointments/search",
        "method": "POST",
        "headers": {
          "X-Api-Key": "your-key"
        }
      }
    }
  ]
}
```

<Tip>
  As solicitações ao endpoint de ferramentas são assinadas com o **mesmo segredo
  de webhook da organização** que assinou esta troca. Consulte
  [Ferramentas de função](/pt/tools/overview) para ver a estrutura exata e o formato
  da solicitação assinada.
</Tip>

---

## Guia rápido de níveis de produto

| Produto | Latência | Raciocínio | Confirmação |
|---------|---------|-----------|-----------------|
| `spark` | Mais baixa | Básico | — |
| `bolt` | Baixa | Aprimorado | — |
| `storm-base` | Média | Forte | — |
| `storm-base-with-ack` | Média | Forte | Preenchimento automático enquanto pensa |
| `storm-extra` | Mais alta | Profundo | — |
| `storm-extra-with-ack` | Mais alta | Profundo | Preenchimento automático enquanto pensa |

---

## Relacionados

<CardGroup cols={2}>
  <Card title="telephony.complete / web.complete" icon="phone-slash" href="/pt/webhooks/call-complete">
    O evento não bloqueante de encerramento da chamada.
  </Card>
  <Card title="Ferramentas de função" icon="screwdriver-wrench" href="/pt/tools/overview">
    Esquema JSON completo para `tools[]` e o contrato de endpoint assinado.
  </Card>
  <Card title="Endpoints de webhook" icon="bolt" href="/pt/webhooks/endpoints">
    Inscreva várias URLs em `telephony.incoming` / `web.incoming`.
  </Card>
  <Card title="Configuração dinâmica de chamadas" icon="wand-magic-sparkles" href="/pt/guides/dynamic-call-config">
    Padrões para prompts, ferramentas e testes A/B por quem liga.
  </Card>
</CardGroup>
