Support / Meetbaar maken

Webhooks instellen en beveiligen

Met een webhook stuurt LeadBot elke nieuwe lead automatisch door naar je CRM, datawarehouse of eigen applicatie. In dit artikel maak je een webhook aan, controleer je de handtekening en los je mislukte bezorgingen op.

Wat is een webhook?

Een webhook is een automatisch HTTP POST-verzoek dat LeadBot naar een URL van jouw systeem stuurt zodra er iets gebeurt, bijvoorbeeld wanneer een bezoeker een formulier verstuurt. Het verzoek bevat de lead als JSON: de contactgegevens, de LeadBot en de pagina waarop de bezoeker de actie uitvoerde.

Nieuwe leads zie je normaal in je Inbox en in je e-mailnotificaties. Wil je ze ook automatisch in een ander systeem verwerken, dan regel je dat met een webhook: LeadBot pusht de gegevens direct naar jouw systeem, zonder handwerk en zonder vertraging. Typische toepassingen:

Voor een webhook heb je een publiek bereikbare URL nodig (bij voorkeur HTTPS) die JSON-POST-verzoeken kan ontvangen. Het bouwen van zo'n endpoint is werk voor een developer; de rest van dit artikel is daarom technischer dan onze andere artikelen.

  • CRM — maak van elke inzending automatisch een lead of contact aan.
  • Automatisering — start een eigen workflow na een formulier, WhatsApp-bericht, terugbelverzoek of e-mailverzoek.
  • Rapportage — stuur conversies door naar een datawarehouse of dashboard.

Webhook aanmaken

Webhooks beheer je in het LeadBot-dashboard. Eén webhook kan naar meerdere events luisteren; maak voor verschillende ontvangende systemen aparte webhooks aan, dan houd je de instellingen en bezorglogs gescheiden.

Bij het aanmaken vul je alleen een naam, de URL, een omschrijving en de events in — er is geen veld voor een secret. LeadBot genereert die dus altijd zelf. Je vindt de secret terug op de detailpagina van de webhook, onder Geavanceerde instellingen, en daar kun je hem ook aanpassen. Een eigen secret meteen meegeven kan alleen via de API. Behandel hem als een wachtwoord: zet hem op je server in een omgevingsvariabele of secrets manager, nooit in je broncode.

  1. 1 Ga in de zijbalk naar Webhooks en klik op Webhook toevoegen.
  2. 2 Geef de webhook een herkenbare naam en vul de URL van je endpoint in.
  3. 3 Selecteer de events die je wilt ontvangen.
  4. 4 Sla de webhook op.
  5. 5 Open de webhook en klik op Webhook testen om een voorbeeldlead te versturen.
  6. 6 Controleer je eigen serverlog en het tabblad Bezorginformatie van de webhook.

Beschikbare events

Selecteer alleen de events die je echt nodig hebt. In elk verzoek zie je aan het veld event in de JSON en aan de header X-Webhook-Event welk event het verzoek veroorzaakte.

  • visitor.form_submission — een bezoeker verstuurt een formulier.
  • visitor.whatsapp_message — een bezoeker verstuurt een WhatsApp-bericht via LeadBot.
  • visitor.callback_request — een bezoeker vraagt om teruggebeld te worden.
  • visitor.email_request — een bezoeker vraagt om contact per e-mail.
  • visitor.ai_chat — een AI-chatgesprek is afgerond. In het dashboard heet dit event 'AI-chat afgerond'. Het wordt eenmaal per gesprek verstuurd, nadat de chat vijf minuten stil is geweest; elk nieuw bericht start die wachttijd opnieuw. Dit event heeft een andere payload dan de vier events hierboven — zie 'Payload van visitor.ai_chat' verderop.

Voorbeeld van de payload (interactie-events)

De vier interactie-events hieronder — form_submission, whatsapp_message, callback_request en email_request — hebben allemaal dezelfde payload. Elke payload heeft een uniek event-id, het eventtype en het tijdstip. Contactvelden kunnen null zijn als de bezoeker ze niet heeft ingevuld. De velden page_url, leadbot_id, leadbot_name en form_data zitten er altijd in; leadbot_id en leadbot_name zijn null wanneer de interactie geen LeadBot heeft. De sleutels verdwijnen dus nooit, zodat de vorm van de payload bij elke bezorging gelijk is — daar rekenen onder andere Zapier-triggers op.

