# Reprises

> Quelles réponses reprendre, après quel délai, et pourquoi une reprise est sans danger tant qu'elle garde la même Idempotency-Key. Avec une boucle de reprise prête à l'emploi.

Une reprise est sans danger dans Noryo Merchant API **à une condition : réutiliser la même `Idempotency-Key`**. Si la tentative précédente a été réglée, vous recevez sa réponse mémorisée ; si elle ne l'a pas été, la requête est traitée. Dans aucun cas l'enrichissement n'est dupliqué ni facturé deux fois.

## Quoi reprendre

| Réponse | Reprendre ? | Comment |
|---|---|---|
| `429 rate_limited` | Oui | Après `Retry-After` secondes. Rien n'a été calculé ni facturé. |
| `503 busy` | Oui | Après `Retry-After` secondes, même clé. Une requête identique était en cours, ou le service était momentanément contendu ; rien n'a été écrit. |
| `503 engine_error` | Oui | Avec un délai croissant, **même `Idempotency-Key`**. Rien n'a été facturé ni mémorisé. Pas de `Retry-After`. |
| `503 pricing_not_activated` | Plus tard | Après `Retry-After` (de l'ordre de plusieurs minutes). La facturation n'est pas encore activée pour l'opération. |
| `500 internal_error` | Oui, avec prudence | Même clé : si la tentative avait été réglée, vous recevez la réponse mémorisée. Si l'erreur persiste, contactez le support avec le `request_id`. |
| Coupure réseau, délai dépassé côté client | Oui | Même clé, même corps. Voir [Délais d'attente](https://developer.noryo.app/fr-FR/merchant/operations/timeouts). |
| `409` (tout code) | Non | Corrigez la requête ou la clé. |
| `400`, `413`, `415` | Non | Corrigez la requête. |
| `401`, `403` | Non | Corrigez la clé, ses scopes ou l'état du compte. |
| `402 sandbox_quota_exhausted` | Non | La dotation ne se recharge pas. |
| `402 no_active_subscription` | Non | Tant que l'abonnement n'est pas actif. |
| `404`, `410` | Non | L'identifiant n'existe pas pour vous, ou son contenu a expiré. |

`Retry-After` n'apparaît que sur les réponses qu'une reprise peut lever. Sa présence est un bon indicateur ; son absence sur un `4xx` veut dire : ne reprenez pas à l'identique.

## Les règles d'une bonne boucle

1. **Générez l'`Idempotency-Key` une fois**, avant la première tentative, et gardez-la pour toutes les reprises de cette opération.
2. **Renvoyez exactement le même corps.** Un corps différent sous la même clé rend `409 idempotency_key_reuse`.
3. **Respectez `Retry-After`** quand il est présent ; sinon, attendez de plus en plus longtemps entre deux tentatives.
4. **Bornez le nombre de tentatives**, puis remontez l'erreur avec son `request_id`.
5. **Journalisez chaque tentative** : chacune a son propre `Noryo-Request-Id`.

## Une boucle en TypeScript

```ts
import { randomUUID } from "node:crypto";

const BASE_URL = "https://api.noryo.app"; // à placer en configuration
const RETRYABLE = new Set([429, 500, 503]);

export async function enrichWithRetries(body: Record<string, unknown>, maxAttempts = 4) {
  const idempotencyKey = randomUUID(); // une fois par opération
  for (let attempt = 1; ; attempt++) {
    let res: Response | null = null;
    try {
      res = await fetch(`${BASE_URL}/merchant/v1/enrichments`, {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.NORYO_API_KEY}`,
          "Content-Type": "application/json",
          "Idempotency-Key": idempotencyKey, // la même à chaque tentative
        },
        body: JSON.stringify(body),         // le même corps à chaque tentative
        signal: AbortSignal.timeout(10_000),
      });
    } catch {
      // coupure réseau ou délai dépassé : la tentative a pu être réglée, la clé le dira
    }

    if (res && !RETRYABLE.has(res.status)) return res;
    if (attempt >= maxAttempts) return res;

    const retryAfter = Number(res?.headers.get("retry-after"));
    const delayMs = retryAfter > 0 ? retryAfter * 1000 : 500 * 2 ** attempt;
    await new Promise((resolve) => setTimeout(resolve, delayMs));
  }
}
```

Le délai de 10 secondes de cet exemple est un choix côté client, pas une valeur publiée par Noryo. Une réponse rendue après une reprise réussie peut porter `Noryo-Idempotency-Replayed: true` : c'est le signe que la tentative précédente avait abouti.

## Les rafraîchissements

Les mêmes règles s'appliquent à `POST …/refresh`, où l'`Idempotency-Key` est de toute façon obligatoire. Le rafraîchissement n'a pas de déduplication économique : la clé est sa seule protection contre une double facturation. Ne la régénérez jamais entre deux tentatives d'un même rafraîchissement.

---

Page canonique : https://developer.noryo.app/fr-FR/merchant/operations/retries
