Je CRM koppelen met een webhook
Na elk gesprek stuurt VoiceHelden een ondertekend bericht naar een adres dat jij instelt. Zo komt elk gesprek in het systeem dat je al gebruikt, via Zapier, Make of n8n.
Waar je hem instelt. In je dashboard onder Koppelingen staat de kaart Webhook (CRM): adres invullen, opslaan, het ondertekeningsgeheim één keer kopiëren, en met de testknop controleren of je systeem het bericht ontvangt. Zie je die kaart nog niet, dan is de koppeling voor jouw account nog niet aangezet; mail dan info@voicehelden.nl. Hieronder staat versie 1 van het bericht; een volgende versie voegt hooguit velden toe en haalt er geen weg.
Wat je nodig hebt
- Een https-URL die een POST aanneemt en binnen tien seconden antwoordt met een statuscode in de 200-reeks. Antwoord eerst, verwerk daarna — anders loop je tegen de time-out aan.
- Een secret. Dat zou je van ons krijgen, één keer. Bewaar het zoals je een wachtwoord bewaart: wie het heeft, kan een bericht namaken.
- Iets wat de handtekening narekent. Zapier, Make en n8n kunnen dat alle drie; zie de recepten onderaan.
Het bericht
Na elk afgerond gesprek gaat er één POST naar jouw URL, met deze headers:
Content-Type | application/json |
|---|---|
X-VoiceHelden-Event | call.completed |
X-VoiceHelden-Delivery | een uuid; bij een herhaalpoging hetzelfde |
X-VoiceHelden-Timestamp | unix-tijd in seconden |
X-VoiceHelden-Signature | sha256=<hex> — HMAC-SHA256 over timestamp + "." + body |
En dit is de body. De waarden hieronder zijn verzonnen; de veldnamen zijn dat niet.
{
"event": "call.completed",
"version": 1,
"delivery_id": "b1f0c6e2-8f1a-4a35-9a0e-2f5b7c3d9e41",
"sent_at": "2026-09-08T09:14:07Z",
"account_id": "kZ8yQ2mVbNfR7pLx3TcW",
"agent_id": "agent_01",
"call": {
"id": "call_9f3a2b7c",
"started_at": "2026-09-08T09:11:52Z",
"ended_at": "2026-09-08T09:13:58Z",
"duration_seconds": 126,
"direction": "inbound",
"from": "+31612345678",
"to": "+31851234567",
"outcome": "appointment"
},
"caller": {
"name": "Jeroen Bakker",
"phone": "+31612345678",
"email": null,
"reason": "Lekkage onder de gootsteen"
},
"summary": "Beller meldt een lekkage onder de gootsteen en wil er zo snel mogelijk iemand bij hebben. Afspraak gezet op dinsdag 9 september om 10:00.",
"transcript_url": "https://app.voicehelden.nl/calls/call_9f3a2b7c",
"appointment": {
"starts_at": "2026-09-09T08:00:00Z",
"ends_at": "2026-09-09T08:30:00Z",
"calendar_event_id": "5k1q7v9m3b2n8x0c"
}
}
Wat de velden betekenen
version— nu altijd1. Verandert de vorm van het bericht, dan gaat dit getal omhoog en blijft versie 1 bestaan zolang er iemand op luistert.call.outcome— één vanappointment,message,transfer,no_action. Hierop stuur je je automatisering.call.direction— nu altijdinbound. De assistent belt niet zelf.caller— alles wat de beller uit zichzelf heeft gezegd. Elk veld kannullzijn; iemand die zijn naam niet noemt, heeft geen naam. Bouw je koppeling zo dat een leeg veld geen fout is.appointment—nullals er geen afspraak is gezet.calendar_event_idisnullals er geen agenda gekoppeld is; het gesprek en de samenvatting komen dan gewoon binnen.agent_id—nullzolang je nog met de standaardassistent werkt.- Tijden staan in ISO 8601 in UTC. Reken zelf om naar Europe/Amsterdam als je ze aan iemand laat zien.
De handtekening controleren
Doe dit vóór je iets met het bericht doet. Zonder controle is je URL een formulier dat iedereen kan invullen die hem kent.
Reken de HMAC over de ruwe body, niet over de body die je framework al heeft geparseerd en weer heeft omgezet naar tekst. Eén spatie verschil en de handtekening klopt niet meer.
ruw = de bytes van de body, precies zoals ze binnenkwamen
tijd = header["X-VoiceHelden-Timestamp"]
basis = tijd + "." + ruw
verwacht = "sha256=" + hex(hmac_sha256(secret, basis))
als NIET constante_tijd_gelijk(verwacht, header["X-VoiceHelden-Signature"]):
antwoord 401 en stop
als abs(nu_in_seconden() - tijd) > 300:
antwoord 401 en stop # ouder dan vijf minuten: opnieuw afgespeeld
verwerk(json_parse(ruw))
antwoord 200
De vergelijking van vijf minuten is onze aanbeveling, geen onderdeel van het bericht: hij houdt tegen dat iemand een geldig bericht van vorige week nog eens naar je toe stuurt.
Als je niet antwoordt
Komt er geen antwoord in de 200-reeks, of helemaal geen antwoord, dan proberen we het opnieuw na 1 minuut, 10 minuten, 1 uur, 6 uur en 24 uur. Daarna stoppen we; dat zijn maximaal vijf pogingen.
- Elke poging draagt dezelfde
delivery_id. Sla die op en negeer een bericht dat je al kent, anders staat dezelfde afspraak twee keer in je CRM. - Antwoord je met HTTP 410, dan zetten we de webhook uit en proberen we het niet opnieuw. Gebruik dat als je de koppeling definitief opruimt.
- Een 4xx die geen 410 is, telt als een mislukte poging en wordt herhaald.
Het testbericht
Bij het aanzetten zou er één bericht komen met er bovenin een extra veld:
"test": true— verder is het bericht identiek van vorm.
Laat je automatisering daarop stoppen, anders staat er straks een verzonnen lekkage in je CRM. In n8n is dat een IF-node, in Zapier een filter.
Recept: n8n
- Nieuwe workflow, node Webhook, methode
POST. Zet Respond op Immediately zodat n8n binnen tien seconden antwoordt. - Zet in dezelfde node onder Options de optie Raw Body aan. Zonder de ruwe body kun je de handtekening niet narekenen.
- Node Crypto → Action Hmac, Type SHA256, Encoding hex. Value: de timestamp-header, een punt, en de ruwe body. Secret: uit een credential, niet in de node getypt.
- Node IF: vergelijk
sha256=plus de uitkomst met de headerx-voicehelden-signature. Ongelijk → einde workflow. - Tweede IF op
test: is die waar, stop dan ook. - Node Switch op
call.outcome, en per uitkomst je eigen vervolg — een CRM-node, of een HTTP Request naar je eigen API. - Kopieer de Production URL (niet de test-URL, die luistert maar één keer) en mail hem naar ons.
Recept: Zapier
- Nieuwe Zap, trigger Webhooks by Zapier → Catch Raw Hook. Kies bewust de raw variant: de gewone Catch Hook parseert de body, en daarna klopt je handtekening niet meer.
- Kopieer de Custom Webhook URL en mail hem naar ons. Wij sturen het testbericht, zodat Zapier de velden leert kennen.
- Stap Code by Zapier (JavaScript): reken de HMAC na met de ingebouwde
crypto-module en gooi een fout als hij niet klopt. Zet het secret in een input data-veld, niet in de code. - Stap Filter by Zapier: alleen doorgaan als
testontbreekt, en bijvoorbeeld alleen alsoutcomegelijk is aanappointment. - Laatste stap: je CRM.
Recept: Make
Het patroon is hetzelfde: Webhooks → Custom webhook, met Get request headers aan en zonder automatische JSON-parsing, zodat de ruwe body bewaard blijft. De handtekening reken je na in een Tools → Set variable-stap met de sha256-functie, en pas daarna zet je de body om met JSON → Parse JSON.
Wat er niet in zit
- Geen audiobestand. Het bericht draagt een samenvatting en een link, geen opname.
- Geen uitgaande oproepen. De webhook meldt wat er is gebeurd; hij kan de assistent niet laten bellen.
- Geen e-mail of WhatsApp naar jou na een gesprek — zie Gesprekken terugzien. Zolang deze webhook niet is gebouwd, komt een gesprek nergens automatisch terecht: je leest het terug in je dashboard.
Kom je er niet uit? Mail info@voicehelden.nl of gebruik de contactpagina. Zet je 085-nummer erbij, dan kunnen we meteen meekijken.
Daarna vanaf €79 per maand, excl. btw. Kaart vooraf, opzeggen kan altijd.