Erreurs
Une seule forme, deux niveaux, et un identifiant de requête sur tout.
L'enveloppe
type est un ensemble figé sur lequel vous pouvez brancher et qui ne s'agrandira jamais. code est précis et additif : traitez donc un code que vous ne reconnaissez pas comme son type. requestId figure sur chaque réponse, succès compris, et c'est ce qui relie un signalement à une ligne de journal.
{ "error": { "type": "validation_error", "code": "invalid_email_address", "message": "Not a valid email address: ada@", "param": "to.0", "docUrl": "https://openemail.uk/docs/api/errors#invalid_email_address", "requestId": "req_e103790543af4…" }}param est pointé et indexé, si bien qu'il désigne l'élément exact (to.0, attachments.2.filename) plutôt que le champ qui le contient.
Codes de statut
| Statut | type | Codes courants |
|---|---|---|
| 400 | invalid_request_error | malformed_json, invalid_idempotency_key |
| 401 | authentication_error | missing_api_key, invalid_api_key, revoked_api_key, invalid_credential_type |
| 403 | permission_error | insufficient_scope, from_address_forbidden |
| 404 | not_found_error | resource_not_found |
| 409 | conflict_error | email_not_cancellable, translation_not_configured |
| 422 | validation_error | invalid_email_address, reserved_header, too_many_recipients, unknown_parameter, idempotency_key_reuse, label_not_directly_settable, unknown_language, translation_too_long |
| 429 | rate_limit_error | send_quota_exceeded, too_many_inboxes |
| 500 | api_error | internal_error |
| 503 | api_error | translation_failed |
Un 404 ne distingue jamais « n'existe pas » de « appartient à un autre espace de travail ». C'est délibéré : la différence est elle-même une information.
Un 429 ne porte jamais de Retry-After : choisissez donc vous-même votre délai d'attente. send_quota_exceeded correspond au quota d'envoi mensuel, qui se réinitialise le premier du mois ; montrez-le à une personne plutôt que de temporiser. too_many_inboxes est le plafond de création de boîtes de réception jetables, et prolonger une boîte que vous détenez déjà ne coûte rien sur ce plafond.
500 et 503 partagent un même type et signifient des choses différentes pour un appelant. Un 503 est une dépendance qui n'a pas répondu (aujourd'hui, le traducteur), et la requête mérite d'être réessayée telle quelle ; un 500 est de notre côté et mérite d'être signalé avec son requestId.
Les gérer
Branchez sur type pour le comportement et lisez code pour le message que vous montrez à un humain. Un code non reconnu n'est pas une erreur de votre client. Cela signifie que nous avons nommé une défaillance plus précisément qu'auparavant.
const res = await fetch(`${BASE}/emails`, { method: 'POST', headers, body }); if (!res.ok) { const { error } = await res.json(); switch (error.type) { case 'rate_limit_error': throw new RateLimited(error.code); case 'validation_error': // error.param points at the offending field throw new BadRequest(`${error.param}: ${error.message}`); case 'authentication_error': // revoked_api_key and expired_api_key are worth telling an operator apart throw new AuthFailed(error.code); default: // quote requestId when you report it throw new Unexpected(error.message, error.requestId); }}