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
| Type | HTTP | Quand |
|---|---|---|
authentication_error | 401 | Clé API manquante, invalide ou révoquée. |
authorization_error | 403 | Clé valide mais pas le droit d'accéder à cette ressource. |
not_found_error | 404 | Ressource inexistante ou hors scope user. |
validation_error | 400 / 422 | Body JSON invalide, schéma Zod refusé, idempotency mismatch. |
rate_limit_error | 429 | Quota par minute dépassé (header Retry-After). |
idempotency_error | 400 / 422 | Même Idempotency-Key avec un body différent du précédent appel. |
api_error | 500 / 502 / 503 | Bug Inkr, erreur upstream Supabase/Resend/Stripe. |
Codes d'erreur fréquents
| Code | Type | Sens |
|---|---|---|
invalid_api_key | authentication_error | Clé inexistante, révoquée ou format invalide. |
idempotency_key_required | validation_error | Header Idempotency-Key manquant sur un POST mutating. |
idempotency_mismatch | idempotency_error | Même clé mais body différent. Régénère une nouvelle UUID. |
field_required | validation_error | Champ JSON obligatoire absent. |
invalid_field_value | validation_error | Type ou contrainte Zod refusée (ex: email mal formé). |
template_not_found | not_found_error | tpl_xxx inexistant ou pas dans ton scope. |
submission_not_found | not_found_error | sub_xxx inexistant ou pas dans ton scope. |
submitter_not_modifiable | validation_error | PATCH sur submitter déjà signé ou refusé. |
pdf_too_large | validation_error | PDF base64 dépasse 10 MiB (passe par pdf_url si besoin). |
pdf_invalid | validation_error | Magic bytes %PDF absents ou Content-Type incorrect. |
rate_limited | rate_limit_error | Quota 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.