RÉFÉRENCE API

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.

400 Bad Request — un champ invalide
{
  "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 »."
  ]
}
400 Bad Request — plusieurs champs invalides
{
  "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

Avant ce correctif, 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

400Bad RequestRequête mal formée
  • –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.
401UnauthorizedAuthentification manquante ou invalide
  • –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).
402Payment RequiredSolde de crédits insuffisant
  • –POST /v1/emails alors que le solde de crédits du compte est inférieur à 1.
403ForbiddenAction refusée malgré une authentification valide
  • –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.
404Not FoundRessource introuvable
  • –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.
409ConflictLa requête entre en conflit avec l'état actuel des données
  • –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.
429Too Many RequestsLimite journalière atteinte, rafale d'envoi trop rapide, ou trop d'échecs d'authentification
  • –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

Toute réponse d’une route soumise à une limite porte trois en-têtes : 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é.

403 Forbidden - domaine d'envoi non vérifié
{
  "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."
  ]
}
CodeStatut HTTPSignification
ACCOUNT_SUSPENDED403Compte suspendu (réputation d'envoi dégradée).
EMAIL_NOT_VERIFIED403Adresse email du compte non confirmée.
INVALID_FROM400Adresse d'expédition (from) illisible.
INVALID_TO400Adresse destinataire (to) illisible.
INVALID_REPLY_TO400Adresse de réponse (replyTo) illisible. Nouveau.
MISSING_BODY400Ni html ni text fourni.
BODY_TOO_LARGE400Un corps (html ou text) dépasse 500 Ko.
DOMAIN_NOT_VERIFIED403Domaine de l'adresse d'expédition non vérifié ou non rattaché au compte.
REPLY_TO_NOT_VERIFIED403Domaine de l'adresse de réponse (replyTo) non vérifié ou non rattaché au compte. Nouveau.
TEST_SENDER_UNAVAILABLE503Mode bac à sable momentanément indisponible (configuration serveur).
TEST_SENDER_RECIPIENT_RESTRICTED403Depuis l'adresse de test, destinataire autre que l'adresse du compte.
INSUFFICIENT_CREDITS402Solde de crédits insuffisant.
DAILY_LIMIT_REACHED429Quota 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.