Logique de nouvelles tentatives
Comprendre comment Mail.Land retente les livraisons webhook échouées
Lorsqu'une livraison webhook échoue, MailLand réessaie automatiquement avec des délais croissants. Cette page couvre le calendrier de nouvelles tentatives, les statuts de livraison, la gestion des échecs et comment surveiller et relancer des livraisons manuellement.
Calendrier de nouvelles tentatives
Cette section couvre tous les événements livrés par le durable dispatcher : message.*,
suppression.*, account.* et inbound.received. Ce qui est propre aux événements entrants se trouve dans
Livraisons entrantes ci-dessous.
MailLand réessaie une livraison échouée jusqu'à 6 fois au total, avec un délai croissant avant chaque nouvelle tentative :
| Tentative | Délai avant cette tentative | Temps total écoulé |
|---|---|---|
| 1 | : (tentative initiale) | 0 |
| 2 | Immédiat | ~0 |
| 3 | 1 minute | ~1 minute |
| 4 | 5 minutes | ~6 minutes |
| 5 | 15 minutes | ~21 minutes |
| 6 | 1 heure | ~1 heure 21 minutes |
Si la 6e tentative échoue, la livraison est marquée comme définitivement échouée : il n'y a pas d'attente supplémentaire ni de 7e tentative.
Retryable vs Non-retryable
Réponses retryables
Ces codes de statut déclenchent des nouvelles tentatives automatiques :
408Request Timeout429Too Many Requests500Internal Server Error502Bad Gateway503Service Unavailable504Gateway Timeout- Erreurs réseau (délais d'attente, erreurs de socket, échecs DNS)
Réponses non-retryables
Ces codes de statut sont traités comme des échecs permanents, pas de nouvelle tentative :
400Bad Request401Unauthorized403Forbidden404Not Found- Toutes les autres erreurs
4xx(sauf 408 et 429) - Une réponse de redirection qui échoue la validation (voir Redirections) ou dépasse la limite de sauts
Si votre endpoint renvoie un en-tête Retry-After sur une réponse retryable, MailLand utilise cette
valeur comme délai avant la tentative suivante à la place du calendrier ci-dessus : pour n'importe
quel statut retryable, pas seulement 429. Il n'y a ni plafond ni variation aléatoire appliquée :
respectez cela aussi de votre côté si vous l'envoyez, car une valeur très élevée est honorée telle
quelle.
Redirections
MailLand ne traite pas votre endpoint comme le ferait un navigateur. Les redirections sont suivies manuellement, avec un maximum de 3 sauts, et chaque saut est revalidé avant que quoi que ce soit ne lui soit envoyé :
- la cible doit être en
https://: une redirection vershttp://est refusée, pas rétrogradée ; - la cible doit résoudre vers une adresse publique : une redirection vers une adresse privée, loopback ou link-local est refusée, et la requête n'est jamais émise ;
- dépasser 3 sauts fait échouer la livraison.
Une redirection refusée est signalée comme un échec de livraison sans code de statut. Si votre endpoint a changé d'adresse, pointez le webhook vers la nouvelle URL plutôt que de compter sur une redirection.
Configuration de la livraison
| Paramètre | Par défaut | Description |
|---|---|---|
| Délai d'attente HTTP | 15 secondes | Temps d'attente maximum par tentative de livraison |
| Nouvelles tentatives max | 6 tentatives | Nombre total de tentatives de livraison incluant la première |
| Seuil de mise en pause automatique | 10 échecs consécutifs | Le webhook est mis en pause après ce nombre d'échecs consécutifs : utilise le pauseThreshold propre au webhook s'il est défini à une valeur supérieure à 0, sinon cette valeur par défaut |
| Déduplication | Tant que l'enregistrement de livraison existe | Les doublons du même événement sont supprimés par la contrainte (WebhookId, EventId) tant que la ligne existe : au moins 30 jours pour les livraisons réussies |
Livraisons entrantes
inbound.received est livré par le même durable dispatcher que tous les autres événements, avec le même
calendrier de nouvelles tentatives, la même prise en compte de Retry-After, le même historique de livraison
et la même mise en pause automatique que ci-dessus.
- Dédupliquez sur
event_id(égal àX-MLND-Event-Id), comme pour tout autre événement. Il est stable par message entrant et par webhook : chaque tentative pour le même e-mail porte le même identifiant. - Un rejeu manuel est un nouvel événement : un nouvel
event_id, l'original figurant dansdata.replay_of.data.idempotency_keyreste identique. - La
download_urlde chaque pièce jointe est signée à chaque tentative et reste valide 15 minutes à partir de là ; l'idde la pièce jointe est le même à chaque tentative. Voir Webhooks entrants.
Afficher l'historique des livraisons
Consultez les livraisons passées pour un webhook :
GET https://api.mail.land/v1/webhooks/{id}/deliveriescurl -X GET "https://api.mail.land/v1/webhooks/wh_abc123/deliveries?limit=20" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."Paramètres de requête
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
cursor | string | - | Curseur opaque issu du nextCursor de la page précédente. À omettre pour la première page. |
limit | int | 20 | Éléments par page |
status | string | - | Filtre : success ou failed |
eventType | string | - | Filtrer par type d'événement (ex. message.delivered) |
from | datetime | - | Date de début |
to | datetime | - | Date de fin |
{
"items": [
{
"publicId": "p8r2kx7m9wq4rtz",
"eventType": "message.delivered",
"messageId": "t_a1b2c3d4e5f6",
"statusCode": 200,
"responseTimeMs": 145,
"success": true,
"errorMessage": null,
"createdAt": "2026-02-17T12:34:58.000Z",
"attemptNumber": 1
},
{
"publicId": "p8r2kx7m3nq1abc",
"eventType": "message.bounced",
"messageId": "t_d4e5f6a1b2c3",
"statusCode": 503,
"responseTimeMs": 15000,
"success": false,
"errorMessage": "Service Unavailable",
"createdAt": "2026-02-17T12:30:00.000Z",
"attemptNumber": 3
}
],
"nextCursor": "eyJ2IjoxLCJzIjoiY3JlYXRlZGF0OmRlc2MiLCJrIjoi...",
"hasMore": true
}Relancer une livraison échouée
Relancez manuellement une livraison échouée spécifique :
POST https://api.mail.land/v1/webhooks/{id}/deliveries/{publicId}/retrycurl -X POST https://api.mail.land/v1/webhooks/wh_abc123/deliveries/p8r2kx7m3nq1abc/retry \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."Retourne 202 Accepted en cas de succès. La nouvelle tentative est mise en file d'attente et utilise la charge utile d'événement originale.
| Statut | Code | Description |
|---|---|---|
202 | - | Nouvelle tentative mise en file d'attente avec succès |
202 | - | La livraison est déjà en file d'attente pour une nouvelle tentative, aucun effet, pas une erreur |
400 | ALREADY_SUCCEEDED | Impossible de relancer une livraison qui a déjà réussi |
400 | INVALID_REQUEST | La livraison est antérieure au suivi durable des tentatives, ou est actuellement en cours |
404 | WEBHOOK_NOT_FOUND | Le webhook n'existe pas |
404 | DELIVERY_NOT_FOUND | L'ID de livraison n'existe pas |
Mise en pause automatique en cas d'échecs
Lorsqu'un webhook accumule suffisamment de tentatives de livraison échouées consécutives, MailLand
le met automatiquement en pause. Le seuil est le pauseThreshold propre au webhook s'il est défini
à une valeur supérieure à 0, sinon la valeur par défaut de 10 tentatives de livraison échouées
consécutives :
- L'
isActivedu webhook est mis àfalse, etpausedAt/pausedReasonsont renseignés :pausedReasonindique le nombre d'échecs et la nature du dernier - Une notification in-app est créée
- Aucun autre événement n'est livré jusqu'à la reprise du webhook
Le compteur porte sur les tentatives, pas sur les événements : un seul événement qui épuise toute son échelle de nouvelles tentatives contribue à plusieurs échecs.
Il n'y a pas de notification d'avertissement plus tôt avant que le seuil ne soit atteint : vérifiez
consecutiveFailures de manière proactive (voir Bonnes pratiques ci-dessous) plutôt que d'attendre
d'en être informé.
Reprendre un webhook mis en pause
POST https://api.mail.land/v1/webhooks/{id}/resumecurl -X POST https://api.mail.land/v1/webhooks/wh_abc123/resume \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."Cela réactive le webhook : cela efface l'état de pause, réinitialise le compteur d'échecs consécutifs à zéro, et le remet actif. La prochaine tentative de livraison d'événement sera effectuée normalement.
Toute livraison réussie réinitialise automatiquement le compteur d'échecs consécutifs à zéro. Un seul succès après 9 échecs empêche la mise en pause du webhook.
Bonnes pratiques
- Retournez 200 rapidement : traitez les charges utiles webhook de manière asynchrone. Mettez l'événement en file d'attente et retournez
200immédiatement. Les gestionnaires de longue durée risquent des délais d'attente. - Utilisez l'ID d'événement pour un traitement idempotent : l'en-tête
X-MLND-Event-Id(et le champevent_idcorrespondant dans la charge utile) est stable à travers les nouvelles tentatives et les rediffusions : le même événement sous-jacent arrive toujours avec le même identifiant, quel que soit le nombre de fois où il est redistribué. Stockez les identifiants traités et ignorez ceux déjà gérés, afin qu'une livraison en double soit un no-op de votre côté. - Surveillez les échecs consécutifs : vérifiez le champ
consecutiveFailureslors de la liste des webhooks. Enquêtez avant d'atteindre le seuil de mise en pause automatique. - Gérez correctement le 429 : si votre point de terminaison est surchargé, retournez
429avec un en-têteRetry-After. MailLand le respectera.
Prochaines étapes
- Vérification de signature : vérifier l'authenticité du webhook
- Événements : référence complète des types d'événements
- Présentation des webhooks : créer et gérer des webhooks
Vérification de signature
Vérifiez les signatures webhook pour garantir l'authenticité des charges utiles webhook entrantes
Versionnage et compatibilité
Comment les payloads de webhook sont versionnés, ce que signifie chaque identifiant et chaque horodatage, et comment les changements et dépréciations sont annoncés