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

# Aangepast kanaal

## Overzicht

Custom Channel verbindt een extern berichtensysteem met een ElevenLabs-agent. Stuur gebruikersberichten naar een ElevenLabs-webhook en ontvang vervolgens antwoorden van de agent op je eigen HTTPS-endpoint.

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

> **Warning**
>
> Custom Channel is niet beschikbaar voor agents of workspaces die de zero-retentionmodus gebruiken.

## Mogelijkheden

| Mogelijkheid              | Ondersteuning                                                         |
| ------------------------- | --------------------------------------------------------------------- |
| Zero-retentionmodus (ZRM) | Niet ondersteund — niet beschikbaar voor ZRM-workspaces en ZRM-agents |
| Bijlagen in berichten     | Niet ondersteund — berichten bevatten alleen tekst                    |

## Instellen

#### Open Custom Channel

Open je agent, selecteer **Channels**, kies **Custom Channel** en klik op **Add trigger**.

#### Configureer de trigger

Selecteer een bestaande verbinding of maak er een en voer vervolgens de **Reply Webhook URL** in.

#### Kopieer de inloggegevens

Klik op **Add** en kopieer vervolgens de **Inbound Webhook URL**, **Inbound Secret** en **Outbound Signing Secret**.

#### Configureer je service

Stuur gebruikersberichten naar de inbound webhook-URL met het inbound secret in `X-Webhook-Secret`. Gebruik het outbound signing secret om elk antwoord te verifiëren.

## Een bericht verzenden

Stuur een `POST`-request naar de gegenereerde webhook-URL:

```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"
  }
}
```

| Veld                   | Verplicht | Beschrijving                                                                                            |
| ---------------------- | --------- | ------------------------------------------------------------------------------------------------------- |
| `data.type`            | Ja        | Moet `user_message` zijn.                                                                               |
| `data.text`            | Ja        | Niet-leeg gebruikersbericht.                                                                            |
| `data.user_identifier` | Nee       | ID voor de externe gebruiker.                                                                           |
| `user_message_id`      | Ja        | Niet-lege idempotency key die door je systeem wordt opgegeven.                                          |
| `conversation_id`      | Nee       | Voeg de geretourneerde ID toe om een gesprek voort te zetten. Laat weg om een nieuw gesprek te starten. |
| `dynamic_variables`    | Nee       | Dynamische variabelen die voor deze beurt aan de agent worden doorgegeven.                              |

ElevenLabs retourneert `202 Accepted` voordat de beurt wordt verwerkt:

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

Stuur nog een request met die `conversation_id` en een nieuwe `user_message_id` om het gesprek voort te zetten.

## Antwoorden ontvangen

ElevenLabs stuurt na elke beurt een `POST`-request naar de reply webhook-URL:

```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
}
```

Als de verwerking mislukt, is `status` gelijk aan `failed`, is `data` gelijk aan `[]` en bevat `error` een beschrijving.

`data` bevat gebeurtenissen in de volgorde van de beurt. Elk item heeft een `type` en een `event`:

* `agent_response` bevat één uiting van de agent. `response_id` identificeert de uiting uniek, terwijl `event_id` deze aan een beurt koppelt. Voeg de waarden van `agent_response` samen als je kanaal één tekstballon per beurt weergeeft.
* `agent_tool_response` rapporteert een toolresultaat en deelt de `event_id` van de beurt. De `status` is `success`, `error`, `blocked` of `skipped`. Een antwoord met `tool_type: "system"`, `tool_name: "end_call"` en `status: "success"` betekent dat de agent het gesprek heeft beëindigd.

Meerdere inkomende berichten kunnen worden samengevoegd tot één beurt. `user_message_ids` bevat de ID's van de gebruikersberichten waarop dit antwoord reageert.

## Handtekeningen van antwoorden verifiëren

Elk antwoord bevat een `ElevenLabs-Signature`-header:

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

De digest is een HMAC-SHA256-handtekening over `{timestamp}.{raw_request_body}` met het outbound signing secret. Verifieer de onbewerkte body voordat je JSON parseert en wijs verouderde timestamps af.

```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");
  }
}
```

## Leveringsgedrag

ElevenLabs doet drie leveringspogingen binnen hetzelfde proces, na ongeveer 0, 0,5 en 2 seconden. Een `2xx`-response markeert de levering als geslaagd.

Requestbodies zijn beperkt tot 256 KiB.