Het veld created is een ISO 8601-tijdstempel met microseconden en een expliciete UTC-offset, bijvoorbeeld 2026-07-16T09:42:13.123456+00:00. Er zit dus geen 'Z' op het eind; gebruik een volwaardige ISO 8601-parser.

In form_data gebruikt LeadBot de zichtbare veldlabels uit je flow als keys. Komt hetzelfde label vaker voor, dan krijgt een volgende key een nummer, bijvoorbeeld Interesse_2. Houd er in je integratie rekening mee dat deze keys mee veranderen wanneer iemand de labels in de flow aanpast.

{
  "id": "f56fb9f5-34b5-4a98-a201-fcfd2061b023",
  "event": "visitor.form_submission",
  "created": "2026-07-16T09:42:13.123456+00:00",
  "email": "bezoeker@example.com",
  "phone": "+31612345678",
  "name": "Robin de Vries",
  "is_qualified": true,
  "hostname": "example.com",
  "page_url": "https://example.com/contact",
  "leadbot_id": 123,
  "leadbot_name": "Contact",
  "form_data": {
    "E-mailadres": "bezoeker@example.com",
    "Vraag": "Ik ontvang graag meer informatie"
  }
}

Payload van visitor.ai_chat

Het event visitor.ai_chat heeft een eigen payload. De event-, contact- en contextvelden zijn hetzelfde, maar in plaats van form_data krijg je de gegevens van het gesprek: conversation_id, started_at, last_message_at, message_count, transcript (het hele gesprek als platte tekst) en messages (dezelfde berichten als lijst, met role 'visitor' of 'assistant'). Verwerk je dit event, bouw daar dan een apart pad voor: code die form_data verwacht, loopt hierop vast.

LeadBot verstuurt het event nadat het gesprek vijf minuten stil is geweest, met de transcriptie tot dat moment. Gaat de bezoeker daarna verder in hetzelfde gesprek, dan volgt er later een tweede bezorging met dezelfde conversation_id en de aangevulde transcriptie.

{
  "id": "0f4a2f1c-2f7e-4a3c-9a0b-6a71b8e7c2d1",
  "event": "visitor.ai_chat",
  "created": "2026-07-16T09:47:31.884210+00:00",
  "email": "bezoeker@example.com",
  "phone": null,
  "name": null,
  "is_qualified": false,
  "hostname": "example.com",
  "page_url": "https://example.com/prijzen",
  "conversation_id": "3b0f9d2a-5f4c-4a1e-8f61-0c2a3f9a77bd",
  "started_at": "2026-07-16T09:41:02.117043+00:00",
  "last_message_at": "2026-07-16T09:42:31.884210+00:00",
  "message_count": 4,
  "transcript": "Visitor: Wat kost het?\n\nAssistant: Dat hangt af van je pakket.",
  "messages": [
    { "role": "visitor", "message": "Wat kost het?" },
    { "role": "assistant", "message": "Dat hangt af van je pakket." }
  ],
  "leadbot_id": 123,
  "leadbot_name": "Contact"
}

Headers en dubbele bezorgingen

LeadBot stuurt bij elk verzoek Content-Type: application/json en User-Agent: Leadbot-Webhooks/1.0 mee, plus drie eigen headers.

Een mislukte bezorging wordt later opnieuw geprobeerd met dezelfde X-Webhook-ID. Bewaar de waarden die je al verwerkt hebt en sla een verzoek met een bekende waarde over; zo belandt dezelfde lead nooit twee keer in je systeem. Let op het verschil: het veld id in de JSON identificeert het event, X-Webhook-ID identificeert de bezorging aan deze specifieke webhook.

  • X-Webhook-Signature-256 — HMAC-SHA256-handtekening van de payload, in de vorm sha256=<hexadecimale waarde>.
  • X-Webhook-Event — het eventtype, bijvoorbeeld visitor.form_submission.
  • X-Webhook-ID — unieke ID van deze bezorging; gebruik deze om dubbele verwerking te voorkomen.

Handtekening controleren

