DocsGuidesReprises et backoff

Reprises et backoff

Gérez proprement les erreurs 429 et 5xx de l'API COFFRIFY grâce aux en-têtes de limite de débit, au backoff exponentiel avec jitter et à l'Idempotency-Key qui permet de rejouer une écriture sans créer de doublon.

Guides5 min de lectureMis à jour le 10 juin 2026
Télécharger en PDF

Sur un réseau, tout appel finit par échouer un jour : une rafale de requêtes dépasse votre quota, un service met une seconde de trop à répondre, une connexion se coupe en plein vol. La bonne nouvelle, c'est que la plupart de ces erreurs sont temporaires et qu'il suffit de réessayer correctement pour qu'elles disparaissent. Ce guide vous montre comment l'API COFFRIFY signale qu'un nouvel essai est possible, comment construire une boucle de reprise propre (backoff exponentiel avec jitter), et comment l'en-tête Idempotency-Key vous permet de rejouer une écriture sans jamais créer de doublon. Le SDK JavaScript fait déjà tout cela pour vous : vous trouverez à la fin ce qu'il prend en charge automatiquement.

Quelles erreurs faut-il réessayer

La règle est simple : réessayez les erreurs transitoires, ne réessayez jamais les erreurs définitives. Un 429 (trop de requêtes) et les 5xx (incident côté serveur, indisponibilité passagère) se résolvent presque toujours d'eux-mêmes après une courte attente. À l'inverse, un 400 ou un 422 (validation_error) signale un corps de requête incorrect, un 401 (invalid_api_key) une clé erronée, un 403 (scope_missing) une autorisation manquante, et un 404 (not_found) une ressource absente : réessayer à l'identique ne changera rien, il faut corriger la requête.

Statut HTTPSignificationÀ réessayer ?
429Quota dépassé (rate_limited)Oui, après Retry-After
500, 502, 503, 504Erreur ou indisponibilité serveurOui, avec backoff
408 ou timeout réseauRéponse trop lente, connexion coupéeOui, avec backoff
400 / 422Requête invalide (validation_error)Non, corrigez le corps
401 / 403Authentification ou scope (invalid_api_key, scope_missing)Non, corrigez la clé
404Ressource introuvable (not_found)Non
409Conflit d'idempotence (idempotency_conflict)Non, changez de clé

Lire les en-têtes de limite de débit

Chaque réponse de l'API transporte l'état de votre quota dans quatre en-têtes. Surveillez-les en continu pour ralentir avant de heurter le mur, plutôt que d'attendre le 429. Le quota s'applique par espace de travail, sur une fenêtre glissante d'une minute (visible dans la valeur ;w=60 de la politique).

En-têteContenu
X-RateLimit-LimitNombre maximal de requêtes autorisées sur la fenêtre
X-RateLimit-RemainingRequêtes encore disponibles dans la fenêtre en cours
X-RateLimit-ResetHorodatage Unix (secondes) auquel le compteur se réinitialise
X-RateLimit-PolicyPolitique appliquée, par exemple 600;w=60 (600 requêtes par 60 s)
Retry-AfterPrésent uniquement sur un 429 : secondes à patienter avant de réessayer

Quand vous recevez un 429, l'en-tête Retry-After vous donne le délai exact à respecter. Respectez-le : c'est la durée la plus courte qui sera de nouveau acceptée. Attendre moins ne fera que générer un nouveau 429.

const res = await fetch("https://api.coffrify.com/v1/transfers", {
headers: { Authorization: `Bearer ${process.env.COFFRIFY_API_KEY}` },
});
 
const remaining = Number(res.headers.get("X-RateLimit-Remaining"));
const reset = Number(res.headers.get("X-RateLimit-Reset"));
 
if (remaining < 10) {
const waitMs = Math.max(0, reset * 1000 - Date.now());
console.warn(`Quota presque atteint, pause de ${Math.ceil(waitMs / 1000)}s`);
}

Le backoff exponentiel avec jitter

Quand aucun Retry-After n'est fourni (cas des 5xx et des timeouts), n'enchaînez pas les essais en boucle serrée : vous aggraveriez la charge. Utilisez un backoff exponentiel, où le délai double à chaque tentative (par exemple 1 s, 2 s, 4 s, 8 s), borné par un plafond. Ajoutez-y du jitter, une part d'aléatoire, pour éviter que tous vos clients ne réessaient à la même milliseconde et ne forment un pic synchronisé. La formule retenue ci-dessous combine les deux.

  • Délai de base : base * 2 ^ tentative, par exemple 500 ms qui devient 1 s, 2 s, 4 s.
  • Plafond : ne jamais dépasser une borne (par exemple 30 s) pour garder des temps de réponse raisonnables.
  • Jitter : multiplier par un facteur aléatoire (par exemple entre 0,5 et 1,5) pour désynchroniser les clients.
  • Nombre d'essais limité : abandonnez après 4 à 5 tentatives et remontez l'erreur plutôt que de boucler indéfiniment.
  • Priorité au Retry-After : si l'en-tête est présent, il l'emporte toujours sur le calcul du backoff.
const RETRYABLE = new Set([408, 429, 500, 502, 503, 504]);
 
