
# Channel Connection API

Deze handleiding laat zien hoe je messaging-kanalen aan een account koppelt met behulp van de API. Het is geschreven voor een ontwikkelaar die een integratie of wrapper bouwt, dus de focus ligt op de exacte verzoeken, de volgorde waarin ze moeten worden uitgevoerd en de antwoorden die je terugkrijgt.

Er is één patroon dat je vooraf moet begrijpen, omdat dit op bijna elk kanaal hier van toepassing is.

## Het connect-then-poll patroon

De meeste kanalen kunnen niet met één enkele API-aanroep worden gekoppeld. Het koppelen van WhatsApp, Instagram of Messenger betekent dat de accounthouder moet inloggen op zijn eigen provider-account en toegang moet goedkeuren. Er is **geen headless (volledig geautomatiseerd) pad** voor die goedkeuring - een echt persoon moet een URL in een browser openen of een QR-code scannen met zijn telefoon.

De flow is dus altijd:

1. **Start de koppeling** met een `POST`. Het antwoord geeft je een URL om te openen of een QR-code om weer te geven.
2. **Geef dit door aan de eindgebruiker** - open de URL in hun browser of toon de QR-code op het scherm zodat ze deze kunnen scannen.
3. **Poll het status-eindpunt** met `GET` met een kort interval (elke paar seconden) totdat de status een gekoppelde staat bereikt.

De taak van jouw integratie is om die lus aan te sturen: toon de URL of QR-code en poll vervolgens totdat het klaar is. Plan je UI rondom de poll - een spinner met een bericht als "wachten tot je klaar bent in je browser" werkt goed.

::: note
**Let op:** Zorg er voordat je begint voor dat API-toegang is ingeschakeld voor het abonnement en dat je een API-sleutel hebt. Zie [API-toegang](../integrations/api-access.md) voor hoe je er een genereert. Alle onderstaande verzoeken gebruiken de basis-URL `https://api.youraiconnector.com/v1` en je moet elk verzoek verifiëren. Zie [Authenticatie](authentication.md) voor de vier geaccepteerde vormen - de voorbeelden hier gebruiken de `X-API-Key`-header, waarbij elk cURL-voorbeeld per pagina de eenvoudigere `?apiKey=`-queryvorm toont.
:::


---

## Instagram + Messenger (Meta)

Instagram en Messenger worden samen in één flow gekoppeld, omdat ze beide op een Facebook-pagina draaien. De accounthouder autoriseert via Facebook, jij haalt de lijst met pagina's op die zij beheren en je kiest welke pagina je wilt koppelen.

### Stap 1 - Start de Instagram + Messenger-verbinding

```
POST /channels/meta/connect
```

Dit retourneert een toestemmings-URL. Er worden geen inloggegevens verzonden in dit verzoek - de koppeling wordt volledig in de browser geautoriseerd.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/meta/connect?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/channels/meta/connect", {
  method: "POST",
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
// Open data.oauth_url in the end user's browser.
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/channels/meta/connect",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
# Open data["oauth_url"] in the end user's browser.
```

**Antwoord**

```json
{
  "success": true,
  "oauth_url": "https://www.facebook.com/v21.0/dialog/oauth?client_id=...&state=...",
  "state_token": "opaque-one-time-token",
  "connect_url": "https://api.youraiconnector.com/v1/channels/meta/connect/page?token=eyJhbGciOi...",
  "connect_url_expires_at": 1717000000000,
  "expires_at": "2026-06-10T12:30:00.000Z"
}
```

Open `oauth_url` in de browser van de eindgebruiker zodat ze kunnen inloggen op Facebook en toegang kunnen goedkeuren. De koppelingspoging verloopt op `expires_at` (ongeveer 30 minuten) - als deze verloopt, begin dan opnieuw. Behandel `state_token` als een kortstondig geheim en log dit niet.

### Makkelijkste optie voor Instagram + Messenger: overhandig `connect_url`

Het antwoord bevat ook een kant-en-klare `connect_url`: een gehoste pagina die het volledige proces voor de accounthouder uitvoert. Ze openen deze, loggen in bij Facebook, en wanneer ze meer dan één Pagina hebben, wordt de lijst getoond en kunnen ze kiezen welke ze willen koppelen - daarna rapporteert de pagina zelf het succes. Geef deze link aan de accounthouder in plaats van zelf `oauth_url` te openen, een Pagina-kiezer te bouwen en te pollen. De link werkt ongeveer 30 minuten (`connect_url_expires_at`); als deze verloopt, start dan een nieuwe verbinding. De handmatige stappen hieronder zijn bedoeld voor integraties die het proces zelf willen aansturen en de Pagina-kiezer zelf willen weergeven.

### Stap 2 - De status pollen totdat pagina's zijn geladen

```
GET /channels/meta/status
```

Nadat de gebruiker het inloggen via Facebook heeft voltooid, moet je dit eindpunt elke paar seconden pollen. Het veld `status` doorloopt deze stappen:

| `status` | Betekenis |
|---|---|
| `pending` | Toestemming nog niet voltooid. Blijf wachten. |
| `token_received` | Geautoriseerd, maar de lijst met pagina's wordt nog geladen. |
| `pages_loaded` | Pagina's zijn beschikbaar - ga door naar stap 3. |
| `connected` | Een pagina is geselecteerd en het kanaal is live. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/channels/meta/status" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/channels/meta/status", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
// Poll until data.status === "pages_loaded".
```

**Python**

```python
res = requests.get(
    "https://api.youraiconnector.com/v1/channels/meta/status",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
# Poll until data["status"] == "pages_loaded".
```

**Antwoord (zodra pagina's zijn geladen)**

```json
{
  "success": true,
  "status": "pages_loaded",
  "pages": [
    {
      "id": "1234567890",
      "name": "My Business Page",
      "category": "Local business",
      "instagram_business_account": {
        "id": "17890000000000000",
        "username": "mybusiness"
      }
    }
  ],
  "selected_page": null
}
```

### Stap 3 - De pagina's weergeven (optioneel)

Als je de paginalijst liever afzonderlijk ophaalt (bijvoorbeeld om een keuzemenu weer te geven), gebruik dan:

```
GET /channels/meta/pages
```

```bash
curl "https://api.youraiconnector.com/v1/channels/meta/pages" \
  -H "X-API-Key: YOUR_API_KEY"
```

Dit retourneert dezelfde `pages`-array als het status-eindpunt. (Het `status`-eindpunt bevat de pagina's al, dus deze aanroep is enkel voor het gemak.)

### Stap 4 - De te verbinden pagina selecteren

```
POST /channels/meta/select-page
```

Stuur de `page_id` van de pagina die de gebruiker heeft gekozen. Het Instagram-account dat aan die pagina is gekoppeld, wordt automatisch verbonden; je hebt het `instagram`-object alleen nodig als je wilt overschrijven welk Instagram-account moet worden gebruikt.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/meta/select-page" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "page_id": "1234567890" }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/channels/meta/select-page", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ page_id: "1234567890" }),
});
const data = await res.json();
```

**Python**

```python
res = requests.post(
    "https://api.youraiconnector.com/v1/channels/meta/select-page",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"page_id": "1234567890"},
)
data = res.json()
```

**Antwoord**

```json
{
  "success": true,
  "page_id": "1234567890",
  "instagram_business_account_id": "17890000000000000"
}
```

Het kanaal is nu verbonden. Een vervolg-`GET /channels/meta/status` zal `status: "connected"` rapporteren.

### Vermeld de berichten van de verbonden pagina

```
GET /channels/meta/posts?platform=instagram
```

Geeft de recente berichten terug van de pagina die je hebt verbonden - Instagram-media of Facebook-berichten. Dit is wat je rendert in een kiezer wanneer je een toegangspunt instelt dat reageert op opmerkingen bij één specifiek bericht.

| Query-parameter | Vereist | Beschrijving |
|---|---|---|
| `platform` | Ja | `instagram` of `facebook`. Iets anders geeft een `400` terug. |
| `limit` | Nee | Hoeveel berichten moeten worden teruggegeven, `1`-`50`. Standaard `25`. |
| `after` | Nee | Cursor voor de volgende pagina - geef de `nextCursor`-waarde van het vorige antwoord door. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/channels/meta/posts?platform=instagram&limit=25" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{
  "success": true,
  "connected": true,
  "platform": "instagram",
  "posts": [
    {
      "id": "17900000000000000",
      "caption": "New spring menu is live",
      "thumbnailUrl": "https://scontent.cdninstagram.com/...",
      "permalink": "https://www.instagram.com/p/Cxxxxxxxxxx/",
      "createdAt": "2026-05-02T09:12:00.000Z",
      "mediaType": "REELS"
    }
  ],
  "nextCursor": "QVFIUkxxxxxxxx"
}
```

`mediaType` is het eigen label van Instagram (`REELS`, `FEED`, `STORY`, of het formaat - `IMAGE`, `VIDEO`, `CAROUSEL_ALBUM`); voor Facebook is dit altijd `POST`. `nextCursor` is `null` op de laatste pagina.

Als er niets kan worden vermeld, geeft de aanroep nog steeds `200` terug met `connected: false` en een lege `posts`-array, plus een `reason` die aangeeft waarom:

| `reason` | Wat te doen |
|---|---|
| _(afwezig)_ | Er is nog geen pagina verbonden - voer eerst de verbindingsstroom uit. |
| `no_instagram_account` | Er is een Facebook-pagina verbonden, maar er is geen Instagram-bedrijfsaccount aan gekoppeld. Facebook-berichten worden nog steeds correct vermeld. |
| `token_expired` | De opgeslagen paginagegevens werken niet meer - verbind het kanaal opnieuw. |

