Webhooks
Webhooks
Enregistrez un point de terminaison et les événements qu’il doit recevoir. Chaque livraison arrive dans la même enveloppe signée, et une livraison qui échoue est retentée pendant environ 24 heures.
Webhooks
Enregistrez un point de terminaison webhook avec les événements que vous souhaitez recevoir. Le signing_secret de la réponse n’est affiché qu’une seule fois, à la création.
const { data: webhook, error } = await honkio.webhooks.create({
url: 'https://example.com/webhooks/honkio',
events: ['message.delivered', 'message.failed', 'message.received'],
})
if (error) throw new Error(error.message)
console.log(webhook.id, webhook.signing_secret) // the secret is shown once: store it nowcurl -X POST https://api.honkio.ca/v1/webhooks \
-H "Authorization: Bearer mk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/honkio",
"events": ["message.delivered", "message.failed", "message.received"]
}'
# Response 201: { "id": "...", "url": "...", "events": [...],
# "signing_secret": "64 hex characters, shown once", "active": true, "created_at": "..." }Les événements SMS, de désabonnement, de numéro de téléphone et de compte et leur contenu sont décrits dans la référence des événements.
Les événements de courriel (email.* et email_domain.*) et leur contenu sont décrits dans la référence des événements.
Chaque charge utile est signée avec HMAC-SHA256 : vérifiez l’en-tête X-HonkIO-Signature. Un évènement message.received contient l’expéditeur, votre numéro, le texte, keyword_action (traitement STOP/START), ainsi que segment_count et cost_cents, le montant facturé pour ce message. Un évènement message.sent contient carrier_message_id, l’identifiant que l’opérateur a attribué au message, avec les deux mêmes champs de facturation.
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"type": "message.delivered",
"created": "2026-09-19T12:00:00.000Z",
"account_id": "clxxxaccountxxxxxxxxxxxxx",
"livemode": true,
"data": {
"message_id": "clxxxmessagexxxxxxxxxxxxx",
"status": "delivered",
"message_status": "DELIVERED",
"to": "+1613XXXXXXX",
"from": "+1416XXXXXXX",
"errors": []
}
}
// Headers: X-HonkIO-Signature, X-HonkIO-Timestamp, X-HonkIO-EventUn même message_id peut émettre message.failed (seulement si l’échec était CARRIER_UNAVAILABLE ; un échec INSUFFICIENT_BALANCE n’émet aucun webhook) puis de nouveau message.queued, puis message.sent, lorsqu’une nouvelle tentative avec la même Idempotency-Key relance un message ayant échoué avant d’atteindre l’opérateur.
L’enveloppe
Chaque évènement arrive sous forme d’un seul objet JSON avec les mêmes six champs de premier niveau. Seul data change d’un évènement à l’autre.
| Champ | Signification |
|---|---|
| id | L’identifiant de l’évènement, un UUID. Il est le même à chaque tentative et à chaque rejeu de cet évènement : conservez-le et ignorez tout id déjà traité. |
| type | Le nom de l’évènement, par exemple message.received. La même valeur est envoyée dans l’en-tête X-HonkIO-Event. |
| created | Le moment de l’évènement, en ISO 8601 UTC avec millisecondes. Une nouvelle tentative ou un rejeu garde la valeur d’origine. |
| account_id | Le compte HonkIO auquel appartient l’évènement. |
| livemode | true quand l’action d’une clé réelle a causé l’évènement, false pour une simulation avec une clé de test. Les évènements de compte et de numéro de téléphone valent toujours true. |
| data | Les champs propres à l’évènement, décrits sous chaque évènement ci-dessous. |
Chaque événement porte aussi un champ livemode au premier niveau : true quand l’action d’une clé réelle l’a causé, false pour une simulation avec une clé de test. Les événements de compte et de numéro de téléphone sont toujours réels.
Nouvelles tentatives, lettres mortes et désactivation
Répondez directement par un code 2xx en moins de 10 secondes : les livraisons ne suivent jamais une redirection, donc un code 3xx compte comme une tentative échouée, tout comme un autre statut, un délai dépassé ou une erreur de connexion. Un évènement échoué est retenté une fois environ une seconde plus tard (sauf si le point de terminaison échouait déjà), puis à environ 1 minute, 5 minutes, 30 minutes, 2 heures, 6 heures et 16 heures d’intervalle : environ 24 heures en tout, jusqu’à huit tentatives. La livraison se fait au moins une fois dès la première tentative : dès qu’une tentative a échoué, l’évènement est conservé et ses nouvelles tentatives survivent à nos redémarrages et déploiements (un plantage pendant la toute première tentative peut faire perdre cet évènement). Chaque tentative est signée à nouveau et porte le même id d’évènement : dédupliquez sur celui-ci. Un évènement dont toutes les tentatives échouent va dans votre file des lettres mortes. Votre point de terminaison reste actif pendant tout ce temps : il n’est désactivé que lorsque toutes les tentatives vers lui ont échoué pendant au moins 24 heures, sans aucune livraison réussie entre-temps, et qu’au moins 5 évènements différents ont échoué ; toute livraison réussie remet ce compte à zéro, tout comme un échec survenu plus de 17 heures après le précédent (le plus long intervalle entre deux tentatives, plus une heure). Nous vous écrivons une heure après le début d’une série d’échecs, puis de nouveau si le point de terminaison est désactivé, et tant qu’il échoue, GET /v1/webhooks/:id indique failing_since, failed_events et last_failure_reason. Les évènements échoués avant la désactivation d’un point de terminaison sont conservés comme lettres mortes que vous pouvez rejouer ; ceux survenus pendant qu’il est désactivé ne lui sont pas livrés et ne sont pas conservés. Un compte SUSPENDED ou CLOSED fonctionne de la même façon : rien n’est livré à son point de terminaison, tout ce qui aurait dû partir va directement dans votre file des lettres mortes, et chacune redevient rejouable une fois le compte réintégré. Listez les lettres mortes avec GET /v1/webhooks/:id/dead-letters, renvoyez-en une avec POST /v1/webhooks/dead-letters/:id/replay (409 DEAD_LETTER_ALREADY_REPLAYED si elle est déjà partie ou qu’un rejeu est en cours, ou 409 ACCOUNT_SUSPENDED tant que le compte est suspendu ou fermé) ou supprimez-la avec DELETE /v1/webhooks/dead-letters/:id, et réactivez le point de terminaison avec POST /v1/webhooks/:id/reactivate. Les lettres mortes rejouées sont conservées 90 jours ; toutes disparaissent à la limite de rétention des messages.
Avec le SDK Node.js, réactivez un point de terminaison désactivé et relancez ce qu’il a manqué :
// Turn a disabled endpoint back on. Missed events are not resent by this: replay them.
const { error: reactivateError } = await honkio.webhooks.reactivate('WEBHOOK_ID')
if (reactivateError) throw new Error(reactivateError.message)
// Dead letters not yet replayed, newest first.
const { data, error } = await honkio.webhooks.deadLetters('WEBHOOK_ID')
if (error) throw new Error(error.message)
for (const deadLetter of data.data) {
const { error: replayError } = await honkio.webhooks.replay(deadLetter.id)
if (replayError) console.error(deadLetter.event_type, replayError.name, replayError.message)
}
HonkIO