> ## Documentation Index
> Fetch the complete documentation index at: https://docs.memory.anonalabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Reason

> Synthesize a single answer across everything a space has accumulated, instead of a ranked list.

Most memory APIs stop at retrieval: they hand back a ranked list and leave the reasoning
to you. Reason goes a step further. It runs an agentic pass across the memories in a
space, connecting related facts, weighing recency, and resolving what matters, then
returns one synthesized answer.

| | [Retrieve](https://docs.memory.anonalabs.com/api-reference/retrieve.md) | Reason |
| - | - | - |
| **Returns** | A ranked list of memories | A single synthesized answer |
| **Latency** | Low | Higher, it reasons across the space |
| **Cost** | Lower | Higher |
| **Use when** | You want context to inject into a prompt | You want a conclusion, not the evidence |

## Reason over a space

```http theme={null}
POST /v1/reason
Authorization: Bearer anona_live_YOUR_KEY
Content-Type: application/json
```

```json theme={null}
{
  "space_id": "customer-support-bot",
  "query": "What have we learned about Alice's career?"
}
```

| Parameter | Type | Required | Description |
| - | - | - | - |
| `space_id` | string | Yes | Which space to synthesize from. |
| `query` | string | Yes | What you want to learn. |
| `org_id` | string (UUID) | No | The organization this request is meant for. Optional: an API key belongs to one organization, and naming a different one is refused with `400 org_mismatch` rather than served from the key's. Same as the `X-Anona-Org` header; send one or the other, and see [Choosing an organization](https://docs.memory.anonalabs.com/api-reference/organization.md#choosing-an-organization-per-request). |
| `user_id` | string | No | Reason only over this end user's memories. |
| `agent_id` | string | No | Reason only over this agent's memories. |
| `session_id` | string | No | Reason only over this conversation's memories. |
| `model` | string | No | Which LLM answers: a tier name (`fast`, `balanced`) or a model id. Defaults to the space's setting, then to the platform default. See [model selection](https://docs.memory.anonalabs.com/api-reference/model-selection.md). |
| `depth` | string | No | `fast` (default) or `thorough`. How hard to look before answering — see [Depth](#depth). Defaults to the space's setting, then to `fast`. |
| `tag_groups` | object\[] | No | Boolean tag filter, narrowing what the reasoning agent may look at. Same expression `/v1/retrieve` takes. See [Compound tag filters](https://docs.memory.anonalabs.com/api-reference/retrieve.md#compound-tag-filters). |

## Depth

Reason keeps a layer of standing answers — [memory models](https://docs.memory.anonalabs.com/api-reference/models.md) —
above the notes and the raw memories. On the default depth, a memory model that is
current can answer on its own, which is what makes a typical call fast. When that model
is out of date, so is the answer, and asking the same question again does not help.

`depth: "thorough"` is how you say *look harder*. It makes the pass read the notes and
the raw memories underneath before it answers, whatever the models say.

```bash cURL theme={null}
curl -X POST https://api.anonalabs.com/v1/reason \
  -H "Authorization: Bearer anona_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "space_id": "accounts",
    "query": "Is the two-year renewal a done deal?",
    "depth": "thorough"
  }'
```

```python Python theme={null}
answer = client.reason(
    space_id="accounts",
    query="Is the two-year renewal a done deal?",
    depth="thorough",
)
```

```typescript TypeScript theme={null}
const answer = await anona.reason({
  spaceId: "accounts",
  query: "Is the two-year renewal a done deal?",
  depth: "thorough",
});
```

| | `fast` (default) | `thorough` |
| - | - | - |
| **Reads** | Memory models first; stops there if they are current | Always the notes and the raw memories too |
| **Latency** | Lower | Higher — several seconds more on a typical space |
| **Cost** | Lower | Higher: more retrieval, more tokens |
| **Use when** | The question is routine and the space is settled | The answer looks stale, or the stakes are worth the wait |

Credits are charged on the tokens the call actually uses, so `thorough` costs more
because it does more, not because it is priced differently.

A very deep question over a very large space can still run out of time; that is
`503 reason_timeout`, and dropping back to `fast` is the first thing to try.

### A default for the space

A space that is habitually asked hard questions can make `thorough` its default, so
callers do not have to remember the field:

```http theme={null}
PUT /v1/spaces/{space_id}/reason-settings
```

```bash cURL theme={null}
curl -X PUT https://api.anonalabs.com/v1/spaces/accounts/reason-settings \
  -H "Authorization: Bearer anona_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "model": null, "depth": "thorough" }'
```

```python Python theme={null}
client.set_reason_settings(space_id="accounts", model=None, depth="thorough")
```

```typescript TypeScript theme={null}
await anona.setReasonSettings({ spaceId: "accounts", model: null, depth: "thorough" });
```

Both clients take `model` and `depth` together for the reason below: they are one
resource, and an argument you leave out is an argument that clears the other field.

A `depth` on the request still wins, so one caller can drop back to `fast` for a simple
question without changing the space.

> [!WARNING]
>
> This `PUT` replaces the whole resource. A body naming only `model` clears `depth`, and
> the other way round — send both fields, or send `DELETE` to clear everything.

## Reason about one user

Pass any of the [scope keys](https://docs.memory.anonalabs.com/api-reference/retrieve.md#scoping-within-a-space) and the answer is synthesized from that
scope alone, the same way [Retrieve](https://docs.memory.anonalabs.com/api-reference/retrieve.md) narrows a search. Without
them, reason covers every memory in the space.

```bash cURL theme={null}
curl -X POST https://api.anonalabs.com/v1/reason \
  -H "Authorization: Bearer anona_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "space_id": "customer-support-bot",
    "query": "What is this customer waiting on?",
    "user_id": "acme_corp"
  }'
```

```python Python theme={null}
answer = client.reason(
    space_id="customer-support-bot",
    query="What is this customer waiting on?",
    user_id="acme_corp",
)
```

```typescript TypeScript theme={null}
const answer = await anona.reason({
  spaceId: "customer-support-bot",
  query: "What is this customer waiting on?",
  userId: "acme_corp",
});
```

A scoped question never falls back to the wider space: memories written without a scope
are not visible to a scoped call, so a space that adopts scoping part-way through will
not see its earlier history here until those memories are rewritten with a scope.

**Response** `200 OK`

```json theme={null}
{
  "status": "complete",
  "insights": "Alice has progressed from junior to senior engineer over 6 years, specializing in distributed systems. She has taken on mentorship responsibilities recently.",
  "sources": {
    "models": [],
    "memories": 7,
    "notes": 2
  },
  "rules_applied": [],
  "usage": {
    "input_tokens": 340,
    "output_tokens": 58
  }
}
```

## What the answer was built from

`sources` says which layer the answer came from, so a result that looks wrong can be
diagnosed instead of guessed at.

| Field | Meaning |
| - | - |
| `models` | Ids of the [memory models](https://docs.memory.anonalabs.com/api-reference/models.md) that grounded the answer. |
| `memories` | How many raw memories were used. |
| `notes` | How many consolidated notes were used. |

These are the memories the reasoning pass **declared it used**, not everything it looked
at — which is what makes "the memory is missing" separable from "the memory is there and
was not used". Those need opposite fixes.

A non-empty `models` list is the first thing to check when an answer looks out of date:
it means a standing answer spoke. Refresh that model, or ask again with
[`depth: "thorough"`](#depth) to read past it.

`"memories": 0, "notes": 0` with no models is not an error — it means the pass found
nothing to build on, which is usually an empty space or a scope that matches no memories.

> [!NOTE]
>
> `sources` carries counts, not the memories themselves, because it rides every answer.
> To see *which* memories are behind a named model, read
> `GET /v1/spaces/{space_id}/models/{model_id}` — that returns the list with their text.

```bash cURL theme={null}
curl -X POST https://api.anonalabs.com/v1/reason \
  -H "Authorization: Bearer anona_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "space_id": "customer-support-bot",
    "query": "What have we learned about Alice'"'"'s career?"
  }'
```

```python Python theme={null}
answer = client.reason(
    space_id="customer-support-bot",
    query="What have we learned about Alice's career?",
)
print(answer)
```

```typescript TypeScript theme={null}
const answer = await anona.reason({
  spaceId: "customer-support-bot",
  query: "What have we learned about Alice's career?",
});
console.log(answer.insights);
```

`client.reason()` returns the synthesized string directly, or `None` when nothing was
found. The TypeScript client returns the whole `InsightsResult`, so read `.insights`.

A bare string has nowhere to carry `sources`, so Python also offers
`client.reason_receipt()` (and `async_reason_receipt`): same question, same answer,
same arguments, returning a `ReasonWithReceipt` with `insights`, `sources`,
`rules_applied`, `model` and `usage`. `str()` of it is the answer, so it can stand in
for `reason` anywhere.

```python Python theme={null}
receipt = client.reason_receipt(
    space_id="customer-support-bot",
    query="What have we learned about Alice's career?",
)
print(receipt.insights, receipt.sources, receipt.model)
```

## Which rules shaped the answer

A space can be given [rules](https://docs.memory.anonalabs.com/api-reference/spaces.md#rules) — standing
instructions it must follow whatever the memories say. `rules_applied` names the
ones that actually applied to this answer:

```json theme={null}
{
  "status": "complete",
  "insights": "The two-year renewal is agreed in principle but not committed: no signed document is recorded.",
  "rules_applied": [
    { "id": "rl_7c1f0a2b", "name": "Never call revenue committed" }
  ]
}
```

| Field | Meaning |
| - | - |
| `id` | The rule's id, as `GET /v1/spaces/{space_id}/rules` returns it. |
| `name` | The name you gave the rule. |

**The rule's text is deliberately not echoed.** It is your own configuration,
readable once from `GET /v1/spaces/{space_id}/rules`, while this rides every
answer — and a space may hold 25 active rules of 2,000 characters each. The name
is what identifies which rule spoke.

`rules_applied` is always present and never null. An empty list is the finding
that no rule applied, which is worth being able to establish: a rule overrides
what the memories say, so *the memories are wrong* and *a rule said otherwise*
look identical in the prose and need opposite fixes. When an answer is
surprising, this is the first field to read after
[`sources`](#what-the-answer-was-built-from).

> [!NOTE]
>
> The TypeScript client returns the whole result, so `rules_applied` is on it. The
> Python `client.reason()` returns the synthesized text alone — read the field from
> the API response if you need it.

## How it works

Reason is read-only and never writes new memories. Internally it runs a multi-step pass:
retrieving relevant facts, expanding context through linked entities, and
cross-referencing related memories. Answer quality improves as more relevant memories
accumulate in the space.

> [!NOTE]
>
> `usage` covers that whole internal pass, not just the answer you see. Reason may make
> several LLM calls before it settles on a synthesis, so `output_tokens` is routinely
> several times larger than the visible answer — a one-sentence conclusion can honestly
> report a few hundred output tokens. Credits are charged on these totals. The same
> applies to [asking about one user](https://docs.memory.anonalabs.com/api-reference/user-profiles.md#ask-about-a-user).

## Choose the model that answers

Reason is the call where the model matters most, because it is an agentic pass, not a single
completion, so a stronger model changes the quality of the synthesis rather than just
its wording. Name one per request, or set a default for the space:

```json theme={null}
{
  "space_id": "customer-support-bot",
  "query": "What is this customer waiting on?",
  "model": "balanced"
}
```

The response echoes the model that actually answered, and credits are charged at that
model's rate. Full detail in [model selection](https://docs.memory.anonalabs.com/api-reference/model-selection.md).

## Error responses

| Status | Code | Cause |
| - | - | - |
| 400 | `bad_request` | Missing `query` or `space_id`. |
| 400 | `org_mismatch` | `org_id` or `X-Anona-Org` names an organization other than the key's. |
| 400 | `invalid_org_id` | `X-Anona-Org` is not a UUID, or was sent more than once. A malformed body `org_id` is `422 validation_error`. |
| 401 | `missing_auth` / `invalid_api_key` | Missing or invalid API key. |
| 403 | `space_access_denied` | You are not a member of this space. |
| 404 | `space_not_found` | The space does not exist. |
| 429 | `rate_limited` | Credit quota or rate limit exceeded. |
| 400 | `unsupported_model` | `model` names something this deployment does not serve. |
| 429 | `model_throttled` | The chosen model is at capacity. Anona does not silently substitute another. |

See the full [error reference](https://docs.memory.anonalabs.com/api-reference/errors.md).

## Next steps

> **[Retrieve API](https://docs.memory.anonalabs.com/api-reference/retrieve.md)**
>
> Query memories for specific information.

> **[Chat](https://docs.memory.anonalabs.com/api-reference/chat.md)**
>
> OpenAI-compatible chat with memory built in.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.