### Instagram + Messenger verbreken

```
DELETE /channels/meta
```

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/channels/meta" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{ "success": true, "disconnected": true }
```

Dit stopt de inkomende routering voor zowel Instagram als Messenger. Het is idempotent - het aanroepen ervan wanneer er niets is verbonden, slaagt nog steeds.

---

## WhatsApp Business

Hiermee wordt een officieel WhatsApp Business-nummer gekoppeld. Het nummer moet al op het account bestaan voordat u de koppeling aanroept. Net als bij Meta autoriseert de accounthouder in zijn browser, waarna u pollt totdat het nummer `ONLINE` rapporteert.

### Stap 1 - Start de WhatsApp Business-verbinding

```
POST /channels/whatsapp/connect
```

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/whatsapp/connect?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "phone_number": "+14155551234" }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/channels/whatsapp/connect", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ phone_number: "+14155551234" }),
});
const data = await res.json();
// Open data.oauth_url in the account holder's browser.
```

**Python**

```python
res = requests.post(
    "https://api.youraiconnector.com/v1/channels/whatsapp/connect",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"phone_number": "+14155551234"},
)
data = res.json()
# Open data["oauth_url"] in the account holder's browser.
```

| Veld | Verplicht | Beschrijving |
|---|---|---|
| `phone_number` | Ja | Het te koppelen nummer, in E.164-indeling (bijv. `+14155551234`). |
| `only_waba_sharing` | Nee | Beperk de autorisatie tot het delen van een bestaand WhatsApp Business-account, waarbij het instellen van een nieuwe afzender wordt overgeslagen. Standaard `false`. |
| `retry` | Nee | Voer de autorisatie opnieuw uit voor een nummer waarvan de vorige poging niet is voltooid. Standaard `false`. |
| `business_name` | Nee | Cosmetische overschrijving voor de bedrijfsnaam die alleen op het toestemmingsscherm wordt getoond (max. 256 tekens). Wordt niet opgeslagen. |
| `description` | Nee | Cosmetische overschrijving voor de bedrijfsomschrijving die alleen op het toestemmingsscherm wordt getoond (max. 256 tekens). Wordt niet opgeslagen. |

**Antwoord**

```json
{
  "success": true,
  "status": "pending",
  "oauth_url": "https://www.facebook.com/v21.0/dialog/oauth?client_id=...&state=...",
  "state_token": "opaque-one-time-token",
  "expires_at": "2026-06-10T12:30:00.000Z"
}
```

Open `oauth_url` in de browser van de accounthouder om te autoriseren. Zodra ze goedkeuring geven, wordt de registratie op de achtergrond voltooid.

### Stap 2 - De status pollen tot ONLINE

```
GET /channels/whatsapp/connect/{phoneNumber}/status
```

Poll dit totdat `status` gelijk is aan `ONLINE`.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/channels/whatsapp/connect/+14155551234/status" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const phone = encodeURIComponent("+14155551234");
const res = await fetch(
  `https://api.youraiconnector.com/v1/channels/whatsapp/connect/${phone}/status`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
// Poll until data.status === "ONLINE".
```

**Python**

```python
import urllib.parse

phone = urllib.parse.quote("+14155551234")
res = requests.get(
    f"https://api.youraiconnector.com/v1/channels/whatsapp/connect/{phone}/status",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
# Poll until data["status"] == "ONLINE".
```

**Antwoord**

```json
{
  "success": true,
  "phone_number": "+14155551234",
  "channel": "whatsapp",
  "status": "ONLINE",
  "status_reason": null,
  "live": true
}
```

Het veld `status` kan het volgende zijn:

| `status` | Betekenis |
|---|---|
| `PENDING` | Geautoriseerd, goedkeuring nog in behandeling. Blijf pollen. |
| `ONLINE` | Verbonden en klaar om te verzenden. |
| `RATE_LIMITED` | Te veel pogingen - wacht voordat u het opnieuw probeert. |
| `REGISTRATION_FAILED` | De installatie kon niet worden voltooid. |
| `DELETED` | De registratie bestaat niet meer. |

`live: true` betekent dat de status in realtime bij de provider is gecontroleerd; `false` betekent dat deze afkomstig is van de laatst opgeslagen status.

### Een WhatsApp Business-nummer verbreken

```
DELETE /channels/whatsapp/{phoneNumber}
```

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/channels/whatsapp/+14155551234" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{ "success": true, "phone_number": "+14155551234", "disconnected": true }
```

Het nummer zelf blijft op het account staan, zodat u het later opnieuw kunt koppelen.

---

## WhatsApp Web

WhatsApp Web koppelt een regulier WhatsApp-nummer door een QR-code te scannen, net zoals bij het koppelen van een apparaat in de WhatsApp-app. Het proces is: start de sessie, haal de QR-code op en toon deze, en pols vervolgens totdat de status `connected` is.

### Stap 1 - Start een WhatsApp Web-koppelingssessie

```
POST /channels/whatsapp-web/connections
```

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/whatsapp-web/connections?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "phone_number": "+15551230000" }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/channels/whatsapp-web/connections", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ phone_number: "+15551230000" }),
});
const data = await res.json();
```

**Python**

```python
res = requests.post(
    "https://api.youraiconnector.com/v1/channels/whatsapp-web/connections",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"phone_number": "+15551230000"},
)
data = res.json()
```

| Veld | Vereist | Beschrijving |
|---|---|---|
| `phone_number` | Ja | Het WhatsApp-nummer om te verbinden, in E.164-formaat. |
| `proxy_country` | Nee | ISO 3166-1 alpha-2 landcode voor de routeringsregio. Wordt automatisch gedetecteerd op basis van het nummer indien weggelaten. |
| `force_new` | Nee | Verwijder elke bestaande sessie en start een nieuwe koppeling. Standaard ingesteld op `false`. |
| `import_contacts` | Nee | Importeer de bestaande contacten van het apparaat bij de eerste verbinding. Standaard ingesteld op `false`. |
| `pause_ai_for_imported_contacts` | Nee | Houd bij het importeren van contacten geautomatiseerde antwoorden voor hen gepauzeerd. Standaard ingesteld op `true`. |
| `import_existing_chats` | Nee | Importeer bestaande chatgeschiedenis (vereist `import_contacts: true`). Standaard ingesteld op `false`. |

**Antwoord**

```json
{
  "success": true,
  "phone_number": "+15551230000",
  "session_id": "session-id",
  "status": "qr_pending",
  "connect_url": "https://api.youraiconnector.com/v1/channels/whatsapp-web/connect?token=eyJhbGciOi...",
  "connect_url_expires_at": 1717000000000,
  "poll_qr_path": "/v1/channels/whatsapp-web/connections/%2B15551230000/qr",
  "poll_status_path": "/v1/channels/whatsapp-web/connections/%2B15551230000/status"
}
```

### Makkelijkste optie voor WhatsApp Web: overhandig `connect_url`

Het antwoord bevat een kant-en-klare `connect_url`: een gehoste pagina die de QR-code toont, deze automatisch ververst terwijl deze roteert, en overschakelt naar een succesbericht zodra het nummer is gekoppeld. Geef deze link simpelweg aan de accounthouder (open deze in een browser, stuur deze naar hen toe, of toon deze als een QR/knop) en laat hen deze scannen met WhatsApp - je hoeft de QR niet zelf op te halen of iets te pollen. De link werkt ongeveer 30 minuten (`connect_url_expires_at`); als deze verloopt voordat ze klaar zijn, start dan een nieuwe verbinding om een verse te krijgen.

Dit is de aanbevolen weg wanneer een persoon een link kan openen. De handmatige stappen hieronder (zelf de QR ophalen, de status pollen) zijn bedoeld voor integraties die de QR in hun eigen interface willen weergeven.

Het antwoord geeft je ook de exacte `poll_qr_path` en `poll_status_path` om te gebruiken, zodat je ze niet zelf hoeft te bouwen.

### Stap 2 - De QR-code ophalen en tonen

```
GET /channels/whatsapp-web/connections/{phoneNumber}/qr
```

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/channels/whatsapp-web/connections/+15551230000/qr" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const phone = encodeURIComponent("+15551230000");
const res = await fetch(
  `https://api.youraiconnector.com/v1/channels/whatsapp-web/connections/${phone}/qr`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
// Render data.qr_data_url as an <img src> for the user to scan.
```

**Python**

```python
import urllib.parse

