MailLandDocs
Webhooks

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 :

TentativeDélai avant cette tentativeTemps total écoulé
1: (tentative initiale)0
2Immédiat~0
31 minute~1 minute
45 minutes~6 minutes
515 minutes~21 minutes
61 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 :

  • 408 Request Timeout
  • 429 Too Many Requests
  • 500 Internal Server Error
  • 502 Bad Gateway
  • 503 Service Unavailable
  • 504 Gateway 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 :

  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not 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 vers http:// 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ètrePar défautDescription
Délai d'attente HTTP15 secondesTemps d'attente maximum par tentative de livraison
Nouvelles tentatives max6 tentativesNombre total de tentatives de livraison incluant la première
Seuil de mise en pause automatique10 échecs consécutifsLe 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éduplicationTant que l'enregistrement de livraison existeLes 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 dans data.replay_of. data.idempotency_key reste identique.
  • La download_url de chaque pièce jointe est signée à chaque tentative et reste valide 15 minutes à partir de là ; l'id de 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}/deliveries
List delivery history
curl -X GET "https://api.mail.land/v1/webhooks/wh_abc123/deliveries?limit=20" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Paramètres de requête

ParamètreTypeDéfautDescription
cursorstring-Curseur opaque issu du nextCursor de la page précédente. À omettre pour la première page.
limitint20Éléments par page
statusstring-Filtre : success ou failed
eventTypestring-Filtrer par type d'événement (ex. message.delivered)
fromdatetime-Date de début
todatetime-Date de fin
Delivery history response
{
  "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}/retry
Retry a delivery
curl -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.

StatutCodeDescription
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
400ALREADY_SUCCEEDEDImpossible de relancer une livraison qui a déjà réussi
400INVALID_REQUESTLa livraison est antérieure au suivi durable des tentatives, ou est actuellement en cours
404WEBHOOK_NOT_FOUNDLe webhook n'existe pas
404DELIVERY_NOT_FOUNDL'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 :

  1. L'isActive du webhook est mis à false, et pausedAt / pausedReason sont renseignés : pausedReason indique le nombre d'échecs et la nature du dernier
  2. Une notification in-app est créée
  3. 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}/resume
Resume a paused webhook
curl -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 200 immé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 champ event_id correspondant 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 consecutiveFailures lors 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 429 avec un en-tête Retry-After. MailLand le respectera.

Prochaines étapes

Sur cette page