# Hooks

A hook runs after a turn ends. It reads what the turn recorded and acts on it: it can never change the turn, its result or its state.

Turn: The turn ends → Hook: Matching hooks requested → Hook: Each hook runs → Done: hook.completed

The turn reaches its ending without waiting for its hooks. The run’s finalization report waits until every hook it owes has reported.

## Declaring a hook

```yaml
OnAfter:
  - Hook: Title
    Locator: embedded://momor.ai/web.api/title
    Frequency: Once
  - Hook: RelatedQuestions
    Locator: embedded://momor.ai/web.api/related-questions
    OnTerminal: Completed
```

- `Hook` (string): The entry’s name, the workflow’s own label for it.
- `Locator` (string, required): The registered hook to run.
- `OnTerminal` (string, default `Completed`): Which endings the hook follows. Values: `Completed`, `Failed`, `Cancelled`, `Always`.
- `Frequency` (string): Once runs the hook on the run’s first matching ending only. Where absent, it runs on every matching ending. Values: `Once`.

## Which endings a hook follows

| OnTerminal | Follows |
| --- | --- |
| `Completed` | A turn that ended with a successful result. |
| `Failed` | A failed attempt. |
| `Cancelled` | A turn cancelled, superseded or ended by the time ceiling. |
| `Always` | Every ending. A hook following every ending must run: if it is interrupted, recovery runs it again until it reports or its attempts are spent. |

A turn replaced by an edit owes no hooks. A hook already requested by an earlier ending is still owed if an edit later opens an alternative turn.

## Hooks Momor ships

| Locator | What it does |
| --- | --- |
| `embedded://momor.ai/web.api/title` | Asks the model for a title for the run and writes it to the run, where the run list shows it. |
| `embedded://momor.ai/web.api/related-questions` | Once a turn has answered, asks the model what the person would naturally ask next and publishes the list. |
| `embedded://momor.ai/web.api/entity-links` | Once a turn has answered, asks the model which things named in the answer the person would want to open, and publishes them. |

## Retries and idempotency

Each hook request carries a stable idempotency key. A hook that fails records `hook.failed` and is tried again, up to the deployment’s limit of five attempts by default; one that succeeds records `hook.completed` and never runs again. A hook with an effect outside Momor should use its key, so a retry does not repeat the effect.

> **Registered hooks only**
> A hook is code registered with the deployment and bound by its locator. Hooks written as actions in the workflow file, and hooks that fire on events while a turn runs, are not available.
