Product Toepassingen Word partner Prijzen Helpcentrum Contact Demo boeken English
Mail ons Probeer 30 dagen gratis

Daarna vanaf €79 per maand, excl. btw. Kaart vooraf, opzeggen kan altijd.

Helpcentrum

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-Typeapplication/json
X-VoiceHelden-Eventcall.completed
X-VoiceHelden-Deliveryeen uuid; bij een herhaalpoging hetzelfde
X-VoiceHelden-Timestampunix-tijd in seconden
X-VoiceHelden-Signaturesha256=<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 altijd 1. Verandert de vorm van het bericht, dan gaat dit getal omhoog en blijft versie 1 bestaan zolang er iemand op luistert.
  • call.outcome — één van appointment, message, transfer, no_action. Hierop stuur je je automatisering.
  • call.direction — nu altijd inbound. De assistent belt niet zelf.
  • caller — alles wat de beller uit zichzelf heeft gezegd. Elk veld kan null zijn; iemand die zijn naam niet noemt, heeft geen naam. Bouw je koppeling zo dat een leeg veld geen fout is.
  • appointmentnull als er geen afspraak is gezet. calendar_event_id is null als er geen agenda gekoppeld is; het gesprek en de samenvatting komen dan gewoon binnen.
  • agent_idnull zolang 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 header x-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 ZapierCatch 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 test ontbreekt, en bijvoorbeeld alleen als outcome gelijk is aan appointment.
  • 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.