Erreurs + limites de débit
Une seule enveloppe d'erreur sur chaque endpoint. Codes de statut HTTP standard + une chaîne code structurée pour la correspondance programmatique. Des en-têtes de limite de débit sur chaque réponse pour vous permettre de doser votre rythme avant d'atteindre un 429.
Enveloppe d'erreur
{
"detail": "Human-readable summary.",
"code": "billing.subscription_past_due",
"field_errors": {
"scheduled_for": ["Must be a future ISO 8601 timestamp."]
}
} detail— toujours présent, sûr à afficher aux humains (il est traduit lorsque la locale est disponible).code— présent pour les états d'erreur distinguables. Stable d'une version d'API à l'autre ; faites la correspondance là-dessus pour la logique de nouvelle tentative.field_errors— présent uniquement sur les échecs de validation 400. Table nom-de-champ → liste de messages.
Matrice des codes de statut
| Statut | Classe | Réessayer ? | Causes courantes |
|---|---|---|---|
| 400 | Validation | Non | Champ requis manquant, mauvais format, violation de règle métier |
| 401 | Auth | Non | Identifiant manquant / incorrect / révoqué |
| 402 | Facturation | Non | Abonnement en retard de paiement, limite de la formule gratuite atteinte |
| 403 | Permission | Non | L'identifiant n'a pas le scope, ou la ressource appartient à une autre organisation |
| 404 | Introuvable | Non | La ressource n'existe pas, ou n'est pas visible pour votre identifiant |
| 409 | Conflit | Parfois | Réutilisation incohérente de l'Idempotency-Key, violation de machine à états |
| 422 | Non traitable | Non | Combinaison sémantiquement invalide de champs valides |
| 429 | Débit limité | Oui, avec backoff | Limite de débit par identifiant ou par organisation atteinte |
| 500 | Erreur serveur | Oui, avec backoff | Inattendu — ouvrez un ticket si cela persiste |
| 502 / 503 / 504 | Transitoire | Oui, avec backoff | Amont / déploiement / charge |
Codes d'erreur notables
| Code | Statut | Signification |
|---|---|---|
billing.subscription_past_due | 402 | L'abonnement Stripe de l'organisation est en retard de paiement. Orientez l'administrateur vers /admin/billing. |
billing.free_plan_minutes_exhausted | 402 | La formule gratuite / pilote a utilisé ses 5 inspections. |
billing.signature_pack_exhausted | 402 | Crédits du pack de signatures épuisés ; passez au paiement à la signature ou achetez un autre pack. |
permission_denied | 403 | L'identifiant n'a pas le scope. Voir field_errors.required_scope. |
idempotency.key_mismatch | 409 | Même clé, corps différent. Utilisez une nouvelle clé. |
session.already_ended | 409 | Appel de end sur une session non ouverte (généralement sans danger — end est idempotent, c'est pour les cas limites). |
kyb.not_verified | 403 | Signature QES demandée sur une organisation qui n'a pas passé le KYB. |
rate_limited | 429 | Limite de débit par identifiant (60 rpm) ou par organisation (600 rpm) atteinte. |
En-têtes de limite de débit
Chaque réponse — succès ou erreur — porte l'état de la limite de débit :
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1716461235 X-RateLimit-Limit— le plafond qui s'applique à cette requête.X-RateLimit-Remaining— combien d'appels restent dans la fenêtre en cours.X-RateLimit-Reset— timestamp Unix auquelRemainingse réinitialise.
Sur un 429, Retry-After est également défini (en secondes) conformément à la RFC 6585.
Portées de limitation de débit
| Portée | Limite | Fenêtre |
|---|---|---|
| Par identifiant | 60 | 1 minute |
| Par organisation (tous identifiants confondus) | 600 | 1 minute |
Autorise un pic pendant la première seconde d'une minute. Atteignez le plafond et les requêtes suivantes dans la fenêtre renvoient 429.
Stratégie de nouvelle tentative
Backoff exponentiel avec jitter pour 429 / 5xx ; ne réessayez pas les 4xx (hormis 408, 425, 429) :
async function withRetry<T>(fn: () => Promise<T>, max = 5): Promise<T> {
let lastErr: unknown;
for (let i = 0; i < max; i++) {
try {
return await fn();
} catch (err: any) {
const status = err?.status;
if (status >= 400 && status < 500 && ![408, 425, 429].includes(status)) {
throw err; // not retryable
}
const base = status === 429 ? (err.retryAfterMs ?? 1000) : 250 * 2 ** i;
const jitter = Math.random() * base * 0.2;
await new Promise((r) => setTimeout(r, base + jitter));
lastErr = err;
}
}
throw lastErr;
} Les SDK implémentent cela en interne sur 429 + 5xx ; si vous préférez gérer les nouvelles tentatives vous-même, définissez retries: 0 dans les options du SDK.
Corrélation des erreurs
Chaque réponse porte un en-tête X-Request-Id (UUID). Citez-le dans tout ticket de support — nous pouvons extraire la trace complète de la requête à partir de ce seul id.
Et ensuite
- Authentification — rotation des identifiants si vous constatez des 401
- Pagination + idempotence — les patterns qui interagissent avec les nouvelles tentatives