# Idempotence

Toutes les requêtes `POST`, `PATCH` et `DELETE` nécessitent un en-tête `Idempotency-Key`. `PUT` accepte aussi cet en-tête : avec une clé, la requête suit les règles de cette page ; sans clé, elle s'exécute à chaque envoi.

```
Idempotency-Key: order-create-2026-04-30-abc123
```

Format : 1 à 64 caractères, `[A-Za-z0-9_-:.]`.

Utilisez une valeur stable et unique par opération logique (par exemple un UUID généré côté client).

## Où le rejeu a réellement lieu[​](#où-le-rejeu-a-réellement-lieu "Lien direct vers Où le rejeu a réellement lieu")

Le rejeu a lieu sur la plateforme elle-même : `https://api.dzbuild.app/v1` et l'alias `dzbuild.com/api/v1` se comportent donc de la même façon. Une réponse stockée est conservée 24 heures et les rejeux reviennent avec `Idempotency-Replay: 1`.

Un `POST /v1/orders` réessayé avec la même clé et le même corps obtient la première réponse au lieu de créer une deuxième commande, quel que soit l'hôte appelé.

Le cache est cloisonné par votre `key_id` **et** par l'`Idempotency-Key` : la même valeur envoyée sous une autre clé API ne déduplique pas.

`POST /v1/signups` et `POST /v1/events` exigent toujours l'en-tête, mais ils sont acceptés de façon asynchrone et leur réponse n'est jamais stockée : aucun cache de rejeu ne protège ces deux routes.

`POST /v1/keys` et `POST /v1/webhooks` renvoient un secret affiché une seule fois, leur réponse n'est donc jamais stockée non plus. Un réessai avec la même clé exécute de nouveau l'appel et peut créer une deuxième clé ou un deuxième webhook. Consultez `GET /v1/keys` ou `GET /v1/webhooks` avant de réessayer l'un des deux.

L'idempotence protège des réessais, pas des accès concurrents

La réponse n'est stockée qu'une fois la première requête terminée : deux requêtes dupliquées réellement simultanées peuvent donc s'exécuter toutes les deux. Sérialisez de votre côté les écritures sujettes aux doublons.

## Les clés sont liées au corps de la requête[​](#les-clés-sont-liées-au-corps-de-la-requête "Lien direct vers Les clés sont liées au corps de la requête")

Une clé appartient à la première requête qui l'a utilisée : sa méthode, son chemin et son corps. La query string n'entre pas dans la comparaison. Réutiliser une clé avec un corps, un chemin ou une méthode **différents** renvoie `422` avec le code `idempotency_key_reuse` et n'exécute rien. Le corps est comparé octet par octet : un réessai doit renvoyer exactement les octets de la première tentative, et non une copie reconstruite avec un autre ordre des champs ou d'autres espaces. Générez une clé par opération logique (un UUID neuf), jamais une par endpoint, par session ou par jour.

## Les erreurs sont mises en cache elles aussi[​](#les-erreurs-sont-mises-en-cache-elles-aussi "Lien direct vers Les erreurs sont mises en cache elles aussi")

Une réponse `4xx` renvoyée pour une écriture est stockée et rejouée pendant 24 heures. Après avoir corrigé une erreur de validation, envoyez la requête corrigée avec une **nouvelle** `Idempotency-Key` : l'ancienne clé répond `422 idempotency_key_reuse` pour le corps modifié. Les refus prononcés avant que la requête n'atteigne son traitement ne sont jamais stockés, par exemple un `401`, les `403` liés au pilote, au plan ou à l'état de l'application, tout `429` et une `Idempotency-Key` absente ou mal formée. Un `403` pour un scope manquant vient du traitement lui-même : il est stocké comme tout autre `4xx`. Les réponses `5xx` ne sont pas mises en cache et peuvent être réessayées avec la même clé. Voir [Erreurs](https://dzbuild.com/fr/fr/api-docs/errors.md).

## Distinguer un rejeu d'un appel neuf[​](#distinguer-un-rejeu-dun-appel-neuf "Lien direct vers Distinguer un rejeu d'un appel neuf")

Basez-vous sur `Idempotency-Replay: 1`. Un rejeu signifie qu'aucun nouvel effet de bord n'a eu lieu : sautez donc tout post-traitement local que vous exécuteriez après une véritable écriture.

À ne pas confondre avec `X-Cache`, un en-tête distinct, réservé aux `GET`, qui vaut toujours `MISS`.
