Documentation
Inbound webhooks
Antwoorden van je gekoppelde WhatsApp-nummer realtime ontvangen. Drop-in compatibel met TextMeBot — bestaande integraties die type / from / from_name / to / file / message verwerken werken zonder aanpassingen.
1. Endpoint configureren
Open /admin/webhook in het customer portal:
- Plak je publieke HTTPS URL.
- Klik Opslaan — er wordt automatisch een 64-char HMAC-secret gegenereerd. Kopieer hem direct, hij wordt nadien gemaskeerd.
- Klik Test fire om een sample-payload naar je endpoint te sturen.
2. Payload (TextMeBot-compatibel)
Bij elk inkomend bericht doet TextMeFlow een POST met content-type application/json en deze body:
{
"type": "text",
"from": "32472932208",
"from_name": "Jan Janssens",
"to": "32499123456",
"file": "null",
"message": "Bedankt voor de bevestiging!"
}
| Veld | Type | Inhoud |
|---|---|---|
| type | string | text, image, audio, video, document of location |
| from | string | Afzender, alleen cijfers (geen +) |
| from_name | string | WhatsApp pushName, mogelijk leeg |
| to | string | Jouw gekoppelde nummer, alleen cijfers |
| file | string | Voor text-berichten letterlijk de string "null" (matcht TextMeBot). Media-URL volgt in een latere release. |
| message | string | Tekst of bijschrift; voor document de filename, voor location de string "lat,lng" |
3. Headers
| Header | Inhoud |
|---|---|
| Content-Type | application/json |
| User-Agent | TextMeFlow-Webhook/1.0 |
| X-TextMeFlow-Event-Id | UUID per bericht — gebruik dit als idempotency-key, retries hergebruiken hetzelfde id. |
| X-TextMeFlow-Signature | sha256=<hex> — HMAC-SHA256 van de raw body met je webhook-secret. |
| X-TextMeFlow-Delivery | UUID per delivery-poging, handig voor log-grep. |
4. Signature verifiëren
Bereken HMAC-SHA256 over de raw request body (niet over een geparseerd object — JSON-encoder verschillen breken anders je hash) en vergelijk constant-time met de sha256=… waarde uit de header.
PHP
// $secret = 'tmf_…' uit /admin/webhook $raw = file_get_contents('php://input'); $expected = 'sha256=' . hash_hmac('sha256', $raw, $secret); $received = $_SERVER['HTTP_X_TEXTMEFLOW_SIGNATURE'] ?? ''; if (! hash_equals($expected, $received)) { http_response_code(401); exit; } $body = json_decode($raw, true);
Node.js (Express)
import crypto from 'node:crypto'; import express from 'express'; const app = express(); // Capture raw body for HMAC; can't run after express.json() ate it. app.use(express.raw({ type: 'application/json' })); app.post('/textmeflow-inbound', (req, res) => { const raw = req.body; // Buffer const expected = 'sha256=' + crypto .createHmac('sha256', process.env.TMF_WEBHOOK_SECRET) .update(raw) .digest('hex'); const received = req.get('X-TextMeFlow-Signature') ?? ''; if ( expected.length !== received.length || !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received)) ) { return res.sendStatus(401); } const body = JSON.parse(raw.toString('utf8')); // …handle body… res.sendStatus(200); });
Python (Flask)
import hmac, hashlib, os from flask import Flask, request, abort app = Flask(__name__) SECRET = os.environ['TMF_WEBHOOK_SECRET'].encode() @app.post('/textmeflow-inbound') def inbound(): raw = request.get_data() expected = 'sha256=' + hmac.new(SECRET, raw, hashlib.sha256).hexdigest() received = request.headers.get('X-TextMeFlow-Signature', '') if not hmac.compare_digest(expected, received): abort(401) body = request.get_json() # …handle body… return '', 200
5. Retry-policy
Een delivery is gelukt bij elke 2xx response binnen 10s. Andere uitkomsten:
| Response | Gedrag |
|---|---|
| 2xx | ✅ delivered, geen retry. |
| 408 / 429 / 5xx | 🔁 retry — max 5 keer met backoff 1m, 5m, 30m, 2h, 6h. |
| andere 4xx | ❌ failed, geen retry. Een 401 betekent meestal dat je secret niet meer matcht — controleer en fix bij jouw kant. |
| timeout / netwerk | 🔁 retry zoals 5xx. |
Na 6 totale pogingen (eerste + 5 retries) wordt de delivery als failed gemarkeerd. Status van elke poging zie je in /admin/webhook.
6. Migreren vanaf TextMeBot
Geen code-wijzigingen nodig — payload-shape is identiek. Eén actie:
- Plak je bestaande TextMeBot-endpoint URL in /admin/webhook.
- Voeg een signature-check toe (TextMeBot heeft die niet — verplichte upgrade voor productie). Zie de snippets hierboven.
Klaar om te ontvangen?
Configureer je endpoint in /admin/webhook en stuur een test-payload met één klik.