# Reason keys

> How a slot gets its sentence, which keys exist and what you can override.

Source: https://www.patterna.nl/docs/reference-reason-keys

Every slot in a slate carries a reason. Nothing is recommended without a sentence the user could read. This page lists the keys the engine uses today and how to change their text. The idea behind it is in [Reasons](https://www.patterna.nl/docs/reasons).

## In the slate

Each item has two fields:

- `reason_key`: the key, chosen from the generator that contributed most to the item's score, when what it would say is true; otherwise `recommended`.
- `reason`: the sentence, one string per locale.

```json
{
  "item_id": "doc_1842",
  "position": 0,
  "reason_key": "similar_to_read",
  "reason": {
    "nl": "Omdat je dit net las",
    "en": "Because you just read this"
  },
  "generator": "item_to_item"
}
```

Show `reason` in the user's locale. If that locale is missing, pick a default of your own (for example `nl`). Hide the reason line when you show your own list.

## The keys

The winning generator decides the key. The sentences below are the defaults the engine ships.

| `reason_key` | Left by | Default `nl` | Default `en` |
|---|---|---|---|
| `similar_to_read` | `item_to_item`, with `context.anchor_item_id` | Omdat je dit net las | Because you just read this |
| `similar` | `item_to_item`, without an anchor | Lijkt op wat je leest | Similar to what you read |
| `matches_goals` | `content_affinity` on a goal the user declared | Past bij je doelen | Matches your goals |
| `people_like_you` | `persona_bayes`, `user_bayes`, `user_to_user` (only when the person's nearest users engaged with the item) | Populair bij leden zoals jij | Popular with members like you |
| `trending` | `trending`, on this surface's own 7-day log (all locales together) | Deze week veel gelezen | Widely read this week |
| `editors_pick` | `curated`; also the control arm and fallback when the surface's list is `curated` | Gekozen door de redactie | Chosen by the editors |
| `new` | `surface_specific` for an item under 30 days old; also the control arm and fallback when the list is `newest` | Nieuw | New |
| `popular` | `trending` before the surface has its own log (the tenant-wide prior); the control arm and fallback when the list is `popular` | Populair | Popular |
| `pairs_with` | `item_to_item` with an anchor of another kind that pairs: a recipe with a product, a workout with a product, a community with a document | Past bij dit recept · Past bij deze workout · Gelezen in deze community | Pairs with this recipe · Pairs with this workout · Read in this community |
| `more_about_tag` | `content_affinity` won on behaviour rather than a declared goal; `item_to_item` with any other cross-kind anchor | Meer over dit onderwerp | More about this topic |
| `recommended` | nothing true to say: the winner's own signal was not positive, a cold-start persona, an old item under `surface_specific` | Aanbevolen | Recommended |

`matches_goals` is only given for a goal the user declared; an affinity learned from behaviour gets `more_about_tag`. A collection counts as a recipe collection when its name contains recipe, recept, maaltijd or meal, and as a workout collection when it contains workout, training, oefening or exercise.

`time_fit`, `format_fit` and the region boost never name a reason, so they have no key. `recommended` is a valid key for templates like any other: override its text if "Aanbevolen" does not fit your design, but keep it free of claims. The rule behind it: [Reasons](https://www.patterna.nl/docs/reasons#the-truthfulness-rule).

## Overriding the text

Overrides live in the flow config under `reasons`, keyed by **reason key** or by **generator kind**, then by locale. A reason-key entry wins over a generator-kind entry, which wins over the default. Editing the texts creates a new flow version, like any other change.

```json
{
  "reasons": {
    "similar_to_read": { "nl": "Lees ook", "en": "Read next" },
    "matches_goals": { "nl": "Past bij wat je leest", "en": "Fits what you read" },
    "trending": { "nl": "Veel gelezen deze week", "en": "Read a lot this week" }
  }
}
```

An override replaces the whole entry for that key: give every locale you serve. A key without an override keeps the defaults above.

## Placeholders

| Placeholder | Filled with |
|---|---|
| `{anchor_title}` | the anchor item's title in the request locale |
| `{tag}` | the taxonomy label of the winning tag in that locale; phases and health claims are never named |
| `{segment}` | the persona's label, from a taxonomy tag of kind `segment` |

When a placeholder cannot be filled for a locale (no anchor, no label in that locale), that locale gets the default text instead. The rendered `reason` never contains a placeholder. Serve also checks overrides when you create a flow version: an unknown key, locale or placeholder answers `400 invalid_reason_template`.

## Special cases

- **Control arm.** The control arm gets the surface's editorial list (`newest`, `curated` or `popular`). It carries a reason too (`new`, `editors_pick` or `popular`), so "a reason for every slot" holds in both arms.
- **Your own fallback.** When your backend shows its own list after a timeout, that list is not from Patterna. Mark those slots as yours, show no Patterna reason, and report no interactions for them.
- **Drafted texts.** The `reasons` job drafts a missing nl or en sentence and saves a new, inactive flow version whose note starts with `REVIEW NEEDED`; it never overwrites a text. It drafts per generator kind, and serve reads generator-kind templates, so the drafts work as they are.
