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

# Searching memories

> Vector search scoped by what you pass — a user's own memories, shared groups, or both.

Once a memory is ingested and the job reaches `succeeded`, it's immediately queryable. There are three read paths:

* **`search`** — vector-ranked retrieval, scoped by the ids you pass. Use when you have a natural-language query.
* **`list`** — paginated browse by scope, no query. Use for "all of Alice's memories".
* **`recall`** — convenience over `search` that combines a user's own memories **and** a shared group's into one ready-to-inject prompt (see [below](#personal-shared-with-recall)).

## Vector search

```ts theme={null}
const results = await client.memories.search({
  query: 'what does the user like to eat?',
  user_id: 'alice',
  limit: 10,
});

for (const m of results.data) {
  console.log(m.score?.toFixed(2), '·', m.text);
}
```

`query` is required (non-empty). The server embeds it and ranks by cosine similarity; `data` is a list of `Memory` rows with `.score` populated.

## Scoping a search

There is **no filter DSL**. Search is *"scope by what you pass"*: supply any combination of the axes below and each one **AND-narrows** the result. An omitted axis is unconstrained, and **at least one** axis is required — an unscoped search returns `422`.

| Axis        | Scopes to                                               |
| ----------- | ------------------------------------------------------- |
| `user_id`   | one user's memories                                     |
| `group_ids` | memories tagged to **any** of these groups (cross-user) |
| `agent_id`  | one agent                                               |
| `app_id`    | one app                                                 |

```ts theme={null}
// Alice's memories only
await client.memories.search({ query, user_id: 'alice' });

// Alice's memories, narrowed to one agent
await client.memories.search({ query, user_id: 'alice', agent_id: 'planner' });
```

Everything you pass is AND'd together; your `org` is always implicit (from the API key).

<Note>
  `user_id` is **required on ingest** but **optional on search** — omit it and pass `group_ids` to read a whole group across users (next section).
</Note>

## Reading shared memories (groups)

Memories [tagged to a group](/guides/ingesting-memories#tagging-memories-to-groups) at ingest are visible to anyone who searches that group. `group_ids` matches **any-of** — a row tagged to *any* of the requested groups qualifies.

```ts theme={null}
// The whole Tokyo trip, across every member — omit user_id:
await client.memories.search({ query, group_ids: ['grp_tokyo2026'] });

// Alice's OWN slice of the trip (intersection: user_id AND group):
await client.memories.search({ query, user_id: 'alice', group_ids: ['grp_tokyo2026'] });
```

Because scoping is AND-everything, `{ user_id, group_ids }` is an **intersection** (Alice's rows that are *also* tagged to the trip), not a union. For *"Alice's own memories **plus** the trip's shared memories"* in one call, use `recall`.

## Personal and shared with recall

`recall` runs the personal and shared scopes in parallel, dedupes, and returns a single context block — the combined read that AND-scoping can't express in one `search`.

```ts theme={null}
const { prompt, memories } = await client.memories.recall({
  query: 'what should we plan for dinner on the trip?',
  pools: [
    { user_id: 'alice' },             // her dietary prefs
    { group_ids: ['grp_tokyo2026'] }, // the trip's shared restaurant picks
  ],
});
// inject `prompt` into your agent's context
```

`prompt` is sectioned by **Personal** + each group's name, with shared lines attributed to their author (`you:` / `<user_id>:`). It resolves group names automatically and won't bleed in the user's *other* groups. Full options in the [SDK reference](/guides/typescript-sdk) and the [Groups guide](/guides/groups).

## Search modes: `retrieve` vs `compose`

`mode` controls whether the server's LLM context-selection pass runs:

| `mode`                | `data`                         | `context`                | Use when                                                    |
| --------------------- | ------------------------------ | ------------------------ | ----------------------------------------------------------- |
| `compose` *(default)* | the **agent-selected** subset  | assembled markdown block | you want a ready-to-inject context prompt                   |
| `retrieve`            | the **raw** vector-ranked rows | `null`                   | you want pure results to process yourself — cheaper, no LLM |

```ts theme={null}
// Raw rows, no LLM pass:
const raw = await client.memories.search({ query, user_id: 'alice', mode: 'retrieve' });

// Assembled context for a prompt (default):
const composed = await client.memories.search({ query, user_id: 'alice', mode: 'compose' });
console.log(composed.context); // "Relevant memories about the user:\n- ..."
```

## Cursor pagination

`search` and `list` return `has_more` and `next_cursor`. The SDK's `list` is an async iterator that paginates automatically:

```ts theme={null}
for await (const memory of client.memories.list({ user_id: 'alice', limit: 50 })) {
  // …each memory across all pages
}
```

For cursor-level control use `listPage`:

```ts theme={null}
const page = await client.memories.listPage({ user_id: 'alice', limit: 50, cursor });
```

`list` accepts the same scope keys plus `type` (`fact` | `artifact` | `episode`) and `order`. Cursors are tenant-scoped — a cursor from one `(org, key)` pair can't be used by another.

## Retrieving a single memory

```ts theme={null}
const memory = await client.memories.get('5b0d0f7d-d502-45d5-847d-e19512b5c517');
```

Returns the full row — including `details.full_content` for artifacts, which search and list omit by default (it can be large). 404s if the id doesn't exist or was deleted.

## See also

* **[Ingesting memories](/guides/ingesting-memories)** — what gets stored, and tagging to groups
* **[Groups](/guides/groups)** — register groups and share memory across users
* **API Reference → Memories → Search** — every field, generated from the live spec
