
# Webhooks

Met webhooks kan <span data-t="appName">Your AI Connector</span> uw andere zakelijke tools automatisch op de hoogte stellen wanneer er iets belangrijks gebeurt — zoals het aanmaken van een nieuw contact, het boeken van een afspraak of het ontvangen van een bericht. In plaats van handmatig op updates te controleren, ontvangen uw gekoppelde systemen direct een melding zodra er iets gebeurt.


---

## Wat zijn webhooks?

Zie een webhook als een automatisch sms-bericht tussen twee apps. Wanneer er iets gebeurt in <span data-t="appName">Your AI Connector</span> (zoals een nieuwe aanmelding van een contact), stuurt het platform direct een melding naar een ander systeem naar keuze. U geeft een webadres op (een "webhook-URL") waar deze meldingen naartoe moeten worden gestuurd — dit wordt meestal verstrekt door uw CRM, automatiseringsplatform of ontwikkelaar.

> **Webhooks sturen alleen gegevens UIT <span data-t="appName">Your AI Connector</span>.** Een webhook is een eenrichtingsverkeer *van* <span data-t="appName">Your AI Connector</span> *naar* je andere tools. Er is **geen webhook-URL die leads, contacten of berichten HET platform IN stuurt.** Om een nieuwe lead toe te voegen — vanuit een websiteformulier, je CRM of GoHighLevel — maakt je systeem in plaats daarvan een **API-aanroep**. Zie [API-toegang](api-access.md) (de *Create a Contact*-bewerking) en [Funnels](funnels.md). Het enige wat je nodig hebt voor de inkomende richting is je **API-sleutel**, die in zijn eigen sectie staat — zie [API-toegang](api-access.md#generating-your-api-key). De pagina **Webhooks** die hier wordt beschreven, is uitsluitend bedoeld voor de uitgaande richting.

::: note
**Opmerking:** Het instellen van webhooks vereist enige technische configuratie. Als je je hier niet prettig bij voelt, deel deze pagina dan met je ontwikkelaar of gebruik een automatiseringsplatform zoals Zapier, Make of Pabbly, die webhook-URL's aanbieden zonder dat er geprogrammeerd hoeft te worden.
:::


Veelvoorkomende toepassingen zijn:

- Nieuwe contacten synchroniseren met je CRM.
- Een workflow activeren in Zapier, Make of Pabbly wanneer een tag wordt toegevoegd.
- Je team in Slack op de hoogte stellen wanneer een menselijke tussenkomst is vereist.
- Je agendasysteem bijwerken wanneer een afspraak is geboekt.
- Gesprekssamenvattingen loggen in je database.

---

## Webhooks instellen

1. Klik in de linkerzijbalk op **Instellingen** (tandwielpictogram).
2. Klik in de zijbalk Instellingen, onder de groep **Integraties**, op **Webhooks**.


Op een account waar nog geen webhooks zijn geconfigureerd, ziet de pagina er als volgt uit:


3. Klik rechtsboven op **New webhook**. Er wordt een formulier geopend op de pagina:


4. Vul het volgende in:
   - **Endpoint-URL** — het webadres waar <span data-t="appName">Your AI Connector</span> gebeurtenismeldingen naartoe zal sturen. U krijgt dit van uw externe systeem (CRM, automatiseringsplatform of eigen server).
   - **Naam** — een label dat u later zult herkennen (bijv. "Slack-meldingen" of "CRM-synchronisatie"). Alleen voor uw eigen referentie.

> **Je webhook-URL moet een publiek bereikbaar `https://`-adres zijn.** Gewone `http://`-adressen, `localhost`- of privénetwerkadressen en interne platformadressen worden geweigerd bij het opslaan. Gebruik voor het testen vanaf je eigen machine een publieke tunnel (webhook.site of ngrok) in plaats van localhost.

5. Klik onder **Events** op de gebeurtenissen die deze webhook moet ontvangen — alle 22 staan vermeld in [The 22 Webhook Events](#the-22-webhook-events).
6. *(Optioneel)* Schakel **Retry failed deliveries** in als je wilt dat <span data-t="appName">Your AI Connector</span> opnieuw probeert te verzenden bij een tijdelijke fout — zie [Retrying Failed Deliveries](#retrying-failed-deliveries).
7. Klik op **Create webhook**. Deze verschijnt in de lijst onder het formulier en je kunt op elk gewenst moment op **Test** in de rij klikken om een voorbeeld-payload naar je eindpunt te sturen.

> **Toestemming vereist.** Voor het toevoegen, bewerken of testen van webhooks is de "bewerken"-toestemming voor Integraties vereist (teamleden met alleen-lezen toegang zien een melding dat het formulier alleen-lezen is).

> **Het ondertekenen van een webhook** vereist dat deze eerst is opgeslagen — open de rij van een bestaande webhook om deze te bewerken, en het paneel **Ondertekeningsgeheim** verschijnt onderaan het bewerkingsformulier. Een gloednieuw, niet-opgeslagen concept heeft nog geen ondertekeningsoptie — zie [Ondertekende payloads](#signed-payloads-verifying-a-webhook-really-came-from-us) hieronder.

---

## Eén webhook voor al uw klantaccounts (bureaus)

Als u een bureau runt, hoeft u niet voor elk klantaccount opnieuw dezelfde webhook aan te maken. Op het bureau-account heeft het webhook-formulier een extra schakelaar: **Ook activeren voor alle klantaccounts**. Schakel deze in en deze webhook ontvangt ook events die plaatsvinden op elk klantaccount onder uw bureau — één endpoint, het hele bureau.

Hoe het werkt:

- **Het `user`-blok vertelt u bij welke klant een event hoort.** Elke notificatie bevat al een `user`-blok dat het account identificeert waarop het event heeft plaatsgevonden, zodat uw automatisering per klant kan routeren.
- **De instellingen van uw webhook zijn overal van toepassing.** De events die u heeft geselecteerd, het ondertekeningsgeheim en de instelling voor opnieuw proberen worden ook gebruikt voor leveringen aan klantaccounts.
- **Geen dubbele leveringen.** Als een klantaccount een eigen webhook heeft die naar dezelfde URL wijst, wordt die gebruikt voor de events van dat account — hetzelfde event komt nooit twee keer aan op één endpoint.
- **Klanten zien het niet.** De webhook verschijnt niet op de eigen Webhooks-pagina van het klantaccount en klanten kunnen deze niet uitschakelen — het is aan u om deze te beheren.
- **Betrouwbaarheid wordt per klantaccount bijgehouden.** Als uw endpoint blijft falen, wordt het automatisch uitgeschakeld voor het account waarvan de leveringen zijn mislukt (zie [Webhook-betrouwbaarheid](#webhook-reliability)), en niet voor het hele bureau tegelijk.

De schakelaar verschijnt alleen bij bureau-accounts. Het instellen hiervan via de API wordt ook ondersteund — zie het `apply_to_sub_accounts`-veld in de [Webhooks API](../api/webhooks.md#one-subscription-for-all-client-accounts-agencies).

---

## Beschikbare triggergebeurtenissen

Je kunt elk van de 22 webhook-gebeurtenissen onafhankelijk in- of uitschakelen. Wanneer een gebeurtenis wordt geactiveerd, stuurt <span data-t="appName">Your AI Connector</span> een melding naar je webhook-URL met de relevante gegevens. Elke gebeurtenis, wat deze betekent en de `event`-code die in de payload wordt geplaatst, staan samen vermeld in [The 22 Webhook Events](#the-22-webhook-events) verderop op deze pagina.

> **Goed om te weten:** **Task Created**, **Task Updated** en **Task Completed** zijn volledig selecteerbaar en worden correct opgeslagen. **Daily Summary Created** is ook een recente toevoeging. Zie [Task Completed Webhook](#task-completed-webhook) hieronder voor de vorm van die payload.

---

## Webhook-triggers op basis van tags

`subscribed_to_tags` koppelt de gebeurtenissen van een webhook niet aan een tag. Het beperkt alleen welke tags een melding voor een gespreks-samenvatting genereren. Om een verzoek te ontvangen wanneer een specifieke tag wordt toegepast, stel je een webhook-URL in op die tag in het tabblad **Tags** van de agent (of campagne).

Het webhook-formulier zelf heeft geen tag-kiezer, noch bij het maken van een nieuwe webhook, noch bij het bewerken ervan, dus `subscribed_to_tags` kan alleen worden gelezen of gewijzigd via de [Webhooks API](../api/webhooks.md), of door ondersteuning te vragen.

> **Goed om te weten:** het bewerken van een bestaande webhook die een `subscribed_to_tags`-lijst heeft (hernoemen, gebeurtenissen wijzigen, opnieuw proberen in-/uitschakelen) wist die lijst niet langer — aangezien het formulier geen tag-kiezer heeft om terug te sturen, laat het opslaan vanaf deze pagina de bestaande lijst nu ongemoeid. (Dit was een echte bug vóór **21 juli 2026**: opslaan vanuit het webhook-formulier wist voorheen de lijst omdat er altijd een lege tag-lijst werd verzonden. Als een webhook vóór die datum zijn `subscribed_to_tags`-lijst is verloren, moet deze opnieuw worden geconfigureerd via de API.)

### Samenvatting genereren voor getagde contactpersonen

Waar een webhook een `subscribed_to_tags`-lijst heeft, kun je **Generate Summary** inschakelen. Wanneer dit is ingeschakeld, genereert <span data-t="appName">Your AI Connector</span> automatisch een gespreks-samenvatting voor de contactpersoon wanneer een van die tags wordt toegepast, en neemt deze op in de webhook-gegevens — volledige context zonder een apart verzoek.

---

## Je webhook testen

1. Open **Instellingen → Integraties → Webhooks**.
2. Klik op **Test** op de rij van uw webhook.
3. Controleer uw externe systeem om te bevestigen dat de testgegevens zijn ontvangen.
4. Controleer de gegevensindeling om er zeker van te zijn dat uw systeem deze correct kan verwerken.

Voor een volledige end-to-end test verstuurt u een bericht dat een van uw geconfigureerde gebeurtenissen zou activeren (een broadcast of een inkomend bericht op een verbonden kanaal) en controleert u of de webhook wordt geactiveerd met de echte gegevens.

::: tip
**Tip:** Gebruik tijdens de ontwikkeling een tool zoals [webhook.site](https://webhook.site) of [RequestBin](https://requestbin.com) om de onbewerkte webhook-gegevens te inspecteren voordat u uw productiesysteem koppelt.
:::


### Wat telt als een succesvolle bezorging

Of je nu op **Test** klikt of de gebeurtenis echt wordt geactiveerd, we sturen hetzelfde:

- Een **POST**-verzoek (nooit GET), met de body als JSON en `Content-Type: application/json`.
- De headers vermeld onder [Signed Payloads](#signed-payloads-verifying-a-webhook-really-came-from-us). Handtekening-headers worden pas toegevoegd zodra je een ondertekeningsgeheim hebt ingesteld.

We beschouwen de bezorging als succesvol wanneer:

- Je endpoint antwoordt met **een 2xx-status** (200, 201, 204 — allemaal prima).
- Het antwoord binnen **30 seconden** binnenkomt.

Een paar dingen die mensen verrassen:

- **De response body wordt genegeerd.** U hoeft geen specifieke JSON terug te sturen. Een lege 200 is voldoende.
- **Omleidingen tellen als een fout.** We volgen deze niet, dus een 301 of 302 (inclusief een omleiding voor een afsluitende slash, of http naar https) wordt geregistreerd als een mislukte aflevering. Sla de uiteindelijke URL op, niet een die omleidt.
- **Query strings worden volledig ondersteund.** `https://your-app.com/hook?token=abc123` wordt exact zo verzonden als u deze heeft opgeslagen, dus een token in de query string plaatsen werkt net zo goed als in het pad.
- **Uw URL moet `https://` zijn en publiekelijk bereikbaar.** Adressen die toebehoren aan de eigen infrastructuur van <span data-t="appName">Your AI Connector</span> worden geweigerd, maar uw eigen eindpunten op Google Cloud Functions, Cloud Run, App Engine, Firebase Hosting of elders zijn prima.
- **Een firewall of bot-beveiligingslaag voor uw eindpunt kan ons blokkeren.** Het meest voorkomende geval is Cloudflare: als uw zone Bot Fight Mode of een beheerde uitdaging aan heeft staan, krijgt ons verzoek een "Just a moment..." uitdagingspagina met een 403 in plaats van dat het uw server bereikt — en een server-naar-server verzoek kan nooit een browser-uitdaging passeren, dus zowel de **Test**-knop als echte gebeurtenissen falen op dezelfde manier. De Test-knop zal u vertellen wanneer dit gebeurt ("Cloudflare is showing a bot challenge to our request"). Los dit op in Cloudflare met een Security / WAF-regel die uitdagingen overslaat voor uw webhook-pad (of voor de `Webhook-Delivery/1.0` user agent), en klik daarna opnieuw op **Test**.
- **Als uw firewall in plaats daarvan een IP-toelatingslijst nodig heeft** (bijvoorbeeld het gratis abonnement van Cloudflare, waar de standaard Bot Fight Mode niet kan worden overgeslagen door een WAF-regel, maar een IP Access Rule ingesteld op Allow wel voorrang krijgt), kunnen we helpen: elke aflevering, of deze nu via de **Test**-knop of een live gebeurtenis komt, wordt verzonden vanaf één vast IPv4-adres (geen bereiken, geen IPv6, geen rotatie). Neem contact op met de ondersteuning en wij geven u het adres dat u op de toelatingslijst kunt zetten. Houd [handtekeningverificatie](#signed-payloads-verifying-a-webhook-really-came-from-us) aan als uw daadwerkelijke vertrouwenscontrole, aangezien deze elke payload valideert, ongeacht waar deze vandaan komt.
- **Het testresultaat vertelt u precies wat uw eindpunt heeft geantwoord.** Een mislukte test toont nu de werkelijke reden (de HTTP-status die uw eindpunt heeft geretourneerd, een time-out, of dat we het adres helemaal niet konden bereiken) in plaats van een algemene foutmelding, en een test op een opgeslagen webhook wordt ondertekend verzonden wanneer ondertekening is ingeschakeld, precies zoals bij een live gebeurtenis.

### n8n, Make of Zapier gebruiken ("Test-URL" vs "Productie-URL")

Automatiseringsplatforms geven je meestal twee verschillende webhook-adressen, en dit brengt mensen vaak in verwarring:

- Een **Test-URL** (in n8n bevat deze `/webhook-test/`). Deze ontvangt alleen gegevens terwijl u actief naar het canvas kijkt en zojuist op **Listen for test event** (of **Test workflow**) hebt geklikt. Het legt één gebeurtenis vast en stopt dan met luisteren — dus meerdere keren achter elkaar op **Test** klikken in <span data-t="appName">Your AI Connector</span> vangt alleen de eerste op, en alleen als het luistervenster op dat exacte moment actief is. Om te testen: klik eerst op **Listen for test event** in n8n, ga dan terug naar <span data-t="appName">Your AI Connector</span> en klik één keer op **Test**.
- Een **Productie-URL** (in n8n bevat deze `/webhook/`, geen `-test`). Dit is de URL die u in <span data-t="appName">Your AI Connector</span> moet plakken voor live-gebeurtenissen. Deze werkt pas zodra uw workflow op **Active** is gezet. Als de workflow niet actief is, weigert n8n het verzoek met een "404 / webhook not registered"-fout, ook al heeft <span data-t="appName">Your AI Connector</span> de gegevens correct verzonden.

Kortom: test met de Test-URL terwijl je luistert, maar om de webhook te laten werken voor echte contacten, sla je de **Productie-URL** op in <span data-t="appName">Your AI Connector</span> en zorg je ervoor dat de workflow **Active** is.

---

## Gegevensindeling van de webhook

Wanneer een webhook wordt geactiveerd, stuurt <span data-t="appName">Your AI Connector</span> gestructureerde gegevens (JSON) naar uw webhook-URL. Als u een automatiseringsplatform zoals Zapier of Make gebruikt, worden deze gegevens automatisch voor u geparseerd. Als u een aangepaste integratie bouwt:

```json
{
  "event": "contactCreated",
  "contact": { "id": "<contact-id>", "first_name": "Jane", "...": "..." },
  "campaign": { "id": "<campaign-id>", "name": "AI Receptionist", "status": "Live" },
  "agent": { "id": "<agent-id>", "name": "Front Desk" },
  "user": { "id": "<account-id>", "email": "owner@example.com" }
}
```

| Veld | Beschrijving |
|---|---|
| `event` | De exacte gebeurtenis-string die de melding heeft geactiveerd (bijvoorbeeld `contactCreated`, `booked`). Dit is **niet** het weergavelabel dat in de gebeurtenissenlijst wordt getoond; elk label en de bijbehorende code staan in [The 22 Webhook Events](#the-22-webhook-events). |
| `contact` | Het contact waar de gebeurtenis over gaat, of `null` voor gebeurtenissen die niet aan een contact zijn gekoppeld (zoals `creditsRecharged`). |
| `campaign` | De campagne waartoe het contact behoort, of `null` als er geen is. |
| `agent` | De agent die het gesprek afhandelt, of `null` als er geen is. |
| `user` | Basisidentiteitsinformatie voor het account dat de gegevens bezit. |

> **`campaign` of `agent` — meestal één, niet beide.** Als uw account gebruikmaakt van agents, worden uw contactpersonen toegewezen aan een agent in plaats van aan een campagne, dus `campaign` komt binnen als `null` en `agent` vertelt u wie het heeft afgehandeld. Oudere accounts op basis van campagnes zien het omgekeerde. Lees wat er is ingevuld; ga er niet vanuit dat `campaign` altijd aanwezig is.

> **Het `agent`-blok is gearriveerd op 15 augustus 2026.** Het bevindt zich naast `campaign` bij de gebeurtenissen die gekoppeld zijn aan een gesprek — een afgesloten chat, niet storen, een hervatting, een de-archivering, een AI-pauze, een nieuw bericht, een gespreks-samenvatting en de webhook die je kunt instellen op een tag — en bevat de `id` en `name` van de afhandelende agent, of `null` wanneer er geen agent bij betrokken is. Het is puur toevoegend: elk veld dat je al ontvangt blijft ongewijzigd, dus een ontvanger die je vóór die datum hebt gebouwd, blijft werken zonder dat er iets bijgewerkt hoeft te worden.

Sommige gebeurtenissen voegen hun eigen extra blok op het hoogste niveau toe. Bijvoorbeeld: **Afspraak geboekt** voegt een `appointment`-blok toe (zie [Webhook voor afspraak geboekt](#appointment-booked-webhook)), **Nieuw bericht** voegt een volledig `message`-blok toe met de tekst (zie [Webhook voor nieuw bericht](#new-message-webhook)), en **Afleveringen** en **Lezingen** voegen een kort `message`-blok toe met alleen het ID en de status van het bericht (zie [Webhook voor afleveringen en lezingen](#deliveries-and-reads-webhook)).

> **Afleveringen en lezingen vertellen je om welk bericht het gaat, maar niet wat erin stond.** Ze bevatten een `message`-blok met het `id` en `status` van het bericht — en dat `id` is hetzelfde `messageId` als wat het [eindpunt voor berichtverzending](../api/messages.md#send-a-message) teruggeeft, zodat je een aflever- of leesbevestiging kunt koppelen aan het exacte bericht dat je hebt verzonden — maar zonder berichtinhoud. **Antwoorden** bevat helemaal geen `message`-blok. Als je de woorden wilt weten die zijn verzonden of ontvangen, abonneer je dan daarnaast op **Nieuw bericht**.

> **Twee dingen om te weten voordat u uw ontvanger schrijft.** Er is geen `timestamp`-veld en geen `data`-wrapper. Elk blok bevindt zich op het hoogste niveau van het JSON-object, zoals hierboven getoond.

### The 22 Webhook Events

De 22 webhook-gebeurtenissen, met het weergavelabel dat je in de app aanvinkt en de `event`-code die in de payload wordt verzonden. De `event`-code is een korte string die **niet** overeenkomt met het weergavelabel, dus laat je ontvanger matchen op de code, niet op het label:

| Weergavelabel (in de app) | `event`-code in de payload | Wat het betekent |
|---|---|---|
| Contact aangemaakt | `contactCreated` | Een nieuw contact is toegevoegd aan je account (handmatig, via import of via API). |
| Contact gepauzeerd | `contact_paused` | Een contactgesprek is gepauzeerd (bot stopt met reageren). |
| Contact hervat | `contact_resumed` | Een gepauzeerd contactgesprek is hervat. |
| Contact Niet storen | `contact_do_not_disturb_changed` | De instelling 'Niet storen' van een contact is ingeschakeld. |
| Contact uit archief gehaald | `contact_unarchived` | Een gearchiveerd contact stuurt een nieuw bericht, waardoor deze terugkeert in je actieve inbox. |
| Nieuw bericht | `new_message` | Elk bericht dat wordt toegevoegd aan een gesprek op elk kanaal — zowel berichten die je contact naar jou stuurt als berichten die je AI of je team naar hen stuurt. Dit is de enige gebeurtenis die de daadwerkelijke berichttekst bevat (zie [Webhook voor nieuw bericht](#new-message-webhook)). |
| Antwoorden | `replied` | Een contact reageert op een bericht. |
| Lezingen | `read` | Een contact leest een bericht (op kanalen die leesbevestigingen ondersteunen). Bevat het ID van het gelezen bericht — zie [Webhook voor afleveringen en lezingen](#deliveries-and-reads-webhook). |
| Afleveringen | `delivered` of `undelivered` | Een bericht is succesvol afgeleverd bij een contact (`undelivered` wanneer aflevering mislukt). Bevat het ID van het bericht — zie [Webhook voor afleveringen en lezingen](#deliveries-and-reads-webhook). |
| Mens ingeschakeld | `humanAlerted` | De AI-bot bepaalt dat hij een gesprek niet kan afhandelen en markeert dit voor menselijke aandacht. |
| Chat beëindigd | `chat_concluded` | De AI-bot besluit dat een gesprek ten einde is (afspraak gemaakt, lead gediskwalificeerd, enz.). |
| Afspraak geboekt | `booked` | Een contact boekt een afspraak via het boekingssysteem. |
| Credits verbruikt | `creditsSpent` | Credits worden afgeschreven van je account. |
| Credits opgewaardeerd | `creditsRecharged` | Credits worden toegevoegd aan je account via automatisch opwaarderen of handmatige aankoop. |
| Laag kredietsaldo | `lowCreditBalance` bij een **Test**-aflevering, `Low Credit Balance` bij een echte | Een vroege waarschuwing dat je kredietsaldo onder je waarschuwingsdrempel is gezakt (100 credits tenzij je zelf een andere instelt). Bedoeld voor bureaus waarvan alle subaccounts uit één pot uitgeven. Het bevat `balance`, `threshold` en `account_email` in plaats van een contactblok, wordt maximaal één keer per 24 uur verzonden zolang het saldo laag blijft, en wordt opnieuw geactiveerd zodra het saldo weer boven de drempel komt. |
| Taak aangemaakt | `taskCreated` | Een taak is aangemaakt. |
| Taak bijgewerkt | `taskUpdated` | Een taak verandert zonder naar een voltooiingsfase te gaan. |
| Taak voltooid | `taskCompleted` | Een taak gaat naar een fase die is geconfigureerd als voltooiingsfase. |
| Dagelijks overzicht aangemaakt | `dailySummaryCreated` | Je dagelijkse overzichtsrapport is gegenereerd. |
| Kanaal verbonden | `channelConnected` | **Nog niet verzonden — selecteerbaar, maar wordt momenteel nergens door gegenereerd. Bouw hier niet op.** Bedoeld voor wanneer een berichtkanaal de verbinding voltooit. |
| Uitzending gestart | `broadcastStarted` | Een uitzending begint met verzenden (de status verandert naar Verzenden). Wordt één keer per start geactiveerd, inclusief wanneer een gepauzeerde uitzending wordt hervat. Bevat een `broadcast`-blok in plaats van een contactblok: id, naam, kanaal, status, vorige status, de lijst waarop het zich richt (`list_id`, `list_name`, `is_smart_list`), `scheduled_at`, `total_contacts`. |
| Uitzending voltooid | `broadcastCompleted` | Een uitzending is voltooid (de status verandert naar Verzonden of Mislukt). Zelfde `broadcast`-blok plus `completed_at` en, indien beschikbaar, `completion_summary` (`total_sent`, `permanently_failed`, `unique_replied`, `failure_rate`, `had_errors`). Gebruik deze twee om een Smart Broadcast List te verbinden met externe tools. |

Nog twee codes verschijnen nooit in die lijst omdat je je er niet op abonneert: `contact_tags_updated`, verzonden door een webhook-URL ingesteld op een individuele tag, en `summary_generated`, verzonden wanneer een chatsamenvatting wordt geschreven voor een tag in de `subscribed_to_tags`-lijst van een webhook.

> **Kanaal verbonden wordt nog niet verzonden.** Het verschijnt in de lijst met gebeurtenissen, maar er is momenteel niets dat dit activeert. Bouw hier niet op.

Notificaties op basis van tags en taken gebruiken hun eigen aparte vormen. Zie [Contact Tags Updated](#contact-tags-updated-webhook) en [Task Completed](#task-completed-webhook).

---

## Webhook Contact aangemaakt

Wordt verzonden wanneer de gebeurtenis **Contact aangemaakt** wordt geactiveerd (een nieuw contact wordt handmatig, via import of via API toegevoegd).

### Gebeurtenisnaam

`contactCreated`

### Payload-indeling

```json
{
  "event": "contactCreated",
  "contact": {
    "id": "<contact-id>",
    "email": "jane@example.com",
    "phone_number": "+15551234567",
    "first_name": "Jane",
    "last_name": "Smith",
    "human_alerted": false,
    "human_alert_reason": null,
    "is_bot_active": true,
    "ad_referral": null
  },
  "campaign": {
    "id": "<campaign-id>",
    "name": "AI Receptionist",
    "status": "Live"
  },
  "agent": {
    "id": "<agent-id>",
    "name": "Front Desk"
  },
  "user": {
    "id": "<account-id>",
    "email": "owner@example.com",
    "first_name": "Alex",
    "last_name": "Doe"
  }
}
```

| Veld | Beschrijving |
|---|---|
| `event` | Altijd `contactCreated` voor dit event. |
| `contact.id` | Het unieke ID van de nieuwe contactpersoon. |
| `contact.email` / `contact.phone_number` | Het e-mailadres en telefoonnummer van de contactpersoon, indien bekend (beide kunnen leeg zijn, afhankelijk van het kanaal). |
| `contact.first_name` / `contact.last_name` | De naam van de contactpersoon, indien bekend. |
| `contact.human_alerted` / `contact.human_alert_reason` | Of de contactpersoon is gemarkeerd voor menselijke aandacht, en waarom. |
| `contact.is_bot_active` | Of de AI-bot momenteel actief is op deze contactpersoon. |
| `contact.ad_referral` | Meta Click-to-WhatsApp advertentie-attributie, of `null` — zie [Click-to-WhatsApp Ad Attribution](click-to-whatsapp-attribution.md). |
| `campaign` | De campagne waaronder de contactpersoon is aangemaakt, of `null`. |
| `agent` | De agent die aan de contactpersoon is toegewezen, of `null`. |
| `user` | Basisidentiteitsinformatie voor het account dat de contactpersoon bezit. |

> **Het "Test"-voorbeeld en een echte gebeurtenis zien er iets anders uit.** De testknop verstuurt tijdelijke gegevens (John Doe, een voorbeeldcampagne). Een echte gebeurtenis voor het aanmaken van een contact bevat de details van het werkelijke contact, en sommige velden kunnen leeg zijn, afhankelijk van het kanaal.

---

## Nieuw bericht-webhook

Deze webhook wordt geactiveerd telkens wanneer er een bericht wordt toegevoegd aan een gesprek, op welk kanaal dan ook. Het omvat beide richtingen: berichten die uw contact naar u stuurt, en berichten die uw AI, uw team of een campagne naar hen stuurt. Het is de enige webhook die de berichttekst bevat, dus dit is de webhook die u moet gebruiken wanneer u gesprekken wilt spiegelen naar een extern systeem.

### Gebeurtenisnaam

`new_message`

### Payload-indeling

```json
{
  "event": "new_message",
  "contact": {
    "id": "<contact-id>",
    "email": "jane@example.com",
    "phone_number": "+15551234567",
    "first_name": "Jane",
    "last_name": "Smith",
    "human_alerted": false,
    "human_alert_reason": null,
    "is_bot_active": true,
    "ad_referral": null
  },
  "agent": {
    "id": "<agent-id>",
    "name": "Front Desk"
  },
  "user": {
    "id": "<account-id>",
    "email": "owner@example.com",
    "first_name": "Alex",
    "last_name": "Doe"
  },
  "message": {
    "id": "<message-id>",
    "body": "Hi, are you open on Saturday?",
    "direction": "inbound",
    "status": "received",
    "created_at": "2026-07-30T17:27:06.000Z",
    "channel": "whatsapp_web"
  }
}
```

| Veld | Beschrijving |
|---|---|
| `event` | Altijd `new_message` voor deze gebeurtenis. Let op: dit is de exacte tekenreeks die wordt verzonden — het is niet het weergavelabel "Nieuw bericht". |
| `contact` | Het contact tot wiens gesprek het bericht behoort. Zelfde vorm als in [Contact aangemaakt](#contact-created-webhook). |
| `agent` | De agent die het gesprek afhandelt (`id` en `name`), of `null` als er geen agent bij betrokken is. |
| `user` | Basisidentiteitsinformatie voor het account dat eigenaar is van het gesprek. |
| `message.id` | Het unieke ID van het bericht. |
| `message.body` | De berichttekst. Leeg voor een bericht dat alleen een bijlage bevat (afbeelding, spraakbericht, document). |
| `message.direction` | `inbound` voor een bericht van het contact, `outbound` voor een bericht verzonden door je AI of door je team vanuit de inbox, en `outbound-api` voor een bericht verzonden door een campagne, een uitzending, een sjabloonverzending of de API. |
| `message.status` | Waar het bericht zich in zijn levenscyclus bevindt: `received` voor inkomend, en `queued` / `sent` / `delivered` / `read` / `failed` / `undelivered` voor uitgaand. Dit is de status op het moment dat het bericht werd aangemaakt, dus een uitgaand bericht komt hier meestal aan als `queued` of `sent` en bereikt daarna `delivered` — gebruik de **Afleveringen**- en **Lezingen**-gebeurtenissen als je die latere overgangen nodig hebt. Ze bevatten hetzelfde `message.id` als dit blok, zodat je de overgang aan dit bericht kunt koppelen (zie [Webhook voor afleveringen en lezingen](#deliveries-and-reads-webhook)). |
| `message.created_at` | Wanneer het bericht is aangemaakt, in UTC (ISO 8601). |
| `message.channel` | Het kanaal waar het bericht doorheen is gegaan, bijvoorbeeld `whatsapp`, `whatsapp_web`, `sms`, `instagram`, `messenger`, `telegram`, `email` of `custom`. |

> **Er is nog steeds geen `campaign`-blok in deze payload.** New Message verzendt `contact`, `agent`, `user` en `message`. Het `agent`-blok is toegevoegd op **15 augustus 2026** en vertelt je welke agent het gesprek afhandelt; als je ook campagnecontext nodig hebt, zoek de contactpersoon dan op via de API met `contact.id`.

> **Interne AI-records activeren deze webhook niet.** Naast echte berichten houdt het platform zijn eigen boekhoudkundige rijen bij in een gesprek (de tool-aanroepen van de AI en interne turn-records). Deze worden nooit verzonden — u ontvangt alleen berichten die daadwerkelijk zijn verzonden of ontvangen.

---

## Webhook voor afleveringen en lezingen

Deze twee gebeurtenissen rapporteren wat er met een bericht is gebeurd nadat het <span data-t="appName">Your AI Connector</span> heeft verlaten: **Afleveringen** wordt geactiveerd wanneer een bericht het contact bereikt (of niet), en **Lezingen** wordt geactiveerd wanneer het contact het opent, op de kanalen die leesbevestigingen ondersteunen.

Beide bevatten een `message`-blok met het ID van het bericht waar de gebeurtenis over gaat, zodat je de update kunt koppelen aan het exacte bericht dat je hebt verzonden.

### Gebeurtenisnamen

`delivered` en `undelivered` voor **Afleveringen**, `read` voor **Lezingen**.

### Payload-indeling

```json
{
  "event": "delivered",
  "contact": {
    "id": "<contact-id>",
    "email": "jane@example.com",
    "phone_number": "+15551234567",
    "first_name": "Jane",
    "last_name": "Smith",
    "ad_referral": null
  },
  "campaign": {
    "id": "<campaign-id>",
    "name": "AI Receptionist",
    "status": "Live"
  },
  "agent": {
    "id": "<agent-id>",
    "name": "Front Desk"
  },
  "user": {
    "id": "<account-id>",
    "email": "owner@example.com",
    "first_name": "Alex",
    "last_name": "Doe"
  },
  "message": {
    "id": "<message-id>",
    "status": "delivered"
  }
}
```

| Veld | Beschrijving |
|---|---|
| `event` | `delivered` of `undelivered` voor **Afleveringen**, `read` voor **Lezingen**. |
| `contact` | Het contact naar wie het bericht is verzonden. |
| `campaign` | De campagne waartoe het contact behoort, of `null`. |
| `agent` | De agent die het gesprek afhandelt, of `null`. |
| `user` | Basisidentiteitsinformatie voor het account dat eigenaar is van de gegevens. |
| `message.id` | Het ID van het bericht waar deze update over gaat. Dit is dezelfde waarde die het [eindpunt voor berichtverzending](../api/messages.md#send-a-message) teruggeeft als `messageId`, en hetzelfde `message.id` dat een [Nieuw bericht](#new-message-webhook)-melding bevat. |
| `message.status` | De nieuwe status, altijd dezelfde tekenreeks als `event` (`delivered`, `undelivered` of `read`). |

> **Hoe koppel je een update aan het bericht dat je hebt verzonden.** Sla het `messageId` op dat je terugkrijgt wanneer je een bericht via de API verzendt. Wanneer er een **Afleveringen**- of **Lezingen**-melding binnenkomt, zoek dan dat opgeslagen ID op in `message.id` in de payload — dat is je aflever- of leesbevestiging voor dat specifieke bericht.

> **Er is hier geen berichttekst.** Het `message`-blok bevat alleen het ID en de status. Abonneer je op [Nieuw bericht](#new-message-webhook) als je ook de inhoud nodig hebt.

> **Het `message`-blok is alleen aanwezig wanneer we weten om welk bericht het ging.** Bij de zeldzame update die we niet kunnen koppelen aan een opgeslagen bericht, wordt het blok volledig weggelaten in plaats van leeg verzonden — controleer dus of `message` bestaat voordat je `message.id` leest.

> **Eén melding per statuswijziging.** Een enkel uitgaand bericht produceert normaal gesproken een `delivered`-melding en vervolgens, op kanalen met leesbevestigingen, een `read`-melding. Een mislukte verzending produceert in plaats daarvan `undelivered`.

---

## Appointment Booked Webhook

Wordt geactiveerd wanneer een contact een afspraak boekt. Het wordt op dezelfde manier geactiveerd, of de AI het nu tijdens een gesprek heeft geboekt, u het handmatig hebt geboekt of het via de API is binnengekomen.

### Gebeurtenisnaam

`booked`

### Payload-indeling

```json
{
  "event": "booked",
  "contact": {
    "id": "<contact-id>",
    "email": "jane@example.com",
    "phone_number": "+15551234567",
    "first_name": "Jane",
    "last_name": "Smith"
  },
  "campaign": {
    "id": "<campaign-id>",
    "name": "AI Receptionist",
    "status": "Live"
  },
  "user": {
    "id": "<account-id>",
    "email": "owner@example.com"
  },
  "appointment": {
    "appointment_id": "<appointment-id>",
    "start_time": "2026-07-20T15:00:00.000Z",
    "end_time": "2026-07-20T15:30:00.000Z",
    "status": "confirmed",
    "room_name": "Room 1",
    "description": "Discovery call",
    "summary": "30 min intro",
    "google_calendar_event_id": null,
    "event": {
      "id": "<service-id>",
      "event_name": "Intro Call",
      "slot_duration": 30,
      "location": "Zoom",
      "meeting_link": "https://...",
      "event_type": "online"
    }
  }
}
```

| Veld | Beschrijving |
|---|---|
| `event` | Altijd `booked` voor deze gebeurtenis. |
| `contact` | De persoon die heeft geboekt. `email` en `phone_number` kunnen leeg zijn, afhankelijk van het kanaal. |
| `appointment.appointment_id` | Het unieke ID van de boeking. |
| `appointment.start_time` / `end_time` | Start en einde van het geboekte tijdslot, in UTC (ISO 8601). |
| `appointment.status` | De huidige status van de boeking. |
| `appointment.room_name` | De ruimte waarin de boeking is geplaatst, indien gebruikt. |
| `appointment.description` / `summary` | Details in vrije tekst die bij de boeking zijn vastgelegd. |
| `appointment.google_calendar_event_id` | Het ID van Google Calendar voor de gesynchroniseerde gebeurtenis. Dit is vaak `null` in de Appointment Booked-webhook, omdat de agenda-afspraak op hetzelfde moment wordt gemaakt als waarop de melding wordt verzonden — haal de afspraak een moment later opnieuw op via het `appointment_id` als je deze nodig hebt, en verwacht een permanente `null` op accounts zonder gekoppelde Google Calendar. |
| `appointment.event` | De dienst die is geboekt: naam, slotlengte, locatie, vergaderlink, type. |

> **`google_calendar_event_id` is vaak `null` in deze webhook, en dat is normaal.** De Google Agenda-gebeurtenis wordt op hetzelfde moment aangemaakt als deze melding wordt verstuurd, dus het ID is meestal nog niet klaar. Haal de afspraak een moment later opnieuw op via het `appointment_id` als u het nodig heeft. Het blijft permanent `null` als het account geen Google Agenda heeft gekoppeld, dus wacht er niet eeuwig op.

> **De "Test"-knop bevat niet het `appointment`-blok.** Gebruik deze om te bevestigen dat uw eindpunt antwoordt, en maak vervolgens één echte boeking om de volledige payload te zien.

> **Twee gevallen waarin deze webhook niet wordt geactiveerd:** afspraken die zijn geïmporteerd vanuit een externe agenda, en boekingen die binnenkomen via de Formitable-integratie.

---

## Webhook voor bijgewerkte contacttags

Wordt geactiveerd wanneer een tag wordt **toegepast** op een contactpersoon, en die tag een webhook-URL heeft geconfigureerd op de agent of campagne waartoe de contactpersoon behoort.

### Gebeurtenisnaam

`contact_tags_updated`

### Wanneer deze wordt geactiveerd

- Er wordt een tag toegepast op een contactpersoon aan wie een agent is toegewezen, een campagne is toegewezen, of beide.
- Ten minste één van de toegepaste tags heeft een webhook-URL ingesteld in het tabblad Tags van die agent of campagne.

Als het contact beide heeft en de tags van de campagne webhook-URL's bevatten, hebben die voorrang; anders worden die van de agent gebruikt.

Als er meerdere tags met verschillende webhook-URL's in dezelfde update worden toegepast, wordt er één verzoek per URL verzonden, waarbij elk verzoek alleen de tags bevat die aan die URL zijn gekoppeld.

**Het verwijderen van een tag verstuurt nooit een verzoek.** De meeste mensen koppelen deze URL's aan een actie — een aanbetaling innen, een tijdslot boeken, een medewerker waarschuwen — dus een tag die van een contactpersoon wordt verwijderd, werd voorheen gebruikt om die actie opnieuw uit te voeren. Dat kan niet meer. Een verwijdering verschijnt nog steeds in `removed_tags` wanneer dit gebeurt in dezelfde update als een toevoeging die naar dezelfde URL gaat, zodat een automatisering die beide arrays leest het volledige beeld behoudt; wat het nooit zal zien, is een verzoek dat uitsluitend door een verwijdering wordt veroorzaakt. (Gewijzigd op **12 augustus 2026**. Vóór die datum verstuurden verwijderingen ook een verzoek.)

### Payload-indeling

```json
{
  "event": "contact_tags_updated",
  "contact": {
    "id": "<contact-id>",
    "email": "jane@example.com",
    "phone_number": "+15551234567",
    "first_name": "Jane",
    "last_name": "Smith",
    "human_alerted": false,
    "is_bot_active": true,
    "ad_referral": {
      "ctwa_clid": "ARAbc123...",
      "source_id": "120210000000000",
      "source_type": "ad",
      "source_url": "https://fb.me/xxxx",
      "headline": "Get 20% off today",
      "body": "Message us now to claim your discount",
      "channel": "whatsapp"
    }
  },
  "added_tags": ["qualified-lead"],
  "removed_tags": ["new-lead"],
  "agent": {
    "id": "<agent-id>",
    "name": "Front Desk"
  },
  "user": {
    "email": "owner@example.com",
    "first_name": "Alex",
    "last_name": "Doe"
  }
}
```

| Veld | Beschrijving |
|---|---|
| `event` | Altijd `contact_tags_updated` voor deze webhook. |
| `contact.id` | Het unieke ID van de contactpersoon wiens tags zijn gewijzigd. |
| `contact.email` / `contact.phone_number` | Het e-mailadres/telefoonnummer van de contactpersoon, indien bekend. |
| `contact.first_name` / `contact.last_name` | De naam van de contactpersoon. |
| `contact.human_alerted` | Of de contactpersoon momenteel is gemarkeerd voor menselijke aandacht. |
| `contact.is_bot_active` | Of de AI-bot momenteel actief is in het gesprek van deze contactpersoon. |
| `contact.ad_referral` | Alleen aanwezig wanneer de contactpersoon je voor het eerst bereikte via een Meta Click-to-WhatsApp (CTWA)-advertentie of -bericht. Anders `null`. |
| `added_tags` | Array van tagnamen die in deze update zijn toegepast. Nooit leeg — een toepassing is wat het verzoek activeert. |
| `removed_tags` | Array van tagnamen die in dezelfde update zijn verwijderd, indien van toepassing. Een verwijdering op zichzelf verstuurt niets. |
| `agent` | De agent die het gesprek van de contactpersoon afhandelt (`id` en `name`), of `null` als er geen agent bij betrokken is. Toegevoegd op **15 augustus 2026**. |
| `user` | Basisidentiteitsinformatie voor het account dat eigenaar is van de contactpersoon. |

### Een tag-webhook testen

Naast het webhook-URL-veld op het tabblad Tags staat een **Test**-knop. Hiermee wordt direct een voorbeeldpayload naar die URL verzonden, zodat u kunt bevestigen dat uw automatisering deze ontvangt voordat u op een echt gesprek hoeft te wachten.

De test verzendt dezelfde `contact_tags_updated`-vorm als hierboven weergegeven, met gebruik van een tijdelijke contactpersoon, met de tag die u test in `added_tags` en een lege `removed_tags`. Wat uw automatisering in de test ziet, is wat deze in productie zal zien.

Twee dingen om te weten:

- **Sla eerst de tag op.** De test zoekt de tag op basis van de opgeslagen naam, dus een gloednieuwe tag of een niet-opgeslagen naamswijziging kan nog niet worden getest. De knop blijft grijs totdat de naam op het scherm overeenkomt met de opgeslagen naam.
- **Een mislukte test telt niet mee voor je webhook.** Tests dragen nooit bij aan de automatische uitschakeling na herhaalde fouten zoals beschreven in [Webhook Reliability](#webhook-reliability).

Als de test mislukt, vertelt het bericht u wat uw eindpunt heeft geantwoord (bijvoorbeeld een `404` of `500`), wat meestal voldoende is om een verkeerde URL of een workflow die niet is ingeschakeld te identificeren.

---

## Webhook voor voltooide taak

> **Alleen ter referentie.** Taak-webhooks (als gegevens) zijn hier gedocumenteerd voor ontwikkelaars; de gebeurtenissen **Task Created**, **Task Updated** en **Task Completed** zijn selecteerbaar in de standaard gebeurtenissenlijst op het webhook-formulier zoals elke andere gebeurtenis — zie [Available Trigger Events](#available-trigger-events) en [The 22 Webhook Events](#the-22-webhook-events).

Deze payload wordt verzonden wanneer een taak overgaat naar een fase die is gemarkeerd als een voltooiingsfase. Een taak die tussen niet-voltooiingsfasen beweegt, verzendt in plaats daarvan de `taskUpdated`-vorm.

### Gebeurtenisnaam

`taskCompleted`

### Wanneer deze wordt geactiveerd

- Een taak wordt bijgewerkt.
- De `stage`-waarde is gewijzigd ten opzichte van de vorige waarde.
- De nieuwe fase is geconfigureerd als een voltooiingsfase in de taakfase-instellingen van het account.

### Payload-indeling

```json
{
  "event": "taskCompleted",
  "contact": {
    "email": "jane@example.com",
    "phone_number": "+15551234567",
    "first_name": "Jane",
    "last_name": "Smith",
    "human_alerted": false,
    "human_alert_reason": null
  },
  "user": {
    "email": "owner@example.com",
    "first_name": "Alex",
    "last_name": "Doe"
  },
  "message": {
    "id": "<task-id>",
    "title": "Follow up with Jane",
    "description": "Confirm pricing and send proposal",
    "type": "follow_up",
    "priority": "high",
    "stage": "<stage-id>",
    "due_date": "2026-01-20T15:00:00Z",
    "source": "ai",
    "source_detail": "<source-detail>",
    "campaign_id": "<campaign-id>",
    "linked_human_alert": "<human-alert-id>",
    "tags": ["qualified-lead"],
    "notes": "Customer requested a callback"
  }
}
```

| Veld | Beschrijving |
|---|---|
| `event` | Altijd `taskCompleted` voor deze webhook. Dezelfde payload-vorm wordt verzonden als `taskUpdated` wanneer een taak verandert zonder een voltooiingsfase te bereiken. |
| `contact` | De contactpersoon die aan de taak is gekoppeld, indien van toepassing. `null` indien niet gekoppeld. |
| `contact.human_alert_reason` | De reden waarom de contactpersoon is gemarkeerd voor menselijke aandacht, indien van toepassing. |
| `user` | Basisidentiteitsinformatie voor het account dat de taak bezit. |
| `message.id` | Het unieke ID van de taak. |
| `message.title` / `description` | De titel en beschrijving van de taak. |
| `message.type` | Het taaktype (bijvoorbeeld `follow_up`, `call`, `custom`). |
| `message.priority` | De taakprioriteit (`low`, `medium`, `high`). |
| `message.stage` | Het ID van de fase waarin de taak zich nu bevindt. |
| `message.due_date` | De vervaldatum van de taak, indien ingesteld. |
| `message.source` | Wat de taak heeft aangemaakt (`ai`, `manual`, `api`). |
| `message.source_detail` | Aanvullende details over de bron. |
| `message.campaign_id` | Het ID van de gekoppelde campagne, of `null`. |
| `message.linked_human_alert` | Het ID van de gekoppelde menselijke waarschuwing, indien van toepassing. |
| `message.tags` | Labels die op de taak zijn toegepast. |
| `message.notes` | Vrije notities over de taak. |

---

## Een webhook uitschakelen (of verwijderen)

Elke webhook heeft een aan/uit-schakelaar, direct op de rij. Het **uitschakelen** ervan stopt het ontvangen van gebeurtenissen, maar behoudt alles wat u heeft geconfigureerd — de URL, de gebeurtenissen, elk ondertekeningsgeheim. Schakel het weer in en het gaat verder waar het gebleven was; niets van wat er gebeurde terwijl het uitgeschakeld was, wordt achteraf afgeleverd.

Gebruik dit wanneer je wilt dat bezorgingen tijdelijk stoppen: je endpoint wordt opnieuw opgebouwd, je bent een drukke integratie aan het debuggen of je pauzeert een automatisering.

Het **verwijderen** van een webhook (het prullenbakpictogram op de rij) verwijdert deze definitief, inclusief het ondertekeningsgeheim. Als u alleen wilt dat de leveringen stoppen, schakel deze dan uit — verwijderen is voor wanneer u helemaal klaar bent met het eindpunt.

> **Dit is niet hetzelfde als het automatisch uitschakelen van een webhook.** Als we je webhook uitschakelen na herhaalde fouten (zie [Webhook-betrouwbaarheid](#webhook-reliability)), zal de bovenstaande schakelaar deze niet opnieuw inschakelen. Zodra je eindpunt is hersteld, bewerk je de webhook en sla je deze op met een gewijzigde URL (elke URL-wijziging schakelt deze opnieuw in), of roep je het [opnieuw inschakelen-eindpunt](../api/webhooks.md) aan via de API — of vraag ondersteuning en wij schakelen het weer voor je in.

---

## Ondertekende Payloads (Verifiëren of een webhook echt van ons afkomstig is)

Iedereen die uw webhook-URL leert kennen, kan er een nepverzoek naar sturen. Als u automatisch actie onderneemt op webhooks — zoals facturering bijwerken of CRM-records aanmaken — kunt u door **ondertekening** in te schakelen verifiëren dat elk verzoek daadwerkelijk van ons afkomstig is.

Ondertekening is **optioneel en standaard uitgeschakeld**, en u schakelt dit per webhook in vanuit de bewerkingsweergave van die webhook (open de rij van een opgeslagen webhook).

### Ondertekening inschakelen

1. Open de webhook (Instellingen → Integraties → Webhooks → klik op de rij van uw webhook).
2. Klik in het gedeelte **Ondertekeningsgeheim** op **Genereren**.
3. Kopieer het geheim (het begint met `whsec_`) en sla het op in uw ontvangende systeem. Behandel het als een wachtwoord.

U kunt op elk gewenst moment terugkeren naar dit paneel om het geheim te onthullen, kopiëren, roteren of uit te schakelen.

### Wat we versturen

Zodra ondertekening is ingeschakeld, bevat elke aflevering voor die webhook deze twee extra HTTP-headers:

| Header | Betekenis |
|---|---|
| `X-Webhook-Signature` | De handtekening, in de vorm `v1=<hex>`. |
| `X-Webhook-Timestamp` | Wanneer we het hebben verzonden, als een Unix-tijdstempel in seconden. |

Deze drie staan op **elke** aflevering, ondertekend of niet:

| Header | Betekenis |
|---|---|
| `X-Webhook-Delivery` | Een uniek ID voor deze gebeurtenis. Blijft hetzelfde bij nieuwe pogingen, dus dit is waarop je ontdubbelt. |
| `X-Webhook-Attempt` | Welke poging dit is (`1` is de eerste poging). |
| `X-Webhook-Event` | De naam van de gebeurtenis, zodat je kunt routeren zonder de body te lezen. |

### Hoe te verifiëren

De handtekening is een HMAC-SHA256 van de string `<timestamp>.<raw request body>`, waarbij je ondertekeningsgeheim als sleutel wordt gebruikt.

**Verifieer tegen de onbewerkte request body — de exacte bytes die u heeft ontvangen.** Als uw framework de JSON parseert en opnieuw serialiseert voordat het controleert, kunnen de bytes veranderen en zal de handtekening niet overeenkomen.

Node.js-voorbeeld:

```js
const crypto = require("crypto");

function verify(rawBody, headers, secret) {
  const timestamp = headers["x-webhook-timestamp"];
  const signature = headers["x-webhook-signature"]; // "v1=<hex>"

  // Reject anything older than 5 minutes so a captured request can't be replayed later.
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;

  const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");

  return crypto.timingSafeEqual(Buffer.from(signature.replace("v1=", "")), Buffer.from(expected));
}
```

Python-voorbeeld:

```python
import hashlib, hmac, time

def verify(raw_body: bytes, headers, secret: str) -> bool:
    timestamp = headers["X-Webhook-Timestamp"]
    signature = headers["X-Webhook-Signature"].replace("v1=", "")

    # Reject anything older than 5 minutes so a captured request can't be replayed later.
    if abs(time.time() - int(timestamp)) > 300:
        return False

    expected = hmac.new(secret.encode(), f"{timestamp}.".encode() + raw_body, hashlib.sha256).hexdigest()

    return hmac.compare_digest(signature, expected)
```

> **Vergelijk handtekeningen met een timing-safe functie** (`timingSafeEqual` / `compare_digest`), niet `==`. Het kost niets en voorkomt een subtiele klasse van aanvallen.

### Het geheim roteren

Klik op **Roteer** om het geheim te vervangen. De overschakeling is onmiddellijk: de eerstvolgende levering wordt alleen ondertekend met het nieuwe geheim. Als uw endpoint live is, accepteer dan gedurende enkele minuten **zowel** het oude als het nieuwe geheim terwijl u het nieuwe implementeert.

Het uitschakelen van ondertekening zorgt er simpelweg voor dat de ondertekeningsheaders niet langer worden verzonden.

---

## Mislukte leveringen opnieuw proberen

Standaard wordt een mislukte levering niet opnieuw geprobeerd — als uw systeem op dat moment offline is, gaat die gebeurtenis verloren.

Schakel **Mislukte leveringen opnieuw proberen** in voor een webhook (in het aanmaak-/bewerkingsformulier) en we blijven het proberen:

| Poging | Wanneer |
|---|---|
| 1 | Onmiddellijk |
| 2 | 1 minuut later |
| 3 | 5 minuten later |
| 4 | 30 minuten later |
| 5 | 2 uur later |

Dat beslaat ongeveer **2 uur en 40 minuten**, waardoor een webhook een onderhoudsvenster of een korte storing aan uw kant kan overbruggen.

**Wat wordt opnieuw geprobeerd:** tijdelijke problemen — uw server die een 5xx-fout retourneert, een time-out of een verbindingsfout.

**Wat niet wordt geprobeerd:** als uw endpoint het verzoek zelf afwijst (elke 4xx), proberen we het niet opnieuw — het opnieuw versturen van hetzelfde verzoek zou alleen leiden tot dezelfde afwijzing.

**Welke gebeurtenissen worden opnieuw geprobeerd:** tag-webhooks (`contact_tags_updated`), de drie taakgebeurtenissen en het dagelijkse overzicht. De rest wordt één keer verzonden, dus daarvoor heeft de schakelaar geen functie. Elke gebeurtenis bevat nog steeds `X-Webhook-Delivery`, dus één ontdubbelingsregel dekt ze allemaal.

> **Schakel opnieuw proberen alleen in als uw endpoint idempotent is.** Opnieuw proberen betekent dat hetzelfde evenement meer dan eens kan aankomen. Gebruik de `X-Webhook-Delivery` header om een herhaling te herkennen: deze blijft bij elke poging voor één evenement hetzelfde, zodat u veilig een ID kunt negeren dat u al heeft verwerkt.

Opnieuw proberen (retries) werkt samen met de automatische uitschakeling na herhaalde fouten (zie [Webhook-betrouwbaarheid](#webhook-reliability)) precies zoals je zou willen: de foutteller telt een **volledige aflevering** pas nadat elke poging tot opnieuw proberen is verbruikt — niet elke individuele poging.

---

## Betrouwbaarheid van webhooks

- <span data-t="appName">Your AI Connector</span> verstuurt webhooks via een beveiligde verbinding (HTTPS). Zorg ervoor dat het webadres dat u opgeeft HTTPS gebruikt.
- Als uw systeem een fout retourneert, wordt de bezorging als mislukt beschouwd.
- Houd de uptime van uw ontvangende systeem in de gaten om te voorkomen dat u gebeurtenissen mist.
- Schakel voor kritieke workflows [Mislukte bezorgingen opnieuw proberen](#retrying-failed-deliveries) in en overweeg ook een fallback-mechanisme.

> **Webhooks worden automatisch uitgeschakeld na herhaalde fouten.** Als je webhook-URL herhaaldelijk faalt (ongeveer 5 fouten achter elkaar, of 3 achter elkaar voor configuratiefouten), stopt <span data-t="appName">Your AI Connector</span> automatisch met het verzenden van gebeurtenissen naar die URL. Om deze weer in te schakelen zodra je eindpunt gezond is: bewerk de webhook en sla deze op met een gewijzigde URL (elke URL-wijziging schakelt deze opnieuw in), of gebruik het [opnieuw inschakelen-eindpunt](../api/webhooks.md) via de API — opnieuw opslaan met dezelfde URL is niet voldoende. Ondersteuning kan het ook voor je opnieuw inschakelen.

---

## Probleemoplossing

| Probleem | Oplossing |
|---|---|
| Webhook wordt niet geactiveerd | Controleer eerst of de webhook niet **uitgeschakeld** staat op de betreffende rij. Bevestig vervolgens of de juiste gebeurtenissen zijn geselecteerd en of je URL bereikbaar is vanaf het internet. |
| Testgebeurtenis werkt, maar echte gebeurtenissen niet | Zorg ervoor dat het specifieke gebeurtenistype is ingeschakeld. Als je een verzoek verwachtte wanneer een tag wordt toegepast, houd er dan rekening mee dat `subscribed_to_tags` de gebeurtenissen van een webhook niet beperkt tot een tag — het beperkt alleen welke tags een notificatie voor een gespreks-samenvatting genereren. Om een verzoek te krijgen wanneer een specifieke tag wordt toegepast, stel je een webhook-URL in op die tag in het tabblad **Tags** van de agent (of campagne) — zie [Webhook voor bijgewerkte contact-tags](#contact-tags-updated-webhook). |
| Er komt niets aan in n8n / Make / Zapier | Je gebruikt waarschijnlijk de **Test-URL** van het platform, die alleen luistert naar een enkele gebeurtenis direct nadat je op "Luisteren naar testgebeurtenis" hebt geklikt. Sla voor live-gebeurtenissen de **Productie-URL** op en zet de workflow op **Actief**. |
| Ontvangen van dubbele gebeurtenissen | Controleer op meerdere webhooks die naar dezelfde URL wijzen. Als **Mislukte afleveringen opnieuw proberen** is ingeschakeld, is een herhaling te verwachten wanneer je eindpunt een gebeurtenis heeft geaccepteerd maar niet op tijd heeft geantwoord — ontdubbel op `X-Webhook-Delivery`. |
| Handtekeningcontrole mislukt altijd | Bijna altijd omdat de body opnieuw werd geserialiseerd vóór de controle. Controleer tegen de **ruwe** (raw) request body, onderteken `<timestamp>.<body>`, en bevestig dat je het huidige geheim gebruikt als je onlangs hebt geroteerd. |
| Opnieuw proberen gebeurt niet | Opnieuw proberen staat uit tenzij ingeschakeld op die specifieke webhook. We proberen 4xx-reacties niet opnieuw. |
| Het `campaign` blok is altijd `null` | Verwacht als je account agents gebruikt: contacten zitten bij een agent in plaats van bij een campagne. Lees in plaats daarvan het `agent` blok — zie [Webhook-dataformaat](#webhook-data-format). |
| Data is leeg of misvormd | Controleer of je ontvangende systeem JSON accepteert. Controleer je serverlogs op parseerfouten. |
| Webhook-URL geeft fouten terug | Test je URL met een tool zoals Postman of [webhook.site](https://webhook.site). |
| Webhook stopte volledig met werken na een storing | Herhaalde fouten schakelen een webhook automatisch uit. Opnieuw opslaan schakelt deze niet opnieuw in — repareer je eindpunt en neem daarna contact op met de ondersteuning. |
| Opslaan of testen geeft een toestemmingsfout | Je hebt de "bewerken"-toestemming voor integraties nodig. Vraag de accounteigenaar om deze te verlenen. |
| De `subscribed_to_tags` lijst van een webhook kwam leeg terug | `subscribed_to_tags` beperkt de gebeurtenissen van een webhook niet tot een tag — het beperkt alleen welke tags een notificatie voor een gespreks-samenvatting genereren. Bewerken via het webhook-formulier wist die lijst niet langer (gefixed op 21 juli 2026). Als een webhook zijn lijst vóór die datum is verloren, stel `subscribed_to_tags` dan opnieuw in via de [Webhooks API](../api/webhooks.md) — zie [Tag-gebaseerde webhook-triggers](#tag-based-webhook-triggers). |

---

## Volgende stappen

- [GoHighLevel-integratie](ghl-integration.md) — gebruik webhooks om <span data-t="appName">Your AI Connector</span> te integreren met GHL.
- [API-toegang](api-access.md) — combineer webhooks met de API voor krachtige automatiseringen.
- [Tags gebruiken om contacten te labelen](../get-started/creating-tags.md) — stel tags in die uw webhooks activeren.
