# Types de transaction

> Le type de transaction dit ce qui s'est passé ; il ne se confond ni avec le rail de paiement, ni avec la catégorie, ni avec la contrepartie.

Chaque enrichissement porte, sous `resolution.transaction_type`, une lecture de ce qui s'est passé : un achat par carte, par exemple. Cette page décrit ce que ce bloc affirme, comment il est établi à partir des signaux reçus, et ce qu'il ne dit pas. Elle suppose que vous avez déjà [enrichi une transaction](https://developer.noryo.app/fr-FR/transaction-intelligence/enrich-a-transaction) et que vous savez [relire un enrichissement](https://developer.noryo.app/fr-FR/transaction-intelligence/read-an-enrichment).

## Ce que le type affirme

`resolution.transaction_type` est un objet à trois champs, tous requis.

| Champ | Type | Contenu |
|---|---|---|
| `canonical` | chaîne | Le type retenu, par exemple `card_purchase`. |
| `evidence` | chaîne | Le code de la règle qui l'a établi. Un code, pas une explication. |
| `contradiction` | chaîne ou `null` | `null` quand aucune contradiction n'est à signaler, comme dans tous les exemples du contrat. |

Le contrat le formule ainsi : `transaction_type` dit ce que la transaction signifie, par opposition à `payment`, qui dit comment l'argent a circulé. Un salaire reçu par virement SEPA est les deux à la fois : le virement est le rail, le salaire est ce que ce virement représente.

```json
{
  "transaction_type": {
    "canonical": "card_purchase",
    "evidence": "rail_rule",
    "contradiction": null
  },
  "payment": {
    "rail": "card",
    "mechanism": "card_purchase"
  }
}
```

Ici, `payment.mechanism` et `transaction_type.canonical` portent la même valeur : deux réponses à deux questions, qui coïncident sans se confondre. `payment.note` est une phrase constante, en français, qui rappelle cette différence ; ce n'est pas une donnée, ne l'analysez pas. Le schéma `Resolution` complet est dans la [référence](https://developer.noryo.app/fr-FR/transaction-intelligence/reference).

## Quatre questions qui se ressemblent

Le contrat sépare quatre lectures sémantiques qui se ressemblent et ne se confondent pas : le type d'entité, l'activité, la catégorie et le type de transaction. Aucune ne se déduit d'une autre. Le rail s'y ajoute comme une cinquième question, d'une autre nature : il ne dit pas ce que la transaction signifie, mais comment l'argent a circulé.

| Question | Champ | Exemple |
|---|---|---|
| Comment l'argent a-t-il circulé ? | `payment.rail` en entrée ; `resolution.payment.rail` et `resolution.payment.mechanism` en sortie | `card`, `sepa_direct_debit` |
| Que s'est-il passé ? | `resolution.transaction_type.canonical` | `card_purchase` |
| Qu'est-ce que la contrepartie ? | `resolution.counterparty.kind` | `brand` |
| Que fait la contrepartie ? | `resolution.counterparty.semantics.activity.code` | `retail.grocery` |
| À quoi l'argent est-il allé ? | `resolution.category.slug` | `alimentation.supermarche` |

Un achat carte reste un `card_purchase` quelle que soit sa catégorie ; la catégorie est une ligne de budget, pas une sorte d'événement, et deux transactions de même catégorie peuvent avoir des types différents. La distinction entre nature d'entité, activité et catégorie est détaillée dans [Contreparties et entités](https://developer.noryo.app/fr-FR/transaction-intelligence/counterparties-and-entities). Il n'existe aucune `category = subscription` : un abonnement n'est ni une catégorie ni un type de transaction, c'est un modèle de relation que seule une [série](https://developer.noryo.app/fr-FR/transaction-intelligence/recurring-series) peut établir.

## Comment le type est établi

Deux signaux d'entrée portent le type : le rail déclaré dans `payment.rail` et le libellé `descriptor`. La nature de la contrepartie, quand elle est connue, peut affiner le résultat. Le champ `evidence` nomme la règle appliquée.

| Code `evidence` | Signification |
|---|---|
| `rail_rule` | Le rail seul a décidé. |
| `descriptor_rule` | Le libellé seul a décidé. |
| `descriptor_sense_rail_mechanism` | Le libellé donne le sens, le rail le mécanisme, sans conflit. |
| `rail_and_descriptor_agree` | Les deux disent la même chose. |
| `descriptor_refines_rail` | Le libellé précise le rail. |
| `rail_refines_descriptor` | Le rail précise le libellé. |
| `rail_descriptor_conflict` | Contradiction ; aucune certitude n'est fabriquée. |
| `no_rule` | Aucune règle ne s'applique. |
| `legacy_note_redacted` | Réponse créée par une version antérieure, où ce champ portait la note interne de la règle ; la note a été retirée et aucun code de règle n'est rendu. |

Le suffixe `+counterparty_nature` (par exemple `rail_rule+counterparty_nature`) indique que la nature de la contrepartie a affiné le type.

Le libellé `descriptor` et la direction `direction` (`incoming` ou `outgoing`) sont requis dans toute requête ; le rail est facultatif. La direction est transmise au moteur dans tous les cas : chaque exemple du contrat la liste dans `signals.used`. Le schéma accepte aussi `payment.raw_type` ; `signals.used` et `signals.ignored` vous disent, réponse par réponse, s'il a été lu.

> **Note** Les champs `payment.mcc`, `payment.card_present` et `payment.network_merchant_id` sont acceptés en entrée mais figurent aujourd'hui dans `signals.ignored` : personne ne les lit. Un MCC envoyé pendant des mois ne pèse pas sur le type. Lisez `signals.used` dans chaque réponse plutôt que de supposer qu'un signal compte.

Les règles de type sont versionnées : `resolution.versions.rules` vaut `txtype_v1` aujourd'hui. Si vous stockez un type, stockez cette version à côté ; sous une autre version, la même valeur peut ne plus dire la même chose.

## Quand les signaux se contredisent

Quand le rail et le libellé racontent deux histoires, le moteur ne tranche pas en silence :

- `evidence` commence par `rail_descriptor_conflict` ; lisez `contradiction` à côté.
- `resolution.signal_conflicts[]` décrit chaque conflit : la `dimension` concernée, les `signals` en désaccord, une phrase `resolution` en français qui dit comment le conflit a été arbitré, la valeur `resolved_to` retenue, et une entrée `<signal>_suggests` par signal pour ce que chacun proposait. Le même tableau est copié dans `signals.conflicting`.
- `policy.outcome` passe à `partial` : le moteur refuse de conclure.
- `confidence.transaction_type` est plafonné par la politique ; la valeur `0` signifie « conflit sans ancêtre commun ».

**Schéma `SignalConflict`**

Deux signaux indépendants qui se contredisent sur une dimension, et comment le conflit a été arbitré. Aucune certitude n'est fabriquée : le type retenu est plafonné (`confidence.transaction_type` <= 0,25).

| Champ | Type | Requis | Description |
| --- | --- | --- | --- |
| `dimension` | string | oui | — |
| `signals` | array<string> | oui | — |
| `resolution` | string | oui | Une phrase, en français : comment le conflit a été arbitré. |
| `resolved_to` | string | oui | — |

## Ce qu'un type n'affirme pas

**Ce n'est pas une identité.** Un `card_purchase` peut être rendu avec `counterparty` à `null` : la nature de la transaction est connue, l'enseigne ne l'est pas. C'est l'exemple `non_resolue` du contrat : `policy.outcome` vaut `partial`, `policy.missing` contient `counterparty_entity`, et le type est bien `card_purchase`.

**Ce n'est pas une catégorie.** Le type dit ce qui s'est passé, pas à quoi l'argent est allé.

**Ce n'est pas une série.** Un type ne dit rien de la récurrence : un `card_purchase` isolé n'est pas un abonnement, et la valeur `recurring_model` d'une entité est une plausibilité de métier, pas un verdict. Seul `series[].type` conclut ; voir [Séries récurrentes](https://developer.noryo.app/fr-FR/transaction-intelligence/recurring-series).

**Ce n'est pas une relation.** Le type ne dit pas qu'un remboursement répond à tel achat : cela relève de `relationships[]` et de son `type` (`refund_of`, `reversal_of`, `transfer_pair`). Le type résolu alimente ces détecteurs (l'exemple `remboursement` du contrat porte la preuve `resolved_type_refund`), mais la relation est un objet à part ; voir [Relations](https://developer.noryo.app/fr-FR/transaction-intelligence/relationships).

**Ce n'est pas l'archétype de résolution.** `policy.archetype` dit quelle politique s'applique et ce que « résolu » exige pour cette nature de transaction : un virement à une personne est `resolved` sans identité, un retrait d'espèces est `resolved` sans marchand, un achat carte exige sa contrepartie. Son vocabulaire est français et conservé tel quel pour `ti_v1` : `achat_carte`, `prelevement`, `remboursement`, `retrait_especes`, `revenu`, `transfert`, `transfert_personne`, `frais_bancaires`, `impot`, `indetermine`, `inconnu` (la nature même est inconnue) et `non_couvert` (la nature est connue mais aucune politique ne la couvre). `transaction_type.canonical` est le type retenu ; `policy.missing` peut contenir `transaction_type` quand l'archétype l'exige et qu'il manque.

> **Attention** `confidence.transaction_type` n'est pas une probabilité : c'est une constante déclarée par la règle, la même pour toute transaction qui touche la même règle. Ne l'utilisez ni comme seuil ni comme rang ; lisez `policy.outcome`. Voir [Sémantique des scores](https://developer.noryo.app/fr-FR/transaction-intelligence/score-semantics).

## Lire le type dans votre code

```ts
const enrichmentId = "enr_3f2504e0-4f89-41d3-9a0c-0305e82c3301";

const response = await fetch(
  `https://api.noryo.app/transactions/v1/enrichments/${enrichmentId}`,
  { headers: { Authorization: `Bearer ${process.env.NORYO_API_KEY}` } },
);

const enrichment = await response.json();
const type = enrichment.resolution.transaction_type;
const missing = enrichment.resolution.policy?.missing ?? [];

if (type?.evidence?.startsWith("rail_descriptor_conflict") || enrichment.resolution.signal_conflicts?.length) {
  // Signaux contradictoires : policy.outcome vaut "partial".
  // Ne concluez pas sur `canonical` seul ; lisez resolution.signal_conflicts.
}

if (missing.includes("transaction_type")) {
  // L'archétype exigeait un type et aucune règle ne l'a fourni.
}
```

---

Page canonique : https://developer.noryo.app/fr-FR/transaction-intelligence/transaction-types
