> This is a page from the ElevenLabs documentation. For a complete page index, fetch https://elevenlabs.io/docs/llms.txt. For the full documentation in a single file, fetch https://elevenlabs.io/docs/llms-full.txt.

# Canal personalizado

## Visão geral

O Canal personalizado conecta um sistema de mensagens externo a um agente da ElevenLabs. Envie mensagens de usuários para um webhook da ElevenLabs e receba as respostas do agente no seu próprio endpoint HTTPS.

> **Warning**
>
> O Canal personalizado está em alfa.

> **Warning**
>
> O Canal personalizado não está disponível para agentes ou espaços de trabalho que usam o modo de retenção zero.

## Recursos

| Recurso                     | Suporte                                                              |
| --------------------------- | -------------------------------------------------------------------- |
| Modo de retenção zero (ZRM) | Não compatível — indisponível para espaços de trabalho e agentes ZRM |
| Anexos em mensagens         | Não compatível — as mensagens contêm apenas texto                    |

## Configuração

#### Abrir o Canal personalizado

Abra seu agente, selecione **Canais**, escolha **Canal personalizado** e clique em **Adicionar gatilho**.

#### Configurar o gatilho

Selecione uma conexão existente ou crie uma e insira a **URL do webhook de resposta**.

#### Copiar as credenciais

Clique em **Adicionar** e copie a **URL do webhook de entrada**, o **Segredo de entrada** e o **Segredo de assinatura de saída**.

#### Configurar seu serviço

Envie mensagens de usuários para a URL do webhook de entrada com o segredo de entrada em `X-Webhook-Secret`. Use o segredo de assinatura de saída para verificar cada resposta.

## Enviar uma mensagem

Envie uma solicitação `POST` para a URL do webhook gerada:

```text
POST /v1/convai/api-integrations/custom_channel/triggers/{trigger_connection_id}/async_message
X-Webhook-Secret: <inbound-secret>
Content-Type: application/json
```

```json
{
  "data": {
    "type": "user_message",
    "text": "Where is my order?",
    "user_identifier": "customer_8427"
  },
  "user_message_id": "msg_01k1e6z3f4t8n9c2",
  "dynamic_variables": {
    "order_id": "order_72491"
  }
}
```

| Campo                  | Obrigatório | Descrição                                                                                  |
| ---------------------- | ----------- | ------------------------------------------------------------------------------------------ |
| `data.type`            | Sim         | Deve ser `user_message`.                                                                   |
| `data.text`            | Sim         | Mensagem do usuário não vazia.                                                             |
| `data.user_identifier` | Não         | Identificador do usuário externo.                                                          |
| `user_message_id`      | Sim         | Chave de idempotência não vazia fornecida pelo seu sistema.                                |
| `conversation_id`      | Não         | Inclua o ID retornado para continuar uma conversa. Omita-o para iniciar uma nova conversa. |
| `dynamic_variables`    | Não         | Variáveis dinâmicas fornecidas ao agente neste turno.                                      |

A ElevenLabs retorna `202 Accepted` antes de processar o turno:

```json
{
  "conversation_id": "conv_01k1e72d4x8p6v3m",
  "status": "queued"
}
```

Para continuar a conversa, envie outra solicitação com esse `conversation_id` e um novo `user_message_id`.

## Receber respostas

A ElevenLabs envia uma solicitação `POST` para a URL do webhook de resposta após cada turno:

```json
{
  "version": "1",
  "conversation_id": "conv_01k1e72d4x8p6v3m",
  "user_message_ids": ["msg_01k1e6z3f4t8n9c2"],
  "status": "completed",
  "data": [
    {
      "type": "agent_response",
      "event": {
        "agent_response": "Your order is scheduled to arrive tomorrow.",
        "response_id": "9f2c1a7e-4b3d-4e8a-9c1f-2d6b8e0a5f31",
        "event_id": 4
      }
    },
    {
      "type": "agent_tool_response",
      "event": {
        "tool_name": "end_call",
        "tool_call_id": "toolu_01k1e70r4b8y",
        "tool_type": "system",
        "event_id": 4,
        "is_called": true,
        "is_error": false,
        "is_blocked": false,
        "status": "success"
      }
    }
  ],
  "error": null
}
```

Se o processamento falhar, `status` será `failed`, `data` será `[]` e `error` conterá uma descrição.

`data` lista os eventos na ordem dos turnos. Cada item tem um `type` e um `event`:

* `agent_response` contém uma fala do agente. `response_id` identifica a fala de forma exclusiva, enquanto `event_id` a associa a um turno. Una os valores de `agent_response` se o seu canal exibir uma bolha de texto por turno.
* `agent_tool_response` informa o resultado de uma ferramenta e compartilha o `event_id` do turno. Seu `status` pode ser `success`, `error`, `blocked` ou `skipped`. Uma resposta com `tool_type: "system"`, `tool_name: "end_call"` e `status: "success"` significa que o agente encerrou a conversa.

Várias mensagens de entrada podem ser agrupadas em um único turno. `user_message_ids` lista os IDs das mensagens de usuários às quais esta resposta está respondendo.

## Verificar assinaturas das respostas

Cada resposta inclui um cabeçalho `ElevenLabs-Signature`:

```text
t=1753876800,v0=<hex-digest>
```

O resumo é uma assinatura HMAC-SHA256 sobre `{timestamp}.{raw_request_body}` usando o segredo de assinatura de saída. Verifique o corpo bruto antes de analisar o JSON e rejeite timestamps antigos.

```python
import hashlib
import hmac
import time


def verify_signature(raw_body: bytes, header: str, secret: str) -> None:
    values = dict(part.split("=", 1) for part in header.split(","))
    timestamp = values["t"]
    if abs(time.time() - int(timestamp)) > 30 * 60:
        raise ValueError("Stale webhook signature")

    expected = hmac.new(
        secret.encode(),
        timestamp.encode() + b"." + raw_body,
        hashlib.sha256,
    ).hexdigest()
    if not hmac.compare_digest(expected, values["v0"]):
        raise ValueError("Invalid webhook signature")
```

```typescript
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifySignature(rawBody: Buffer, header: string, secret: string): void {
  const values = Object.fromEntries(header.split(",").map((part) => part.split("=", 2)));
  const timestamp = values.t;
  if (!timestamp || Math.abs(Date.now() / 1000 - Number(timestamp)) > 30 * 60) {
    throw new Error("Stale webhook signature");
  }

  const expected = createHmac("sha256", secret).update(`${timestamp}.`).update(rawBody).digest();
  const received = Buffer.from(values.v0 ?? "", "hex");
  if (received.length !== expected.length || !timingSafeEqual(expected, received)) {
    throw new Error("Invalid webhook signature");
  }
}
```

## Comportamento de entrega

A ElevenLabs faz três tentativas de entrega em andamento, aproximadamente aos 0, 0,5 e 2 segundos. Uma resposta `2xx` indica que a entrega foi bem-sucedida.

Os corpos das solicitações são limitados a 256 KiB.