# Contreparties et entités

> Comment la résolution d'identité nomme une contrepartie, ce que le graphe d'entités distingue (marque, entité légale, intermédiaire) et ce qui reste volontairement non résolu.

Une transaction enrichie porte, sous `resolution`, ce que Noryo a compris de l'autre bout de l'opération : la contrepartie. Cette page décrit comment cette identité est établie, ce que le graphe sépare, et ce que signifie une absence.

## Ce que rend la résolution d'identité

`resolution.counterparty` est un objet, ou `null`. Quand il est présent :

| Champ | Contenu |
|---|---|
| `entity_id` | L'identifiant stable et opaque de l'entité dans le graphe partagé : un UUID nu, le même d'une réponse et d'un workspace à l'autre. `series[].evidence` le cite sous la forme `entite:<uuid>`. |
| `name` | Le nom de l'entité, tel que le graphe le connaît. |
| `kind` | Ce que l'entité est : `brand` ou `organisation`. |
| `evidence` | Comment le motif a apparié le libellé : `match` (`jetons` : un alias entier lu dans le libellé ; `tronque` : un fragment coupé par la banque), `pattern` (l'alias apparié), `coverage`, et `covered` sur la voie tronquée. |
| `semantics` | Ce que le graphe sait de l'entité (voir plus bas), ou `null` quand il n'en sait rien. |

`coverage` et `covered` sont des rapports de longueurs, des propriétés du texte et non une preuve d'identité : n'en faites pas un seuil (voir [Sémantique des scores](https://developer.noryo.app/fr-FR/transaction-intelligence/score-semantics)). À côté, `counterparty_type` qualifie la contrepartie en une chaîne (`merchant`, `unknown` dans les exemples du contrat).

**Schéma `Resolution`**

La forme rendue par le moteur `ti_v4`.

| Champ | Type | Requis | Description |
| --- | --- | --- | --- |
| `policy` | object | — | Ce que « RÉSOLU » veut dire dépend de la nature de la transaction. Un virement à une personne est `resolved` sans qu'aucune identité ne soit créée — c'est la bonne réponse, pas un échec. Un retrait d'espèces est `resolved` sans marchand. Un achat carte, lui, exige sa contrepartie. |
| `policy.outcome` | string | oui | `resolved` : toutes les dimensions exigées par l'archétype sont là. `partial` : la nature est connue, une dimension exigée manque — ou des signaux se contredisent, auquel cas on refuse de conclure. `unresolved` : on ignore jusqu'à la nature. Les erreurs techniques (401, 409, 503) ne sont jamais une issue sémantique : elles ne portent pas ce champ. Valeurs : `resolved`, `partial`, `unresolved`. |
| `policy.archetype` | string | oui | Le vocabulaire est français et c'est celui de la base (le vocabulaire des archétypes de résolution), conservé tel quel pour `ti_v1`. `inconnu` : la nature même de la transaction est inconnue. `non_couvert` : la nature est connue mais aucune politique ne la couvre (`missing = [resolution_policy]`). Valeurs : `achat_carte`, `frais_bancaires`, `impot`, `indetermine`, `prelevement`, `remboursement`, `retrait_especes`, `revenu`, `transfert`, `transfert_personne`, `inconnu`, `non_couvert`. |
| `policy.missing` | array<string> | oui | Les dimensions exigées par l'archétype et absentes. |
| `policy.policy_version` | string | oui | Valeurs : `ti_resolution_v1`. |
| `normalized` | object | — | Ce que le normaliseur a lu. Des chaînes, pas des scores. |
| `normalized.descriptor` | string | — | — |
| `normalized.signal` | string | — | — |
| `normalized.useful` | string | — | — |
| `normalized.residual` | string | — | — |
| `normalized.normalizer_version` | string | — | — |
| `counterparty` | object \| null | — | `null` est un résultat, pas une panne : un inconnu honnête. |
| `counterparty.entity_id` | string · uuid | — | L'identifiant stable de l'entité dans le graphe, sous forme d'UUID nu, sans préfixe. Il est opaque : ne lui prêtez aucune structure. Le même identifiant désigne la même entité d'une réponse à l'autre et d'un workspace à l'autre (le graphe est partagé ; l'identifiant n'est pas une donnée de votre workspace). Il est cité tel quel dans `series[].evidence` sous la forme `entite:<uuid>`. |
| `counterparty.name` | string | — | — |
| `counterparty.kind` | string | — | Valeurs : `brand`, `organisation`. |
| `counterparty.evidence` | object | — | Comment le motif a apparié le libellé. Deux rapports de longueur, qui sont des propriétés du texte et pas une preuve d'identité. `match` dit par quelle voie (`jetons` : un alias entier dans le libellé ; `tronque` : un fragment de libellé coupé par la banque). `pattern` est l'alias apparié, en clair. |
| `counterparty.evidence.match` | string | — | Valeurs : `jetons`, `tronque`. |
| `counterparty.evidence.pattern` | string | — | — |
| `counterparty.evidence.coverage` | number ≥ 0 | — | — |
| `counterparty.evidence.covered` | number · 0.6–1 | — | — |
| `counterparty.semantics` | CounterpartySemantics \| null | — | Quatre choses différentes qui se ressemblent, et que ce bloc refuse de confondre. Le type d'entité (`counterparty.kind` : marque, entité légale, intermédiaire de paiement) dit ce que c'est. L'activité (`activity`) dit ce que ça fait, en noms d'industrie. La catégorie (`category`, hors de cet objet) dit à quoi l'argent est allé, en lignes de budget. Le type de transaction (`transaction_type`) dit ce qui s'est passé. Aucun ne se déduit d'un autre. Et il n'existe aucun `category = subscription` : un abonnement n'est pas une sorte de dépense, c'est un modèle de relation commerciale. `null` quand le graphe ne sait rien de cette entité — une abstention, pas une panne. |
| `counterparty.semantics.activity` | object \| null | — | Le métier, sur deux niveaux (`parent` puis `code`). On s'arrête au parent quand la source s'y arrête : un rôle `utility` dit « réseau » sans dire lequel, et on rend alors `utilities`, jamais `utilities.electricity`. Ce qui n'est pas su ne se complète pas. |
| `counterparty.semantics.activity.code` | string | — | — |
| `counterparty.semantics.activity.label` | string | — | — |
| `counterparty.semantics.activity.parent` | string \| null | — | — |
| `counterparty.semantics.recurring_model` | string \| null | — | Ce qui est habituel dans ce métier — pas un verdict sur cette transaction. Un service de streaming se vend par abonnement ; un fournisseur d'électricité facture de façon répétée sans qu'on s'abonne ; un supermarché vend à l'acte. C'est une plausibilité qui renforce un rythme déjà observé dans le workspace. Seul, il ne conclut jamais : `commonly_subscription_based` sur une transaction isolée ne fait pas un abonnement, et c'est la série (`series[].type = subscription`) qui tranche. Valeurs : `commonly_subscription_based`, `commonly_recurring_but_not_subscription`, `usually_one_off`, `both_recurring_and_one_off`, `unknown`, `null`. |
| `counterparty.semantics.roles` | array<string> | — | Les rôles portés par l'entité dans le graphe (`merchant`, `utility`, `government`, `financial_institution`, `payment_intermediary`…). Un rôle n'est pas un métier, mais il en dit assez pour en dériver un quand aucun catalogue ne couvre l'entité. |
| `counterparty.semantics.legal_entity` | object \| null | — | Une marque n'est pas une entité légale. « Netflix » est ce qu'on lit sur le relevé ; « Netflix Inc » est ce qui contracte. Les deux existent séparément dans le graphe, reliés par `brand_of`, parce que les fusionner ferait disparaître le jour où une marque change de propriétaire. `null` quand aucune société mère n'est connue — pas quand il n'y en a pas. |
| `counterparty.semantics.legal_entity.entity_id` | string · uuid | — | Le même espace d'identifiants que `counterparty.entity_id` : UUID nu, opaque, stable. |
| `counterparty.semantics.legal_entity.name` | string | — | — |
| `counterparty.semantics.provenance` | object \| null | — | D'où vient ce qu'on affirme. `nature` classe la connaissance par autorité déclarée, dans cet ordre : `curated` (une personne l'a vérifié), `deterministic` (dérivé mécaniquement d'un fait déjà tenu), `provider` (un tiers l'annonce), `inferred` (nous l'avons déduit), `participant` (un seul client l'a dit). Cet ordre est déclaré, pas déduit d'un nombre : sans lui, un 0,95 déduit l'emporterait sur un 0,90 vérifié. `strength` ne départage qu'à nature égale, et n'est pas calibrée. Et aucune connaissance globale ne vient d'un seul client : ce qu'un workspace observe reste dans ce workspace. Une nature `participant` ne peut jamais devenir une vérité du graphe partagé. |
| `counterparty.semantics.provenance.nature` | string | — | Valeurs : `curated`, `deterministic`, `provider`, `inferred`, `participant`. |
| `counterparty.semantics.provenance.source` | string | — | — |
| `counterparty.semantics.provenance.strength` | number · 0–1 | — | — |
| `counterparty.semantics.provenance.calibration` | string | — | Toujours `non_calibree` dans cette version, et c'est une contrainte, pas une convention : la valeur est un vocabulaire en base, les colonnes qui la portent y font référence, et une garde refuse tout littéral hors vocabulaire chez chaque producteur. Le jour où une calibration réelle existe, elle entrera par une migration déclarée, avec le corpus qui la défend. Valeurs : `non_calibree`. |
| `counterparty.semantics.provenance.evidence` | object | — | Comment la provenance a été établie. Les clefs varient : `derivation` (`categorie_de_depense_vers_activite`, `role_vers_activite`…), `categorie_source` ou `role`, et `note`, une phrase constante en français. Aucune de ces valeurs n'est un nombre. |
| `counterparty_type` | string | — | — |
| `counterparty_note` | string \| null | — | Dit pourquoi aucune identité n'a été établie. |
| `category` | object \| null | — | Une catégorie n'est pas une identité. Quand le libellé désigne un métier (« BOULANGERIE DUPONT », « PHARMACIE DE LA GARE ») et non une enseigne connue, la réponse rend ce métier ici et laisse `counterparty` à `null`. Rendre « Boulangerie » comme contrepartie reviendrait à nommer un rayon à la place d'une entreprise. Et c'est une ligne de budget, pas un métier. Cette catégorie répond à « à quoi cet argent est-il allé », du point de vue de la personne. Ce que l'entité fait est un autre champ, `counterparty.semantics.activity`, et les deux ne se substituent pas : EDF vend de l'électricité (`activity = utilities.electricity`) et la dépense se range sous `logement.electricite`. `source` dit d'où vient la ligne : `entity_graph` quand elle est portée par l'enseigne reconnue, `generic_trade_entry` quand elle est déduite d'un mot de métier lu dans le libellé. La première est bornée par `confidence.entity` — une enseigne mal reconnue ne donne pas une catégorie sûre. Quand une règle de votre organisation a posé la catégorie (voir `customization`), `source` est absent : `slug` et `label` viennent alors du catalogue Noryo, et aucune des deux origines Noryo ne serait vraie. |
| `category.slug` | string | — | — |
| `category.label` | string \| null | — | — |
| `category.source` | string | — | Valeurs : `entity_graph`, `generic_trade_entry`. |
| `identity_abstention` | string \| null | — | Pourquoi le moteur a refusé de nommer, alors qu'il aurait pu. `trade_not_identity` : un métier a été reconnu, pas une enseigne. `atm_operator_not_counterparty` : sur un mouvement d'espèces, la banque reconnue exploite le distributeur — elle n'est pas en face. `account_bank_not_counterparty` : sur un frais bancaire, c'est la banque du compte, et la nommer n'apprend rien. `person_named_not_brand` : le libellé nomme une personne, et l'enseigne reconnue appartient au motif du virement. Une abstention n'est pas une panne : `policy.outcome` peut rester `resolved` si l'archétype n'exigeait pas d'entité. |
| `intermediaries` | array<object> | — | Les facilitateurs de paiement lus dans le libellé (`PAYPAL *NETFLIX` en donne un). Ils ne sont jamais la contrepartie. `evidence` est un code et une source — plus jamais le motif d'appariement lui-même, qui est une règle interne. |
| `intermediaries[].name` | string | oui | — |
| `intermediaries[].role` | string | oui | — |
| `intermediaries[].entity_id` | string · uuid \| null | — | L'entité du facilitateur dans le graphe, quand elle y est. |
| `intermediaries[].carries_sub_merchant` | boolean | — | — |
| `intermediaries[].evidence` | object | oui | — |
| `intermediaries[].evidence.code` | string | oui | Le code du facilitateur au référentiel. |
| `intermediaries[].evidence.source` | string | oui | Valeurs : `referentiel_facilitateurs`. |
| `transaction_type` | object | — | Ce que la transaction signifie, par opposition à `payment` qui dit comment l'argent a circulé. `evidence` est un code de règle, pas une explication : la note interne de la règle sortait ici dans une version antérieure ; les réponses créées alors portent `legacy_note_redacted`. |
| `transaction_type.canonical` | string | oui | — |
| `transaction_type.evidence` | string | oui | `rail_rule` : le rail seul a décidé. `descriptor_rule` : le libellé seul. `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` / `rail_refines_descriptor` : l'un précise l'autre. `rail_descriptor_conflict` : contradiction, aucune certitude fabriquée (voir `signal_conflicts`). `no_rule` : rien ne s'applique. Le suffixe `+counterparty_nature` dit que la nature de la contrepartie a affiné le type. Motif : `^(rail_rule\|descriptor_rule\|descriptor_sense_rail_mechanism\|rail_and_descriptor_agree\|descriptor_refines_rail\|rail_refines_descriptor\|rail_descriptor_conflict\|no_rule)(\+counterparty_nature)?$\|^legacy_note_redacted$`. |
| `transaction_type.contradiction` | string \| null | oui | — |
| `payment` | object \| null | — | Ce que le moteur a compris du rail et du mécanisme de paiement. |
| `payment.rail` | string \| null | — | — |
| `payment.mechanism` | string \| null | — | — |
| `payment.note` | string \| null | — | Une phrase explicative constante, en français, qui rappelle la différence entre `rail` et `transaction_type`. Pas une donnée. |
| `signal_conflicts` | array<SignalConflict> | — | Les signaux qui se contredisent. Quand il y en a, `policy.outcome` est `partial` : on refuse de conclure. |
| `signal_conflicts[].dimension` | string | oui | — |
| `signal_conflicts[].signals` | array<string> | oui | — |
| `signal_conflicts[].resolution` | string | oui | Une phrase, en français : comment le conflit a été arbitré. |
| `signal_conflicts[].resolved_to` | string | oui | — |
| `submerchant_lane` | string | — | L'issue de la voie « sous-marchand » (un facilitateur porte l'enseigne réelle). `none` : voie non ouverte. `no_candidate` : ouverte, aucune entité. `resolved` : une entité unique. `ambiguous` : plusieurs, abstention. Absent sur les réponses les plus anciennes, créées avant que cette voie existe. Valeurs : `none`, `no_candidate`, `resolved`, `ambiguous`. |
| `truncated_lane` | string | — | L'issue de la voie « libellé tronqué » (la banque a coupé le nom). Mêmes valeurs que `submerchant_lane`. Absent sur les réponses les plus anciennes, créées avant que cette voie existe. Valeurs : `none`, `no_candidate`, `resolved`, `ambiguous`. |
| `confidence` | object | — | Le nom est conservé pour `ti_v1` ; ce n'est pas une confiance. Chacun de ces nombres est une constante déclarée — posée par la personne qui a écrit le motif ou la règle, et lue telle quelle. Toute transaction qui touche le même motif reçoit le même nombre. Mesuré sur 644 libellés : la précision en fonction du seuil n'est pas monotone — 0,85 donne 0,86 de précision, 0,88 donne 0,33, 0,92 donne 0,25, 0,97 donne 0,79. Ce nombre n'ordonne pas le juste avant le faux. Ne l'utilisez ni comme probabilité ni comme seuil : utilisez `policy.outcome`. `0` a un sens précis par champ : abstention (`entity`), rôle inconnu (`counterparty`), conflit sans ancêtre commun (`transaction_type`). `location` vaut toujours `0` : rien n'est résolu sur cet axe dans cette version. |
| `confidence.entity` | number · 0–1 | oui | — |
| `confidence.counterparty` | number · 0–1 | oui | — |
| `confidence.transaction_type` | number · 0–1 | oui | — |
| `confidence.category` | number · 0–1 | oui | — |
| `confidence.location` | number | oui | Valeurs : `0`. |
| `confidence.calibration` | string | oui | Toujours `non_calibree` dans cette version, et c'est une contrainte, pas une convention : la valeur est un vocabulaire en base, les colonnes qui la portent y font référence, et une garde refuse tout littéral hors vocabulaire chez chaque producteur. Le jour où une calibration réelle existe, elle entrera par une migration déclarée, avec le corpus qui la défend. Valeurs : `non_calibree`. |
| `timings_ms` | object | — | Des durées, en millisecondes. Rien à voir avec une confiance. |
| `timings_ms.normalization` | number | — | — |
| `timings_ms.entity_resolution` | number | — | — |
| `timings_ms.submerchant` | number | — | — |
| `timings_ms.total` | number | — | — |
| `versions` | object | — | Les versions qui ont produit cette réponse. Un client qui conserve un nombre doit conserver la version à côté : la même valeur, sous une autre version, peut ne plus mesurer la même chose. |
| `versions.graph` | string | — | L'ontologie du graphe d'entités (`onto_v1`). |
| `versions.rules` | string | — | Les règles de type de transaction (`txtype_v1`). |
| `versions.engine` | string | oui | Le moteur de résolution (`ti_v4`). |
| `versions.resolution_policy` | string | oui | La même valeur que `policy.policy_version`. |
| `versions.calibration` | string | oui | Toujours `non_calibree` dans cette version, et c'est une contrainte, pas une convention : la valeur est un vocabulaire en base, les colonnes qui la portent y font référence, et une garde refuse tout littéral hors vocabulaire chez chaque producteur. Le jour où une calibration réelle existe, elle entrera par une migration déclarée, avec le corpus qui la défend. Valeurs : `non_calibree`. |

## Le graphe d'entités : marque, entité légale, intermédiaire

Le graphe sépare trois choses qu'un relevé bancaire mélange.

**La marque** est ce que vous lisez sur le relevé : « Netflix », « Monoprix ». La réponse la rend en contrepartie, avec `kind` = `brand`.

**L'entité légale** est ce qui contracte : « Netflix Inc ». Elle vit séparément dans le graphe, reliée à la marque par `brand_of`, et se lit dans `counterparty.semantics.legal_entity` (`entity_id`, `name`). Les fusionner ferait disparaître le jour où une marque change de propriétaire. `legal_entity` vaut `null` quand aucune société mère n'est connue, pas quand il n'y en a pas.

**L'intermédiaire de paiement** est le facilitateur lu dans le libellé : `PAYPAL *NETFLIX` en donne un. Il n'est jamais la contrepartie. Il se lit dans `resolution.intermediaries[]` : `name`, `role`, `entity_id` quand il est dans le graphe, `carries_sub_merchant`, et une `evidence` (`code` et `source`). `submerchant_lane` dit l'issue de la voie « sous-marchand », ouverte quand un facilitateur porte l'enseigne réelle : `none` (non ouverte), `no_candidate`, `resolved` (une entité unique) ou `ambiguous` (plusieurs candidats, abstention). `truncated_lane` fait de même pour un libellé coupé par la banque.

## Quatre choses qui se ressemblent

Quatre champs répondent à quatre questions, et aucun ne se déduit d'un autre.

| Question | Champ | Exemple |
|---|---|---|
| Qu'est-ce que c'est ? (type d'entité) | `counterparty.kind` | `brand` |
| Que fait-elle ? (noms d'industrie, `parent` puis `code`) | `counterparty.semantics.activity` | `retail.grocery`, parent `retail` |
| Où l'argent est-il allé ? (lignes de budget) | `resolution.category` | `alimentation.supermarche` |
| Que s'est-il passé ? | `resolution.transaction_type.canonical` | `card_purchase` |

EDF illustre l'écart entre activité et catégorie : l'entité vend de l'électricité (`activity` = `utilities.electricity`) et la dépense se range sous `logement.electricite`. L'activité s'arrête au parent quand la source s'y arrête : un rôle `utility` donne `utilities`, jamais `utilities.electricity`. Le type de transaction, lui, est détaillé dans [Types de transaction](https://developer.noryo.app/fr-FR/transaction-intelligence/transaction-types).

> **Note** Il n'existe aucune `category = subscription`. Un abonnement n'est pas une sorte de dépense, c'est un modèle de relation commerciale : il se lit dans `recurring_model` et, surtout, dans les séries.

Une catégorie n'est pas non plus une identité. Quand le libellé désigne un métier (« BOULANGERIE DUPONT ») et non une enseigne connue, la réponse rend ce métier dans `category` avec `source` = `generic_trade_entry` et laisse `counterparty` à `null` : « Boulangerie » nommerait un rayon, pas une entreprise. `source` = `entity_graph` signale au contraire une catégorie portée par l'enseigne reconnue.

## `recurring_model` : un modèle de relation, pas une catégorie

`counterparty.semantics.recurring_model` dit ce qui est habituel dans ce métier : `commonly_subscription_based` (le streaming se vend par abonnement), `commonly_recurring_but_not_subscription` (un fournisseur d'électricité facture de façon répétée sans qu'on s'abonne), `usually_one_off` (un supermarché vend à l'acte), `both_recurring_and_one_off`, `unknown`, ou `null`.

Ce n'est pas un verdict sur la transaction envoyée, mais une plausibilité qui renforce un rythme déjà observé dans votre workspace ; seule, elle ne conclut jamais. `commonly_subscription_based` sur une transaction isolée ne fait pas un abonnement : c'est la série qui tranche, par `series[].type` = `subscription`. Et une contrepartie n'est pas une série : une même enseigne peut porter plusieurs séries, et une série ne se réduit jamais à « toutes les transactions de cette contrepartie » (voir [Séries récurrentes](https://developer.noryo.app/fr-FR/transaction-intelligence/recurring-series)).

## Provenance : d'où vient ce qu'on affirme

`counterparty.semantics.provenance` dit d'où vient la connaissance. `nature` la classe par autorité déclarée, dans cet ordre : `curated` (une personne l'a vérifié), `deterministic` (dérivé mécaniquement d'un fait déjà tenu), `provider` (un tiers l'annonce), `inferred` (Noryo l'a déduit), `participant` (un seul client l'a dit). Cet ordre est déclaré, jamais déduit d'un nombre : `strength` (classe `authority_tiebreak`) ne départage que deux sources de même nature, et n'est pas calibrée (`calibration` = `non_calibree`).

Ce qu'un workspace observe reste dans ce workspace : une nature `participant` ne devient jamais une vérité du graphe partagé.

`roles` liste les rôles portés par l'entité dans le graphe (notamment `merchant`, `utility`, `government`, `financial_institution`, `payment_intermediary`) ; un rôle n'est pas un métier, mais il permet d'en dériver un quand aucun catalogue ne couvre l'entité.

**Schéma `CounterpartySemantics`**

Quatre choses différentes qui se ressemblent, et que ce bloc refuse de confondre. Le type d'entité (`counterparty.kind` : marque, entité légale, intermédiaire de paiement) dit ce que c'est. L'activité (`activity`) dit ce que ça fait, en noms d'industrie. La catégorie (`category`, hors de cet objet) dit à quoi l'argent est allé, en lignes de budget. Le type de transaction (`transaction_type`) dit ce qui s'est passé. Aucun ne se déduit d'un autre. Et il n'existe aucun `category = subscription` : un abonnement n'est pas une sorte de dépense, c'est un modèle de relation commerciale. `null` quand le graphe ne sait rien de cette entité — une abstention, pas une panne.

| Champ | Type | Requis | Description |
| --- | --- | --- | --- |
| `activity` | object \| null | — | Le métier, sur deux niveaux (`parent` puis `code`). On s'arrête au parent quand la source s'y arrête : un rôle `utility` dit « réseau » sans dire lequel, et on rend alors `utilities`, jamais `utilities.electricity`. Ce qui n'est pas su ne se complète pas. |
| `activity.code` | string | — | — |
| `activity.label` | string | — | — |
| `activity.parent` | string \| null | — | — |
| `recurring_model` | string \| null | — | Ce qui est habituel dans ce métier — pas un verdict sur cette transaction. Un service de streaming se vend par abonnement ; un fournisseur d'électricité facture de façon répétée sans qu'on s'abonne ; un supermarché vend à l'acte. C'est une plausibilité qui renforce un rythme déjà observé dans le workspace. Seul, il ne conclut jamais : `commonly_subscription_based` sur une transaction isolée ne fait pas un abonnement, et c'est la série (`series[].type = subscription`) qui tranche. Valeurs : `commonly_subscription_based`, `commonly_recurring_but_not_subscription`, `usually_one_off`, `both_recurring_and_one_off`, `unknown`, `null`. |
| `roles` | array<string> | — | Les rôles portés par l'entité dans le graphe (`merchant`, `utility`, `government`, `financial_institution`, `payment_intermediary`…). Un rôle n'est pas un métier, mais il en dit assez pour en dériver un quand aucun catalogue ne couvre l'entité. |
| `legal_entity` | object \| null | — | Une marque n'est pas une entité légale. « Netflix » est ce qu'on lit sur le relevé ; « Netflix Inc » est ce qui contracte. Les deux existent séparément dans le graphe, reliés par `brand_of`, parce que les fusionner ferait disparaître le jour où une marque change de propriétaire. `null` quand aucune société mère n'est connue — pas quand il n'y en a pas. |
| `legal_entity.entity_id` | string · uuid | — | Le même espace d'identifiants que `counterparty.entity_id` : UUID nu, opaque, stable. |
| `legal_entity.name` | string | — | — |
| `provenance` | object \| null | — | D'où vient ce qu'on affirme. `nature` classe la connaissance par autorité déclarée, dans cet ordre : `curated` (une personne l'a vérifié), `deterministic` (dérivé mécaniquement d'un fait déjà tenu), `provider` (un tiers l'annonce), `inferred` (nous l'avons déduit), `participant` (un seul client l'a dit). Cet ordre est déclaré, pas déduit d'un nombre : sans lui, un 0,95 déduit l'emporterait sur un 0,90 vérifié. `strength` ne départage qu'à nature égale, et n'est pas calibrée. Et aucune connaissance globale ne vient d'un seul client : ce qu'un workspace observe reste dans ce workspace. Une nature `participant` ne peut jamais devenir une vérité du graphe partagé. |
| `provenance.nature` | string | — | Valeurs : `curated`, `deterministic`, `provider`, `inferred`, `participant`. |
| `provenance.source` | string | — | — |
| `provenance.strength` | number · 0–1 | — | — |
| `provenance.calibration` | string | — | Toujours `non_calibree` dans cette version, et c'est une contrainte, pas une convention : la valeur est un vocabulaire en base, les colonnes qui la portent y font référence, et une garde refuse tout littéral hors vocabulaire chez chaque producteur. Le jour où une calibration réelle existe, elle entrera par une migration déclarée, avec le corpus qui la défend. Valeurs : `non_calibree`. |
| `provenance.evidence` | object | — | Comment la provenance a été établie. Les clefs varient : `derivation` (`categorie_de_depense_vers_activite`, `role_vers_activite`…), `categorie_source` ou `role`, et `note`, une phrase constante en français. Aucune de ces valeurs n'est un nombre. |

## `policy.outcome` : ce que « résolu » veut dire

`resolution.policy` rend l'issue de la résolution :

- `outcome` : `resolved` (toutes les dimensions exigées par l'archétype sont là), `partial` (la nature est connue, mais une dimension exigée manque ou des signaux se contredisent), `unresolved` (la nature même de la transaction est inconnue).
- `archetype` : la nature de la transaction, dans le vocabulaire français de la base : `achat_carte`, `prelevement`, `transfert_personne`, `retrait_especes`, `frais_bancaires`, `impot`, `remboursement`, `revenu`, `transfert`, `indetermine`, `inconnu`, `non_couvert`.
- `missing` : les dimensions exigées et absentes (`counterparty_entity`, `counterparty_type`, `transaction_type`, `resolution_policy`).

Ce que « résolu » veut dire dépend de l'archétype. Un virement à une personne est `resolved` sans qu'aucune identité ne soit créée : c'est la bonne réponse, pas un échec. Un retrait d'espèces est `resolved` sans marchand. Un achat carte, lui, exige sa contrepartie : sans elle, `outcome` vaut `partial` et `missing` contient `counterparty_entity`.

Pour décider d'un traitement, lisez `policy.outcome`, jamais les nombres de `confidence` : ce sont des constantes déclarées par motif, pas des probabilités.

## Ce qui reste non résolu, et pourquoi

Une absence est un résultat, et la réponse dit lequel.

- `counterparty` = `null`, avec `counterparty_note` : aucune identité n'a été établie, et la note dit pourquoi.
- `identity_abstention` : le moteur a refusé de nommer alors qu'il l'aurait pu : `trade_not_identity` (un métier reconnu, pas une enseigne), `atm_operator_not_counterparty` (la banque reconnue exploite le distributeur), `account_bank_not_counterparty` (sur un frais bancaire, nommer la banque du compte n'apprend rien), `person_named_not_brand` (le libellé nomme une personne). `policy.outcome` peut rester `resolved` si l'archétype n'exigeait pas d'entité.
- `signal_conflicts` non vide : deux signaux indépendants se contredisent, et `policy.outcome` passe à `partial`.
- Le lieu : rien n'est résolu sur cet axe dans cette version, et `confidence.location` vaut toujours `0`. L'IBAN de `counterparty_hint` est réduit dès l'entrée à son pays et à son code banque.

Lisez `signals.used` et `signals.ignored` : c'est la seule façon de savoir si un signal envoyé pèse sur la réponse.

## Exemple

L'exemple `achat_carte` du contrat :

```bash
curl -X POST https://api.noryo.app/transactions/v1/enrichments \
  -H "Authorization: Bearer $NORYO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "descriptor": "CARTE 09/09 MONOPRIX PARIS 15",
    "direction": "outgoing",
    "occurred_at": "2026-09-09T07:31:00+02:00",
    "amount": { "value": "49.90", "currency": "EUR" },
    "payment": { "rail": "card", "mcc": "5411", "card_present": true },
    "location_hint": { "country": "FR" },
    "account_hint": { "iban": "FR7630006000011234567890189" },
    "external_transaction_id": "tx-2026-09-09-0042"
  }'
```

Extrait de la `resolution` rendue :

```json
{
  "policy": { "outcome": "resolved", "archetype": "achat_carte", "missing": [], "policy_version": "ti_resolution_v1" },
  "counterparty": {
    "entity_id": "b83d1756-258c-4541-adc7-54eec83dbd3c",
    "name": "Monoprix",
    "kind": "brand",
    "evidence": { "match": "jetons", "pattern": "MONOPRIX", "coverage": 0.4 },
    "semantics": {
      "roles": ["merchant"],
      "activity": { "code": "retail.grocery", "label": "alimentation generale", "parent": "retail" },
      "legal_entity": { "entity_id": "5ecee4f8-fab0-444f-8711-29ff6e3f875a", "name": "Groupe Casino" },
      "recurring_model": "usually_one_off",
      "provenance": { "nature": "curated", "source": "catalogue_cure", "strength": 0.75, "calibration": "non_calibree" }
    }
  },
  "counterparty_type": "merchant",
  "counterparty_note": null,
  "category": { "slug": "alimentation.supermarche", "label": "Supermarché", "source": "entity_graph" },
  "identity_abstention": null,
  "intermediaries": [],
  "transaction_type": { "canonical": "card_purchase", "evidence": "rail_rule", "contradiction": null }
}
```

---

Page canonique : https://developer.noryo.app/fr-FR/transaction-intelligence/counterparties-and-entities
