Définition
L'idempotency key (clé d'idempotence) est un identifiant unique qu'un client envoie avec une requête mutante (typiquement un POST) pour garantir qu'en cas de retry réseau, l'opération ne sera exécutée qu'une seule fois.
Le serveur stocke la clé ; si la même requête revient, il renvoie la réponse précédente sans rejouer l'opération. C'est une best practice critique pour toute API de paiement, où une double exécution signifie double paiement, double facturation ou état corrompu. Stripe, Bridge, Adyen, Mollie, AWS et bien d'autres l'imposent ou la supportent.
Pourquoi c'est critique
Sur le réseau, l'incertitude est la règle : une requête peut expirer sans qu'on sache si elle a été reçue, traitée, ou si un retry va doubler l'opération. Sans clé d'idempotence, impossible de retry en sécurité ; avec, le client peut retry sereinement, le serveur renvoyant la réponse stockée si l'opération a déjà eu lieu.
Comment ça marche
Format et bonnes pratiques
- Génération : UUID v4 (aléatoire), ULID (trié temporellement, debug-friendly), ou hash métier déterministe (
SHA256(user_id + transaction_ref)). - Header :
Idempotency-Key: 4a7f3b80-fe5c-4d12-9c7e-83b3a4ab9b1c. L'IETF a publié un draft, mais la convention est adoptée bien avant son officialisation. - Durée de cache : 24h chez Stripe et Bridge, 1h chez Mollie. Recommandé : 24 à 48h pour les paiements, 7 à 30 jours pour l'asynchrone. Au-delà, un retry rejouerait l'opération.
- Scope : par marchand / par compte (recommandé), plutôt que global (risque de collision).
Payload différent, même clé
Si la même clé est envoyée avec un payload différent, le serveur devrait rejeter la requête (409 Conflict), comme le font Stripe et AWS. Cela évite qu'un payment 200 € soit silencieusement ignoré après un payment 100 € portant la même clé.
Cas d'usage
- Paiements :
POST /payments,/transfers,/charges,/refunds— toujours. - Création de ressources :
POST /customers,/subscriptions— pour éviter les doublons. - Opérations coûteuses :
POST /kyc,/loans/disburse. - Inutile sur :
GET(idempotent par nature),PUTavec ID explicite,DELETE.
Idempotency-Key vs autres techniques
| Technique | Garantie | Complexité |
|---|---|---|
| Idempotency-Key (header) | Exactly-once par appel | Faible côté client |
| Token de transaction unique | Exactly-once par transaction métier | Moyenne |
| Verrou DB optimiste | Exactly-once par ressource | Moyenne |
| Saga + commands | Exactly-once par flow | Élevée |
L'Idempotency-Key agit au niveau API : elle ne protège pas d'un bug métier (un utilisateur qui paie deux fois via deux boutons différents génère deux clés distinctes).
Implémentation côté serveur
Stockage typique : Redis (TTL natif, clé idempotency:{merchant}:{key}), DynamoDB pour la scalabilité, ou PostgreSQL à faible volume.
def handle_payment(req):
key = req.headers.get("Idempotency-Key")
if not key:
return create_payment(req)
cached = redis.get(f"idempotency:{merchant}:{key}")
if cached:
return cached
with redis.lock(f"idempotency-lock:{merchant}:{key}", ttl=30):
cached = redis.get(f"idempotency:{merchant}:{key}")
if cached:
return cached
response = create_payment(req)
redis.setex(f"idempotency:{merchant}:{key}", 86400, response)
return responseLe lock est nécessaire si deux requêtes portant la même clé arrivent en même temps (race condition de retry), pour éviter une double exécution en parallèle.
Comportement en cas d'échec
- Timeout client, succès serveur : retry avec la même clé → réponse cachée (succès).
- Erreur 5xx : la plupart des fournisseurs rejouent (un 5xx n'est pas considéré comme définitif).
- Erreur 4xx : retry avec la même clé → la réponse 4xx cachée est renvoyée.
Ce que l'Idempotency-Key n'est pas
- Pas une transaction distribuée : elle ne garantit pas la cohérence inter-services.
- Pas une protection métier : deux clics sur deux boutons génèrent deux clés.
- Pas un remplacement de l'authentification : c'est complémentaire.
- Pas requise sur GET / PUT : nativement idempotents.
- Pas (encore) un standard formel : draft IETF, mais convention quasi universelle.
Dans l'écosystème PSD2 / Open Finance
- Berlin Group : recommande
X-Request-ID(équivalent). - STET : header
X-Request-ID. - OBIE UK :
x-idempotency-key. - PSP acquéreurs (Stripe, Adyen, Mollie) : universellement adopté.
- Webhooks : la clé est aussi cruciale en réception, pour ne pas traiter deux fois un même événement.
Exemples concrets
- Stripe : header
Idempotency-Key, cache 24h, rejet du payload différent — le standard du secteur. - Bridge :
Idempotency-Key+X-Request-IDselon l'endpoint. - Mollie :
Idempotency-Key, cache 1h. - AWS : paramètre
client-token. - Cas vécu : un marchand sans clé d'idempotence retry après timeout → double prélèvement sur 0,5 % des paiements → dizaines de milliers d'euros de remboursements et perte de confiance.
- Pattern UI : générer la clé au moment du clic et la persister dans le state du formulaire — un reclic 5 secondes plus tard réutilise la même clé, sans double charge.