LIVE · CATENA D'AUDIT · UE
SISTEMA · 99,99% UPTIME
v 1.0 ↗ FATTO IN UE

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

StatoClasseRetry?Cause comuni
400ValidazioneNoCampo obbligatorio mancante, formato errato, violazione di regola di business
401AuthNoCredenziale mancante / errata / revocata
402FatturazioneNoAbbonamento scaduto, limite del piano free raggiunto
403PermessoNoLa credenziale non ha lo scope, oppure la risorsa è in un'altra org
404Non trovatoNoLa risorsa non esiste, oppure non è visibile alla tua credenziale
409ConflittoA volteMismatch nel riuso della Idempotency-Key, violazione della macchina a stati
422Non elaborabileNoCombinazione semanticamente non valida di campi validi
429Rate limit superatoSì, con backoffThrottle per-credenziale o per-org raggiunto
500Errore serverSì, con backoffImprevisto — apri un ticket se persiste
502 / 503 / 504TransitorioSì, con backoffUpstream / deploy / carico

Codici di errore notevoli

CodiceStatoCosa significa
billing.subscription_past_due402L'abbonamento Stripe dell'org è scaduto. Indirizza l'admin a /admin/billing.
billing.free_plan_minutes_exhausted402Il piano Free / Pilot ha esaurito le sue 5 ispezioni.
billing.signature_pack_exhausted402Crediti del pacchetto firme esauriti; paga a firma o acquista un altro pacchetto.
permission_denied403La credenziale non ha lo scope. Vedi field_errors.required_scope.
idempotency.key_mismatch409Stessa chiave, body diverso. Usa una nuova chiave.
session.already_ended409Chiamata a end su una sessione non aperta (di solito sicura — end è idempotente, questo è per i casi limite).
kyb.not_verified403Firma QES richiesta su un'org che non ha superato il KYB.
rate_limited429Throttle 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 cui Remaining si azzera.

Su un 429, è impostato anche Retry-After (in secondi) secondo RFC 6585.

Scope del throttle

ScopeLimiteFinestra
Per credenziale601 minuto
Per org (su tutte le credenziali)6001 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