Errori + rate limit
Un unico envelope di errore su ogni endpoint. Status code HTTP standard + una stringa code strutturata per il matching programmatico. Header dei rate limit su ogni risposta così puoi regolare il ritmo prima di incappare in un 429.
Envelope di errore
{
"detail": "Human-readable summary.",
"code": "billing.subscription_past_due",
"field_errors": {
"scheduled_for": ["Must be a future ISO 8601 timestamp."]
}
} detail— sempre presente, sicuro da mostrare agli utenti (è tradotto dove la lingua è disponibile).code— presente per gli stati di errore distinguibili. Stabile tra le versioni dell'API; fai il match su questo per la logica di retry.field_errors— presente solo sui fallimenti di validazione 400. Mappa di nome-campo → lista di messaggi.
Matrice degli status code
| Stato | Classe | Retry? | Cause comuni |
|---|---|---|---|
| 400 | Validazione | No | Campo obbligatorio mancante, formato errato, violazione di regola di business |
| 401 | Auth | No | Credenziale mancante / errata / revocata |
| 402 | Fatturazione | No | Abbonamento scaduto, limite del piano free raggiunto |
| 403 | Permesso | No | La credenziale non ha lo scope, oppure la risorsa è in un'altra org |
| 404 | Non trovato | No | La risorsa non esiste, oppure non è visibile alla tua credenziale |
| 409 | Conflitto | A volte | Mismatch nel riuso della Idempotency-Key, violazione della macchina a stati |
| 422 | Non elaborabile | No | Combinazione semanticamente non valida di campi validi |
| 429 | Rate limit superato | Sì, con backoff | Throttle per-credenziale o per-org raggiunto |
| 500 | Errore server | Sì, con backoff | Imprevisto — apri un ticket se persiste |
| 502 / 503 / 504 | Transitorio | Sì, con backoff | Upstream / deploy / carico |
Codici di errore notevoli
| Codice | Stato | Cosa significa |
|---|---|---|
billing.subscription_past_due | 402 | L'abbonamento Stripe dell'org è scaduto. Indirizza l'admin a /admin/billing. |
billing.free_plan_minutes_exhausted | 402 | Il piano Free / Pilot ha esaurito le sue 5 ispezioni. |
billing.signature_pack_exhausted | 402 | Crediti del pacchetto firme esauriti; paga a firma o acquista un altro pacchetto. |
permission_denied | 403 | La credenziale non ha lo scope. Vedi field_errors.required_scope. |
idempotency.key_mismatch | 409 | Stessa chiave, body diverso. Usa una nuova chiave. |
session.already_ended | 409 | Chiamata a end su una sessione non aperta (di solito sicura — end è idempotente, questo è per i casi limite). |
kyb.not_verified | 403 | Firma QES richiesta su un'org che non ha superato il KYB. |
rate_limited | 429 | Throttle per-credenziale (60 rpm) o per-org (600 rpm) raggiunto. |
Header dei rate limit
Ogni risposta — successo o errore — porta con sé lo stato del throttle:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1716461235 X-RateLimit-Limit— il tetto che si applica a questa richiesta.X-RateLimit-Remaining— quante chiamate restano nella finestra corrente.X-RateLimit-Reset— timestamp Unix a cuiRemainingsi azzera.
Su un 429, è impostato anche Retry-After (in secondi) secondo RFC 6585.
Scope del throttle
| Scope | Limite | Finestra |
|---|---|---|
| Per credenziale | 60 | 1 minuto |
| Per org (su tutte le credenziali) | 600 | 1 minuto |
Burstabile per il primo secondo di un minuto. Raggiungi il tetto e le ulteriori richieste nella finestra vanno in 429.
Strategia di retry
Backoff esponenziale con jitter per 429 / 5xx; non ritentare i 4xx (a parte 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;
} Gli SDK lo implementano internamente su 429 + 5xx; se preferisci gestire i retry da solo, imposta retries: 0 nelle opzioni dell'SDK.
Correlazione degli errori
Ogni risposta porta un header X-Request-Id (UUID). Citalo in ogni ticket di supporto — possiamo recuperare la traccia completa della richiesta da quel solo id.
Cosa c'è dopo
- Autenticazione — rotazione delle credenziali se vedi dei 401
- Paginazione + idempotenza — i pattern che interagiscono con i retry