phone = urllib.parse.quote("+15551230000")
res = requests.get(
    f"https://api.youraiconnector.com/v1/channels/whatsapp-web/connections/{phone}/qr",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
# Render data["qr_data_url"] for the user to scan.
```

**Antwoord**

```json
{
  "success": true,
  "phone_number": "+15551230000",
  "status": "qr_pending",
  "qr_code": "2@raw-qr-payload-string...",
  "qr_data_url": "data:image/png;base64,iVBORw0KGgo...",
  "expires_at": "2026-06-10T12:05:00.000Z"
}
```

Render de QR-code zodat de gebruiker deze kan scannen met zijn telefoon (WhatsApp > Gekoppelde apparaten > Apparaat koppelen):

- `qr_data_url` is een kant-en-klare afbeelding - plaats deze direct in een `<img src>`.
- `qr_code` is de ruwe payload als je de afbeelding liever zelf genereert.

De QR-code is kort geldig. Als je dit direct na het starten van de sessie aanroept, krijg je mogelijk een `404` met "QR code not available yet" - wacht even en probeer het opnieuw. Als je een `410` krijgt ("QR code expired"), start de verbinding dan opnieuw om een verse code te krijgen.

### Stap 3 - De status polsen tot verbinding is gemaakt

```
GET /channels/whatsapp-web/connections/{phoneNumber}/status
```

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/channels/whatsapp-web/connections/+15551230000/status" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const phone = encodeURIComponent("+15551230000");
const res = await fetch(
  `https://api.youraiconnector.com/v1/channels/whatsapp-web/connections/${phone}/status`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
// Poll until data.status === "connected" (or "open").
```

**Python**

```python
import urllib.parse

phone = urllib.parse.quote("+15551230000")
res = requests.get(
    f"https://api.youraiconnector.com/v1/channels/whatsapp-web/connections/{phone}/status",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
# Poll until data["status"] == "connected" (or "open").
```

**Antwoord**

```json
{
  "success": true,
  "phone_number": "+15551230000",
  "status": "connected",
  "has_qr": false,
  "qr_expires_at": null,
  "last_activity": null,
  "message_count": null,
  "proxy": null,
  "live": true
}
```

| `status` | Betekenis |
|---|---|
| `not_initialized` | Nog geen sessie (terminale fout). |
| `qr_pending` | Wachten tot de QR-code wordt gescand. |
| `connecting` | Gescand, installatie wordt afgerond. |
| `connected` / `open` | Gekoppeld en actief - dit is succes. |
| `disconnected` | Sessie beëindigd (terminale fout). |

### Een WhatsApp Web-sessie verbreken

```
DELETE /channels/whatsapp-web/connections/{phoneNumber}
```

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/channels/whatsapp-web/connections/+15551230000" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{ "success": true, "phone_number": "+15551230000", "status": "removed" }
```

Hiermee wordt het apparaat ontkoppeld en de verbinding verwijderd. Het ruimt altijd de lokale status op, dus het is idempotent, zelfs als de onderliggende sessie al was verdwenen.

---

## Telegram

> **Beschikbaarheid:** Telegram maakt verbinding zoals elk ander kanaal en is beschikbaar voor elk account — het hoeft niet specifiek voor je te worden ingeschakeld. De onderstaande Telegram-endpoints kunnen nog steeds `403` retourneren als Telegram niet is inbegrepen in het abonnement van het account; in dat geval luidt de foutmelding `"This channel is not included in your current plan. Upgrade to unlock it."`.

Telegram verbindt een persoonlijk account via telefoonnummer plus een eenmalige inlogcode (en een tweefactorwachtwoord, als het account er een heeft ingesteld). Het proces is: start de sessie, dien de code in, dien optioneel het wachtwoord in en bevestig vervolgens via de status.

### Stap 1 - Start een Telegram-verbindingssessie

```
POST /channels/telegram/connect
```

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/telegram/connect?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "phone_number": "+14155550100" }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/channels/telegram/connect", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ phone_number: "+14155550100" }),
});
const data = await res.json();
```

**Python**

```python
res = requests.post(
    "https://api.youraiconnector.com/v1/channels/telegram/connect",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"phone_number": "+14155550100"},
)
data = res.json()
```

| Veld | Vereist | Beschrijving |
|---|---|---|
| `phone_number` | Ja | Het telefoonnummer van het account om te verbinden, in E.164-indeling. |
| `mode` | Nee | `code` (standaard) stuurt een eenmalige inlogcode naar het account; `qr` retourneert een inlogtoken en QR-URL om weer te geven. |
| `proxy_country` | Nee | ISO 3166-1 alpha-2 landcode voor de uitgaande netwerkroute. |
| `force_new` | Nee | Wanneer `true`, wordt elke bestaande sessie verwijderd en opnieuw begonnen. |

**Antwoord**

```json
{
  "success": true,
  "phone_number": "+14155550100",
  "status": "code_required",
  "session_id": "session-id",
  "connect_url": "https://api.youraiconnector.com/v1/channels/telegram/connect/page?token=eyJhbGciOi...",
  "connect_url_expires_at": 1717000000000
}
```

In de `code`-modus ontvangt het account een inlogcode in Telegram en is `status` gelijk aan `code_required`. (In de `qr`-modus bevat het antwoord ook `login_token` en `qr_url` om weer te geven voor het scannen, en is `status` gelijk aan `qr_required`.)

### Makkelijkste optie voor Telegram: overhandig `connect_url`

Het antwoord bevat een kant-en-klare `connect_url`: een gehoste pagina die de verbinding zelf voltooit. In de `code`-modus voert de accounthouder de inlogcode in - en een wachtwoord voor tweestapsverificatie als hun account daarover beschikt. In de `qr`-modus toont de pagina een QR-code die zichzelf ververst, zodat ze deze kunnen scannen vanuit de Telegram-app. Hoe dan ook, de pagina rapporteert zelf het succes, dus je kunt deze link gewoon aan de accounthouder geven in plaats van je eigen UI te bouwen en te pollen. De link werkt ongeveer 30 minuten (`connect_url_expires_at`); als deze verloopt, start dan een nieuwe verbinding om een nieuwe link te krijgen.

De onderstaande handmatige stappen (zelf de code verzamelen, deze indienen, de status pollen; of `qr_url` weergeven en pollen) zijn bedoeld voor integraties die de UI zelf willen weergeven.

### Stap 2 - De inlogcode indienen

```
POST /channels/telegram/connect/{phoneNumber}/verify-code
```

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/telegram/connect/+14155550100/verify-code" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "code": "12345" }'
```

**JavaScript**

```javascript
const phone = encodeURIComponent("+14155550100");
const res = await fetch(
  `https://api.youraiconnector.com/v1/channels/telegram/connect/${phone}/verify-code`,
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ code: "12345" }),
  }
);
const data = await res.json();
```

**Python**

```python
import urllib.parse

phone = urllib.parse.quote("+14155550100")
res = requests.post(
    f"https://api.youraiconnector.com/v1/channels/telegram/connect/{phone}/verify-code",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"code": "12345"},
)
data = res.json()
```

**Antwoord**

```json
{
  "success": true,
  "phone_number": "+14155550100",
  "status": "connected",
  "telegram_user_id": "100000001",
  "username": "myhandle"
}
```

Als `status` gelijk is aan `connected`, ben je klaar. Als het account tweefactorauthenticatie heeft ingeschakeld, zal `status` in plaats daarvan `password_required` zijn - ga naar stap 3.

### Stap 3 - Het tweefactorwachtwoord indienen (alleen indien nodig)

```
POST /channels/telegram/connect/{phoneNumber}/verify-password
```

Roep dit alleen aan wanneer stap 2 `password_required` retourneerde.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/telegram/connect/+14155550100/verify-password" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "password": "the-2fa-password" }'
```

**JavaScript**

```javascript
const phone = encodeURIComponent("+14155550100");
const res = await fetch(
  `https://api.youraiconnector.com/v1/channels/telegram/connect/${phone}/verify-password`,
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ password: "the-2fa-password" }),
  }
);
const data = await res.json();
```

**Python**

```python
import urllib.parse

phone = urllib.parse.quote("+14155550100")
res = requests.post(
    f"https://api.youraiconnector.com/v1/channels/telegram/connect/{phone}/verify-password",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"password": "the-2fa-password"},
)
data = res.json()
```

**Antwoord**

```json
{
  "success": true,
  "phone_number": "+14155550100",
  "status": "connected",
  "telegram_user_id": "100000001",
  "username": "myhandle"
}
```

### Telegram-status controleren

```
GET /channels/telegram/connect/{phoneNumber}/status
```

```bash
curl "https://api.youraiconnector.com/v1/channels/telegram/connect/+14155550100/status" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{
  "success": true,
  "phone_number": "+14155550100",
  "status": "connected",
  "telegram_user_id": "100000001",
  "live": true
}
```

`status` kan `connected`, `code_required`, `password_required`, `initializing`, `disconnected`, `not_initialized` of `error` zijn.

### Telegram verbreken

```
DELETE /channels/telegram/{phoneNumber}
```

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/channels/telegram/+14155550100" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{ "success": true, "phone_number": "+14155550100", "status": "removed" }
```

Idempotent - herhaalde aanroepen slagen.

---

## Instagram (persoonlijk account)

> Bèta met beperkte beschikbaarheid, per account ingeschakeld. Hiermee wordt een persoonlijk Instagram-account gekoppeld door in te loggen met de gebruikersnaam en het wachtwoord (niet de officiële Business API). Als het account niet is ingeschakeld voor de bèta, retourneert de koppelingsaanroep een toestemmingsfout.

Omdat hiervoor de eigen Instagram-inloggegevens van de accounthouder nodig zijn, is de eenvoudigste weg om ze de gehoste `connect_url` te geven en ze daar hun inloggegevens te laten invoeren - jouw integratie verwerkt het wachtwoord nooit.

### Stap 1 - Start een Instagram (persoonlijke) verbinding

```
POST /channels/instagram-private/connect
```

Verstuur de Instagram `username` en `password`.

**Antwoord**

```json
{
  "success": true,
  "status": "connected",
  "connect_url": "https://api.youraiconnector.com/v1/channels/instagram-private/connect/page?token=eyJhbGciOi...",
  "connect_url_expires_at": 1717000000000
}
```

Als het account tweefactorauthenticatie heeft of Instagram een controlepunt presenteert, komt `status` terug als `two_factor_required` of `challenge_required` - dien de code in bij `/connect/{id}/verify-2fa` of `/connect/{id}/verify-challenge` hieronder, en pols vervolgens `/connect/{id}/status` totdat `connected`. `{id}` is de genormaliseerde Instagram-gebruikersnaam die wordt teruggegeven als `account_id`/`username` in het bovenstaande antwoord - gebruik deze bij elke onderstaande stap.

