---
title: "Incorpore o widget da web"
description: "Adicione um agente de voz ao seu site de marketing ou suporte com uma única tag de script — visitantes falam com ele diretamente pelo navegador, sem precisar de número de telefone."
---

O widget da web oferece aos visitantes do seu site uma conversa de clique para falar
com um agente de IA, usando o microfone do navegador. É um SDK separado
de JavaScript / React com sua própria [referência do SDK](/pt/widget/overview)
— este guia se concentra na configuração do lado do ThunderPhone de que o widget precisa.

<Note>
  Você pode fazer tudo isso sem cURL: a página do painel **Widgets da web**
  (`/dashboard/web-widgets`) cria o widget, define seu modo
  e agente, gerencia domínios permitidos e fornece o snippet de incorporação.
</Note>

## Pré-requisitos

<Steps>
  <Step title="Crie um agente">
    O agente cujo prompt e voz executarão a sessão do widget. Defina
    `widget_enabled: true` (o padrão).
  </Step>
  <Step title="Defina o modo de roteamento">
    - `mode="agent"` — um agente estático por chave. O mais simples.
    - `mode="webhook"` — seu servidor escolhe o agente por visitante por meio de um
      [webhook `web.incoming`](/pt/webhooks/call-incoming). Use isso para
      usuários autenticados, testes A/B ou roteamento por página.
  </Step>
  <Step title="Liste os domínios permitidos">
    Informe cada nome de host que incorporará o widget. Os navegadores enviam a
    origem da página com cada solicitação de sessão, portanto a lista impede que sua chave
    funcione quando alguém a copia para outro site. Isso não é
    autenticação: um script fora de um navegador pode enviar qualquer cabeçalho
    `Origin`. `localhost` não é permitido automaticamente em produção; para
    testar localmente com a API de produção, adicione `localhost` à
    lista.
  </Step>
  <Step title="Escolha as substituições do navegador">
    Decida quais configurações de sessão sua página pode enviar. Chaves novas não aceitam
    nenhuma; consulte [Substituições do navegador](#browser-overrides).
  </Step>
</Steps>

## Criar uma chave publicável

<CodeGroup>
```bash Static agent
curl -X POST https://api.thunderphone.com/v1/publishable-key \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name":            "Marketing site (prod)",
    "mode":            "agent",
    "agent_id":        12,
    "allowed_domains": ["example.com", "*.example.com"],
    "allowed_overrides": ["context"]
  }'
```

```bash Dynamic via webhook
curl -X POST https://api.thunderphone.com/v1/publishable-key \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name":            "Support (dynamic)",
    "mode":            "webhook",
    "webhook_url":     "https://example.com/thunderphone/widget-hook",
    "allowed_domains": ["support.example.com"]
  }'
```
</CodeGroup>

A resposta inclui uma `key` que começa com `pk_live_...`. **Chaves publicáveis
são públicas por definição** — é seguro incluí-las no bundle do seu front-end.
Consulte a [referência de chaves publicáveis](/api-reference/publishable-keys) para
ver todos os campos.

<Warning>
  `allowed_domains` deve conter pelo menos uma entrada. `*.example.com`
  corresponde a subdomínios (por exemplo, `api.example.com`), mas **não** ao
  domínio raiz. Curingas genéricos como `*` ou `*.*` são rejeitados.
</Warning>

## Adicione o widget ao seu site

Três opções de integração são abordadas na
[documentação do SDK do widget](/pt/widget/overview):

<CardGroup cols={3}>
  <Card title="Componente React" icon="react" href="/pt/widget/react">
    `<ThunderPhoneWidget publishableKey="pk_live_..." />`.
  </Card>
  <Card title="Hook headless" icon="circle-nodes" href="/pt/widget/headless-hook">
    `useThunderPhone()` para interfaces personalizadas.
  </Card>
  <Card title="Tag de script CDN" icon="code" href="/pt/widget/cdn-script-tag">
    `ThunderPhone.mount({...})` para sites sem bundler.
  </Card>
</CardGroup>

As três opções aceitam a mesma `publishableKey` e renderizam o botão do microfone
e o elemento de áudio durante a chamada.

## Substituições do navegador

O widget pode enviar quatro configurações por sessão a partir da página:

| Configuração | Prop do widget | O que ela altera |
|---------|-------------|-----------------|
| `context` | `context` | Texto da página, adicionado ao prompt do agente como referência |
| `variables` | nenhuma (somente `POST /v1/widget/session` bruto) | Valores para placeholders `{{variable}}` no prompt |
| `voice` | `voice` | A voz do agente nesta sessão |
| `language` | `language` | O idioma no qual a sessão começa |

Sua chave publicável é pública, portanto qualquer pessoa que a tenha pode enviar essas
configurações, inclusive por meio de um script que falsifica seu domínio. Por isso, cada uma fica
desativada até que você a permita na chave: marque-a em **Substituições do navegador** ao criar ou editar
o widget no painel, ou defina `allowed_overrides` pela
[API de chaves publicáveis](/api-reference/publishable-keys). Permita apenas o que
sua página realmente envia.

Uma solicitação de sessão que contenha uma configuração não permitida pela chave falha
com `400` e a identifica:

```json
{
  "error": "override_not_allowed",
  "fields": ["voice"],
  "message": "This publishable key does not allow the browser to set `voice`. ..."
}
```

A verificação se aplica em ambos os modos. No modo webhook, uma configuração permitida também é
encaminhada ao seu webhook `web.incoming` (`context` chega como
`website_context`). Chaves criadas antes de essa configuração existir permitem todas as
quatro, para que incorporações existentes continuem funcionando; desative o que você não usa.

O `context` do widget é truncado em 12.000 caracteres (aproximadamente 3.400
tokens de texto típico em inglês) e conta para a
[sobretaxa pelo tamanho do prompt](/pt/guides/billing-and-topups).
O agente recebe a instrução de tratá-lo como conteúdo da página, não como instruções.

## Webhooks no modo widget

Quando `mode="webhook"`, o ThunderPhone chama seu `webhook_url` a cada
início de sessão com uma carga `web.incoming`. Retorne a configuração do agente
que você deseja executar para aquele visitante — ela segue o mesmo
[esquema de resposta](/pt/webhooks/call-incoming) das chamadas
telefônicas:

```json
{
  "prompt":  "You are a VIP concierge for Jane Doe.",
  "voice":   "john",
  "product": "storm-base",
  "tools":   [ /* per-customer tools */ ]
}
```

Você pode combinar o contexto da sua própria sessão (qual cliente está navegando,
em qual página ele está) no prompt e trocar agentes por implantação.

## Observe as sessões

As sessões do widget aparecem em
[`GET /v1/calls`](/api-reference/calls#list-calls) com
`direction="widget"` — com a mesma transcrição, gravação, avaliação e
cobrança das chamadas telefônicas. Filtre por `direction` para criar um
painel exclusivo para widgets.

---

## Próximas etapas

<CardGroup cols={2}>
  <Card title="Referência do SDK do widget" icon="window-maximize" href="/pt/widget/overview">
    Detalhes de integração com React / hook / CDN.
  </Card>
  <Card title="Configuração dinâmica por chamada" icon="bolt" href="/pt/guides/dynamic-call-config">
    Implemente o fluxo `mode="webhook"` de ponta a ponta.
  </Card>
  <Card title="Referência de chaves publicáveis" icon="key" href="/api-reference/publishable-keys">
    Todos os campos do recurso de chave.
  </Card>
  <Card title="API de sessões de microfone" icon="microphone" href="/api-reference/mic-sessions">
    Ignore o widget; use o LiveKit diretamente para interfaces personalizadas.
  </Card>
</CardGroup>
