Recevoir et vérifier les événements
Les webhooks transmettent les changements d’état reçus par la plateforme. Ce guide décrit la création d’un endpoint, la signature et le payload avec l’API REST et les primitives cryptographiques de Node.js. Le SDK npm public n’est pas encore disponible.
Règles de base
Les callbacks utilisent une signature HMAC-SHA256 sur `timestamp.payload`. Le timestamp et la signature arrivent dans les headers HTTP.
POST /v1/webhooks
const apiKey = process.env.COUSSEMA_API_KEY ?? '';
const response = await fetch('https://api.coussema.com/v1/webhooks', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://example.com/webhooks/coussema',
subscribed_events: ['message.delivered', 'message.failed'],
}),
});
const payload = await response.json();
if (!response.ok) throw new Error(`Webhook request failed: ${response.status}`);
// Stockez payload.signingSecret immédiatement dans un coffre serveur.
// Ne le journalisez jamais et ne l’envoyez jamais au navigateur.Headers de callback
La plateforme livre les callbacks avec les headers suivants. Le `x-coussema-delivery-id` vous aide à faire de l’idempotence côté receveur.
Payload livré
Le corps du callback contient l’organisation et l’objet message normalisé.
{
"organization_id": "2f0dd8f4-7b1c-4f38-b4c1-f4bc8aa6e8f4",
"message": {
"id": "a2f7e5cb-bbe9-4d7d-bd17-80d1d7a5e5af",
"campaign_id": null,
"recipient_phone": "+243810000000",
"sender_name": "Coussema",
"status": "delivered",
"source_surface": "api",
"sent_at": "2026-04-09T10:11:11.000Z",
"failed_at": null,
"delivered_at": "2026-04-09T10:13:45.000Z",
"failure": null
}
}Vérification Node.js
Le worker de la plateforme retry les callbacks qui ne retournent pas de 2xx. Vérifiez d’abord la signature, puis dédupliquez et persistez l’événement.
import { createHmac, timingSafeEqual } from 'node:crypto';
function handleWebhook(rawBody, headers) {
const timestamp = headers.get('x-coussema-timestamp');
const signature = headers.get('x-coussema-signature');
const secret = process.env.COUSSEMA_WEBHOOK_SECRET ?? '';
if (!timestamp || !signature || !secret) throw new Error('Missing signature data.');
const [version, incomingDigest = ''] = signature.split('=');
const timestampMs = Date.parse(timestamp);
const timestampIsFresh = Number.isFinite(timestampMs)
&& Math.abs(Date.now() - timestampMs) <= 5 * 60 * 1000;
const expectedDigest = createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`, 'utf8')
.digest('hex');
const valid = timestampIsFresh
&& version === 'v1'
&& incomingDigest.length === expectedDigest.length
&& timingSafeEqual(Buffer.from(incomingDigest, 'utf8'), Buffer.from(expectedDigest, 'utf8'));
if (!valid) throw new Error('Invalid Coussema webhook signature.');
return JSON.parse(rawBody);
}- Acceptez et dédupliquez par `x-coussema-delivery-id`.
- Répondez en 2xx une fois le callback enregistré dans votre système.
- Utilisez le secret retourné à la création pour vérifier la signature.
- Traitez `message.delivered` et `message.failed` comme les états métier principaux.