### Stap 2 - Dien de tweefactorcode in (indien gevraagd)

```
POST /channels/instagram-private/connect/{id}/verify-2fa
```

Roep dit alleen aan wanneer stap 1 (of stap 3) `two_factor_required` teruggaf.

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/instagram-private/connect/yourbrand/verify-2fa" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "code": "123456" }'
```

**Antwoord**

```json
{
  "success": true,
  "account_id": "yourbrand",
  "status": "connected",
  "ig_user_id": "17890000000000000",
  "username": "yourbrand"
}
```

`status` kan terugkomen als `connected` (klaar), `two_factor_required` (verkeerde code, probeer het opnieuw), of `challenge_required` (Instagram wil ook een controlepuntcode - ga naar stap 3).

### Stap 3 - Dien de controlepuntbevestigingscode in (indien gevraagd)

```
POST /channels/instagram-private/connect/{id}/verify-challenge
```

Roep dit alleen aan wanneer een vorige stap `challenge_required` teruggaf. Dezelfde aanvraag- en antwoordvorm als stap 2 hierboven.

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/instagram-private/connect/yourbrand/verify-challenge" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "code": "123456" }'
```

### Controleer Instagram (persoonlijke) status

```
GET /channels/instagram-private/connect/{id}/status
```

Pols dit totdat `status` gelijk is aan `connected`, of totdat het een terminale fout rapporteert.

```bash
curl "https://api.youraiconnector.com/v1/channels/instagram-private/connect/yourbrand/status" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{
  "success": true,
  "account_id": "yourbrand",
  "status": "connected",
  "ig_user_id": "17890000000000000",
  "username": "yourbrand",
  "live": true
}
```

`status` kan `connected`, `two_factor_required`, `challenge_required`, `initializing`, `disconnected`, `not_initialized` of `error` zijn. `live: true` betekent dat dit live is gelezen van de verbindingsworker in plaats van een gecachte waarde.

### Makkelijkste optie voor Instagram (persoonlijk): overhandig `connect_url`

Het antwoord bevat een `connect_url`: een gehoste pagina waar de accounthouder zijn Instagram-gebruikersnaam en -wachtwoord invoert (en een 2FA- of controlepuntcode als Instagram daarom vraagt), en die zelf het succes rapporteert. De inloggegevens gaan rechtstreeks naar Instagram en worden niet opgeslagen. Geef deze link aan de accounthouder in plaats van hun wachtwoord in je eigen gebruikersinterface te verzamelen. De link werkt ongeveer 30 minuten (`connect_url_expires_at`).

### Instagram ontkoppelen (persoonlijk)

```
DELETE /channels/instagram-private/{id}
```

Idempotent - herhaalde aanroepen slagen.

### Volgers synchroniseren

```
POST /channels/instagram-private/{id}/sync-followers
```

Activeert handmatig een volgerssynchronisatie voor een gekoppeld account - dezelfde taak die automatisch op de achtergrond wordt uitgevoerd, hier beschikbaar gesteld voor een "Volgers vernieuwen"-actie op aanvraag. Het haalt de huidige volgerslijst van het account op, registreert nieuwe volgers en (wanneer een Live-campagne volgersbereik heeft ingeschakeld) stuurt nieuwe volgers een openings-DM, tot een dagelijks limiet.
```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/instagram-private/yourbrand/sync-followers" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{
  "success": true,
  "accountId": "yourbrand",
  "totalFollowers": 1204,
  "newFollowers": 6,
  "dmsSent": 6,
  "isBaselineSeed": false
}
```

> Deze vijf velden zijn de enige plek op deze pagina die `camelCase` teruggeven in plaats van `snake_case` - zo is dit eindpunt momenteel geconfigureerd, het is geen typefout. `isBaselineSeed: true` betekent dat dit de allereerste synchronisatie was na het koppelen, waarbij alleen de beginlijst met volgers wordt vastgelegd en nooit outreach-DM's worden verzonden (dus `dmsSent` is altijd `0` bij die uitvoering).

De allereerste aanroep voor een account kan even duren (het doorlopen van de volledige volgerslijst); latere aanroepen zijn sneller omdat alleen nieuwe volgers worden vergeleken. `404` betekent dat het account niet is gekoppeld; `412` betekent dat de initialisatie van de koppeling nog niet is voltooid - wacht even en probeer het opnieuw.

---

## LINE

LINE is het eenvoudigste kanaal om te verbinden omdat er geen browseromleiding of polling nodig is. De klant maakt een Messaging API-kanaal aan in de LINE Developers-console, kopieert twee waarden en u dient deze in met één enkele aanroep. Vervolgens geeft u hen een webhook-URL die ze in de console kunnen plakken.

### Stap 1 - Verbinden met de kanaalreferenties

```
POST /channels/line
```

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/line?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channel_access_token": "LONG_LIVED_CHANNEL_ACCESS_TOKEN",
    "channel_secret": "CHANNEL_SECRET"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/channels/line", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    channel_access_token: "LONG_LIVED_CHANNEL_ACCESS_TOKEN",
    channel_secret: "CHANNEL_SECRET",
  }),
});
const data = await res.json();
```

**Python**

```python
res = requests.post(
    "https://api.youraiconnector.com/v1/channels/line",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "channel_access_token": "LONG_LIVED_CHANNEL_ACCESS_TOKEN",
        "channel_secret": "CHANNEL_SECRET",
    },
)
data = res.json()
```

| Veld | Vereist | Beschrijving |
|---|---|---|
| `channel_access_token` | Ja | Het langdurige Messaging API-kanaaltoegangstoken van het officiële account. Wordt gebruikt voor het verzenden en ontvangen van berichten. |
| `channel_secret` | Ja | Het Messaging API-kanaalgeheim, gebruikt om inkomende gebeurtenissignaturen te verifiëren. |
| `channel_id` | Nee | Het numerieke kanaal-ID. Alleen ter informatie. |

**Antwoord**

```json
{
  "success": true,
  "status": "connected",
  "bot_user_id": "Uxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "basic_id": "@mybusiness",
  "display_name": "My Business",
  "picture_url": "https://...",
  "chat_mode": "bot",
  "chat_mode_ok": true,
  "webhook_url": "https://api.youraiconnector.com/line/webhook/..."
}
```

Twee velden zijn van belang voor wat u hierna doet:

- **`webhook_url`** - de klant moet dit in het veld **Webhook URL** van hun LINE-kanaal in de LINE Developers-console plakken (en "Use webhook" inschakelen). Totdat ze dit doen, komen er geen inkomende berichten aan. Toon dit duidelijk aan hen.
- **`chat_mode_ok`** - wanneer `false`, staat het officiële account in de "chat"-modus en zal het geen berichten ontvangen of verzenden totdat het is overgeschakeld naar de "bot"-modus in de LINE Official Account Manager. Koppel uw onboarding aan deze vlag en vertel de klant om de modus te wijzigen.

> De `channel_access_token` en `channel_secret` worden nooit door een endpoint geretourneerd. Sla ze aan uw kant op als u ze opnieuw nodig heeft; anders opnieuw plakken vanuit de LINE-console.

De `bot_user_id` die hier wordt geretourneerd, is de verbindings-ID die u gebruikt in de status-, verificatie- en verbrekingsaanroepen hieronder.

### Stap 2 - Opnieuw verifiëren na webhook-instelling

```
POST /channels/line/{botUserId}/verify-webhook
```

Nadat de klant de webhook-URL heeft geconfigureerd en is overgeschakeld naar de bot-modus, roept u dit aan om het opgeslagen token opnieuw te valideren en de gecachte chatmodus te vernieuwen.

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/line/Uxxxxxxxx.../verify-webhook" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{
  "success": true,
  "token_valid": true,
  "chat_mode": "bot",
  "chat_mode_ok": true,
  "webhook_url": "https://api.youraiconnector.com/line/webhook/..."
}
```

Als `token_valid` gelijk is aan `false`, verifieert het opgeslagen toegangstoken niet langer - laat de klant het opnieuw uitgeven in de console en roep `POST /channels/line` opnieuw aan met het nieuwe token.

### LINE-status controleren

```
GET /channels/line/{botUserId}/status
```

```bash
curl "https://api.youraiconnector.com/v1/channels/line/Uxxxxxxxx.../status" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{
  "success": true,
  "bot_user_id": "Uxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "channel": "line",
  "status": "connected",
  "basic_id": "@mybusiness",
  "display_name": "My Business",
  "picture_url": "https://...",
  "chat_mode": "bot",
  "is_active": true,
  "live": false
}
```

LINE heeft geen live statusfeed, dus `live` is hier altijd `false` - de waarden weerspiegelen de status die is vastgelegd op het moment van verbinden (of de laatste verificatie).

### LINE ontkoppelen

```
DELETE /channels/line/{botUserId}
```

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/channels/line/Uxxxxxxxx..." \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{ "success": true, "status": "removed", "bot_user_id": "Uxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }
```

---

## Viber

Viber koppelt op dezelfde manier als LINE - plak het authenticatietoken van de bot vanuit het Viber-beheerderspaneel in één aanroep - met één verschil dat het vermelden waard is: bij het koppelen wordt onze webhook direct op je bot GEREGISTREERD, dus er is daarna geen aparte console-stap nodig. Dat betekent ook dat een koppelingspoging kan mislukken als onze ingress de synchrone webhook-controle van Viber niet kan beantwoorden, niet alleen als het token zelf onjuist is.

### Stap 1 - Koppelen met het authenticatietoken van de bot

```
POST /channels/viber
```

| Veld | Verplicht | Beschrijving |
|---|---|---|
| `auth_token` | Ja | Het authenticatietoken van de bot, uit het Viber-beheerderspaneel (Mijn botinstellingen). |

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/viber?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "auth_token": "444d5555e6666f7777a8888b9999c000" }'
```

