12 · webhooks
Webhooks : recevoir et émettre des événements
Deux sens : vos systèmes déclenchent l'agent (entrant), et la plateforme vous prévient des transferts et des fins de conversation (sortant). Dans les deux cas, chaque message est signé en HMAC-SHA256 sur le corps brut.
- Entrant : POST /api/v1/webhooks/{tenantId}/{sourceId}. Créez une source (un secret est généré et affiché une seule fois), puis un déclencheur : type d'événement → bot → canal (WhatsApp ou SMS) → modèle de message.
- En-têtes : X-Webhook-Signature: sha256=<hex> (ou X-Hub-Signature-256), X-Webhook-Timestamp (anti-rejeu, tolérance de 300 s par défaut), X-Webhook-Id (idempotence).
- Le type d'événement est lu dans le champ type, event ou event_type du JSON. Réponse 202 { ok, matched } ; toute signature invalide renvoie 401 sans détail.
- Sortant : Paramètres → Webhooks. Événements : handoff.opened, handoff.taken, handoff.closed, conversation.closed.
- Chaque appel sortant est un POST JSON { event, at, … } avec les en-têtes X-Recla-Event et, si un secret est défini, X-Recla-Signature: sha256=<hex>. Délai de 8 s, réponse 2xx attendue ; le dernier statut est visible dans l'interface.
- Le bouton « Tester » envoie un événement webhook.test. URL en http ou https, sans identifiants intégrés.
Webhook entrant — signer et envoyer (cURL)
BODY='{"type":"order.shipped","id":"evt_42","msisdn":"+2250700000000","order":"CMD-2291"}'
SECRET="secret-de-la-source"
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')
curl -X POST https://api.votredomaine.com/api/v1/webhooks/TENANT_ID/SOURCE_ID \
-H "Content-Type: application/json" \
-H "X-Webhook-Signature: sha256=$SIG" \
-H "X-Webhook-Timestamp: $(date +%s)" \
-H "X-Webhook-Id: evt_42" \
-d "$BODY"Webhook sortant — vérifier la signature (Node.js)
import crypto from "node:crypto";
import express from "express";
const app = express();
// Il faut le corps BRUT : la signature porte sur les octets reçus.
app.post("/recla-webhook", express.raw({ type: "application/json" }), (req, res) => {
const expected = "sha256=" + crypto.createHmac("sha256", process.env.WEBHOOK_SECRET).update(req.body).digest("hex");
const received = req.get("X-Recla-Signature") ?? "";
const ok = received.length === expected.length && crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
if (!ok) return res.sendStatus(401);
const { event, at, ...data } = JSON.parse(req.body.toString());
console.log(req.get("X-Recla-Event"), event, at, data);
res.sendStatus(200); // 2xx attendu sous 8 secondes
});
app.listen(3000);