Webhooks
Créez un endpoint, vérifiez la signature et traitez les événements de livraison avec l’API REST. Le SDK npm public n’est pas encore disponible.
Signature
Les callbacks utilisent une signature HMAC-SHA256 sur timestamp.payload. Le timestamp et la signature arrivent dans les headers HTTP.
- Retourner un 2xx rapidement quand le callback est traité.
- Traiter
x-coussema-eventcomme la source de vérité pour le type d’événement. - Vérifier
x-coussema-signatureavec le secretsigningSecretrenvoyé à la création. - Enregistrer les
message.deliveredetmessage.faileddans votre système métier.
POST /v1/webhooks
- message.queued
- message.sent
- message.delivered
- message.failed
javascript
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}`);
console.log(payload.signingSecret);En-têtes
| Header | Valeur |
|---|---|
| content-type | application/json |
| user-agent | Coussema-Webhooks/1.0 |
| x-coussema-delivery-id | delivery UUID |
| x-coussema-event | message.delivered |
| x-coussema-timestamp | 2026-04-09T10:15:30.000Z |
| x-coussema-signature | v1=<hmac-sha256-hex> |
Payload
json
{
"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
javascript
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);
}