Sur un réseau peu fiable, une requête POST peut échouer après avoir été exécutée côté serveur : la mission est créée, mais vous ne recevez jamais la réponse. Si vous réessayez naïvement, vous créez un doublon. L’idempotence résout ce problème. En attachant un en-tête Idempotency-Key à vos requêtes mutantes, vous garantissez que l’opération ne produit son effet qu’une seule fois, quel que soit le nombre de fois où vous la rejouez.
L’idempotence est le filet de sécurité de la création de mission. Combinée à la réconciliation par webhook, elle rend votre intégration robuste face aux pannes transitoires.

En bref

En-tête

Idempotency-Key: <votre-clé> — une chaîne libre que vous générez (un UUID v4 est idéal).

Portée

Les POST mutants : toutes les créations (mission, duplication, documents) en priorité, mais aussi refacturations, options, annulation, dates.

Durée de vie

Une clé est mémorisée 24 h. Au-delà, la même clé est traitée comme neuve.

Rejeu

Une réémission identique renvoie la réponse en cache avec l’en-tête Idempotent-Replayed: true.

Comment l’utiliser

1

Générez une clé unique par opération métier

Une clé d’idempotence représente une intention : « créer cette mission-ci ». Générez une nouvelle clé pour chaque opération distincte — un UUID v4 fait parfaitement l’affaire.
2

Attachez-la à la requête mutante

Ajoutez l’en-tête Idempotency-Key à votre POST (en plus de X-Api-Key).
3

Rejouez la même clé en cas d'incertitude

Si la requête échoue par timeout, erreur réseau, 5xx ou 502 avec code: relay_timeout, réémettez-la à l’identique (même clé, même corps). Une réponse déjà obtenue est rejouée depuis le cache. Après une erreur, la requête est relancée : la création de mission et le dépôt de documents sont alors dédoublonnés (la mission ou les documents déjà enregistrés vous sont renvoyés) ; les autres modifications (dates, options, refacturation, annulation) ne le sont pas, mais sont idempotentes par nature ou renvoient un 409 si elles ont déjà été appliquées — relisez la mission en cas de doute. Exception : une réponse avec outcome_unknown: true ne se rejoue pas, vérifiez d’abord.

Exemple : créer une mission de façon sûre

L’en-tête Idempotency-Key est posé une fois ; les deux mêmes appels ne créent qu’une seule mission.
La réponse 201 est identique à chaque rejeu (extrait) :
price est le prix HT de l’aller en chaîne décimale ; transport_id vaut null à la création (la référence M-xxxxx est attribuée juste après et se lit via GET /transports/{id}).

Sémantique détaillée

Le tableau ci-dessous décrit comment le serveur traite une clé selon le scénario.

Le rejeu : Idempotent-Replayed

Quand le serveur renvoie une réponse en cache, il ajoute l’en-tête de réponse :
Cet en-tête vous permet de distinguer une exécution réelle d’un rejeu, par exemple pour la journalisation. Le corps et le code HTTP, eux, sont strictement identiques à la première réponse. Il est aussi posé quand une création (mission ou documents) est reconnue après un 502 relay_timeout : la réponse renvoie alors ce qui avait déjà été créé.
Vérifiez Idempotent-Replayed après chaque retry : sa présence confirme que votre réémission n’a pas créé de doublon.

Conflit de clé : 409

Réutiliser une même clé pour une requête différente — autre corps, autre route ou autre mission — est une erreur de votre côté : la clé identifie une opération précise, elle ne peut pas en désigner deux. Le serveur répond alors 409 Conflict au format RFC 9457 (application/problem+json) :
N’arrondissez pas, ne reformatez pas et ne ré-ordonnez pas le corps entre deux réémissions d’une même clé : la moindre différence est considérée comme un corps divergent et déclenche un 409. Pour une nouvelle opération, générez une nouvelle clé.

Les erreurs serveur ne sont pas cachées

Une réponse 5xx (par exemple 502 Bad Gateway) n’est pas mémorisée. La clé reste attachée à l’empreinte de la requête, ce qui rend le retry sûr : réémettez exactement la même requête (même clé, même corps) et elle est relancée sans risque de doublon. Si le corps a changé entre-temps (re-sérialisation, champ ajouté), le retry est refusé en 409 idempotency_key_reused plutôt que de créer une seconde opération.
Un 502 avec code: relay_timeout ne veut pas dire « rien n’a été fait ». Le service de création n’a pas répondu à temps, mais la mission a pu être enregistrée. Réessayez avec la même Idempotency-Key et le même corps : si la mission existe, elle vous est renvoyée (201 identique) au lieu d’en créer une seconde. Sans clé, ce nouvel essai peut créer un doublon (deux convoyages, deux factures) : vérifiez d’abord vos missions via GET /transports.
Logique de retry recommandée

Quand l’utiliser

L’idempotence protège les opérations qui créent ou modifient un état et dont un doublon aurait des conséquences. Posez systématiquement une Idempotency-Key sur :
Le cas d’usage prioritaire. Un doublon créerait deux missions facturables. C’est ici que l’idempotence apporte le plus de valeur.
Évite d’appliquer deux fois le même changement de prix lors d’un retry.
Empêche d’ajouter la même option (et de recalculer le prix) en double.
Après une réponse 200, un retry avec la même clé rejoue cette réponse (frais compris). Après un 502 (relay_timeout), en revanche, l’annulation a pu aboutir sans que la réponse soit mémorisée : le retry, même clé ou pas, reçoit alors 409 (code: already_cancelled) et n’a aucun effet. Lisez les frais appliqués via GET /transports/{id} (price.amount_ht) ou GET /transports/{id}/price-history.
Évite de réémettre une notification de changement d’horaires au client.
Empêche l’ajout en double des mêmes pièces jointes à la mission. Après un 502 relay_timeout, un retry avec la même clé ne devrait pas créer de doublon.
Une mission ne se duplique qu’une fois : après un 502 relay_timeout, un nouvel essai renvoie 409 (code: already_duplicated, avec duplicated_to) si la duplication a abouti. Lisez la mission créée via duplicated_to.
Les requêtes GET sont déjà idempotentes par nature : elles ne modifient aucun état et n’ont pas besoin d’en-tête Idempotency-Key.

Bonnes pratiques

  • Une clé par intention métier. Générez la clé avant le premier envoi et conservez-la pour tous les retries de cette même opération. Ne réutilisez jamais une clé pour une opération différente.
  • Persistez la clé côté client (file d’attente, base) jusqu’à obtenir une réponse définitive, afin de pouvoir rejouer après un crash de votre processus.
  • Branchez votre logique sur le code HTTP (status), pas sur le texte de detail qui peut évoluer.
  • TTL 24 h : une clé expirée n’offre plus de protection. Une opération reprise au-delà de 24 h sera traitée comme nouvelle — utilisez une clé fraîche.

Étapes suivantes

Démarrage rapide

Créez votre première mission de bout en bout, avec idempotence.

Format des erreurs

Comprenez les réponses 409, 422 et 5xx (RFC 9457).

Webhooks

Réconciliez les événements manqués et déduplisez côté consommateur.