# Les deux identifiants

> Idempotency-Key protège le transport, external_transaction_id protège la facture. Ce que fait chacun, pourquoi au moins l'un est obligatoire, et pourquoi envoyer les deux.

Noryo Merchant API reconnaît deux identifiants. Ce ne sont pas la même chose, et les confondre est l'erreur la plus coûteuse que cette API permette.

| | `Idempotency-Key` | `external_transaction_id` |
|---|---|---|
| Où | En-tête de requête | Champ du corps (création uniquement) |
| Ce qu'il protège | Le **transport** : une reprise réseau | L'**identité économique** : la même transaction |
| Durée de vie | 24 heures | Pas d'expiration |
| Même valeur, même requête | Rejoue la réponse mémorisée, `Noryo-Idempotency-Replayed: true` | Rend le résultat courant de la transaction, `deduplicated: true`, non facturé |
| Même valeur, requête différente | `409 idempotency_key_reuse` | `409 transaction_id_conflict` |
| Portée | Workspace et environnement | Workspace et environnement |

## Au moins l'un des deux est obligatoire

Sur `POST /merchant/v1/enrichments`, une requête sans `Idempotency-Key` ni `external_transaction_id` est refusée :

```json
{
  "type": "https://developer.noryo.app/errors/identifier_required",
  "title": "Identifier required",
  "status": 400,
  "code": "identifier_required",
  "detail": "provide an Idempotency-Key or an external_transaction_id",
  "request_id": "req_…"
}
```

Sans l'un des deux, la branche de déduplication serait sautée entièrement, et deux appels identiques deviendraient deux événements facturés que plus rien ne distingue. L'API refuse plutôt que de laisser cela arriver. Sur le rafraîchissement, l'`Idempotency-Key` est obligatoire dans tous les cas.

## Envoyez les deux

La clé protège la reprise, l'identifiant de transaction protège la facture. Ils couvrent des pannes différentes :

- **Une reprise après un délai dépassé**, quelques secondes plus tard : l'`Idempotency-Key` rend le corps mémorisé, octet pour octet.
- **Le même relevé réimporté trois semaines plus tard** : la clé d'idempotence a expiré depuis longtemps, mais l'`external_transaction_id` reconnaît la transaction et rend son résultat sans le facturer à nouveau.
- **Le rafraîchissement d'un résultat** : il n'est possible que si l'appel d'origine portait un `external_transaction_id`. Voir [Rafraîchir](https://developer.noryo.app/fr-FR/merchant/refresh).

## `external_transaction_id` : la déduplication économique

L'`external_transaction_id` est votre identifiant de la transaction : une chaîne de 1 à 255 caractères, opaque pour Noryo et comparée telle quelle. Le premier appel qui le porte, dans un workspace et un environnement donnés, enregistre la transaction et son entrée économique.

| Appel suivant avec le même identifiant | Réponse |
|---|---|
| Même entrée économique | `200` avec le résultat courant de la transaction : `deduplicated: true`, `deduplicated_by: "external_transaction_id"`, `billable: false`, `units: 0` |
| Entrée économique différente | `409 transaction_id_conflict` |
| Contenu du résultat expiré (plus de 90 jours) | `410 result_content_expired`. Voir [Rétention et 410](https://developer.noryo.app/fr-FR/merchant/retention). |

Le « résultat courant » est le premier résultat de la transaction, remplacé seulement par un rafraîchissement qui aboutit (`refresh_recalculated`). Un rafraîchissement `partial` ou `not_found` ne le remplace pas.

Deux précisions :

- **« Même entrée »** se juge sur la forme canonique des six champs économiques, pas sur les octets : une différence de casse ou d'espaces dans le `descriptor` n'en est pas une, une différence de montant en est une. Voir [Formats](https://developer.noryo.app/fr-FR/merchant/requests/formats).
- **Une panne du moteur n'enregistre pas la transaction.** Un `503 engine_error` ne réserve pas l'identifiant : la reprise suivante est traitée comme un premier appel.

## `Idempotency-Key` : la protection du transport

1 à 255 octets d'ASCII visible (`0x21` à `0x7E`). **Noryo n'applique aucune normalisation** : pas de rognage, pas de changement de casse, pas de traitement Unicode. `KEY-1` et `key-1` sont deux clés différentes. Le seul traitement d'espaces est celui que la pile HTTP retire elle-même autour de toute valeur d'en-tête, ce qui échappe à notre contrôle comme au vôtre.

Fenêtre de rejeu : **24 heures**. À l'intérieur, la même clé avec la même requête rend la réponse mémorisée, avec `Noryo-Idempotency-Replayed: true`. Le détail des cas (concurrence, refus, expiration) est dans [Idempotence](https://developer.noryo.app/fr-FR/merchant/requests/idempotency).

---

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