Erreurs
L'API HTTP renvoie des codes HTTP standards. Toute erreur porte un corps JSON de la même forme, produite par le framework NestJS. Le relais SMTP a sa propre table de codes à trois chiffres, documentée séparément.
Forme du corps d’erreur
statusCode reprend le code HTTP et error son libellé standard. message est toujours une chaîne : la phrase à afficher telle quelle. errors est toujours un tableau de chaînes non vide : le détail, un élément par champ invalide quand plusieurs champs sont en cause. Les deux sont liés par un invariant : message === errors.join(' ') — message n’est que la concaténation de errors, séparée par un espace.
{
"message": "L'adresse du destinataire est invalide : indiquez une seule adresse comme « client@exemple.gn ».",
"error": "Bad Request",
"statusCode": 400,
"errors": [
"L'adresse du destinataire est invalide : indiquez une seule adresse comme « client@exemple.gn »."
]
}{
"message": "L'adresse d'expédition est invalide : utilisez « adresse@domaine » ou « Nom <adresse@domaine> ». L'adresse du destinataire est invalide : indiquez une seule adresse comme « client@exemple.gn ». Le sujet est obligatoire et ne doit pas dépasser 300 caractères.",
"error": "Bad Request",
"statusCode": 400,
"errors": [
"L'adresse d'expédition est invalide : utilisez « adresse@domaine » ou « Nom <adresse@domaine> ».",
"L'adresse du destinataire est invalide : indiquez une seule adresse comme « client@exemple.gn ».",
"Le sujet est obligatoire et ne doit pas dépasser 300 caractères."
]
}Migration : message n'est plus jamais un tableau
message changeait de forme selon le chemin d’échec : une chaîne pour une erreur levée à la main, un tableau pour les erreurs de validation agrégées par NestJS. Un code qui lisait message[0] pour afficher la phrase recevait donc, sans erreur visible, soit la phrase entière soit sa première lettre — selon le chemin emprunté. Remplacez message[0] par errors[0] : message est désormais toujours la chaîne complète à afficher, et errors porte le détail champ par champ.Codes HTTP
- –Paramètre manquant, mal typé ou hors bornes (POST /v1/emails : from, to, subject, html/text).
- –Nom de domaine syntaxiquement invalide sur POST /v1/domains.
- –Nom de clé API vide ou trop long sur POST /v1/api-keys.
- –Pack de recharge inconnu, méthode de paiement invalide, ou numéro de téléphone mal formé sur POST /v1/billing/topup-requests.
- –Paramètre de pagination (page, limit) non numérique ou hors bornes.
- –Champ non déclaré dans le corps JSON, par exemple une faute de frappe dans un nom de paramètre : désormais refusé par un 400 (« Le champ « X » n'est pas accepté. »), là où il était auparavant ignoré silencieusement.
- –En-tête Authorization absent, mal formé, ou clé API inconnue/révoquée (routes API key).
- –Session absente ou expirée (routes tableau de bord, authentifiées par cookie).
- –POST /v1/emails alors que le solde de crédits du compte est inférieur à 1.
- –Domaine d'envoi non vérifié sur POST /v1/emails.
- –Envoi depuis l'adresse de test (mode bac à sable) vers un destinataire autre que l'adresse email de votre compte sur POST /v1/emails.
- –Compte suspendu (réputation d'envoi dégradée) — sur toute route authentifiée, API key ou session.
- –Domaine, clé API ou email référencé par :id qui n'existe pas — ou qui appartient à un autre compte : Zendou renvoie volontairement la même 404 dans les deux cas, pour ne jamais révéler l'existence de la ressource d'un tiers.
- –Nom de domaine déjà enregistré (par vous ou par un autre compte) sur POST /v1/domains.
- –Une demande de recharge est déjà en attente avec la même référence de transaction sur POST /v1/billing/topup-requests.
- –Quota journalier : POST /v1/emails alors que le nombre d'emails envoyés depuis minuit UTC a atteint la limite journalière du compte (200 par défaut, relevée automatiquement avec l'ancienneté et le volume — voir Facturation). Message : « Limite journalière atteinte : réessayez demain ou demandez une augmentation de quota. »
- –Rafale : plus de 60 requêtes par minute sur POST /v1/emails, indépendamment du quota journalier et du solde de crédits. Message : « Trop de requêtes. Réessayez dans quelques instants. »
- –Trop d'échecs d'authentification depuis une même adresse IP sur POST /v1/emails (clé API inconnue ou révoquée présentée à répétition). Utile à savoir en intégration : tester avec une mauvaise clé peut donc, après plusieurs essais, renvoyer un 429 plutôt qu'un 401. Une clé valide n'est jamais concernée, même si l'IP est bloquée.
En-têtes de limitation de débit
X-RateLimit-Limit (budget de la fenêtre), X-RateLimit-Remaining (requêtes encore acceptées dans cette fenêtre) et X-RateLimit-Reset (secondes avant remise à zéro). Un 429 porte en plus Retry-After (secondes avant de pouvoir réessayer), désormais aussi bien sur le 429 de rafale que sur celui de quota journalier atteint. Quand plusieurs compteurs s’appliquent à une même requête, ce sont les valeurs du compteur le plus contraignant, celui qui bloquera le client en premier, qui sont publiées.Code d’erreur machine (POST /v1/emails)
Toute réponse d’erreur 4xx de POST /v1/emails porte, en plus de message et errors, un champ code : une chaîne stable et lisible par machine, sur laquelle brancher la logique d’un client plutôt que sur le texte français de message, susceptible d’être reformulé.
{
"message": "Le domaine d'envoi n'est pas vérifié : ajoutez-le à votre compte et validez ses enregistrements DNS avant d'envoyer.",
"error": "Forbidden",
"statusCode": 403,
"code": "DOMAIN_NOT_VERIFIED",
"errors": [
"Le domaine d'envoi n'est pas vérifié : ajoutez-le à votre compte et validez ses enregistrements DNS avant d'envoyer."
]
}| Code | Statut HTTP | Signification |
|---|---|---|
| ACCOUNT_SUSPENDED | 403 | Compte suspendu (réputation d'envoi dégradée). |
| EMAIL_NOT_VERIFIED | 403 | Adresse email du compte non confirmée. |
| INVALID_FROM | 400 | Adresse d'expédition (from) illisible. |
| INVALID_TO | 400 | Adresse destinataire (to) illisible. |
| INVALID_REPLY_TO | 400 | Adresse de réponse (replyTo) illisible. Nouveau. |
| MISSING_BODY | 400 | Ni html ni text fourni. |
| BODY_TOO_LARGE | 400 | Un corps (html ou text) dépasse 500 Ko. |
| DOMAIN_NOT_VERIFIED | 403 | Domaine de l'adresse d'expédition non vérifié ou non rattaché au compte. |
| REPLY_TO_NOT_VERIFIED | 403 | Domaine de l'adresse de réponse (replyTo) non vérifié ou non rattaché au compte. Nouveau. |
| TEST_SENDER_UNAVAILABLE | 503 | Mode bac à sable momentanément indisponible (configuration serveur). |
| TEST_SENDER_RECIPIENT_RESTRICTED | 403 | Depuis l'adresse de test, destinataire autre que l'adresse du compte. |
| INSUFFICIENT_CREDITS | 402 | Solde de crédits insuffisant. |
| DAILY_LIMIT_REACHED | 429 | Quota journalier atteint. |
INVALID_REPLY_TO et REPLY_TO_NOT_VERIFIED sont nouveaux, liés à la prise en charge du champ replyTo, voir Envoyer un email.
Pour le détail des messages exacts renvoyés par POST /v1/emails, voir le tableau d’erreurs de la page « Envoyer un email ».
Ces codes ne couvrent que l’API HTTP. Le relais SMTP répond en codes à trois chiffres (RFC 5321), pas en HTTP — sa propre table de correspondance est documentée sur cette page.