> ## 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.

# Extraction settings

> Teach a space your domain: what a write should keep, in your vocabulary, before it ever becomes a memory.

Recording a memory is not storage. Your text goes through one pass that decides which
facts are worth keeping long-term and how they are phrased, and only what survives that
pass is ever stored. It is the highest-leverage step in the whole system: a detail the
extractor drops is not merely ranked low later, it is not there at all, and no amount of
query tuning brings it back.

By default every space runs the same general-purpose rules. **Extraction settings** point
those rules at your domain: the terms your team uses, the fields that always matter, the
noise worth skipping.

> [!NOTE]
>
> Settings apply to writes made **after** you save them. Memories already stored are never
> re-extracted, so changing these is safe and never rewrites history.

> [!TIP]
>
> Everything on this page is also on the **Extraction** tab of a space in the
> [dashboard](https://memory.anonalabs.com).

## The knobs

`guidance` is **added** to the standard rules and applies in every mode. Reach for it
first; it is the one most spaces ever need.

`custom_prompt` **replaces** those rules, and only applies while `mode` is `custom`. The
memory format is untouched either way: dates, entities and the fields of a memory are
read the same, so custom rules change what gets kept, never the shape of what comes back.

`labels` is different in kind. The two above steer what a write *keeps*; labels ask the
same pass to *classify* what it kept, along dimensions you define. A labelled memory can
then be filtered on directly. See [Label taxonomy](#label-taxonomy).

## Read the settings

```http theme={null}
GET /v1/spaces/{space_id}/extraction-settings
Authorization: Bearer anona_live_YOUR_KEY
```

```bash cURL theme={null}
curl https://api.anonalabs.com/v1/spaces/engineering/extraction-settings \
  -H "Authorization: Bearer anona_live_YOUR_KEY"
```

```python Python theme={null}
settings = client.get_extraction_settings("engineering")
```

```typescript TypeScript theme={null}
const settings = await anona.getExtractionSettings("engineering");
```

**Response** `200 OK`

```json theme={null}
{
  "space_id": "engineering",
  "mode": "concise",
  "guidance": "Engineering log. Always capture service names, metric values with units, and the named owner.",
  "custom_prompt": null,
  "labels": null,
  "free_form_entities": null
}
```

Every field is nullable, and `null` means **unset**: that field follows the platform
default and keeps following it. That is a different thing from setting it to whatever the
default happens to be today.

## Update the settings

```http theme={null}
PUT /v1/spaces/{space_id}/extraction-settings
Authorization: Bearer anona_live_YOUR_KEY
Content-Type: application/json
```

```json theme={null}
{
  "mode": "concise",
  "guidance": "Engineering log. Always capture service names, metric values with units, and the named owner. Treat incidents as dated events. Skip standup small talk."
}
```

| Field | Type | Default | Description |
| - | - | - | - |
| `mode` | `concise` \| `verbose` \| `verbatim` \| `custom` | `concise` | How much detail a memory keeps. See [modes](#modes). |
| `guidance` | string | unset | Added to the standard rules, in every mode. Max 4,000 characters. |
| `custom_prompt` | string | unset | Replaces the standard rules. Only applies when `mode` is `custom`. Max 8,000 characters. |
| `labels` | array | unset | Dimensions to classify every memory along. Max 12 groups. Each group's `description` is instruction text the extractor reads, so it must describe the label and not how to extract. See [Label taxonomy](#label-taxonomy). |
| `free_form_entities` | boolean | `true` | When `false`, only entities belonging to a label group are kept. Requires `labels`. |

```bash cURL theme={null}
curl -X PUT https://api.anonalabs.com/v1/spaces/engineering/extraction-settings \
  -H "Authorization: Bearer anona_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "concise",
    "guidance": "Engineering log. Always capture service names, metric values with units, and the named owner."
  }'
```

```python Python theme={null}
settings = client.set_extraction_settings(
    space_id="engineering",
    mode="concise",
    guidance=(
        "Engineering log. Always capture service names, metric values with "
        "units, and the named owner."
    ),
)
```

```typescript TypeScript theme={null}
const settings = await anona.setExtractionSettings({
  spaceId: "engineering",
  mode: "concise",
  guidance:
    "Engineering log. Always capture service names, metric values with units, " +
    "and the named owner.",
});
```

`PUT` replaces the whole record, so a field you leave out is **cleared**, not kept. The
body always describes the state you want, not a patch on top of what is there. The SDK
methods send the whole record too, so an argument you omit clears that field.

**Response** `200 OK`. The stored settings, in the same shape as the `GET`.

## Reset to the defaults

```http theme={null}
DELETE /v1/spaces/{space_id}/extraction-settings
Authorization: Bearer anona_live_YOUR_KEY
```

```bash cURL theme={null}
curl -X DELETE https://api.anonalabs.com/v1/spaces/engineering/extraction-settings \
  -H "Authorization: Bearer anona_live_YOUR_KEY"
```

```python Python theme={null}
client.reset_extraction_settings("engineering")
```

```typescript TypeScript theme={null}
await anona.resetExtractionSettings("engineering");
```

**Response** `204 No Content`

The space goes back to the standard rules. Stored memories are unaffected.

## Modes

| Mode | What a write becomes |
| - | - |
| `concise` | One tight fact per idea. The default, and the right choice for conversational memory. |
| `verbose` | Every specific the text contains. Larger memories and a higher cost per write; useful when detail matters more than volume. |
| `verbatim` | Your text stored exactly as written, with only the surrounding metadata derived. For content whose wording is the point: policies, clauses, incident notes. |
| `custom` | The rules in `custom_prompt` instead of the standard ones. |

## Writing good guidance

Extraction responds to specifics, not adjectives. "Be thorough" changes little; naming a
field changes a lot.

```text Support theme={null}
Customer support transcripts. Always capture the product area, the plan the customer is
on, and any promised follow-up with its date. "P1" means severity, not priority. Skip
greetings and pleasantries.
```

```text Clinical theme={null}
Clinical notes. Always capture medication names with dosage and frequency, and
appointment dates. "PT" means physical therapy, never patient. Never infer a diagnosis
that was not stated outright.
```

```text Sales theme={null}
Sales calls. Always capture the accounts and people named, budget or timeline
commitments, and objections raised. Record who said a thing, not only that it was said.
```

Three habits that pay off:

* **Name your vocabulary.** Ambiguous abbreviations are the single most common source of
  wrong extractions, and one sentence fixes them.
* **List the fields that must never be dropped.** That is what turns a good guess into a
  reliable one.
* **Say what to skip.** Excluding noise is as valuable as including signal, and it keeps
  memories smaller and recall sharper.

Guidance rides along on every write, so keep it to the rules that genuinely matter rather
than a full manual.

## Label taxonomy

Guidance changes what a write keeps. A **label taxonomy** changes what a write is *filed
under*: you name the dimensions that matter in your domain, and extraction classifies
every memory along them at write time. Because the classification happens once, on the
way in, filtering on it later costs nothing and cannot drift.

A group with `tag: true` is the useful case. Its classification is written onto the memory
as the tag `"<key>:<value>"`, which means
[`tag_groups` on retrieve](https://docs.memory.anonalabs.com/api-reference/retrieve#compound-tag-filters) can filter on it
like any other tag.

### Group types

| `type` | Shape | Use it for |
| - | - | - |
| `value` | one of `values` | A dimension with a fixed, short vocabulary: status, severity, stage. |
| `multi-values` | any number of `values` | The same, where more than one can apply at once. |
| `text` | one free string | A dimension whose vocabulary you cannot list in advance, where one answer is right. |
| `multi-text` | any number of free strings | The same, where a memory legitimately has several: names, aliases, identifiers. |

`value` and `multi-values` need their `values` listed. `text` and `multi-text` must not
list any: they take whatever the memory itself supplies.

### Identity: the case `multi-text` exists for

The most useful taxonomy is usually the simplest one. A single `name` group teaches
extraction to record every surface form a thing is known by, so a later query can match
the word your user actually typed rather than the word your docs happen to use.

```json Body theme={null}
{
  "labels": [
    {
      "key": "name",
      "type": "multi-text",
      "tag": true,
      "description": "Every name the subject of this memory is known by, including abbreviations, acronyms and short forms. Write them lowercase, words separated by single spaces, with no punctuation."
    }
  ]
}
```

```python Python theme={null}
client.set_extraction_settings(
    space_id="engineering",
    labels=[
        {
            "key": "name",
            "type": "multi-text",
            "tag": True,
            "description": (
                "Every name the subject of this memory is known by, including "
                "abbreviations, acronyms and short forms. Write them lowercase, "
                "words separated by single spaces, with no punctuation."
            ),
        }
    ],
)
```

```typescript TypeScript theme={null}
await anona.setExtractionSettings({
  spaceId: "engineering",
  labels: [
    {
      key: "name",
      type: "multi-text",
      tag: true,
      description:
        "Every name the subject of this memory is known by, including " +
        "abbreviations, acronyms and short forms. Write them lowercase, words " +
        "separated by single spaces, with no punctuation.",
    },
  ],
});
```

A memory about Kubernetes then carries `name:kubernetes`, `name:k8s` and `name:kube`, and
all three retrieve it:

```bash cURL theme={null}
curl -X POST https://api.anonalabs.com/v1/retrieve \
  -H "Authorization: Bearer anona_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "space_id": "engineering",
    "query": "what did we decide about k8s",
    "tag_groups": [{ "tags": ["name:k8s"], "match": "any_strict" }]
  }'
```

```python Python theme={null}
results = client.retrieve(
    space_id="engineering",
    query="what did we decide about k8s",
    tag_groups=[{"tags": ["name:k8s"], "match": "any_strict"}],
)
```

```typescript TypeScript theme={null}
const results = await anona.retrieve({
  spaceId: "engineering",
  query: "what did we decide about k8s",
  tagGroups: [{ tags: ["name:k8s"], match: "any_strict" }],
});
```

Naming the format in the description is what makes that exact match reliable, because tags
are matched as exact strings. Tell extraction how to write a name and normalise your query
candidates the same way.

### A classification dimension

The other common shape is a small fixed vocabulary, which lets a query exclude a whole
class of memory:

```json theme={null}
{
  "labels": [
    {
      "key": "state",
      "type": "value",
      "tag": true,
      "description": "Whether this is current guidance. Use 'historical' when the text says it was replaced or superseded.",
      "values": [
        { "value": "current", "description": "Active guidance that should shape answers now" },
        { "value": "historical", "description": "Superseded or previously used; record only" }
      ]
    }
  ]
}
```

Retrieved with a `not`, that is "everything except what we have moved on from":

```json theme={null}
{
  "tag_groups": [{ "not": { "tags": ["state:historical"], "match": "any_strict" } }]
}
```

### Reserved keys

A key becomes the part of the tag before the colon, so it must be lowercase letters,
digits, `-` or `_`, and it may not be `anona`. That namespace carries
[user, agent and session scoping](https://docs.memory.anonalabs.com/guides/scoping-multi-tenant.md), and a label group is written by
extraction rather than by you, so a group allowed to claim it could file a memory under
another end user's scope.

### What gets refused

Every one of these is a setting that would look saved and do nothing, which is worse than
an error:

| Refused | Why |
| - | - |
| `value` or `multi-values` with no `values` | Extraction never asks for a dimension with no vocabulary, so the group classifies nothing. |
| `text` or `multi-text` with `values` | These types have no fixed vocabulary, so the list would be ignored. |
| Two groups with the same `key` | They would compile to one dimension, and claim one tag namespace. |
| `key` of `anona`, or with uppercase, spaces or punctuation | See [Reserved keys](#reserved-keys). Tags match exactly, so `Name` and `name` would be two filters for one group. |
| `free_form_entities: false` with no `labels` | It keeps only entities belonging to a label group, so with none defined it would keep none at all. |
| A taxonomy over 24,000 bytes, or over 12 groups | It is sent to the extractor on every write, so its size is a cost on each one. |
| `custom_prompt` with `mode` anything but `custom`, or unset | The extractor reads the prompt only in custom mode, so it would read back as saved and never be used. Set `mode` to `custom`, or leave the prompt out. |

### What a `description` is, and what it must not be

A group's `description` is instruction text. It is rendered into the extraction prompt on
every write and the model reads it, so it is the single most powerful field here and the
easiest one to write badly.

Describe **the label**: what the dimension means, what its values mean, and how to write a
value. Do not describe **extraction**: what counts as a memory, when to split one fact into
two, what to leave out. The model cannot tell whose job it is reading about, and a
description phrased as extraction policy quietly changes what gets stored.

This is not a theoretical risk. One `name` group whose description said "those are separate
memories with their own names" and "a name must pick this memory out on its own" took the
same 35 document corpus from 52 memories to 10, and every job still reported `completed`
with no error. The same description, rewritten to talk only about names, stored 48.

> [!WARNING]
>
> Extraction cannot fail loudly here. A description that steers the extractor produces fewer
> memories, not an error, and the job that wrote them says `completed`. Compare
> `items_submitted` against `memory_count` on the job (see
> [Async jobs](https://docs.memory.anonalabs.com/api-reference/memories.md)) after a taxonomy change: a large gap between the two
> is the signal, and a taxonomy is the first thing to suspect.

Keep descriptions short for the same reason you keep `guidance` short: the whole taxonomy
is sent on every write, so its length is a cost on each one. Keep them to what changes a
decision.

Labels apply to writes made after you save them. Memories already stored keep whatever
they were written with, so switching a taxonomy on does not retag your history.

## Verifying a change

There is no error when guidance is unhelpful; extraction simply keeps different things.
The quickest way to see the effect:

1. Save the settings.
2. Record a representative piece of text with [`POST /v1/record`](https://docs.memory.anonalabs.com/api-reference/memories).
3. Read the memory back with [`GET /v1/spaces/{space_id}/memories`](https://docs.memory.anonalabs.com/api-reference/memories)
   and check the facts are the ones you wanted.

With a label taxonomy there is something concrete to check: the memory you read back
carries a `tags` entry of `"<key>:<value>"` for every group you marked `tag: true`. If the
tags are absent, extraction did not find the dimension in the text, and the group's
`description` is what to change.

Adjust and repeat. Because nothing is re-extracted, older memories keep whatever they
were stored with.

## Permissions

Extraction settings decide what every member's writes turn into, so reading and changing
them is limited to the space's **owner**. A member of a shared space gets
`403 space_owner_only`.

These routes are not metered, so an org that has run out of credits can still change or
turn off its settings.

## Errors

| Code | Status | Meaning |
| - | - | - |
| `space_not_found` | 404 | No space with that id. |
| `space_owner_only` | 403 | Only the owning org can read or change extraction settings. |
| `validation_error` | 422 | An unknown `mode`, an unknown field name, text past a length limit, a `custom_prompt` outside custom mode, or a label group that would not do anything. See [what gets refused](#what-gets-refused). |


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