Types d'événements
Lisoloo émet cinq types d’événements, un par transition de statut.
Tous partagent la même enveloppe extérieure ; le bloc data varie par
événement.
Enveloppe
Section intitulée « Enveloppe »{ "event_id": "f8a3b7c1-2e4d-4a9b-9d8f-7c6e5b4a3d2c", "event_type": "<type>", "timestamp": "2026-05-27T10:15:08Z", "message_id": "507f1f77bcf86cd799439011", "data": { ... }}| Champ | Type | Notes |
|---|---|---|
event_id | UUID | Unique par événement. À utiliser pour l’idempotence. |
event_type | string | Un de sms.queued, sms.processing, sms.sent, sms.delivered, sms.failed. |
timestamp | ISO 8601 | Quand la passerelle a émis l’événement. |
message_id | string | Le message_id de POST /send. |
data | object | Charge utile spécifique à l’événement. |
sms.queued
Section intitulée « sms.queued »Déclenché immédiatement après que POST /send retourne 201.
{ "event_id": "...", "event_type": "sms.queued", "timestamp": "2026-05-27T10:15:00Z", "message_id": "507f1f77bcf86cd799439011", "data": { "total_recipients": 3, "total_messages": 3, "total_cost": 0.06, "currency": "USD", "scheduled_at": null }}Pour les envois planifiés, scheduled_at est la première paire
(date, time) de scheduled_dates (ou la première occurrence
calculée pour récurrent). Sinon null — sms.processing suivra en
quelques secondes.
sms.processing
Section intitulée « sms.processing »Déclenché quand le connecteur opérateur récupère le job.
{ "event_type": "sms.processing", "data": { "recipient_phone": "+243998857000", "carrier": "vodacom_drc" }}Un événement par destinataire. carrier est le slug interne du
connecteur — informationnel seulement, pas partie de l’API stable.
sms.sent
Section intitulée « sms.sent »Déclenché quand le SMSC accepte le message pour livraison (côté opérateur).
{ "event_type": "sms.sent", "data": { "recipient_phone": "+243998857000", "carrier": "vodacom_drc", "sent_at": "2026-05-27T10:15:03Z" }}Le destinataire n’a pas encore reçu le SMS à ce point — il est dans la file sortante de l’opérateur.
sms.delivered
Section intitulée « sms.delivered »Déclenché quand le combiné ACK la livraison (ou que l’opérateur rapporte « delivered » dans son accusé asynchrone).
{ "event_type": "sms.delivered", "data": { "recipient_phone": "+243998857000", "carrier": "vodacom_drc", "delivered_at": "2026-05-27T10:15:08Z", "segments": 1 }}Terminal pour ce destinataire.
sms.failed
Section intitulée « sms.failed »Déclenché sur tout chemin vers failed.
{ "event_type": "sms.failed", "data": { "recipient_phone": "+243998857000", "carrier": "vodacom_drc", "failed_at": "2026-05-27T10:15:05Z", "error_code": "1502", "error_message": "Numéro inaccessible", "retryable": false }}Terminal. Le error_code correspond au
catalogue d’erreurs pour la cause au niveau opérateur.
La passerelle ne retry pas les SMS échoués d’elle-même —
l’opérateur a déjà tenté ses propres retries avant de rapporter
failed.
Les événements se déclenchent dans l’ordre où la passerelle observe
les transitions de la machine d’états, mais l’ordre de livraison
HTTP n’est pas garanti. Une réponse lente de votre endpoint sur
sms.processing suivie d’une réponse rapide sur sms.delivered peut
résulter en delivered arrivant en premier.
Si l’ordre strict compte, ignorez event_type du fil et à la place
chargez GET /status/{message_id} à la réception de tout événement —
traitez le webhook comme un signal « quelque chose a changé ».
Idempotence
Section intitulée « Idempotence »Votre handler doit être idempotent sur event_id. La passerelle
re-tentera sur 5xx ou timeout ; le retry porte le même event_id.
Une implémentation typique :
def handle_event(event: dict, db) -> None: event_id = event["event_id"] if db.events.find_one({"event_id": event_id}): return # déjà traité process_event(event, db) db.events.insert_one({"event_id": event_id, "received_at": now()})Voir aussi
Section intitulée « Voir aussi »- Présentation des webhooks — sémantique de livraison
- Configuration webhook — setup Basic auth
- Cycle de vie d’un message — la machine d’états