Fouten
Eén vorm, twee niveaus, en een request-id op alles.
De envelop
type is een bevroren verzameling waarop je kunt vertakken en die nooit zal groeien. code is specifiek en aanvullend, dus behandel een code die je niet herkent als zijn type. requestId staat op elke respons, ook op geslaagde, en is wat een melding aan een logregel koppelt.
{ "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 is met punten en indexen opgebouwd, zodat het naar precies het juiste element wijst (to.0, attachments.2.filename) in plaats van naar het veld dat het bevat.
Statuscodes
| Status | type | Veelvoorkomende codes |
|---|---|---|
| 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 |
Een 404 maakt nooit onderscheid tussen "bestaat niet" en "hoort bij een andere workspace". Dat is bewust: het verschil is zelf informatie.
Een 429 draagt nooit Retry-After, dus kies je eigen wachttijd. send_quota_exceeded is het maandelijkse verzendtegoed en het wordt op de eerste van de maand gereset, dus toon het aan een mens in plaats van terug te schakelen. too_many_inboxes is het plafond voor het aanmaken van wegwerp-inboxen, en het verlengen van een inbox die je al hebt kost daar niets van.
500 en 503 delen een type en betekenen verschillende dingen voor een aanroeper. Een 503 is een afhankelijkheid die niet antwoordde (vandaag is dat de vertaler), en het verzoek is het waard ongewijzigd opnieuw te proberen; een 500 is van ons en is het waard gemeld te worden met zijn requestId.
Ermee omgaan
Vertak op type voor gedrag en lees code voor de melding die je aan een mens toont. Een niet-herkende code is geen fout in je client. Het betekent dat we een storing preciezer hebben benoemd dan voorheen.
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); }}