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

# record, retrieve, reason

> Store memories, search them and ask questions over them from the shell, with every flag the three commands take.

The three memory calls. Each takes `--space`, `--api-key`, `--base-url` and
`--json`, plus the [global options](https://docs.anonalabs.com/cli/scripting.md#global-options). A flag you
do not give is left out of the request, so the server's own defaults apply. A
malformed value (a date that does not parse, a `--metadata` that is not a JSON
object, an empty tag) exits `2` and sends nothing. Dates are ISO 8601:
`2026-06-01`, `2026-06-01T09:00:00Z` or `2026-06-01T09:00:00+02:00`, sent as
you typed them.

| Command | pip | npm |
| - | - | - |
| `record`, `retrieve`, `reason` with the flags marked *0.24.0* below | yes | 0.19.0 |
| the flags marked *0.25.0* | 0.25.0 | 0.19.0 |

See [What each package has](https://docs.anonalabs.com/cli/overview.md#what-each-package-has).

## record

Store a memory. A write is extracted into the facts it contains rather than
stored verbatim, so one sentence can become two memories.

```bash theme={null}
anona record "The team ships on Tuesdays." --space notes
git log -1 --format=%B | anona record - --tags git --session-id "$BRANCH" --space notes
anona record --file notes/decision.md --timestamp 2025-06-01 --metadata '{"source": "adr"}' --space notes
```

| Flag | What it sends | Since |
| - | - | - |
| `CONTENT` | The memory, as a complete sentence. `-` reads it from stdin | 0.24.0 |
| `--file PATH` | Read the content from a file (UTF-8). Not together with `CONTENT` | 0.24.0 |
| `--tags a,b` | `tags`, comma-separated. Repeating the flag adds more | 0.24.0 |
| `--timestamp DATE` | `timestamp`: when the event happened, not when you record it. Use it when importing history | 0.24.0 |
| `--metadata JSON` | `metadata`: a JSON object, e.g. `'{"source": "cli"}'` | 0.24.0 |
| `--user-id`, `--agent-id`, `--session-id` | The [scope](https://docs.anonalabs.com/guides/scoping-multi-tenant.md) to file the memory under | 0.24.0 |
| `--async` | Queue the write and return a job id at once instead of waiting for extraction | 0.25.0 |
| `--batch FILE` | Record up to 100 memories from a JSON Lines file (`-` for stdin). Always queued. See [Batch ingest](#batch-ingest) | 0.25.0 |
| `--wait` | With `--async` or `--batch`: poll the job until it finishes, and exit with its outcome | 0.25.0 |
| `--wait-timeout SECONDS` | With `--wait`: give up after this long and exit `8` (default 300) | 0.25.0 |

`record` prints what was stored, and the memory ids:

```text theme={null}
Stored 2 memories in space 'default': 0f3c…, 7ab1…
```

**A write that extracts to nothing is not a success.** Short, context-free
content often produces no fact at all. The API accepts the request with
`status: "nothing_extracted"` and no ids; `record` prints the API's advice on
stderr and exits **`3`**. The usual fix is to record again with a sentence of
context: what the thing is, or why it matters.

Empty content, from any of the three sources, exits `2`.

**`--json`:** `{"memory_id", "memory_ids": [...], "status", "message"?, "usage"}`.
With `--async`: `{"job_id", "status"}`.

### Background writes

pip 0.25.0; npm 0.19.0

A plain `record` waits for extraction, which is one model call. `--async`
queues the write and returns as soon as the server has accepted it:

```bash theme={null}
anona record "Quarterly planning moved to the first Monday." --async --space notes
# Queued in space 'notes'. Job: job_…
# Check it with `anona jobs get job_… --space notes`.
```

Add `--wait` to queue it and then poll until it finishes. Polling a job is a
read: it costs nothing and is safe to repeat. The exit code then reports what
the job did: `0` stored, `3` completed but stored nothing, `1` failed or was
cancelled, `8` still running when `--wait-timeout` ran out (check later with
`anona jobs get`). `--wait` on a plain `record` exits `2`, since a plain record
already waits.

With `--wait --json`, the document is
`{"submitted": <the queued response>, "jobs": [<one jobs get document per job>]}`.

### Batch ingest

pip 0.25.0; npm 0.19.0

`--batch FILE` records up to 100 memories in one queued call. The file is
[JSON Lines](https://jsonlines.org): one item per line, either a JSON string
(the content alone) or an object with `content` and, optionally, any of
`context`, `timestamp`, `metadata`, `tags`, `user_id`, `agent_id`,
`session_id`. Blank lines are skipped.

```json theme={null}
{"content": "Acme renewed on the annual plan.", "timestamp": "2026-03-02", "user_id": "acme"}
```

```bash theme={null}
cat > history.jsonl <<'EOF'
"The release branch is cut on Mondays."
{"content": "Acme renewed on the annual plan.", "timestamp": "2026-03-02", "tags": ["billing"], "user_id": "acme"}
{"content": "Staging has no seed data.", "metadata": {"source": "wiki"}}
EOF

anona record --batch history.jsonl --space notes --wait
jq -c '{content: .text, timestamp: .date}' export.json | anona record --batch - --space notes --json
```

The whole file is checked before anything is sent: a line that is not valid
JSON, an unknown field, an empty `content`, an empty file or more than 100 items
exits `2`, names the line, and sends nothing. Split a larger export into files
of 100. `--tags`, `--timestamp` and `--metadata` cannot be combined with
`--batch`; put them on each line. `--user-id`, `--agent-id` and `--session-id`
apply to every item.

**`--json`:** `{"job_id", "job_ids", "status", "accepted"}`, or with `--wait`
the `submitted` / `jobs` document above. For very large imports, see
[Bulk import with webhooks](https://docs.anonalabs.com/guides/bulk-import-webhooks.md).

## retrieve

Search your memories by meaning.

```bash theme={null}
anona retrieve "when do we ship" --space notes
anona retrieve "deploy schedule" --space ops --json --limit 3 | jq -r '.results[].content'
anona retrieve "what changed" --space ops --occurred-after 2026-06-01
```

| Flag | What it sends | Since |
| - | - | - |
| `QUERY` | What to search for | 0.24.0 |
| `--limit N` | `limit`: 1-100, default 10 | 0.24.0 |
| `--min-score X` | `min_score`: drop results below this. Read the note below first | 0.24.0 |
| `--tags a,b` | `tags`: only memories carrying these tags. Repeatable | 0.24.0 |
| `--tags-match MODE` | `tags_match`: `any` (default), `all`, `any_strict`, `all_strict` or `exact`. `any` and `all` also return memories with **no tags at all**, which match every non-strict filter; `any_strict`, `all_strict` and `exact` exclude untagged memories | 0.24.0 |
| `--as-of DATE` | `as_of`: only memories **recorded** at or before this instant | 0.24.0 |
| `--occurred-after DATE` | `occurred_after`: only memories about events at or after this | 0.24.0 |
| `--occurred-before DATE` | `occurred_before`: only memories about events at or before this | 0.24.0 |
| `--user-id`, `--agent-id`, `--session-id` | Only memories recorded under that scope. Strict: an unscoped memory is not returned | 0.24.0 |
| `--tag-groups JSON` | `tag_groups`: a [compound tag filter](https://docs.anonalabs.com/api-reference/retrieve.md#compound-tag-filters), AND-ed with any scope. `@FILE` reads it from a file | 0.25.0 |
| `--member-id ID` | Only memories this member wrote; `me` for your own. Shared spaces only | 0.25.0 |
| `--no-prefer-observations` | Return the raw facts beside the synthesized note built from them. The default (`--prefer-observations`) returns only the note | 0.25.0 |
| `--format MODE` | `results`: one line per memory (default). `block`: the [prompt-ready context string](https://docs.anonalabs.com/api-reference/retrieve.md#prompt-ready-context) | 0.25.0 |
| `--max-tokens N` | With `--format block`: cap the block at about this many tokens | 0.25.0 |

Plain output is one memory per line, with its relevance score and the first
eight characters of its id:

```text theme={null}
1. 1.1  3f2a9c1e  The user prefers pnpm.
2. 6e-05  9b7d0e44  Builds run on Node 22.
```

A search that matches nothing prints `No memories matched.` and exits `0`.

**Scores are close to two-valued**: about 1.1 for a near-verbatim match, about
0.0001 for a correct match worded differently. Most `--min-score` floors
therefore keep only the near-verbatim matches. See the
[retrieve API](https://docs.anonalabs.com/api-reference/retrieve.md) before setting one.

A search carrying a scope id always matches tags strictly, whatever
`--tags-match` says. `--as-of` is about when a memory was recorded;
`--occurred-after` / `--occurred-before` about when the thing it describes
happened. History imported today has today's record time and its own event
time.

**`--tag-groups`** takes a JSON array of `{"tags": [...], "match": ...}`,
`{"and": [...]}`, `{"or": [...]}` and `{"not": {...}}` expressions; a single
object is taken as a one-element array. Only the shape is checked locally, the
grammar and limits are the server's:

```bash theme={null}
anona retrieve "open work" --space notes \
  --tag-groups '[{"or": [{"tags": ["project:alpha"]}, {"tags": ["project:beta"]}]}, {"not": {"tags": ["status:archived"]}}]'
anona retrieve "open work" --space notes --tag-groups @filter.json
```

**`--format block`** prints the context string itself, ready to paste into a
prompt:

```bash theme={null}
anona retrieve "how does this user like answers" --space support --user-id alice \
  --format block --max-tokens 800 > context.txt
```

`--max-tokens` without `--format block` exits `2`.

**`--json`:** `{"results": [{"memory_id", "content", "relevance_score", "memory_type", "tags", ...}]}`,
every row as the [retrieve API](https://docs.anonalabs.com/api-reference/retrieve.md) returns it. With
`--format block`: `{"results": [...], "context", "token_estimate"}`. An empty
search is `{"results": []}` with exit `0`.

## reason

Ask a question and get an answer synthesized from your memories. Use it when no
single memory holds the answer.

```bash theme={null}
anona reason "What is our release process?" --space notes
anona reason "how does this team ship?" --space notes --depth thorough --json | jq .sources
```

| Flag | What it sends | Since |
| - | - | - |
| `QUERY` | The question | 0.24.0 |
| `--depth MODE` | `depth`: `fast` or `thorough`. `fast` lets a current [memory model](https://docs.anonalabs.com/api-reference/models.md) answer on its own; `thorough` always reads the notes and raw memories under it, slower and costlier. Omitted, the space's [reason settings](https://docs.anonalabs.com/cli/settings.md#reason-settings) apply, else `fast` | 0.24.0 |
| `--user-id`, `--agent-id`, `--session-id` | Reason only over that scope | 0.24.0 |

`reason` is an agent loop rather than a lookup, so it takes tens of seconds and
sometimes longer. It says so on stderr while it works, so
`anona reason "..." > answer.md` captures only the answer. `--depth thorough`
is the second look to ask for when an answer reads stale.

**`--json`:** `{"insights", "sources": {"models", "memories", "notes"}, "rules_applied": [...], "model", "usage"}`.
`sources` says what the answer was built from, which the plain output does not
show.

## Related

* [Scripting & automation](https://docs.anonalabs.com/cli/scripting.md): exit codes, retries, `--json`.
* [Jobs](https://docs.anonalabs.com/cli/jobs.md): check or cancel what `--async` and `--batch` queued.
* API: [Memories](https://docs.anonalabs.com/api-reference/memories.md), [Retrieve](https://docs.anonalabs.com/api-reference/retrieve.md), [Reason](https://docs.anonalabs.com/api-reference/reason.md).


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