> For the complete documentation index, see [llms.txt](https://docs.intram.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.intram.org/merchant-api-v1/idempotency.md).

# Idempotency-Key

Toute requête mutante (POST / PUT / PATCH) vers le Merchant API doit porter un header `Idempotency-Key`. C'est ce qui vous permet de **retenter** une requête sans risquer un double payout ou un double refund — même si la première tentative a coupé en cours de route.

## Format de la clé

```http
Idempotency-Key: po-2026-0001-attempt-1
```

* Longueur : **8 à 128 caractères**
* Charset : `[A-Za-z0-9_-]` (alphanum + underscore + tiret)
* Unique pour une opération métier — typiquement un UUID v4 ou ton ID interne suffixé

```javascript
// Exemple Node.js
const idem = crypto.randomUUID();   // 36 caractères, conforme
```

## Comment ça marche côté serveur

1. À réception, l'API calcule une empreinte SHA-256 de `(method + path + body)` et l'associe à la clé d'idempotence dans Redis avec un TTL de **24 h**.
2. Si la clé n'existe pas → la requête est traitée normalement. La réponse (status + body) est mémorisée.
3. Si la clé existe **avec la même empreinte** → la réponse mémorisée est renvoyée immédiatement, avec un header `Idempotent-Replay: true`. **Le traitement ne se rejoue pas.**
4. Si la clé existe **avec une empreinte différente** → 409 Conflict (`idempotency_conflict`). Tu as réutilisé une clé pour une opération différente, c'est interdit.

## Quand tu DOIS rejouer

| Cas                                  | Action                                                                    |
| ------------------------------------ | ------------------------------------------------------------------------- |
| Timeout côté client (pas de réponse) | Rejoue avec **la même** `Idempotency-Key`                                 |
| 5xx serveur                          | Rejoue avec la même clé, après un petit délai                             |
| Connexion réseau coupée              | Rejoue avec la même clé                                                   |
| 4xx (sauf 409 conflict)              | **Ne rejoue pas** — corrige la requête, puis utilise une **nouvelle** clé |

## Quand tu DOIS changer de clé

* Quand le contenu métier change (montant différent, destinataire différent…)
* Quand tu rejoues volontairement la même opération une seconde fois (par exemple un payout récurrent hebdomadaire)

## Erreurs

```json
// Clé manquante sur une mutation
HTTP 400
{
  "error": true,
  "code": "missing_idempotency_key",
  "message": "Idempotency-Key header is required for mutating requests"
}

// Format invalide
HTTP 400
{
  "error": true,
  "code": "invalid_idempotency_key",
  "message": "Idempotency-Key must be 8-128 alphanumeric / - / _ characters"
}

// Réutilisation avec un autre body
HTTP 409
{
  "error": true,
  "code": "idempotency_conflict",
  "message": "Idempotency-Key already used with a different request body"
}
```

## Bonne pratique : préfixer par l'opération métier

| Mauvais           | Bon                         |
| ----------------- | --------------------------- |
| `abc123`          | `payout-2026-05-20-batch-1` |
| `42`              | `refund-tx-AB12CD34EF-full` |
| `uuid-v4-anonyme` | `payment-req-order-4521`    |

Préfixer par l'opération métier facilite le debug : si tu vois une 409, tu sais immédiatement quel flow a généré le doublon.

## Cas d'usage : rejeu sûr après timeout

```javascript
async function safePayout(payload) {
  // 1. Génère la clé UNE SEULE FOIS
  const idem = `payout-${payload.reference}-${Date.now()}`;

  // 2. Boucle de retry avec la MÊME clé
  for (let attempt = 1; attempt <= 3; attempt++) {
    try {
      return await signedPost('/payouts', payload, idem);
    } catch (err) {
      // Si c'est un 5xx ou réseau, rejoue. Sinon, propage.
      if (err.response && err.response.status >= 500) {
        await sleep(2 ** attempt * 1000);
        continue;
      }
      throw err;
    }
  }
}
```

Si la première tentative a en fait abouti (mais que la connexion a coupé avant que tu ne reçoives la réponse), la seconde verra `Idempotent-Replay: true` et te rendra le résultat d'origine — pas de double débit.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.intram.org/merchant-api-v1/idempotency.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