Elke bezorging is ondertekend met een HMAC-SHA256-handtekening, berekend over de JSON-body met jouw secret als sleutel. Door de handtekening te controleren weet je zeker dat het verzoek echt van LeadBot komt. Verwerk de payload pas na een geslaagde controle en antwoord met status 401 als de controle mislukt.

Bereken de handtekening altijd over de ongewijzigde, ruwe request body en parse de JSON pas daarna: opnieuw serialiseren kan spaties of veldvolgorde veranderen en levert dan een andere handtekening op. Stel je framework zo in dat de raw body beschikbaar blijft en vergelijk met een timing-safe functie, zoals in dit Node.js-voorbeeld.

import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyLeadBotWebhook(rawBody, signatureHeader, secret) {
  const supplied = signatureHeader?.replace(/^sha256=/, "") ?? "";
  const expected = createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  const suppliedBuffer = Buffer.from(supplied, "hex");
  const expectedBuffer = Buffer.from(expected, "hex");

  return suppliedBuffer.length === expectedBuffer.length &&
    timingSafeEqual(suppliedBuffer, expectedBuffer);
}

Snel antwoorden en opnieuw proberen

Antwoord zo snel mogelijk met een statuscode van 200 tot en met 299; elke andere uitkomst telt als mislukt. Wacht niet tot je CRM of een andere trage dienst klaar is: controleer de handtekening, zet het werk in een wachtrij en stuur direct je antwoord.

Mislukt een bezorging door een time-out, netwerkfout of foutstatus, dan probeert LeadBot het standaard nog twee keer: na 5 en na 10 minuten. In totaal zijn dat dus drie afleverpogingen. Op de detailpagina van de webhook pas je dit onder Geavanceerde instellingen aan met de velden Aantal pogingen (0 tot 10, waarbij de eerste poging meetelt) en Time-out per verzoek (1 tot 300 seconden). De wachttijd voor een nieuwe poging is steeds 5 minuten maal het aantal pogingen dat al is gedaan. Omdat een bezorging dus meerdere keren kan binnenkomen, is het belangrijk dat je verwerking idempotent is. Zo bouw je dat op:

  1. 1 Lees de ruwe request body en de headers.
  2. 2 Controleer de handtekening in X-Webhook-Signature-256.
  3. 3 Controleer of je de X-Webhook-ID al eerder hebt verwerkt.
  4. 4 Sla het verzoek op of zet het in een interne wachtrij.
  5. 5 Antwoord met een 2xx-statuscode.
  6. 6 Verwerk de lead daarna verder in je eigen systeem.

Bezorginformatie en problemen oplossen

Open een webhook en ga naar het tabblad Bezorginformatie. Daar zie je per bezorging het event, de status, de HTTP-statuscode, het aantal pogingen en het tijdstip, en waar beschikbaar de response of foutmelding. Bezorglogs worden 30 dagen bewaard.

Loopt het aantal mislukte bezorgingen te ver voor op het aantal geslaagde, dan zet LeadBot de webhook op Inactief en ontvangt je endpoint geen nieuwe events meer. Dat gebeurt zodra het verschil groter is dan tien. LeadBot kijkt daarbij naar de totalen over de hele levensduur van de webhook, niet naar het aantal fouten op rij: een webhook die afwisselend slaagt en faalt kan dus ook uitvallen, terwijl een webhook met veel geslaagde bezorgingen in het verleden een reeks fouten juist overleeft. Los het probleem op en klik op Webhook testen: na een geslaagde bezorging staat de webhook automatisch weer op Actief. De meest voorkomende problemen:

  • 401 of 403 — controleer of je de juiste secret gebruikt en de handtekening over exact de ruwe body berekent.
  • 404 — controleer het volledige pad van de URL en of je endpoint POST-verzoeken accepteert.
  • Timeout — antwoord eerder met 2xx en verplaats trage verwerking naar een wachtrij.
  • 5xx — bekijk je eigen serverlogs; LeadBot probeert het opnieuw zolang er pogingen over zijn.
  • Geen bezorging — controleer of de webhook op Actief staat, het juiste event geselecteerd is en je in de juiste organisatie werkt.

Kom je er niet uit?

Geen ticketnummer — je spreekt altijd een mens. We denken graag met je mee, van installatie tot optimalisatie.

Het LeadBot-team aan het werk op kantoor