**Antwoord**

```json
{
  "success": true,
  "status": "connected",
  "bot_id": "botIdFromViber",
  "bot_name": "My Business Bot",
  "bot_avatar": "https://...",
  "bot_uri": "mybusinessbot",
  "subscribers_count": 0,
  "webhook_url": "https://api.youraiconnector.com/v1/incoming-viber-message/...",
  "event_types": ["delivered", "seen", "failed", "subscribed", "unsubscribed", "conversation_started"]
}
```

Het authenticatietoken wordt nooit door een eindpunt teruggegeven - sla het aan jouw kant op als je het opnieuw moet plakken. `bot_id` is de koppelings-ID die wordt gebruikt door de status-, verificatie- en ontkoppelingsaanroepen hieronder.

### Viber-status controleren

```
GET /channels/viber/{botId}/status
```

Rapporteert de opgeslagen koppelingsstatus. Voeg `?live=true` toe om de bot ook opnieuw te controleren bij Viber en de gecachte webhook-registratie te vernieuwen - handig voordat je ervan uitgaat dat een stille bot daadwerkelijk defect is.

```bash
curl "https://api.youraiconnector.com/v1/channels/viber/botIdFromViber/status?live=true" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{
  "success": true,
  "bot_id": "botIdFromViber",
  "channel": "viber",
  "status": "connected",
  "bot_name": "My Business Bot",
  "bot_avatar": "https://...",
  "bot_uri": "mybusinessbot",
  "webhook_url": "https://api.youraiconnector.com/v1/incoming-viber-message/...",
  "registered_webhook": "https://api.youraiconnector.com/v1/incoming-viber-message/...",
  "webhook_ok": true,
  "subscribers_count": 128,
  "is_active": true,
  "live": true
}
```

`webhook_ok: false` betekent dat de webhook van de bot niet langer naar ons verwijst - inkomende berichten komen niet aan. Dit betekent meestal dat een andere tool daarna dezelfde bot heeft gekoppeld (de webhook-registratie van Viber werkt volgens het principe 'laatste schrijver wint'). Herstel dit met de hieronder genoemde herverificatie-aanroep; het is niet nodig om de klant te vragen het token opnieuw te plakken. `live` is `false` wanneer het antwoord de laatst gecachte status is in plaats van een verse controle bij Viber.

### De webhook opnieuw registreren

```
POST /channels/viber/{botId}/verify-webhook
```

De reparatieactie voor `webhook_ok: false` - registreert onze webhook opnieuw op de bot met behulp van het reeds opgeslagen authenticatietoken.

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/viber/botIdFromViber/verify-webhook" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{ "success": true, "token_valid": true, "webhook_ok": true, "webhook_url": "https://api.youraiconnector.com/v1/incoming-viber-message/...", "event_types": ["delivered", "seen", "failed", "subscribed", "unsubscribed", "conversation_started"] }
```

`token_valid: false` betekent dat het opgeslagen token niet langer werkt - koppel opnieuw met `POST /channels/viber` en een nieuw token.

### Viber ontkoppelen

```
DELETE /channels/viber/{botId}
```

Deregistreert onze webhook aan de kant van Viber (best-effort) en verwijdert de verbinding.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/channels/viber/botIdFromViber" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{ "success": true, "status": "removed", "bot_id": "botIdFromViber", "webhook_removed": true }
```

---

## TikTok

> **Beschikbaarheid:** Bèta met beperkte beschikbaarheid, ingeschakeld per account. Het verbinden van TikTok geeft een toestemmingsfout totdat het account hiervoor is ingeschakeld.

TikTok Business Messaging is een volledig OAuth-kanaal zoals Meta, maar eenvoudiger aan de polling-kant: er is geen specifieke status-pollingstap om tegenaan te bouwen, omdat het verbonden account vanzelf verschijnt zodra TikTok terugstuurt en de verbinding is geschreven. Het onderstaande status-eindpunt bestaat voor het op verzoek bevestigen van de status (ondersteuningstools, gezondheidscontroles), niet als iets waar je tijdens het verbinden op moet loopen.

### Stap 1 - De TikTok-verbinding starten

```
POST /channels/tiktok/connect
```

Vereist geen inloggegevens - de accounthouder autoriseert volledig in zijn browser.

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/tiktok/connect?apiKey=YOUR_API_KEY"
```

**Antwoord**

```json
{
  "success": true,
  "status": "pending_authorization",
  "oauth_url": "https://www.tiktok.com/v2/auth/authorize?client_key=...&state=...",
  "state_token": "opaque-one-time-token",
  "expires_at": "2026-06-10T12:30:00.000Z"
}
```

Open `oauth_url` in de browser van de accounthouder zodat deze kan inloggen bij TikTok en toegang kan goedkeuren. De status verloopt na `expires_at` (ongeveer 30 minuten) - als deze verloopt, begin dan opnieuw. Er is geen `connect_url` hosted-page snelkoppeling voor TikTok; zelf `oauth_url` openen is de enige weg.

### TikTok-status controleren

```
GET /channels/tiktok/{openId}/status
```

`openId` is de open_id van het TikTok Business-account, bekend zodra de OAuth-callback is uitgevoerd.

```bash
curl "https://api.youraiconnector.com/v1/channels/tiktok/openIdFromTikTok/status" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{
  "success": true,
  "open_id": "openIdFromTikTok",
  "channel": "tiktok",
  "status": "connected",
  "business_id": "openIdFromTikTok",
  "username": "mybusiness",
  "display_name": "My Business",
  "avatar_url": "https://...",
  "status_reason": null,
  "is_active": true,
  "live": false
}
```

TikTok heeft geen goedkope live gezondheidscontrole, dus `live` is hier altijd `false` - de velden weerspiegelen wat connect (of de laatste tokenvernieuwing) heeft geschreven. `status: "reauth_required"` met `status_reason` ingesteld betekent dat het account opnieuw door het verbindingsproces moet; TikTok-tokens worden automatisch vernieuwd bij een jaarlijkse rotatie, en dit is wat verschijnt als die rotatie ooit mislukt.

### TikTok ontkoppelen

```
DELETE /channels/tiktok/{openId}
```

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/channels/tiktok/openIdFromTikTok" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{ "success": true, "status": "removed", "open_id": "openIdFromTikTok" }
```

---

## GoHighLevel

GoHighLevel (GHL) is een CRM-integratie, geen berichtenkanaal - het verbinden ervan verbruikt geen kanaalslot in het abonnement, omdat het gebruikmaakt van de bestaande kanalen van het account in plaats van een nieuwe toe te voegen. Het is ook de enige integratie op deze pagina die **meer dan één verbinding tegelijk** kan bevatten: elk GHL-subaccount ("locatie") waarop de klant de app installeert, krijgt zijn eigen vermelding.

### Stap 1 - De GHL-verbinding starten

```
POST /channels/ghl/connect
```

| Veld | Vereist | Beschrijving |
|---|---|---|
| `brand` | Nee | Welke GHL-marktplaatsvermelding moet worden geautoriseerd. Standaard is dit de standaardvermelding - alleen relevant als uw implementatie meer dan één marktplaats-app heeft geconfigureerd. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/ghl/connect?apiKey=YOUR_API_KEY"
```

**Antwoord**

```json
{
  "success": true,
  "status": "pending_authorization",
  "oauth_url": "https://marketplace.gohighlevel.com/oauth/chooselocation?client_id=...&state=...",
  "state_token": "opaque-one-time-token",
  "brand": "dmchamp",
  "expires_at": "2026-06-10T12:30:00.000Z"
}
```

Open `oauth_url` in de browser van de accounthouder zodat deze een GHL-locatie kan kiezen en toegang kan goedkeuren. De status verloopt op `expires_at` (ongeveer 30 minuten).

### GHL-verbindingen weergeven

```
GET /channels/ghl/status
```

In tegenstelling tot andere kanalen is dit niet de status van één verbinding - het geeft elke locatie weer die het account heeft verbonden.

```bash
curl "https://api.youraiconnector.com/v1/channels/ghl/status" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{
  "success": true,
  "connections": [
    {
      "location_id": "abc123location",
      "company_id": "xyz789company",
      "brand": "dmchamp",
      "status": "connected",
      "status_reason": null,
      "scopes": ["conversations.readonly", "conversations.write", "conversations/message.write"],
      "connected_at": "2026-06-01T10:00:00.000Z",
      "conversation_provider_id": "provider-id-in-ghl",
      "trigger_subscriptions": [
        { "id": "sub_1", "key": "InboundMessage", "workflow_id": "wf_123" }
      ]
    }
  ]
}
```

### Een GHL-locatie loskoppelen

```
DELETE /channels/ghl/{locationId}
```

Verwijdert de verbinding hier, waardoor elke synchronisatie en trigger voor die locatie wordt gestopt. Dit verwijdert de app niet aan de GHL-kant - de klant verwijdert deze uit hun GHL-marktplaatsinstallaties als ze dat ook willen.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/channels/ghl/abc123location" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{ "success": true, "status": "disconnected", "location_id": "abc123location" }
```

