# Counterparties and entities

> How identity resolution names a counterparty, what the entity graph keeps apart (brand, legal entity, intermediary) and what is deliberately left unresolved.

An enriched transaction carries, under `resolution`, what Noryo understood about the other end of the operation: the counterparty. This page explains how that identity is established, what the graph keeps apart, and what an absence means.

## What identity resolution returns

`resolution.counterparty` is an object, or `null`. When present:

| Field | Content |
|---|---|
| `entity_id` | The entity's stable, opaque identifier in the shared graph: a bare UUID, the same from one response and one workspace to the next. `series[].evidence` quotes it as `entite:<uuid>`. |
| `name` | The entity's name, as the graph knows it. |
| `kind` | What the entity is: `brand` or `organisation`. |
| `evidence` | How the pattern matched the descriptor: `match` (`jetons` for a whole alias read in the descriptor, `tronque` for a fragment cut short by the bank), `pattern` (the matched alias), `coverage`, and `covered` on the truncated lane. |
| `semantics` | What the graph knows about the entity (see below), or `null` when it knows nothing. |

`coverage` and `covered` are ratios of lengths, properties of the text rather than proof of identity: do not use them as a threshold (see [Score semantics](https://developer.noryo.app/en-GB/transaction-intelligence/score-semantics)). Alongside, `counterparty_type` qualifies the counterparty as a string (`merchant`, `unknown` in the contract's examples).

**Schema `Resolution`**

The shape returned by the `ti_v4` engine.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `policy` | object | — | What "RESOLVED" means depends on the nature of the transaction. A transfer to a person is `resolved` without any identity being created: that is the right answer, not a failure. A cash withdrawal is `resolved` without a merchant. A card purchase, however, requires its counterparty. |
| `policy.outcome` | string | yes | `resolved`: every dimension the archetype requires is present. `partial`: the nature is known, but a required dimension is missing, or signals contradict each other, in which case the engine declines to conclude. `unresolved`: even the nature is unknown. Technical errors (401, 409, 503) are never a semantic outcome: they do not carry this field. Values: `resolved`, `partial`, `unresolved`. |
| `policy.archetype` | string | yes | The vocabulary is french and it is the database's (the vocabulary of resolution archetypes), kept as is for `ti_v1`. `inconnu`: the very nature of the transaction is unknown. `non_couvert`: the nature is known but no policy covers it (`missing = [resolution_policy]`). Values: `achat_carte`, `frais_bancaires`, `impot`, `indetermine`, `prelevement`, `remboursement`, `retrait_especes`, `revenu`, `transfert`, `transfert_personne`, `inconnu`, `non_couvert`. |
| `policy.missing` | array<string> | yes | The dimensions required by the archetype that are absent. |
| `policy.policy_version` | string | yes | Values: `ti_resolution_v1`. |
| `normalized` | object | — | What the normaliser read. Strings, not scores. |
| `normalized.descriptor` | string | — | — |
| `normalized.signal` | string | — | — |
| `normalized.useful` | string | — | — |
| `normalized.residual` | string | — | — |
| `normalized.normalizer_version` | string | — | — |
| `counterparty` | object \| null | — | `null` is a result, not a failure: an honest unknown. |
| `counterparty.entity_id` | string · uuid | — | The stable identifier of the entity in the graph, as a bare UUID, with no prefix. It is opaque: do not assume any structure in it. The same identifier designates the same entity from one response to the next and from one workspace to the next (the graph is shared; the identifier is not data belonging to your workspace). It is quoted as is in `series[].evidence` in the form `entite:<uuid>`. |
| `counterparty.name` | string | — | — |
| `counterparty.kind` | string | — | Values: `brand`, `organisation`. |
| `counterparty.evidence` | object | — | How the pattern matched the descriptor. Two length ratios, which are properties of the text and not proof of identity. `match` says by which route (`jetons`: a whole alias in the descriptor; `tronque`: a descriptor fragment cut short by the bank). `pattern` is the matched alias, in plain text. |
| `counterparty.evidence.match` | string | — | Values: `jetons`, `tronque`. |
| `counterparty.evidence.pattern` | string | — | — |
| `counterparty.evidence.coverage` | number ≥ 0 | — | — |
| `counterparty.evidence.covered` | number · 0.6–1 | — | — |
| `counterparty.semantics` | CounterpartySemantics \| null | — | Four different things that look alike, and that this block refuses to confuse. The entity type (`counterparty.kind`: brand, legal entity, payment intermediary) says what it is. The activity (`activity`) says what it does, in industry names. The category (`category`, outside this object) says where the money went, in budget lines. The transaction type (`transaction_type`) says what happened. None is derived from another. And there is no `category = subscription`: a subscription is not a kind of spending, it is a model of commercial relationship. `null` when the graph knows nothing about this entity: an abstention, not a failure. |
| `counterparty.semantics.activity` | object \| null | — | The trade, on two levels (`parent` then `code`). It stops at the parent when the source stops there: a `utility` role says "network" without saying which one, and `utilities` is then returned, never `utilities.electricity`. What is not known is not filled in. |
| `counterparty.semantics.activity.code` | string | — | — |
| `counterparty.semantics.activity.label` | string | — | — |
| `counterparty.semantics.activity.parent` | string \| null | — | — |
| `counterparty.semantics.recurring_model` | string \| null | — | What is usual in this trade, not a verdict on this transaction. A streaming service sells by subscription; an electricity supplier bills repeatedly without anyone subscribing; a supermarket sells per purchase. It is a plausibility that reinforces a rhythm already observed in the workspace. On its own, it never concludes: `commonly_subscription_based` on an isolated transaction does not make a subscription, and it is the series (`series[].type = subscription`) that decides. Values: `commonly_subscription_based`, `commonly_recurring_but_not_subscription`, `usually_one_off`, `both_recurring_and_one_off`, `unknown`, `null`. |
| `counterparty.semantics.roles` | array<string> | — | The roles the entity holds in the graph (`merchant`, `utility`, `government`, `financial_institution`, `payment_intermediary`…). A role is not a trade, but it says enough to derive one when no catalogue covers the entity. |
| `counterparty.semantics.legal_entity` | object \| null | — | A brand is not a legal entity. "Netflix" is what you read on the statement; "Netflix Inc" is what contracts. The two exist separately in the graph, linked by `brand_of`, because merging them would erase the day a brand changes owner. `null` when no parent company is known, not when there is none. |
| `counterparty.semantics.legal_entity.entity_id` | string · uuid | — | The same identifier space as `counterparty.entity_id`: a bare, opaque, stable UUID. |
| `counterparty.semantics.legal_entity.name` | string | — | — |
| `counterparty.semantics.provenance` | object \| null | — | Where what we assert comes from. `nature` ranks knowledge by declared authority, in this order: `curated` (a person verified it), `deterministic` (derived mechanically from a fact already held), `provider` (a third party states it), `inferred` (we deduced it), `participant` (a single client said so). This order is declared, not deduced from a number: without it, a deduced 0.95 would beat a verified 0.90. `strength` only breaks ties at equal nature, and is not calibrated. And no global knowledge comes from a single client: what a workspace observes stays in that workspace. A `participant` nature can never become a truth of the shared graph. |
| `counterparty.semantics.provenance.nature` | string | — | Values: `curated`, `deterministic`, `provider`, `inferred`, `participant`. |
| `counterparty.semantics.provenance.source` | string | — | — |
| `counterparty.semantics.provenance.strength` | number · 0–1 | — | — |
| `counterparty.semantics.provenance.calibration` | string | — | Always `non_calibree` in this version, and that is a constraint, not a convention: the value is a vocabulary in the database, the columns that carry it reference it, and a guard rejects any literal outside the vocabulary at every producer. When a real calibration exists, it will enter through a declared migration, together with the corpus that supports it. Values: `non_calibree`. |
| `counterparty.semantics.provenance.evidence` | object | — | How the provenance was established. The keys vary: `derivation` (`categorie_de_depense_vers_activite`, `role_vers_activite`…), `categorie_source` or `role`, and `note`, a constant sentence in French. None of these values is a number. |
| `counterparty_type` | string | — | — |
| `counterparty_note` | string \| null | — | Says why no identity was established. |
| `category` | object \| null | — | A category is not an identity. When the descriptor names a trade ("BOULANGERIE DUPONT", "PHARMACIE DE LA GARE") rather than a known merchant, the response returns that trade here and leaves `counterparty` at `null`. Returning "Boulangerie" as the counterparty would amount to naming a shop aisle instead of a business. And it is a budget line, not a trade. This category answers "where did this money go?", from the person's point of view. What the entity does is another field, `counterparty.semantics.activity`, and the two do not substitute for each other: EDF sells electricity (`activity = utilities.electricity`) and the spend files under `logement.electricite`. `source` says where the line comes from: `entity_graph` when it is carried by the recognised merchant, `generic_trade_entry` when it is deduced from a trade word read in the descriptor. The first is bounded by `confidence.entity`: a poorly recognised merchant does not yield a reliable category. When a rule of your organisation set the category (see `customization`), `source` is absent: `slug` and `label` then come from the Noryo catalogue, and neither Noryo origin would be true. |
| `category.slug` | string | — | — |
| `category.label` | string \| null | — | — |
| `category.source` | string | — | Values: `entity_graph`, `generic_trade_entry`. |
| `identity_abstention` | string \| null | — | Why the engine declined to name, when it could have. `trade_not_identity`: a trade was recognised, not a merchant. `atm_operator_not_counterparty`: on a cash movement, the recognised bank operates the cash machine; it is not on the other side. `account_bank_not_counterparty`: on a bank fee, it is the account's own bank, and naming it teaches nothing. `person_named_not_brand`: the descriptor names a person, and the recognised merchant belongs to the transfer reference. An abstention is not a failure: `policy.outcome` can remain `resolved` if the archetype did not require an entity. |
| `intermediaries` | array<object> | — | The payment facilitators read in the descriptor (`PAYPAL *NETFLIX` yields one). They are never the counterparty. `evidence` is a code and a source, and no longer the matching pattern itself, which is an internal rule. |
| `intermediaries[].name` | string | yes | — |
| `intermediaries[].role` | string | yes | — |
| `intermediaries[].entity_id` | string · uuid \| null | — | The facilitator's entity in the graph, when it is there. |
| `intermediaries[].carries_sub_merchant` | boolean | — | — |
| `intermediaries[].evidence` | object | yes | — |
| `intermediaries[].evidence.code` | string | yes | The facilitator's code in the reference catalogue. |
| `intermediaries[].evidence.source` | string | yes | Values: `referentiel_facilitateurs`. |
| `transaction_type` | object | — | What the transaction means, as opposed to `payment`, which says how the money moved. `evidence` is a rule code, not an explanation: the rule's internal note came out here in an earlier version; the responses created at that time carry `legacy_note_redacted`. |
| `transaction_type.canonical` | string | yes | — |
| `transaction_type.evidence` | string | yes | `rail_rule`: the rail alone decided. `descriptor_rule`: the descriptor alone. `descriptor_sense_rail_mechanism`: the descriptor gives the meaning, the rail the mechanism, with no conflict. `rail_and_descriptor_agree`: both say the same thing. `descriptor_refines_rail` / `rail_refines_descriptor`: one narrows down the other. `rail_descriptor_conflict`: contradiction, no certainty manufactured (see `signal_conflicts`). `no_rule`: nothing applies. The `+counterparty_nature` suffix says that the nature of the counterparty refined the type. Pattern: `^(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 | yes | — |
| `payment` | object \| null | — | What the engine understood of the rail and the payment mechanism. |
| `payment.rail` | string \| null | — | — |
| `payment.mechanism` | string \| null | — | — |
| `payment.note` | string \| null | — | A constant explanatory sentence, in French, restating the difference between `rail` and `transaction_type`. Not data. |
| `signal_conflicts` | array<SignalConflict> | — | The signals that contradict each other. When there are any, `policy.outcome` is `partial`: the engine declines to conclude. |
| `signal_conflicts[].dimension` | string | yes | — |
| `signal_conflicts[].signals` | array<string> | yes | — |
| `signal_conflicts[].resolution` | string | yes | A sentence, in French: how the conflict was arbitrated. |
| `signal_conflicts[].resolved_to` | string | yes | — |
| `submerchant_lane` | string | — | The outcome of the "sub-merchant" lane (a facilitator carries the real merchant). `none`: lane not opened. `no_candidate`: opened, no entity. `resolved`: a single entity. `ambiguous`: several, abstention. Absent on the oldest responses, created before this lane existed. Values: `none`, `no_candidate`, `resolved`, `ambiguous`. |
| `truncated_lane` | string | — | The outcome of the "truncated descriptor" lane (the bank cut the name short). Same values as `submerchant_lane`. Absent on the oldest responses, created before this lane existed. Values: `none`, `no_candidate`, `resolved`, `ambiguous`. |
| `confidence` | object | — | The name is kept for `ti_v1`; this is not a confidence. Each of these numbers is a declared constant, set by the person who wrote the pattern or the rule, and read as is. Every transaction that hits the same pattern receives the same number. Measured on 644 descriptors: precision as a function of the threshold is not monotone: 0.85 gives 0.86 precision, 0.88 gives 0.33, 0.92 gives 0.25, 0.97 gives 0.79. This number does not rank right above wrong. Use it neither as a probability nor as a threshold: use `policy.outcome`. `0` has a precise meaning per field: abstention (`entity`), unknown role (`counterparty`), conflict with no common ancestor (`transaction_type`). `location` is always `0`: nothing is resolved on this axis in this version. |
| `confidence.entity` | number · 0–1 | yes | — |
| `confidence.counterparty` | number · 0–1 | yes | — |
| `confidence.transaction_type` | number · 0–1 | yes | — |
| `confidence.category` | number · 0–1 | yes | — |
| `confidence.location` | number | yes | Values: `0`. |
| `confidence.calibration` | string | yes | Always `non_calibree` in this version, and that is a constraint, not a convention: the value is a vocabulary in the database, the columns that carry it reference it, and a guard rejects any literal outside the vocabulary at every producer. When a real calibration exists, it will enter through a declared migration, together with the corpus that supports it. Values: `non_calibree`. |
| `timings_ms` | object | — | Durations, in milliseconds. Nothing to do with a confidence. |
| `timings_ms.normalization` | number | — | — |
| `timings_ms.entity_resolution` | number | — | — |
| `timings_ms.submerchant` | number | — | — |
| `timings_ms.total` | number | — | — |
| `versions` | object | — | The versions that produced this response. A client that keeps a number must keep the version alongside it: the same value, under another version, may no longer measure the same thing. |
| `versions.graph` | string | — | The ontology of the entity graph (`onto_v1`). |
| `versions.rules` | string | — | The transaction type rules (`txtype_v1`). |
| `versions.engine` | string | yes | The resolution engine (`ti_v4`). |
| `versions.resolution_policy` | string | yes | The same value as `policy.policy_version`. |
| `versions.calibration` | string | yes | Always `non_calibree` in this version, and that is a constraint, not a convention: the value is a vocabulary in the database, the columns that carry it reference it, and a guard rejects any literal outside the vocabulary at every producer. When a real calibration exists, it will enter through a declared migration, together with the corpus that supports it. Values: `non_calibree`. |

## The entity graph: brand, legal entity, intermediary

The graph separates three things a bank statement blends together.

**The brand** is what you read on the statement: "Netflix", "Monoprix". The response returns it as the counterparty, with `kind` = `brand`.

**The legal entity** is what contracts: "Netflix Inc". It lives separately in the graph, linked to the brand by `brand_of`, and is returned under `counterparty.semantics.legal_entity` (`entity_id`, `name`). Merging the two would erase the day a brand changes owner. `legal_entity` is `null` when no parent company is known, not when there is none.

**The payment intermediary** is the facilitator read in the descriptor: `PAYPAL *NETFLIX` yields one. It is never the counterparty. It is returned in `resolution.intermediaries[]`: `name`, `role`, `entity_id` when it is in the graph, `carries_sub_merchant`, and an `evidence` (`code` and `source`). `submerchant_lane` reports the outcome of the sub-merchant lane, opened when a facilitator carries the real merchant: `none` (not opened), `no_candidate`, `resolved` (a single entity) or `ambiguous` (several candidates, abstention). `truncated_lane` does the same for a descriptor cut short by the bank.

## Four things that look alike

Four fields answer four questions, and none is derived from another.

| Question | Field | Example |
|---|---|---|
| What is it? (entity type) | `counterparty.kind` | `brand` |
| What does it do? (industry names, `parent` then `code`) | `counterparty.semantics.activity` | `retail.grocery`, parent `retail` |
| Where did the money go? (budget lines) | `resolution.category` | `alimentation.supermarche` |
| What happened? | `resolution.transaction_type.canonical` | `card_purchase` |

EDF illustrates the gap between activity and category: the entity sells electricity (`activity` = `utilities.electricity`) and the spend files under `logement.electricite`. Activity stops at the parent when the source stops there: a `utility` role yields `utilities`, never `utilities.electricity`. Transaction type, for its part, is detailed in [Transaction types](https://developer.noryo.app/en-GB/transaction-intelligence/transaction-types).

> **Note** There is no `category = subscription`. A subscription is not a kind of spend, it is a model of commercial relationship: it is read in `recurring_model` and, above all, in the series.

Nor is a category an identity. When the descriptor names a trade ("BOULANGERIE DUPONT") rather than a known merchant, the response returns that trade in `category` with `source` = `generic_trade_entry` and leaves `counterparty` at `null`: "Boulangerie" would name a shop aisle, not a business. `source` = `entity_graph` signals, on the contrary, a category carried by the recognised merchant.

## `recurring_model`: a relationship model, not a category

`counterparty.semantics.recurring_model` says what is usual in that trade: `commonly_subscription_based` (a streaming service sells by subscription), `commonly_recurring_but_not_subscription` (an electricity supplier bills repeatedly without anyone subscribing), `usually_one_off` (a supermarket sells per purchase), `both_recurring_and_one_off`, `unknown`, or `null`.

It is not a verdict on the transaction you sent, but a plausibility that reinforces a rhythm already observed in your workspace; on its own, it never concludes. `commonly_subscription_based` on an isolated transaction does not make a subscription: the series decides, through `series[].type` = `subscription`. And a counterparty is not a series: one merchant can carry several series, and a series never reduces to "every transaction with that counterparty" (see [Recurring series](https://developer.noryo.app/en-GB/transaction-intelligence/recurring-series)).

## Provenance: where a claim comes from

`counterparty.semantics.provenance` says where the knowledge comes from. `nature` ranks it by declared authority, in this order: `curated` (a person verified it), `deterministic` (derived mechanically from a fact already held), `provider` (a third party states it), `inferred` (Noryo deduced it), `participant` (a single client said so). The order is declared, never deduced from a number: `strength` (class `authority_tiebreak`) only breaks ties between two sources of the same nature, and is not calibrated (`calibration` = `non_calibree`).

What a workspace observes stays in that workspace: a `participant` nature never becomes a truth of the shared graph.

`roles` lists the roles the entity holds in the graph (among them `merchant`, `utility`, `government`, `financial_institution`, `payment_intermediary`); a role is not a trade, but it says enough to derive one when no catalogue covers the entity.

**Schema `CounterpartySemantics`**

Four different things that look alike, and that this block refuses to confuse. The entity type (`counterparty.kind`: brand, legal entity, payment intermediary) says what it is. The activity (`activity`) says what it does, in industry names. The category (`category`, outside this object) says where the money went, in budget lines. The transaction type (`transaction_type`) says what happened. None is derived from another. And there is no `category = subscription`: a subscription is not a kind of spending, it is a model of commercial relationship. `null` when the graph knows nothing about this entity: an abstention, not a failure.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `activity` | object \| null | — | The trade, on two levels (`parent` then `code`). It stops at the parent when the source stops there: a `utility` role says "network" without saying which one, and `utilities` is then returned, never `utilities.electricity`. What is not known is not filled in. |
| `activity.code` | string | — | — |
| `activity.label` | string | — | — |
| `activity.parent` | string \| null | — | — |
| `recurring_model` | string \| null | — | What is usual in this trade, not a verdict on this transaction. A streaming service sells by subscription; an electricity supplier bills repeatedly without anyone subscribing; a supermarket sells per purchase. It is a plausibility that reinforces a rhythm already observed in the workspace. On its own, it never concludes: `commonly_subscription_based` on an isolated transaction does not make a subscription, and it is the series (`series[].type = subscription`) that decides. Values: `commonly_subscription_based`, `commonly_recurring_but_not_subscription`, `usually_one_off`, `both_recurring_and_one_off`, `unknown`, `null`. |
| `roles` | array<string> | — | The roles the entity holds in the graph (`merchant`, `utility`, `government`, `financial_institution`, `payment_intermediary`…). A role is not a trade, but it says enough to derive one when no catalogue covers the entity. |
| `legal_entity` | object \| null | — | A brand is not a legal entity. "Netflix" is what you read on the statement; "Netflix Inc" is what contracts. The two exist separately in the graph, linked by `brand_of`, because merging them would erase the day a brand changes owner. `null` when no parent company is known, not when there is none. |
| `legal_entity.entity_id` | string · uuid | — | The same identifier space as `counterparty.entity_id`: a bare, opaque, stable UUID. |
| `legal_entity.name` | string | — | — |
| `provenance` | object \| null | — | Where what we assert comes from. `nature` ranks knowledge by declared authority, in this order: `curated` (a person verified it), `deterministic` (derived mechanically from a fact already held), `provider` (a third party states it), `inferred` (we deduced it), `participant` (a single client said so). This order is declared, not deduced from a number: without it, a deduced 0.95 would beat a verified 0.90. `strength` only breaks ties at equal nature, and is not calibrated. And no global knowledge comes from a single client: what a workspace observes stays in that workspace. A `participant` nature can never become a truth of the shared graph. |
| `provenance.nature` | string | — | Values: `curated`, `deterministic`, `provider`, `inferred`, `participant`. |
| `provenance.source` | string | — | — |
| `provenance.strength` | number · 0–1 | — | — |
| `provenance.calibration` | string | — | Always `non_calibree` in this version, and that is a constraint, not a convention: the value is a vocabulary in the database, the columns that carry it reference it, and a guard rejects any literal outside the vocabulary at every producer. When a real calibration exists, it will enter through a declared migration, together with the corpus that supports it. Values: `non_calibree`. |
| `provenance.evidence` | object | — | How the provenance was established. The keys vary: `derivation` (`categorie_de_depense_vers_activite`, `role_vers_activite`…), `categorie_source` or `role`, and `note`, a constant sentence in French. None of these values is a number. |

## `policy.outcome`: what "resolved" means

`resolution.policy` reports the outcome of resolution:

- `outcome`: `resolved` (every dimension the archetype requires is present), `partial` (the nature is known, but a required dimension is missing or signals contradict each other), `unresolved` (the very nature of the transaction is unknown).
- `archetype`: the nature of the transaction, in the database's French vocabulary: `achat_carte`, `prelevement`, `transfert_personne`, `retrait_especes`, `frais_bancaires`, `impot`, `remboursement`, `revenu`, `transfert`, `indetermine`, `inconnu`, `non_couvert`.
- `missing`: the required dimensions that are absent (`counterparty_entity`, `counterparty_type`, `transaction_type`, `resolution_policy`).

What "resolved" means depends on the archetype. A transfer to a person is `resolved` without any identity being created: that is the right answer, not a failure. A cash withdrawal is `resolved` without a merchant. A card purchase, however, requires its counterparty: without it, `outcome` is `partial` and `missing` contains `counterparty_entity`.

To decide on a treatment, read `policy.outcome`, never the numbers under `confidence`: those are constants declared per pattern, not probabilities.

## What stays unresolved, and why

An absence is a result, and the response says which one.

- `counterparty` = `null`, with `counterparty_note`: no identity was established, and the note says why.
- `identity_abstention`: the engine declined to name when it could have: `trade_not_identity` (a trade was recognised, not a merchant), `atm_operator_not_counterparty` (the recognised bank operates the cash machine), `account_bank_not_counterparty` (on a bank fee, naming the account's own bank teaches nothing), `person_named_not_brand` (the descriptor names a person). `policy.outcome` can remain `resolved` if the archetype did not require an entity.
- `signal_conflicts` not empty: two independent signals contradict each other, and `policy.outcome` becomes `partial`.
- Location: nothing is resolved on that axis in this version, and `confidence.location` is always `0`. The `counterparty_hint` IBAN is reduced on entry to its country and bank code.

Read `signals.used` and `signals.ignored`: it is the only way to know whether a signal you sent weighs on the response.

## Example

The contract's `achat_carte` example:

```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"
  }'
```

Excerpt of the `resolution` returned:

```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 }
}
```

---

Canonical page: https://developer.noryo.app/en-GB/transaction-intelligence/counterparties-and-entities
