· 4 min · TextMeFlow Team

WhatsApp API webhooks lokaal testen met ngrok

Je wilt een nieuwe WhatsApp-integratie bouwen — een bot, een CRM-koppeling, een notificatiestroom — maar je server draait op localhost. TextMeFlow kan daar niets naar versturen: webhooks zijn uitgaande POST-requests vanaf onze servers naar een publiek bereikbare URL. Tot je code op een echte server staat, heb je een tunnel nodig. Dit artikel laat zien hoe je dat met ngrok doet, zonder dat je per ongeluk een halfafgewerkte endpoint in productie laat staan.

Waarom je lokaal al moet testen

Je zou kunnen wachten tot je endpoint gedeployed is om te zien of de payload klopt, maar dat is traag: elke wijziging vereist een nieuwe deploy om te zien of je JSON-parsing, je signature-check en je businesslogica samen werken. Lokaal testen met een echte, live webhook — in plaats van een handmatig in elkaar geknutselde test-payload — vangt fouten die je anders pas in productie ziet: afwijkende headers, de exacte vorm van from_name bij groepsberichten, of een media-bericht met een file-veld dat je niet had voorzien.

Stap 1: start ngrok

Installeer ngrok (brew install ngrok of download van hun site) en start een tunnel naar de poort waarop je lokale server draait:

ngrok http 3000

Ngrok geeft je een publieke URL zoals https://a1b2c3d4.ngrok-free.app die forwardt naar http://localhost:3000. Die URL verandert bij elke herstart op het gratis plan — hou daar rekening mee als je de volgende stap meermaals doet.

Stap 2: configureer de webhook tijdelijk

Ga naar /admin/webhook en zet de ngrok-URL in als endpoint, bijvoorbeeld https://a1b2c3d4.ngrok-free.app/webhooks/whatsapp. Zodra je dat opslaat, krijg je het webhook secret te zien — kopieer het meteen, want het wordt daarna gemaskeerd. Stuur jezelf een test-WhatsApp-bericht naar je gekoppelde nummer en kijk of de request op je lokale server toekomt.

Behandel die ngrok-configuratie als wegwerpwerk: zodra je klaar bent met testen, zet je het endpoint terug naar je echte productie-URL. Een vergeten ngrok-tunnel die na een laptop-herstart niet meer bestaat, is de snelste manier om stilletjes alle inkomende berichten te verliezen.

Stap 3: verifieer de signature, ook lokaal

De verleiding is groot om de HMAC-check over te slaan "want het is maar lokaal testen" — maar dan test je niet wat er echt in productie gebeurt. Lees de raw body voordat enige JSON-middleware erbij komt, en controleer X-TextMeFlow-Signature meteen:

const crypto = require('crypto');
const express = require('express');
const app = express();

app.post('/webhooks/whatsapp', express.raw({ type: 'application/json' }), (req, res) => {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', process.env.TMF_WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers['x-textmeflow-signature'] || ''))) {
    return res.sendStatus(401);
  }

  const payload = JSON.parse(req.body);
  console.log(payload);
  res.sendStatus(200);
});

app.listen(3000);

Meer details over de headers, retry-logica en de veiligste manier om de signature te vergelijken staan in het artikel over webhook signatures.

Stap 4: gebruik de ngrok-inspector om te herspelen

Ngrok's lokale webinterface op http://127.0.0.1:4040 logt elke request die binnenkomt, inclusief headers en body. Dat is handig om twee dingen te doen zonder telkens een nieuw WhatsApp-bericht te moeten versturen:

  • De exacte payload bekijken die TextMeFlow stuurde, om je parsing-code daarop te schrijven.
  • Dezelfde request opnieuw afspelen ("Replay") nadat je een bugfix hebt gedeployed op je lokale server, zonder opnieuw een bericht te moeten sturen.

Let wel op: een replay via de ngrok-inspector hergebruikt dezelfde X-TextMeFlow-Event-Id. Als je idempotency-logica op dat veld gebaseerd is — wat we aanraden — zal een replay dus terecht genegeerd worden door je eigen code. Dat is geen bug, dat is je dedup-logica die werkt.

Veelgemaakte fouten

De webhook blijft op "pending" staan of faalt steeds: controleer of je endpoint binnen 10 seconden een 2xx teruggeeft. Zware verwerking (een CRM bijwerken, een externe API aanroepen) voer je na die response uit, niet ervoor — anders lijkt je lokale server op een timeout en krijg je onnodige retries met de backoff van 1m, 5m, 30m, 2u en 6u.

Een tweede klassieke fout: de ngrok-URL wisselt bij elke herstart (gratis plan), maar het webhook-secret blijft hetzelfde zolang je het endpoint niet opnieuw aanmaakt. Als je de URL in /admin/webhook bijwerkt maar je lokale .env nog het oude secret heeft staan, krijg je overal 401's — dat is zelden "een aanval", meestal gewoon een niet-gesynchroniseerd secret.

Klaar om live te gaan

Zodra je endpoint lokaal correct signeert, parseert en binnen de 10 seconden antwoordt, hoef je alleen de URL in /admin/webhook te vervangen door je productie-adres. Alle TextMeFlow-infrastructuur draait in de EU (Parijs), dus ook je test- en productieverkeer blijft binnen de EU. Nog geen account? Het gratis plan geeft je 50 berichten per maand, voor altijd — genoeg om deze hele flow end-to-end te testen. Start gratis.

Zelf WhatsApp-berichten versturen via API?

Gratis voor altijd tot 50 berichten/maand. QR scannen en binnen 5 minuten verstuur je je eerste bericht.

Gratis voor altijd