---

## Telefoonnummers (kopen en vrijgeven)

In plaats van een bestaand nummer te koppelen, kunt u direct een nieuw WhatsApp-geschikt nummer kopen. Zoek naar beschikbare nummers, koop er een en pols vervolgens totdat de inrichting is voltooid.

::: note
**Let op:** Nummers die hier worden gekocht, zijn geschikt voor WhatsApp. De registratie van de WhatsApp-afzender verloopt op de achtergrond na aankoop, dus je moet de status pollen totdat deze `ONLINE` bereikt voordat je berichten verstuurt. Credits worden bij aankoop afgeschreven en worden **niet** terugbetaald wanneer je het nummer vrijgeeft.
:::


### Stap 1 - Zoek beschikbare nummers

```
GET /phone-numbers/available?country_code=ISO2
```

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/phone-numbers/available?country_code=US&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/phone-numbers/available?country_code=US",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
```

**Python**

```python
res = requests.get(
    "https://api.youraiconnector.com/v1/phone-numbers/available",
    params={"country_code": "US"},
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

| Query-parameter | Vereist | Beschrijving |
|---|---|---|
| `country_code` | Ja | ISO 3166-1 alpha-2 landcode om in te zoeken (bijv. `US`, `GB`, `NL`). |
| `type` | Nee | Voorkeursnummerklasse, `local` of `mobile`. Beide klassen kunnen nog steeds worden geretourneerd. |

**Antwoord**

```json
{
  "success": true,
  "phone_numbers": [
    {
      "phone_number": "+14155551234",
      "purchase_credits": 50,
      "monthly_credits": 50,
      "cost_usd": 1.15
    }
  ]
}
```

Elk resultaat toont de eenmalige `purchase_credits` en de terugkerende `monthly_credits`. Een door het platform geleverd nummer kost minimaal 50 credits per maand, stijgend met de maandelijkse prijs van de provider zelf, in rekening gebracht bij aankoop en bij elke verlenging. Gebruik de `purchase_credits` / `monthly_credits` die de zoekopdracht retourneert; bereken nooit zelf een prijs. De eerste zoekopdracht op een nieuw account voorziet in enkele onderliggende bronnen, dus dit kan iets langzamer zijn dan latere zoekopdrachten.

### Stap 2 - Een nummer kopen

```
POST /phone-numbers
```

Gebruik een `phone_number` uit de zoekresultaten.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/phone-numbers" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "+14155551234",
    "country_code": "US",
    "display_name": "Support line"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/phone-numbers", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    phone_number: "+14155551234",
    country_code: "US",
    display_name: "Support line",
  }),
});
const data = await res.json();
```

**Python**

```python
res = requests.post(
    "https://api.youraiconnector.com/v1/phone-numbers",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "phone_number": "+14155551234",
        "country_code": "US",
        "display_name": "Support line",
    },
)
data = res.json()
```

| Veld | Verplicht | Beschrijving |
|---|---|---|
| `phone_number` | Ja | Een nummer dat is geretourneerd door de zoekopdracht naar beschikbare nummers, in E.164-indeling. |
| `country_code` | Ja | ISO 3166-1 alpha-2 landcode (bijv. `US`). |
| `display_name` | Nee | Een vriendelijk label. Standaard is dit het telefoonnummer. |
| `category` | Nee | Optioneel categorielabel. |

**Antwoord**

```json
{
  "success": true,
  "phone_number": "+14155551234",
  "channel": "whatsapp",
  "whatsapp_status": "PURCHASED",
  "outgoing_status": "PURCHASED",
  "status": "PURCHASED",
  "purchase_credits": 50,
  "monthly_credits": 50
}
```

Het nummer begint in de `PURCHASED`-status. De WhatsApp-registratie verloopt vervolgens op de achtergrond: `PURCHASED` -> `PENDING` -> `ONLINE`.

> Als de aankoop mislukt omdat een zakelijk adres ontbreekt of een ander vereist detail niet is ingesteld, ontvang je een `400` met een beschrijvende `error`. Stel het ontbrekende detail in en probeer het opnieuw.

### Stap 3 - Pollen tot ONLINE

```
GET /phone-numbers/{phoneNumber}/status
```

Dit is het gedeelde eindpunt voor de status van telefoonnummers - het werkt voor gekochte WhatsApp-nummers evenals voor je andere verbonden nummers.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/phone-numbers/+14155551234/status" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const phone = encodeURIComponent("+14155551234");
const res = await fetch(
  `https://api.youraiconnector.com/v1/phone-numbers/${phone}/status`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
// Poll until data.status === "ONLINE".
```

**Python**

```python
import urllib.parse

phone = urllib.parse.quote("+14155551234")
res = requests.get(
    f"https://api.youraiconnector.com/v1/phone-numbers/{phone}/status",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
# Poll until data["status"] == "ONLINE".
```

**Antwoord**

```json
{
  "success": true,
  "phone_number": "+14155551234",
  "channel": "whatsapp",
  "status": "ONLINE",
  "status_reason": null,
  "live": true
}
```

### Stap 4 - Een nummer vrijgeven

```
DELETE /phone-numbers/{phoneNumber}
```

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/phone-numbers/+14155551234" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{ "success": true, "phone_number": "+14155551234", "released": true }
```

Wat dit doet hangt af van wiens nummer het is.

Voor een nummer dat **via het platform is gehuurd**, is het een echte vrijgave: de WhatsApp-afzender wordt afgemeld, het nummer wordt teruggegeven aan de provider en verwijderd uit het account, er wordt een afkoelperiode van 7 dagen toegepast waarin het nummer door niemand opnieuw kan worden gekocht, en er worden geen credits terugbetaald.

Voor een nummer waarbij **het account zichzelf heeft meegebracht** (zijn eigen Twilio-account, zijn eigen Meta-app of WhatsApp Business-account, of een Android SMS-gateway), verwijdert dezelfde aanroep het alleen uit het account. Er wordt niets vrijgegeven bij de upstream-provider en er wordt geen afkoelperiode vastgelegd, dus het nummer kan onmiddellijk opnieuw worden verbonden. De WhatsApp-afzenderregistratie, indien aanwezig, blijft mogelijk wel of niet behouden: bij het afbreken wordt geprobeerd de afzender te verwijderen met behulp van de door het platform beheerde Twilio-inloggegevens van het account. Bij een account dat nog op de beheerde configuratie staat, zijn die inloggegevens geldig en wordt de afzender verwijderd, dus opnieuw verbinden betekent opnieuw registreren. Bij een account dat is overgestapt op zijn eigen Twilio, kan de verwijdering niet worden geverifieerd en blijft de afzender geregistreerd in dat account — opnieuw verbinden is dan slechts het opnieuw koppelen van de bestaande afzender.

### Een nummer toevoegen dat u al bezit (BYO)

```
POST /phone-numbers/byo
```

Slaat het bovenstaande zoek-en-koop-proces volledig over. Gebruik dit wanneer het account zijn eigen nummer meebrengt (hun eigen Twilio, hun eigen Meta WhatsApp Business-account of een Android SMS-gateway) in plaats van er een te huren via het platform. Dit registreert alleen het nummer - er worden geen credits in rekening gebracht en er wordt hier niets ingericht bij een provider. Het nummer blijft inactief totdat de accounthouder de WhatsApp OAuth voltooit om er een afzender op te registreren (dezelfde stroom die de knop "Eigen nummer meebrengen" in het dashboard start).

| Veld | Vereist | Beschrijving |
|---|---|---|
| `phone_number` | Ja | Het toe te voegen nummer, in E.164-formaat (bijv. `+14155551234`). |
| `country_code` | Ja | ISO 3166-1 alpha-2 landcode (bijv. `US`). |
| `display_name` | Nee | Een vriendelijk label. Standaard is dit het telefoonnummer. |
| `category` | Nee | Optioneel categorielabel. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/phone-numbers/byo?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "+14155551234",
    "country_code": "US",
    "display_name": "Support line"
  }'
