> 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.

# Canale personalizzato

## Panoramica

Custom Channel collega un sistema di messaggistica esterno a un agente ElevenLabs. Invia i messaggi degli utenti a un webhook ElevenLabs, quindi ricevi le risposte dell'agente sul tuo endpoint HTTPS.

> **Warning**
>
> Custom Channel è in alpha.

> **Warning**
>
> Custom Channel non è disponibile per agenti o workspace che usano la modalità zero-retention.

## Funzionalità

| Funzionalità                  | Supporto                                                    |
| ----------------------------- | ----------------------------------------------------------- |
| Modalità zero-retention (ZRM) | Non supportata — non disponibile per workspace e agenti ZRM |
| Allegati nei messaggi         | Non supportati — i messaggi possono contenere solo testo    |

## Configurazione

#### Apri Custom Channel

Apri il tuo agente, seleziona **Canali**, scegli **Custom Channel** e fai clic su **Aggiungi trigger**.

#### Configura il trigger

Seleziona una connessione esistente o creane una, quindi inserisci l'**URL del webhook di risposta**.

#### Copia le credenziali

Fai clic su **Aggiungi**, quindi copia l'**URL del webhook in entrata**, il **secret in entrata** e il **secret di firma in uscita**.

#### Configura il tuo servizio

Invia i messaggi degli utenti all'URL del webhook in entrata con il secret in entrata in `X-Webhook-Secret`. Usa il secret di firma in uscita per verificare ogni risposta.

## Invia un messaggio

Invia una richiesta `POST` all'URL del webhook generato:

```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                  | Obbligatorio | Descrizione                                                                                 |
| ---------------------- | ------------ | ------------------------------------------------------------------------------------------- |
| `data.type`            | Sì           | Deve essere `user_message`.                                                                 |
| `data.text`            | Sì           | Messaggio dell'utente non vuoto.                                                            |
| `data.user_identifier` | No           | Identificatore dell'utente esterno.                                                         |
| `user_message_id`      | Sì           | Chiave di idempotenza non vuota fornita dal tuo sistema.                                    |
| `conversation_id`      | No           | Includi l'ID restituito per continuare una conversazione. Omettilo per iniziarne una nuova. |
| `dynamic_variables`    | No           | Variabili dinamiche fornite all'agente per questo turno.                                    |

ElevenLabs restituisce `202 Accepted` prima di elaborare il turno:

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

Per continuare la conversazione, invia un'altra richiesta con quel `conversation_id` e un nuovo `user_message_id`.

## Ricevi le risposte

ElevenLabs invia una richiesta `POST` all'URL del webhook di risposta dopo ogni 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 l'elaborazione non riesce, `status` è `failed`, `data` è `[]` e `error` contiene una descrizione.

`data` elenca gli eventi nell'ordine dei turni. Ogni elemento ha un `type` e un `event`:

* `agent_response` contiene un singolo messaggio dell'agente. `response_id` identifica univocamente il messaggio, mentre `event_id` lo associa a un turno. Unisci i valori di `agent_response` se il tuo canale mostra una sola bolla di testo per turno.
* `agent_tool_response` riporta l'esito di uno strumento e condivide l'`event_id` del turno. Il relativo `status` può essere `success`, `error`, `blocked` o `skipped`. Una risposta con `tool_type: "system"`, `tool_name: "end_call"` e `status: "success"` indica che l'agente ha concluso la conversazione.

Più messaggi in entrata possono essere raggruppati in un unico turno. `user_message_ids` elenca gli ID dei messaggi utente a cui questa risposta risponde.

## Verifica le firme delle risposte

Ogni risposta include un header `ElevenLabs-Signature`:

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

Il digest è una firma HMAC-SHA256 su `{timestamp}.{raw_request_body}` che usa il secret di firma in uscita. Verifica il body non elaborato prima di analizzare il JSON e rifiuta i timestamp obsoleti.

```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 di consegna

ElevenLabs effettua tre tentativi di consegna nel processo, approssimativamente a 0, 0,5 e 2 secondi. Una risposta `2xx` indica che la consegna è riuscita.

I body delle richieste sono limitati a 256 KiB.