# Créer un enrichissement

> POST /merchant/v1/enrichments en détail : le corps, les identifiants, la réponse, ce qui est facturé, et chaque issue possible, du rejeu au 409 en passant par le 503.

`POST /merchant/v1/enrichments` rend une identité marchande pour un libellé bancaire brut. Il exige le scope `enrich:write`.

## Le corps

| Champ | Obligatoire | Rôle |
|---|---|---|
| `descriptor` | oui | Le libellé brut, 512 caractères au plus. Le seul champ qui oriente aujourd'hui la résolution. Non conservé. |
| `amount` | non | Montant de la transaction. Entre dans l'identité économique. |
| `currency` | non | ISO 4217 en majuscules. Entre dans l'identité économique. |
| `occurred_at` | non | Date et heure en UTC, suffixe `Z`. Entre dans l'identité économique. |
| `country` | non | ISO 3166-1 alpha-2 en majuscules. Entre dans l'identité économique ; ne filtre pas la réponse. |
| `mcc` | non | Code de catégorie marchande, quatre chiffres en chaîne. Entre dans l'identité économique. |
| `external_transaction_id` | non* | Votre identifiant de la transaction. Porte la déduplication économique. |

\* Au moins l'un des deux est requis : l'en-tête `Idempotency-Key` ou le champ `external_transaction_id`. En production, envoyez les deux. Voir [Les deux identifiants](https://developer.noryo.app/fr-FR/merchant/identifiers).

Les formats exacts sont dans [Formats](https://developer.noryo.app/fr-FR/merchant/requests/formats), les refus dans [Validation](https://developer.noryo.app/fr-FR/merchant/requests/validation).

## Exemple

```bash
curl -sS -X POST "https://api.noryo.app/merchant/v1/enrichments" \
  -H "Authorization: Bearer $NORYO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6c1a2f0e-1b7d-4c55-8f3a-0d9e2b4c7a61" \
  -d '{
    "descriptor": "CB MCDONALDS PARIS 4519",
    "amount": 12.40,
    "currency": "EUR",
    "occurred_at": "2026-09-06T11:42:00Z",
    "country": "FR",
    "mcc": "5814",
    "external_transaction_id": "tx_8f21c0"
  }'
```

## La réponse

Un `200` porte quatre objets :

```json
{
  "enrichment_id": "enr_ffe80956-cc3e-4e1f-b4b6-724ad231fbb7",
  "merchant": {
    "name": "McDonald's",
    "category": "alimentation.restaurant",
    "location": { "city": "PARIS", "postal_code": "75018" },
    "logo_url": "https://www.mcdonalds.fr/apple-touch-icon.png",
    "confidence": 0.96
  },
  "enrichment": { "status": "resolved", "reason": null, "environment": "sandbox", "source": "noryo", "customized": false },
  "usage": { "billable": false, "units": 1, "deduplicated": false, "deduplicated_by": null }
}
```

Avec les en-têtes `Noryo-Request-Id`, `Noryo-Idempotency-Replayed`, `Noryo-RateLimit-Limit` et `Noryo-RateLimit-Remaining`. Chaque champ est décrit dans [Enrichment](https://developer.noryo.app/fr-FR/merchant/models/enrichment) et [Merchant](https://developer.noryo.app/fr-FR/merchant/models/merchant).

**Un `200` ne veut pas dire qu'un marchand a été trouvé.** Lisez `enrichment.status` : `resolved`, `partial` ou `not_found`. Voir [Statuts](https://developer.noryo.app/fr-FR/merchant/enrichments/statuses).

## Ce qui est facturé

| Statut | Production | Sandbox |
|---|---|---|
| `resolved` | `billable: true`, `units: 1` | `billable: false`, `units: 1` (décompté de la dotation) |
| `partial` | non facturé, `units: 0` | `units: 0` |
| `not_found` | non facturé, `units: 0` | `units: 0` |

## Ce qui se passe si…

| Situation | Réponse |
|---|---|
| Vous renvoyez la même requête avec la même `Idempotency-Key` dans les 24 heures | `200`, le corps mémorisé, `Noryo-Idempotency-Replayed: true` ; rien n'est facturé |
| Vous renvoyez la même transaction (même `external_transaction_id`, même entrée), quelle que soit la date | `200`, le résultat courant, `deduplicated: true`, `billable: false`, `units: 0` |
| Même `Idempotency-Key`, autre requête | `409 idempotency_key_reuse` |
| Même `external_transaction_id`, autre entrée économique | `409 transaction_id_conflict` |
| Ni `Idempotency-Key` ni `external_transaction_id` | `400 identifier_required` |
| La même transaction, dont le résultat courant a dépassé ses 90 jours de rétention (jamais pour un `not_found`) | `410 result_content_expired` : le contenu a été purgé. Voir [Rétention et 410](https://developer.noryo.app/fr-FR/merchant/retention). |
| La dotation Sandbox est épuisée et le résultat aurait coûté une unité | `402 sandbox_quota_exhausted` |
| Le moteur de résolution ne répond pas | `503 engine_error` : rien n'est facturé ni mémorisé ; reprenez avec la même `Idempotency-Key` |
| Une requête identique est en cours avec la même clé | La vôtre attend sa fin et rend le rejeu, ou `503 busy` avec `Retry-After` |
| Votre seau de débit est vide | `429 rate_limited` avec `Retry-After` ; rien n'est calculé ni facturé |

## En TypeScript

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

const BASE_URL = "https://api.noryo.app"; // à placer en configuration

export async function createEnrichment(tx: {
  id: string; descriptor: string; amount?: number; currency?: string; occurredAt?: string;
}) {
  const res = await fetch(`${BASE_URL}/merchant/v1/enrichments`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.NORYO_API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": randomUUID(), // à générer une fois par opération et à garder pour les reprises
    },
    body: JSON.stringify({
      descriptor: tx.descriptor,
      external_transaction_id: tx.id,
      // Omettez un champ absent : null est refusé.
      ...(tx.amount !== undefined ? { amount: tx.amount } : {}),
      ...(tx.currency ? { currency: tx.currency.toUpperCase() } : {}),
      ...(tx.occurredAt ? { occurred_at: tx.occurredAt } : {}),
    }),
  });
  const body = await res.json();
  if (!res.ok) throw Object.assign(new Error(body.code), { problem: body });
  return body; // lisez body.enrichment.status avant body.merchant
}
```

Pour les reprises, voir [Reprises](https://developer.noryo.app/fr-FR/merchant/operations/retries).

---

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