async function withRetry(makeRequest, { maxRetries = 5, base = 500, cap = 30000 } = {}) {
for (let attempt = 0; ; attempt++) {
let res;
try {
res = await makeRequest();
} catch (networkErr) {
// Erreur réseau (connexion coupée) : on traite comme transitoire
if (attempt >= maxRetries) throw networkErr;
await sleep(backoffDelay(attempt, base, cap));
continue;
}
 
if (res.ok || !RETRYABLE.has(res.status)) return res;
if (attempt >= maxRetries) return res; // on rend la dernière réponse d'erreur
 
// Le serveur a la priorité : on respecte Retry-After s'il est fourni
const retryAfter = Number(res.headers.get("Retry-After"));
const delay = Number.isFinite(retryAfter) && retryAfter > 0
? retryAfter * 1000
: backoffDelay(attempt, base, cap);
 
await sleep(delay);
}
}
 
function backoffDelay(attempt, base, cap) {
const exp = Math.min(cap, base * 2 ** attempt);
return Math.floor(exp * (0.5 + Math.random())); // jitter 0,5x à 1,5x
}
 
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

Rejouer sans doublon avec l'Idempotency-Key

Réessayer une lecture (GET) est sans danger : la relancer ne change rien. Mais réessayer une écriture (POST, PUT, PATCH, DELETE) pose un risque réel. Imaginez un POST /v1/transfers qui crée bien le transfert côté serveur, mais dont la réponse se perd à cause d'un timeout réseau : sans précaution, votre boucle de reprise relancerait l'appel et créerait un second transfert. L'en-tête Idempotency-Key élimine ce risque.

Le principe : vous générez une clé unique (un UUID convient parfaitement) avant le premier essai, et vous l'envoyez à l'identique sur tous les essais de cette même opération. Le serveur mémorise le résultat associé à cette clé. Si l'appel a déjà abouti, il vous renvoie la réponse d'origine déjà enregistrée au lieu d'exécuter l'action une seconde fois. La clé doit faire entre 8 et 255 caractères.

POST/v1/transfersCréation de transfert : joignez un en-tête Idempotency-Key pour pouvoir réessayer sans risque de doublon.
import { randomUUID } from "node:crypto";
 
// La clé est générée UNE fois, hors de la boucle de reprise,
// puis réutilisée à l'identique sur chaque tentative.
const idempotencyKey = randomUUID();
 
const res = await withRetry(() =>
fetch("https://api.coffrify.com/v1/transfers", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.COFFRIFY_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey,
},
body: JSON.stringify({
files: [{ name: "contrat.pdf", size: 482113, mime_type: "application/pdf" }],
expires_in_hours: 72,
}),
}),
);
 
if (res.headers.get("X-Coffrify-Idempotent-Replay") === "true") {
console.log("Réponse rejouée : le transfert existait déjà, aucun doublon créé.");
}

Lorsque le serveur renvoie une réponse mémorisée plutôt que de réexécuter l'action, il ajoute l'en-tête X-Coffrify-Idempotent-Replay: true. C'est le signal qu'aucun doublon n'a été créé : votre reprise a abouti proprement. Trois règles encadrent l'usage des clés :

  • Une clé par opération métier. Réutilisez la même clé uniquement pour rejouer le même appel ; pour une nouvelle création, générez une clé fraîche.
  • Même clé, corps différent : rejet. Si vous renvoyez une clé déjà utilisée avec un corps de requête modifié, l'API répond 409 avec le code idempotency_conflict. Ne recyclez jamais une clé pour une charge utile différente.
  • Traitement concurrent. Si deux requêtes portant la même clé arrivent en même temps, la seconde reçoit 409 avec le code idempotency_in_progress : patientez quelques secondes puis réessayez, le résultat enregistré sera alors disponible en rejeu.

Ce que le SDK JavaScript fait pour vous

Si vous utilisez le SDK officiel (package coffrify, le seul disponible aujourd'hui ; les autres langages reposent sur l'API REST en attendant), vous n'avez rien de tout cela à écrire. Le client intègre la reprise de bout en bout : il détecte les 429 et les 5xx, lit Retry-After quand il est présent, applique sinon un backoff exponentiel avec jitter, et plafonne le nombre de tentatives. Pour les méthodes d'écriture, il génère et réutilise automatiquement un Idempotency-Key à travers les essais, de sorte qu'une reprise ne crée jamais de doublon.

import { Coffrify } from "coffrify";
 
// Le client applique reprise + backoff + idempotence par défaut.
const coffrify = new Coffrify({
apiKey: process.env.COFFRIFY_API_KEY,
maxRetries: 4, // ajustable ; mettre 0 pour désactiver la reprise
});
 
// Aucune boucle à écrire : un 429 ou un 503 transitoire est réessayé
// en interne, et un Idempotency-Key est posé automatiquement.
const transfer = await coffrify.transfers.create({
files: [{ name: "contrat.pdf", size: 482113, mime_type: "application/pdf" }],
expires_in_hours: 72,
});
 
console.log(transfer.share_url);

Vous gardez la main : réglez maxRetries à la création du client pour durcir ou assouplir la politique, ou mettez-le à 0 si votre infrastructure gère déjà les reprises en amont. Les erreurs définitives (validation_error, invalid_api_key, scope_missing, not_found) ne sont jamais réessayées et remontent immédiatement sous forme d'exception, avec le request_id attaché pour le diagnostic.


Cette page vous a-t-elle aidé ?
Anonyme, dédupliqué 24h par signature locale.