Développeurs

Codes d'erreur

Une seule forme d'erreur sur toute l'API, et un vocabulaire de codes qui ne bouge pas. Branchez votre code sur error.code ; error.message est une phrase destinée à un humain, susceptible d'être reformulée ou traduite.

Format
{
  "error": {
    "code": "rate_limited",
    "message": "Limite de 5000 requêtes par heure atteinte."
  }
}

Les codes

CodeHTTPCe que ça veut direQuoi faire
unauthorized401 Clé absente, invalide, révoquée ou expirée : un seul code pour les quatre, volontairement. Vérifiez l'en-tête Authorization: Bearer …, puis l'état de la clé dans API & webhooks. Un cookie de session ne vaut pas clé ici.
forbidden403 La clé est valide mais n'a pas la portée requise (une clé en lecture seule sur une écriture). Créez une clé avec la portée voulue. Ne réutilisez pas une clé d'écriture pour de la lecture.
not_found404 La ressource n'existe pas : ou n'appartient pas à votre organisation. Les deux donnent la même réponse. Vérifiez l'identifiant. Utilisez celui renvoyé par nos listes, jamais un identifiant reconstruit.
validation_failed422 Un paramètre connu a une valeur invalide. Le message nomme le champ. Corrigez le paramètre. Les paramètres inconnus, eux, sont ignorés sans erreur.
rate_limited429 Budget horaire épuisé : ou formule sans accès API (le message le précise). Attendez Retry-After secondes. Surveillez X-RateLimit-Remaining, présent sur toutes les réponses, pour ralentir avant d'y arriver.
internal_error500 Une panne de notre côté. Réessayez avec un délai croissant. Si ça persiste, écrivez-nous avec la date et l'endpoint.

Une règle de gestion suffit. Rejouez rate_limited et internal_error avec un délai croissant ; sur unauthorized, forbidden, not_found et validation_failed, arrêtez et alertez : rejouer une requête que nous avons refusée pour sa forme ne la rendra pas valide.

Exemple de gestion

Node
async function ogerant(path, { key, tries = 4 } = {}) {
  for (let attempt = 1; attempt <= tries; attempt++) {
    const res = await fetch(`https://app.ogerant.com${path}`, {
      headers: { authorization: `Bearer ${key}` },
    });
    if (res.ok) return res.json();

    const { error } = await res.json().catch(() => ({ error: {} }));

    // Rejouable : on attend ce qu'on nous dit d'attendre, sinon un backoff.
    if (error.code === "rate_limited" || error.code === "internal_error") {
      const wait = Number(res.headers.get("retry-after")) || 2 ** attempt;
      await new Promise((r) => setTimeout(r, wait * 1000));
      continue;
    }

    // Non rejouable : la requête est fautive, insister n'y changera rien.
    throw new Error(`${error.code}: ${error.message}`);
  }
  throw new Error("épuisé après plusieurs tentatives");
}

Et si je reçois autre chose ?

Toute réponse de /api/v1/ qui ne suit pas ce format est un bug de notre côté : signalez-le. En particulier, une page HTML là où vous attendiez du JSON signifie presque toujours que l'URL appelée n'est pas une route /api/v1/ : vérifiez le préfixe.