LIVE · AUDIT-KETEN · EU-GEHOST
SYSTEEM · 99,99% UPTIME
v 1.0 ↗ GEMAAKT IN DE EU

Fouten + rate limits

Eén error-envelope over elk endpoint. Standaard HTTP-statuscodes + een gestructureerde code-string voor programmatische matching. Rate-limit-headers op elke response zodat u uw tempo kunt bepalen voordat u een 429 raakt.

Error-envelope

{
  "detail": "Human-readable summary.",
  "code": "billing.subscription_past_due",
  "field_errors": {
    "scheduled_for": ["Must be a future ISO 8601 timestamp."]
  }
}
  • detail — altijd aanwezig, veilig om aan mensen te tonen (wordt vertaald waar de locale beschikbaar is).
  • code — aanwezig voor onderscheidbare foutstatussen. Stabiel over API-versies; match hierop voor retry-logica.
  • field_errors — alleen aanwezig bij 400-validatiefouten. Map van veldnaam → lijst met berichten.

Statuscode-matrix

StatusKlasseOpnieuw proberen?Veelvoorkomende oorzaken
400ValidatieNeeOntbrekend verplicht veld, verkeerd formaat, schending van bedrijfsregel
401AuthNeeOntbrekende / foute / ingetrokken credential
402FactureringNeeAbonnement achterstallig, gratis-planlimiet bereikt
403RechtenNeeCredential mist scope, of resource zit in een andere org
404Niet gevondenNeeResource bestaat niet, of is niet zichtbaar voor uw credential
409ConflictSomsIdempotency-Key-hergebruik komt niet overeen, schending van state machine
422OnverwerkbaarNeeSemantisch ongeldige combinatie van geldige velden
429SnelheidslimietJa, met backoffPer-credential- of per-org-throttle bereikt
500ServerfoutJa, met backoffOnverwacht — open een ticket als het aanhoudt
502 / 503 / 504TijdelijkJa, met backoffUpstream / deploy / belasting

Opvallende foutcodes

CodeStatusWat het betekent
billing.subscription_past_due402Stripe-abonnement van de org is achterstallig. Verwijs de beheerder naar /admin/billing.
billing.free_plan_minutes_exhausted402Gratis / Pilot-niveau heeft zijn 5 inspecties gebruikt.
billing.signature_pack_exhausted402Handtekeningpakket-credits opgebruikt; pay-per-sig of koop een nieuw pakket.
permission_denied403Credential mist de scope. Zie field_errors.required_scope.
idempotency.key_mismatch409Dezelfde sleutel, andere body. Gebruik een nieuwe sleutel.
session.already_ended409end aanroepen op een niet-open sessie (meestal veilig — end is idempotent, dit is voor de randgevallen).
kyb.not_verified403QES-handtekening aangevraagd op een org die KYB niet heeft doorlopen.
rate_limited429Per-credential- (60 rpm) of per-org- (600 rpm) throttle bereikt.

Rate-limit-headers

Elke response — succes of fout — draagt de throttle-status:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1716461235
  • X-RateLimit-Limit — de limiet die op dit verzoek van toepassing is.
  • X-RateLimit-Remaining — hoeveel calls resteren in het huidige venster.
  • X-RateLimit-Reset — Unix-timestamp waarop Remaining reset.

Bij een 429 wordt ook Retry-After gezet (in seconden) volgens RFC 6585.

Throttle-scopes

ScopeLimietVenster
Per credential601 minuut
Per org (over alle credentials)6001 minuut

Burstbaar in de eerste seconde van een minuut. Bereik de limiet en verdere verzoeken binnen het venster geven 429.

Retry-strategie

Exponentiële backoff met jitter voor 429 / 5xx; retry geen 4xx (behalve 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;
}

De SDK's implementeren dit intern bij 429 + 5xx; wilt u retries liever zelf afhandelen, zet dan retries: 0 in de SDK-opties.

Foutcorrelatie

Elke response draagt een X-Request-Id-header (UUID). Vermeld deze in elk supportticket — we kunnen alleen op basis van dat id de volledige request-trace ophalen.

Wat nu