Inkr API

Erreurs

Format d'erreur normé, codes HTTP, gestion des retries idempotents.

L'API Inkr utilise une enveloppe d'erreur normée inspirée de Stripe. Tous les codes HTTP d'erreur (4xx, 5xx) retournent ce format :

{
  "error": {
    "type": "validation_error",
    "code": "field_required",
    "message": "Le champ 'submitters' est requis."
  }
}

Types d'erreurs

TypeHTTPQuand
authentication_error401Clé API manquante, invalide ou révoquée.
authorization_error403Clé valide mais pas le droit d'accéder à cette ressource.
not_found_error404Ressource inexistante ou hors scope user.
validation_error400 / 422Body JSON invalide, schéma Zod refusé, idempotency mismatch.
rate_limit_error429Quota par minute dépassé (header Retry-After).
idempotency_error400 / 422Même Idempotency-Key avec un body différent du précédent appel.
api_error500 / 502 / 503Bug Inkr, erreur upstream Supabase/Resend/Stripe.

Codes d'erreur fréquents

CodeTypeSens
invalid_api_keyauthentication_errorClé inexistante, révoquée ou format invalide.
idempotency_key_requiredvalidation_errorHeader Idempotency-Key manquant sur un POST mutating.
idempotency_mismatchidempotency_errorMême clé mais body différent. Régénère une nouvelle UUID.
field_requiredvalidation_errorChamp JSON obligatoire absent.
invalid_field_valuevalidation_errorType ou contrainte Zod refusée (ex: email mal formé).
template_not_foundnot_found_errortpl_xxx inexistant ou pas dans ton scope.
submission_not_foundnot_found_errorsub_xxx inexistant ou pas dans ton scope.
submitter_not_modifiablevalidation_errorPATCH sur submitter déjà signé ou refusé.
pdf_too_largevalidation_errorPDF base64 dépasse 10 MiB (passe par pdf_url si besoin).
pdf_invalidvalidation_errorMagic bytes %PDF absents ou Content-Type incorrect.
rate_limitedrate_limit_errorQuota par minute dépassé.

Idempotence

Tous les POST mutants (/v1/submissions*, /v1/templates, /v1/webhook_endpoints, /v1/submitters/*/embed_token) exigent le header Idempotency-Key. Une UUID v4 fraîche est recommandée :

-H "Idempotency-Key: $(uuidgen)"

Format accepté : ^[A-Za-z0-9_\-+.:=]{1,255}$.

Comportement :

  • Premier appel avec une clé donnée : exécuté normalement, réponse cached côté Inkr (TTL 24h).
  • Retry avec la même clé + même body : Inkr replay la réponse cached (même HTTP status, même JSON, même headers).
  • Retry avec la même clé + body différent : 422 idempotency_mismatch.

Cette idempotence te permet de retry en confiance côté ton serveur en cas de timeout réseau ou de 5xx, sans risquer de créer deux submissions facturables.

Bonnes pratiques retry

async function withRetry(fn, maxAttempts = 3) {
  let lastError
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn()
    } catch (err) {
      lastError = err
      // Ne retry que sur erreurs réseau ou 5xx (pas sur 4xx)
      if (err.status && err.status < 500 && err.status !== 429) throw err

      // Backoff exponentiel + jitter
      const delay = Math.min(2 ** attempt * 1000, 10000) + Math.random() * 500
      await new Promise((r) => setTimeout(r, delay))
    }
  }
  throw lastError
}

Ne retry jamais sur 400, 401, 403, 404 : le retry produira la même erreur. Retry uniquement sur 429 (avec respect de Retry-After) et 5xx.