```

**Antwoord** (`201 Created`):

```json
{
  "success": true,
  "phone_number": "+14155551234",
  "channel": "whatsapp",
  "type": "BYO",
  "whatsapp_status": "ADDED",
  "outgoing_status": "ADDED",
  "is_active": false
}
```

Een `phone_number` die geen echt E.164-nummer is (of die eruitziet als het WhatsApp-testnummer van Meta, waarmee nooit echte klanten kunnen worden gebericht) retourneert `400`. Het toevoegen van een nummer dat al op het account bestaat - zelfs als het iets anders is gespeld, zoals de `+52` versus `+521` vormen van Mexico - retourneert `409` in plaats van een dubbele rij aan te maken.

### Een nummer instellen als primair

```
POST /phone-numbers/{phoneNumber}/set-primary
```

Zet atomair één nummer op `is_active: true` en elk ander nummer op het account op `is_active: false` - het account eindigt nooit met twee actieve nummers, of geen, tijdens het verzoek. `is_active` kan bewust niet worden ingesteld via het algemene update-eindpunt; deze specifieke aanroep is de enige manier om te wijzigen welk nummer primair is.

```bash
curl -X POST "https://api.youraiconnector.com/v1/phone-numbers/+14155551234/set-primary" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{
  "success": true,
  "phone_number": {
    "id": "+14155551234",
    "phone_number": "+14155551234",
    "display_name": "Support line",
    "channel": "whatsapp",
    "is_active": true,
    "whatsapp_status": "ONLINE"
  }
}
```

`phone_number` is hier het volledige nummerobject (dezelfde vorm die `GET /phone-numbers` retourneert), niet alleen de string. Een `phoneNumber` die niet op het account staat, retourneert `404`.

### Het record van een nummer verwijderen (zonder het vrij te geven)

```
DELETE /phone-numbers/{phoneNumber}/record
```

Een eenvoudige verwijdering van het nummerrecord op dit account - geen vrijgave of deregistratie aan de kant van de provider, en geen afkoelperiode van 7 dagen zoals bij de bovenstaande vrijgavestap. Gebruik dit om BYO-, WhatsApp Web-, Telegram- of LINE-records, of een verouderde invoer, te wissen zonder het beheerde vrijgaveproces te doorlopen. In tegenstelling tot een vrijgave is het verwijderen van een nummer dat niet op het account staat een `404`, geen stilzwijgend succes.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/phone-numbers/+14155551234/record" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Antwoord**

```json
{ "success": true, "phone_number": "+14155551234", "deleted": true }
```

---

## Een kanaal naar een campagne routeren

Het verbinden van een kanaal zorgt ervoor dat berichten **in** het account terechtkomen. Het bepaalt niet **welke AI-agent ze beantwoordt**.

Routering wordt afgehandeld door **Entry Points** op een AI-agent, niet door campagnes. Elk kanaal heeft één standaard Entry Point voor het kanaal dat de agent benoemt die nieuwe, onbekende contacten op dat kanaal beantwoordt:

| Wat u wilt doen | Aanroep |
|---|---|
| Een kanaal toewijzen aan de agent die het moet beantwoorden | `PUT /entry-points/channel-defaults` met body `{ "channel": "instagram", "agent_id": "AGENT_ID" }` |
| Controleren of de Entry Points-ladder live is voor het account | `GET /entry-points/routing-status`, wat `{ "success": true, "cutover_enabled": true }` retourneert zodra Entry Points de routering van dat account bepalen |
| Een kanaal verlaten zonder dat een agent het beantwoordt | `DELETE /entry-points/channel-defaults?channel=instagram` |

Totdat een kanaal een Entry Point heeft, wordt een eerste bericht van iemand met wie je nog nooit hebt gesproken wel opgeslagen, maar wordt dit door niets opgepikt en antwoordt er geen assistent. Dit is de stap die de meeste integraties missen: Instagram verbinden en een Agent aanmaken is op zichzelf niet genoeg — je moet het kanaal ook naar de Agent verwijzen. De volledige set aanroepen — inclusief één Agent per WhatsApp-nummer, trefwoord- en commentaarregels — staat in de [Entry Points API](entry-points.md).

`POST /channels/campaign` schrijft nog steeds de verouderde routeringskaart voor campagnes per kanaal, hieronder gedocumenteerd, maar die kaart wordt niet langer geraadpleegd voor inkomende routering op welk account dan ook; deze wordt alleen bewaard voor rollback. Bouw hier niet op voort.

### Eén of meer kanalen routeren (verouderde routeringskaart voor campagnes)

`POST /channels/campaign`

**Aanvraagvelden**

| Veld | Verplicht | Beschrijving |
|---|---|---|
| `campaign_id` | Ja | De campagne die nieuwe contacten op deze kanalen moet beantwoorden. Moet bij het account horen. |
| `channels` | Ja | Een niet-lege array van kanalen om te routeren. Toegestaan: `whatsapp`, `whatsapp_web`, `telegram`, `instagram`, `messenger`, `chat_widget`, `custom_channel`, `sms`, `email`. |

Het routeringsslot en de `enabled_channels`-lijst van de campagne worden samen bijgewerkt in één atomische operatie, zodat ze nooit uit elkaar kunnen lopen. Een kanaal dat al naar een andere campagne is gerouteerd, wordt simpelweg opnieuw naar deze campagne verwezen.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/campaign?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
    "channels": ["instagram", "messenger"]
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/channels/campaign", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    campaign_id: "NBCXrhqGPSFsd6MV7pRo",
    channels: ["instagram", "messenger"],
  }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/channels/campaign",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
        "channels": ["instagram", "messenger"],
    },
)
data = res.json()
```

**Antwoord**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "channels": ["instagram", "messenger"]
}
```

### Waaraan moet worden voldaan om routering daadwerkelijk te laten werken

Op een account dat nog steeds de verouderde routeringskaart voor campagnes leest, slaagt de routering als een API-aanroep, maar drie zaken in de campagne bepalen of een echt inkomend bericht wordt beantwoord. Controleer alle drie wanneer een gerouteerd kanaal stil blijft.

| Vereiste | Wat er anders gebeurt |
|---|---|
| `type` is `Incoming from Unknown Contacts` of `Combined` | Het verzoek wordt afgewezen met `400`. Uitgaande en Trefwoord-campagnes kunnen geen routeringsslot bevatten. |
| `status` is `Live` | De routering wordt opgeslagen maar pikt nooit iets op. Een `Draft`-campagne is de meest voorkomende oorzaak van "Ik heb het gerouteerd en er gebeurt niets". |
| `ai_mode` is `true` | Het contact wordt aangemaakt en het bericht opgeslagen, maar de assistent antwoordt nooit. |

Trefwoordmatching bevindt zich nu op Entry Points — maak een Entry Point van het type `keyword` aan op de AI-agent die moet antwoorden.

### Eén campagne per kanaal

Elk kanaal bevat precies één verouderd routeringsslot. Het routeren van een tweede campagne naar hetzelfde kanaal wijst het slot stilletjes opnieuw toe en retourneert `200` — er is geen conflictfout. De vorige campagne blijft de contacten afhandelen die het al heeft; het stopt alleen met het ontvangen van nieuwe.

### De routering van een kanaal wissen

`DELETE /channels/campaign/{channel}`

Verwijdert de routering voor een enkel kanaal, ongeacht naar welke campagne het momenteel verwijst, en haalt het kanaal weg van de `enabled_channels` van die campagne. Nieuwe onbekende contacten op het kanaal worden niet langer opgepikt door een campagne. Contacten die al in de campagne zitten, gaan door zoals voorheen.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/channels/campaign/instagram?apiKey=YOUR_API_KEY"
```

**Antwoord**

```json
{
  "success": true,
  "channel": "instagram",
  "cleared": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

Het is idempotent: het wissen van een kanaal dat nooit was gerouteerd, geeft ook `200` terug, met `cleared: false` en `campaign_id: null`. Dit eindpunt vereist de functie **inkomende campagnes** in het abonnement; zonder deze functie krijgt u een `403`.


---

## Gebruik je eigen Meta-app (Instagram + Messenger)

Standaard verloopt de Instagram + Messenger-verbinding via de Meta-app van het platform, dus de naam van die app is wat de accounthouder ziet op het toestemmingsscherm van Facebook. Als je wilt dat het toestemmingsscherm in plaats daarvan **jouw** merk toont, kun je je eigen Meta-app registreren en de volledige flow daarheen leiden. Zodra dit is geconfigureerd, is het van toepassing op je account — er verandert niets in de bovenstaande verbindingsaanroepen, behalve de branding.

> **Dit geldt alleen voor Instagram + Messenger.** WhatsApp, WhatsApp Web, Telegram en LINE-verbindingen worden niet beïnvloed door een aangepaste Meta-app.

### Wat je app eerst nodig heeft

Dit is het onderdeel dat tijd kost en het vindt volledig plaats aan de kant van Meta:

1. **Een app** van het type Business, met de producten Messenger en Instagram toegevoegd.
2. **Geavanceerde toegang** (via Meta App Review) voor: `pages_show_list`, `pages_messaging`, `pages_manage_metadata`, `pages_read_engagement`, `instagram_basic`, `instagram_manage_messages`. Zonder geavanceerde toegang kunnen alleen mensen met een rol in je app de verbinding voltooien — de verbindingen van je klanten zullen mislukken. App Review duurt doorgaans enkele weken en vereist bedrijfsverificatie.
3. **Een Facebook Login for Business-configuratie** aangemaakt binnen je app, met dezelfde machtigingen. Het numerieke configuratie-ID is per app, dus je moet je eigen ID aanmaken.

Als je app een van de vereiste machtigingen mist, mislukt de verbinding op het moment van verbinden met een duidelijke foutmelding waarin staat wat er ontbreekt (zichtbaar in de `/status` poll als `byo_app_missing_permissions`) — in plaats van dat het lijkt te werken en pas bij het eerste bericht mislukt.

### Stap 1 - Sla je app op

`PUT /account-config/meta-app`

| Veld | Vereist | Beschrijving |
|---|---|---|
| `app_id` | Ja | Je Meta App-ID (Instellingen → Basis). |
| `app_secret` | Ja | Je Meta App Secret. Wordt geverifieerd bij Meta voordat deze wordt opgeslagen, en vervolgens versleuteld. Wordt nooit geretourneerd door een endpoint. |
| `config_id` | Ja | Het numerieke ID van de Facebook Login for Business-configuratie binnen je app. |

Alle drie zijn vereist voor de Facebook Login-flow. Als je alleen de Instagram Login token-push-route uitvoert die hieronder wordt beschreven, kun je ze volledig weglaten.

```bash
curl -X PUT "https://api.youraiconnector.com/v1/account-config/meta-app?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "app_id": "1234567890123456",
    "app_secret": "your-app-secret",
    "config_id": "9876543210987654"
  }'
