Référence
Requêtes de webhook
Le format exact des requêtes de webhook d’Emailit, de la méthode et des en-têtes au tableau JSON d’événements, ainsi que l’ordre, les doublons et la manière de répondre.
Cette page décrit ce qui arrive sur votre endpoint : la requête HTTP, ses en-têtes, le corps JSON et la réponse qu’attend Emailit. Consultez-la lorsque vous écrivez ou déboguez un gestionnaire de webhook.
Requête
| Propriété | Valeur |
|---|---|
| Méthode | POST |
| Corps | Un tableau JSON de 1 à 100 événements, compact (sans espaces superflus) |
| Timeout | 30 secondes |
| Redirections | Non suivies. Une réponse 3xx compte comme un échec. |
| Simultanéité | Une requête à la fois par webhook. Les lots sont envoyés les uns après les autres. |
| Latence | Emailit recherche de nouveaux événements toutes les quelques secondes : la plupart des événements arrivent en quelques secondes. |
POST /webhooks/emailit HTTP/1.1
Host: acme.com
Content-Type: application/json
User-Agent: Emailit-Webhooks/2.0
X-Emailit-Signature: 7242b580f77ec488f8ac21b57f092e3db61a431694f13074c7b4c85a4dd04be3
X-Emailit-Timestamp: 1790846045En-têtes
| En-tête | Description |
|---|---|
Content-Type |
Toujours application/json. |
User-Agent |
Toujours Emailit-Webhooks/2.0. |
X-Emailit-Signature |
HMAC-SHA256 encodé en hexadécimal de {timestamp}.{raw body}, calculé avec le secret complet du webhook (whsec_ compris) comme clé. Consultez Signature des requêtes. |
X-Emailit-Timestamp |
Horodatage Unix, en secondes, de la signature de la requête par Emailit. Chaque tentative, nouvelles tentatives comprises, est signée de nouveau avec un nouvel horodatage. |
Corps
Le corps est toujours un tableau JSON, même lorsqu’il ne contient qu’un seul événement. Chaque élément a cette structure :
| Champ | Type | Description |
|---|---|---|
event_id |
chaîne | L’ID de l’événement, qui commence par evt_. Un même événement conserve le même ID d’une tentative à l’autre. Les événements de test commencent par evt_test_. |
type |
chaîne | Le type d’événement, par exemple email.delivered. |
object |
objet | La ressource concernée par l’événement. |
data |
objet | Contient de nouveau object. data.object et l’object de premier niveau sont identiques ; lisez celui que vous préférez. |
Les champs de object dépendent du type. Les événements de statut d’e-mail décrivent l’e-mail ; email.clicked et email.loaded décrivent le clic ou le chargement et imbriquent l’e-mail ; les événements de ressource comme contact.created décrivent la ressource.
Exemple de lot
Cette requête contient deux événements : une livraison et un clic ultérieur sur un lien du même e-mail.
[
{
"event_id": "evt_2xGk7Nq1VbD5sR8tLmW3eYhC6aP",
"type": "email.delivered",
"object": {
"id": "em_2xGk7Lr3XcB8pQ1wYzK4dTfG9hJ",
"object": "email",
"from": "billing@acme.com",
"to": "ada@example.com",
"subject": "Your receipt #1042",
"status": "delivered",
"meta": { "order_id": "1042" },
"updated_at": "2026-10-01T09:14:03.512000+00:00",
"created_at": "2026-10-01T09:14:01.207000+00:00"
},
"data": {
"object": {
"id": "em_2xGk7Lr3XcB8pQ1wYzK4dTfG9hJ",
"object": "email",
"from": "billing@acme.com",
"to": "ada@example.com",
"subject": "Your receipt #1042",
"status": "delivered",
"meta": { "order_id": "1042" },
"updated_at": "2026-10-01T09:14:03.512000+00:00",
"created_at": "2026-10-01T09:14:01.207000+00:00"
}
}
},
{
"event_id": "evt_2xGkC8w2TnF4yR7bMqL1sVdJ6pE",
"type": "email.clicked",
"object": {
"id": "click_2xGkC7v5PmD3xQ9aLnK2rUcH8oW",
"object": "click",
"email_id": "em_2xGk7Lr3XcB8pQ1wYzK4dTfG9hJ",
"email": {
"id": "em_2xGk7Lr3XcB8pQ1wYzK4dTfG9hJ",
"rcpt_to": "ada@example.com",
"mail_from": "billing@acme.com",
"subject": "Your receipt #1042",
"created_at": "2026-10-01T09:14:01.207000+00:00",
"campaign": null,
"meta": { "order_id": "1042" }
},
"link": {
"id": "link_2xGk7Mb6QwE1zS8cNpT4vXfK3gB",
"url": "https://acme.com/orders/1042"
},
"contact": { "id": "con_2xFh3Ka9LmP2nR6tVwB8cDqY5sZ", "email": "ada@example.com" },
"ip_address": "203.0.113.24",
"user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X)",
"created_at": "2026-10-01T09:20:47.884000+00:00"
},
"data": {
"object": {
"id": "click_2xGkC7v5PmD3xQ9aLnK2rUcH8oW",
"object": "click",
"email_id": "em_2xGk7Lr3XcB8pQ1wYzK4dTfG9hJ",
"email": {
"id": "em_2xGk7Lr3XcB8pQ1wYzK4dTfG9hJ",
"rcpt_to": "ada@example.com",
"mail_from": "billing@acme.com",
"subject": "Your receipt #1042",
"created_at": "2026-10-01T09:14:01.207000+00:00",
"campaign": null,
"meta": { "order_id": "1042" }
},
"link": {
"id": "link_2xGk7Mb6QwE1zS8cNpT4vXfK3gB",
"url": "https://acme.com/orders/1042"
},
"contact": { "id": "con_2xFh3Ka9LmP2nR6tVwB8cDqY5sZ", "email": "ada@example.com" },
"ip_address": "203.0.113.24",
"user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X)",
"created_at": "2026-10-01T09:20:47.884000+00:00"
}
}
}
]Les horodatages sont au format ISO 8601 en UTC. meta contient les métadonnées que vous avez associées à l’envoi, ou null. contact vaut null lorsque le destinataire ne fait pas partie de vos contacts, et campaign vaut null pour les e-mails envoyés via l’API et SMTP.
Ordre
Au sein d’une requête, les événements sont classés du plus ancien au plus récent. D’une requête à l’autre, l’ordre n’est pas garanti : un lot en échec est réessayé plus tard alors que des événements plus récents sont peut-être déjà arrivés, et une relance manuelle peut renvoyer d’anciens événements après des nouveaux.
Ne vous fiez pas à l’ordre d’arrivée. Pour les événements d’e-mail, utilisez les champs status et updated_at de l’e-mail et conservez l’état le plus avancé que vous avez vu (par exemple, n’écrasez pas clicked avec un delivered tardif). En cas de doute, récupérez l’état actuel avec Récupérer un e-mail.
Doublons
Un même événement peut arriver plusieurs fois :
- Lorsque votre endpoint dépasse le timeout ou renvoie une erreur après avoir traité une partie d’un lot, le lot entier est réessayé, y compris les événements que vous avez déjà traités.
- Lorsque vous utilisez Retry failed ou relancez une requête individuelle.
Enregistrez chaque event_id traité, avec une contrainte d’unicité, et ignorez les événements déjà vus.
Répondre
| Votre réponse | Résultat |
|---|---|
N’importe quel 2xx dans les 30 secondes |
Chaque événement du lot est marqué Delivered. Le corps de la réponse est ignoré. |
3xx, 4xx ou 5xx |
Le lot est en échec et fait l’objet d’une nouvelle tentative. |
| Aucune réponse dans les 30 secondes, erreur de connexion, erreur TLS | Le lot est en échec et fait l’objet d’une nouvelle tentative. Le tableau de bord affiche le code de statut 0. |
Pour les requêtes en échec, Emailit conserve les 2 000 premiers caractères du corps de votre réponse. Vous pouvez le consulter dans l’onglet Requests du webhook : renvoyer un court message d’erreur facilite donc le débogage.
Recommandations :
- Vérifiez, puis accusez réception rapidement. Contrôlez la signature, placez les événements dans une file d’attente ou une table, et renvoyez
200. Effectuez les traitements lents, comme les appels à d’autres API, dans une tâche en arrière-plan. - Renvoyez
2xxpour les événements que vous ignorez. Sinon, ils sont réessayés pendant des jours. - Renvoyez
401ou403pour une signature invalide. La requête est réessayée comme pour tout échec, ce qui vous laisse le temps de corriger un secret mal configuré.
Pour le calendrier des nouvelles tentatives et ce qui se passe lorsqu’un endpoint continue d’échouer, consultez Nouvelles tentatives et échecs.