```

**Antwoord**

```json
{
  "success": true,
  "app_id": "1234567890123456",
  "config_id": "9876543210987654",
  "verify_token": "1f4c…a9",
  "webhook_urls": {
    "instagram": "https://api.youraiconnector.com/v1/incoming-instagram-message/byo/YOUR_ACCOUNT_ID",
    "messenger": "https://api.youraiconnector.com/v1/incoming-messenger-message/byo/YOUR_ACCOUNT_ID"
  }
}
```

### Stap 2 - Configureer je app om met ons te communiceren

In het dashboard van je Meta-app:

1. **Webhooks** - stel voor zowel de Instagram- als Messenger-producten de Callback URL in op de bijbehorende `webhook_urls`-waarde uit het antwoord, en de Verify token op `verify_token`. Abonneer je op de velden `messages`, `messaging_postbacks` en `comments`.
2. **Geldige OAuth Redirect URI's** - voeg `https://api.youraiconnector.com/v1/auth-meta-callback-handler` toe zodat de toestemmingsflow kan terugkeren.

`GET /account-config/meta-app` retourneert altijd hetzelfde installatiemateriaal; `DELETE /account-config/meta-app` verwijdert de app (toekomstige verbindingen vallen terug op de platform-app — verwijder ook het webhook-abonnement binnen je app).

### Stap 3 - Verbinden zoals gebruikelijk

Er verandert verder niets. `POST /channels/meta/connect` (en de gehoste `connect_url`-pagina) gebruikt automatisch jouw app voor jouw account; de `uses_byo_meta_app: true` van het antwoord bevestigt welke app het toestemmingsscherm zal tonen. Het versturen van berichten, het selecteren van pagina's en het verbreken van de verbinding werken op identieke wijze.

## Gebruik je eigen Instagram Login-app (token push)

Het bovenstaande gedeelte behandelt de Facebook Login-flow, waarbij het account verbinding maakt via een Facebook-pagina. Meta biedt ook de **Instagram API met Instagram Login** (Business Login voor Instagram): de accounthouder authenticeert op Instagram zelf, zonder dat er een Facebook-account of -pagina aan te pas komt.

Als je platform al een eigen Meta-app met dat product gebruikt, heb je helemaal geen OAuth-flow aan onze kant nodig. Je klanten autoriseren **jouw** app en jij pusht ons de voltooide inloggegevens per account:

1. Je slaat de inloggegevens van je Instagram-app eenmalig op (zodat we je webhooks kunnen verifiëren).
2. Per account push je het Instagram-bedrijfsaccount-ID + het langdurige Instagram-gebruikerstoken dat je app heeft verkregen.
3. Je koppelt de Instagram messaging-webhook van je app aan ons. Gebeurtenissen voor accounts die je nooit hebt gepusht, worden bevestigd en genegeerd.
4. Jij beheert de levenscyclus van het token: ververs tokens in je eigen systeem en push elk ververst token met dezelfde aanroep. Wij verversen nooit een gepusht token.

### Wat je app eerst nodig heeft

- Het **Instagram**-product ("API setup with Instagram login") toegevoegd aan je Meta-app. Dat product heeft zijn **eigen App ID en App Secret-paar**, los van de Facebook App ID/Secret — je vindt deze in het configuratiepaneel van het product.
- **Advanced Access** (via Meta App Review) voor `instagram_business_basic` en `instagram_business_manage_messages` (voeg `instagram_business_manage_comments` toe als je reactie-automatiseringen gebruikt). Zonder dit kunnen alleen mensen met een rol in je app deze autoriseren.

### Stap 1 - Sla je Instagram-app-inloggegevens op

Hetzelfde eindpunt als hierboven — stuur het Instagram-paar naar `PUT /account-config/meta-app`. De Facebook-velden zijn niet nodig voor deze route: stuur het paar alleen als je alleen Instagram Login uitvoert, of samen met de Facebook-velden als je beide uitvoert. Een opslag beschrijft altijd de volledige instelling, dus welke set je ook weglaat, wordt verwijderd.

| Veld | Verplicht | Beschrijving |
|---|---|---|
| `instagram_app_id` | Samen | Het numerieke App ID van het Instagram-product zelf (niet het Facebook App ID). |
| `instagram_app_secret` | Samen | Het App Secret van het Instagram-product zelf. Versleuteld opgeslagen, wordt nooit geretourneerd. |

```bash
curl -X PUT "https://api.youraiconnector.com/v1/account-config/meta-app?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "instagram_app_id": "1122334455667788",
    "instagram_app_secret": "your-instagram-app-secret"
  }'
```

**Response** — bevat de Instagram-Login webhook-URL (de `instagram` en `messenger` URL's verschijnen alleen wanneer de Facebook-velden ook zijn opgeslagen):

```json
{
  "success": true,
  "instagram_app_id": "1122334455667788",
  "verify_token": "1f4c…a9",
  "webhook_urls": {
    "instagram_login": "https://api.youraiconnector.com/v1/incoming-instagram-login-message/byo/YOUR_ACCOUNT_ID"
  }
}
```

Stel in het **Webhooks**-paneel van je app voor het Instagram-product de Callback URL in op `webhook_urls.instagram_login`, de Verify token op `verify_token`, en abonneer je op de velden `messages` en `comments`.

### Stap 2 - Push een token per account

`PUT /channels/instagram-login/token`

Werkt met `sub_account_id` zoals elke andere route, dus een agency-sleutel kan zijn volledige vloot inrichten.

| Veld | Verplicht | Beschrijving |
|---|---|---|
| `ig_user_id` | Ja | Het **Instagram-bedrijfsaccount-ID** — het `user_id`-veld uit `GET https://graph.instagram.com/v21.0/me?fields=user_id,username`. Dit is hetzelfde ID dat Instagram-webhooks dragen als `entry.id`. ⚠️ Dit is **niet** het `id`-veld uit `/me` — dat is app-scoped en verschilt per Meta-app. Het pushen van het app-scoped ID resulteert in een `400` die de fout benoemt. |
| `access_token` | Ja | Het langdurige Instagram-gebruikerstoken dat je app voor dat account heeft verkregen. Wordt live gevalideerd tegen Instagram voordat het wordt opgeslagen: het token moet werken en toebehoren aan `ig_user_id`. |
| `expires_at` | Nee | ISO-8601 verloopdatum van het token. Stuur anders `expires_in` (seconden). Standaard 60 dagen. |
| `username` | Nee | De @handle van het account; we lezen deze sowieso uit Instagram. |

```bash
curl -X PUT "https://api.youraiconnector.com/v1/channels/instagram-login/token?apiKey=YOUR_AGENCY_KEY&sub_account_id=CLIENT_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "ig_user_id": "17841400000000000",
    "access_token": "IGAAR…",
    "expires_at": "2026-11-01T00:00:00Z"
  }'
```

**Antwoord**

```json
{
  "success": true,
  "ig_user_id": "17841400000000000",
  "username": "acme.studio",
  "expires_at": "2026-11-01T00:00:00.000Z",
  "webhook_url": "https://api.youraiconnector.com/v1/incoming-instagram-login-message/byo/YOUR_ACCOUNT_ID"
}
```

Als onderdeel van de push abonneren we je app op de webhooks van dat account (`subscribed_apps` met het gepushte token), zodat berichten beginnen binnen te komen zonder dat er een extra aanroep van jouw kant nodig is.

**Vernieuwen** - stuur het vernieuwde token naar hetzelfde eindpunt met dezelfde `ig_user_id`; dit werkt het opgeslagen token en de vervaldatum ter plekke bij.

**Conflicten** - één Instagram-account is nooit actief op twee verbindingen. Als het account al ergens anders is verbonden, of op dit specifieke account via de Facebook-pagina-flow, retourneert de push een `409` die aangeeft welke verbinding je eerst moet verbreken. Een verbinding via de Facebook-flow wordt nooit automatisch vervangen, omdat deze mogelijk ook Messenger bedient.

### Stap 3 - Verbinding verbreken wanneer een klant vertrekt

`DELETE /channels/instagram-login/token` (dezelfde auth en `sub_account_id`) annuleert de webhooks op basis van 'best-effort' en verwijdert de opgeslagen inloggegevens. Dit slaagt altijd, zelfs als het token al is verlopen — en zodra de inloggegevens zijn verwijderd, worden de webhook-gebeurtenissen van dat account genegeerd.

---

## Tips voor het bouwen van een betrouwbare wrapper

- **Poll voorzichtig.** Elke paar seconden is ruim voldoende. Stop zodra u een eindstatus bereikt (`connected` / `ONLINE`, of een foutstatus), en stel een redelijke algehele time-out in op de lus (de browser/QR-stappen verlopen, zie elke `expires_at`).
- **URL-codeer telefoonnummers in het pad.** De voorloop `+` moet worden verzonden als `%2B`. De eindpunten herstellen ook kale cijfers, maar coderen is de veilige standaard.
- **Verwacht nooit geheimen terug.** Toegangstokens, kanaalgeheimen en paginatokens worden geaccepteerd of opgeslagen, maar worden nooit in een antwoord geretourneerd.
- **Behandel de auth-poort.** Een `403` betekent dat API-toegang niet in het abonnement zit, of dat het kanaal dat u verbindt niet is inbegrepen in het abonnement van het account. Zie [API-toegang](../integrations/api-access.md).
- **Let op de snelheidslimiet.** Geverifieerde verzoeken zijn beperkt tot 300 per minuut; een `429` betekent even wachten en opnieuw proberen. Zie [Authenticatie](authentication.md).

## Volgende stappen

- [Authenticatie](authentication.md) - de vier geaccepteerde auth-vormen en foutindeling.
- [API-toegang](../integrations/api-access.md) - het genereren en beheren van je API-sleutel.
