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

# Memories

> What Memories are, how they differ from Knowledge, and how to write them correctly.

## 1. What it is

Memories are user-scoped context HydraDB stores so your application can personalize responses over time.

A memory is a unit of user-specific context HydraDB stores on behalf of an agent - a preference, a past conversation, an inferred trait, or a raw fact. Memories let an agent adapt its responses to a specific user across sessions, and surface again through [Recall](/essentials/recall) when relevant.

Think of memories as persistent user-specific context. That's distinct from **Knowledge**, which is shared, tenant-wide context ingested from documents and apps.

|                          | Memories                                         | Knowledge                               |
| ------------------------ | ------------------------------------------------ | --------------------------------------- |
| **Content**              | User preferences, conversations, inferred traits | Documents, files, app-generated content |
| **Scope**                | Per-user (`sub_tenant_id`)                       | Shared across all users in a tenant     |
| **Recall endpoint**      | `recall_preferences`                             | `full_recall`                           |
| **`vectorstore_status`** | Index `0` (`[0]`)                                | Index `1` (`[1]`)                       |

***

## 2. What it does

When you add a memory, HydraDB:

1. Parses the input (text, markdown, or user–assistant pairs).
2. Optionally runs inference (`infer: true`) to extract preferences and insights.
3. Processes and indexes it for future retrieval.
4. Makes it available to [`recall_preferences`](/api-reference/endpoint/recall-preferences).

The `infer` flag controls how much structure HydraDB extracts on your behalf - see [How `infer` works](#5-how-it-works).

***

## 3. When to use it

Use memories when responses need to adapt to a specific user over time:

* Capture stated or implied preferences ("prefers concise answers").
* Store past conversations the agent should reference later.
* Record decisions, outcomes, or feedback tied to a user.
* Build up a behavioral profile that compounds across sessions.

Use [Knowledge ingestion](/api-reference/endpoint/upload-knowledge) instead when the content is shared across users - product docs, policy PDFs, internal wikis, Slack threads.

| If the content is...                   | Store it as... |
| -------------------------------------- | -------------- |
| About a specific user or session       | Memory         |
| Shared across all users in your tenant | Knowledge      |

***

## 4. Types of memories

A memory item should typically provide either `text` or `user_assistant_pairs`, not both.

### Raw text

```json theme={"dark"}
{
  "text": "Prefers detailed technical explanations and works in PST.",
  "infer": true
}
```

### Markdown

```json theme={"dark"}
{
  "text": "# Meeting Notes\n\n## Key Points\n- Budget approved",
  "is_markdown": true,
  "infer": false
}
```

### User–assistant pairs

Best for capturing implicit signals from dialogue.

```json theme={"dark"}
{
  "user_assistant_pairs": [
    { "user": "Remember I like dark mode", "assistant": "Noted." }
  ],
  "infer": true
}
```

***

## 5. How it works

### `infer`: extract preferences for me, or store what I give you

The `infer` flag is the most important decision when writing a memory. It controls how much work your application has to do *before* sending content to HydraDB.

**`infer: true` - HydraDB extracts the preference for you.**

You hand HydraDB raw signal - interaction logs, dialogue, behavior, observations - and HydraDB infers the underlying preference, trait, or fact.

Concretely: ship the events that imply a preference, and HydraDB derives the preference. For example, dump a stream of UI interaction logs showing that a user switched to dark mode on Day 1, kept it on for a month, and toggled it back to light once before reverting. HydraDB infers `"user prefers dark mode"` and indexes that. Your application never has to write the extraction logic.

This is the right mode for:

* Dialogue (`user_assistant_pairs`) where the preference is implicit
* Behavior logs and event streams
* Feedback ("the last summary was too long") where the actionable trait isn't spelled out
* Anything where the raw input is messy and the useful trait has to be derived

`custom_instructions` only takes effect when `infer: true`. Use it to guide what gets extracted (e.g., `"Focus on UI and notification preferences only"`).

**`infer: false` (default) - HydraDB stores what you give it.**

You've already done the extraction. The text you send *is* the memory, verbatim. HydraDB stores and indexes it as-is. No inference, no derivation.

This is the right mode for:

* Facts you've already captured (`"User's plan tier is Pro"`)
* Pre-structured notes
* Anything you want to retrieve exactly as written

The trade-off: `infer: false` is faster and deterministic; `infer: true` is more useful when your input is raw and unstructured.

### Storage and scoping

Memories live in a separate vector store from Knowledge:

* `recall_preferences` searches **Memories** (`vectorstore_status` index `0`).
* `full_recall` searches **Knowledge** (`vectorstore_status` index `1`).

<Warning>
  <strong>Memories and Knowledge are separate stores.</strong>
  <p>For personalized responses grounded in shared context, call <code>recall\_preferences</code> and <code>full\_recall</code> in parallel and merge the results before prompting your LLM.</p>
</Warning>

Within a tenant, memories are scoped by `sub_tenant_id`. For per-user separation (B2C), use the user's ID as the `sub_tenant_id`. For shared memory across a workspace, use a stable workspace-level ID. If you omit `sub_tenant_id`, the memory is written to the tenant's default sub-tenant. See [Multi-tenant support](/essentials/multi-tenant).

Ingestion is asynchronous - memories aren't immediately searchable. Poll [`/ingestion/verify_processing`](/api-reference/endpoint/verify-processing) with the returned `source_id` until `indexing_status` is `completed`.

***

## 6. Key parameters

### Top-level request body

| Field           | Type    | Required | Description                                                                 |
| --------------- | ------- | -------- | --------------------------------------------------------------------------- |
| `memories`      | array   | Yes      | One or more memory items.                                                   |
| `tenant_id`     | string  | Yes      | Your tenant identifier.                                                     |
| `sub_tenant_id` | string  | No       | Sub-tenant scope. If omitted, HydraDB uses the tenant's default sub-tenant. |
| `upsert`        | boolean | No       | Default `true`. Replaces existing entries with the same `source_id`.        |

### Memory item fields

| Field                  | Type                           | Description                                                                                                                                                                                                                                                                                                                  |
| ---------------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `source_id`            | string                         | Optional unique ID; auto-generated if omitted. Acts as the upsert key.                                                                                                                                                                                                                                                       |
| `text`                 | string                         | Raw text or markdown. Use this **or** `user_assistant_pairs`.                                                                                                                                                                                                                                                                |
| `user_assistant_pairs` | array of `{ user, assistant }` | Conversation pairs. Use this **or** `text`.                                                                                                                                                                                                                                                                                  |
| `is_markdown`          | boolean                        | Treat `text` as markdown. Default `false`.                                                                                                                                                                                                                                                                                   |
| `infer`                | boolean                        | Run inference. Default `false`. See [How it works](#5-how-it-works).                                                                                                                                                                                                                                                         |
| `custom_instructions`  | string                         | Guides inference extraction. Only applies when `infer: true`.                                                                                                                                                                                                                                                                |
| `user_name`            | string                         | The user's name. Used during inference and personalization workflows. Default `"User"`.                                                                                                                                                                                                                                      |
| `title`                | string                         | Display title for this memory.                                                                                                                                                                                                                                                                                               |
| `expiry_time`          | integer                        | TTL in seconds. After expiry, the memory is no longer returned by recall.                                                                                                                                                                                                                                                    |
| `metadata`             | object                         | Tenant-level fields aligned with the tenant metadata schema. Keys defined in `tenant_metadata_schema` with `enable_match: true` are matchable via top-level keys in `metadata_filters` (pre-filtered, fast).                                                                                                                 |
| `additional_metadata`  | object                         | Document-level free-form key-value pairs attached to the memory. Matchable at recall by nesting under `additional_metadata` (canonical) or `document_metadata` (legacy alias) inside `metadata_filters`. Applied post-retrieval. See [Full recall → Metadata filters](/api-reference/endpoint/full-recall#metadata-filters). |
| `relations`            | object                         | Optional relation metadata for linking this memory to other stored context.                                                                                                                                                                                                                                                  |

***

## N. Forceful relations

Forceful relations let you explicitly link Memory items to each other at ingestion time. When a query matches a memory that has forceful relations declared, HydraDB pulls the linked memories into `additional_context` alongside the primary results.

This is separate from the entity and relationship graph HydraDB builds automatically during ingestion. Graph extraction is derived from content; forceful relations are declared by you.

Set the `relations` field on any item in the `memories` array:

```json theme={"dark"}
{
  "relations": {
    "cortex_source_ids": ["memory-id-1", "memory-id-2"]
  }
}
```

<CodeGroup>
  ```bash cURL theme={"dark"}
  curl -X POST 'https://api.hydradb.com/memories/add_memory' \
    -H "Authorization: Bearer $HYDRA_DB_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "tenant_id": "acme_corp",
      "sub_tenant_id": "user_alex",
      "memories": [
        {
          "text": "User prefers concise answers",
          "source_id": "pref-concise",
          "infer": true,
          "relations": { "cortex_source_ids": ["pref-tone", "pref-format"] }
        }
      ]
    }'
  ```

  ```typescript TypeScript SDK theme={"dark"}
  await client.upload.addMemory({
    tenant_id: "acme_corp",
    sub_tenant_id: "user_alex",
    memories: [
      {
        text: "User prefers concise answers",
        source_id: "pref-concise",
        infer: true,
        relations: { cortex_source_ids: ["pref-tone", "pref-format"] },
      },
    ],
  });
  ```

  ```python Python SDK theme={"dark"}
  client.upload.add_memory(
      tenant_id="acme_corp",
      sub_tenant_id="user_alex",
      memories=[
          {
              "text": "User prefers concise answers",
              "source_id": "pref-concise",
              "infer": True,
              "relations": {"cortex_source_ids": ["pref-tone", "pref-format"]},
          }
      ],
  )
  ```
</CodeGroup>

At recall, linked memories are returned in the `additional_context` field. Set `search_forceful_relations: true` (the default) on the request to include them.

<Warning>
  **Memory items only.** Forceful relations work within the same store. Pointing a Memory item at a Knowledge source ID (or vice versa) will silently return nothing at recall time.
</Warning>

<Warning>
  **`thinking` mode only.** Forceful-relation context is fetched only when `mode: "thinking"` is set on the recall request. In `fast` mode, `search_forceful_relations` is ignored even if set to `true`.
</Warning>

## 8. Minimal working example

Store a user preference scoped to a specific user.

<CodeGroup>
  ```bash cURL theme={"dark"}
  curl -X POST 'https://api.hydradb.com/memories/add_memory' \
    -H "Authorization: Bearer $HYDRA_DB_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "tenant_id": "acme_corp",
      "sub_tenant_id": "user_john_123",
      "upsert": true,
      "memories": [
        {
          "text": "Prefers short, direct answers with no preamble.",
          "infer": true,
          "user_name": "John"
        }
      ]
    }'
  ```

  ```typescript TypeScript SDK theme={"dark"}
  import { HydraDBClient } from "@hydradb/sdk";

  const client = new HydraDBClient({
    token: process.env.HYDRA_DB_API_KEY,
  });

  const result = await client.upload.addMemory({
    tenant_id: "acme_corp",
    sub_tenant_id: "user_john_123",
    upsert: true,
    memories: [
      {
        text: "Prefers short, direct answers with no preamble.",
        infer: true,
        user_name: "John",
      },
    ],
  });
  ```

  ```python Python SDK theme={"dark"}
  import os
  from hydra_db import HydraDB

  client = HydraDB(token=os.environ["HYDRA_DB_API_KEY"])

  result = client.upload.add_memory(
      tenant_id="acme_corp",
      sub_tenant_id="user_john_123",
      upsert=True,
      memories=[
          {
              "text": "Prefers short, direct answers with no preamble.",
              "infer": True,
              "user_name": "John",
          }
      ],
  )
  ```
</CodeGroup>

You'll get back a `source_id` for each memory and an initial `status` of `queued`:

```json theme={"dark"}
{
  "results": [
    { "source_id": "mem_abc123", "status": "queued", "infer": true }
  ],
  "success_count": 1,
  "failed_count": 0
}
```

Poll [`/ingestion/verify_processing`](/api-reference/endpoint/verify-processing) with the returned `source_id` until `indexing_status` is `completed`. After that the memory will surface in [`recall_preferences`](/api-reference/endpoint/recall-preferences).

***

## 9. Inference in practice

A realistic example. The user has been toggling dark mode in your app. Instead of extracting the preference yourself, ship the raw log:

<CodeGroup>
  ```bash cURL theme={"dark"}
  curl -X POST 'https://api.hydradb.com/memories/add_memory' \
    -H "Authorization: Bearer $HYDRA_DB_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "tenant_id": "acme_corp",
      "sub_tenant_id": "user_john_123",
      "memories": [
        {
          "text": "User opened the app 14 times in the last week. Switched to dark mode on first session. Toggled back to light once on 2026-04-12 at 3pm, then switched back to dark within 4 minutes. Has not changed theme since.",
          "infer": true,
          "user_name": "John",
          "custom_instructions": "Focus on display and theme preferences."
        }
      ]
    }'
  ```

  ```typescript TypeScript SDK theme={"dark"}
  const result = await client.upload.addMemory({
    tenant_id: "acme_corp",
    sub_tenant_id: "user_john_123",
    memories: [
      {
        text:
          "User opened the app 14 times in the last week. " +
          "Switched to dark mode on first session. " +
          "Toggled back to light once on 2026-04-12 at 3pm, " +
          "then switched back to dark within 4 minutes. " +
          "Has not changed theme since.",
        infer: true,
        user_name: "John",
        custom_instructions: "Focus on display and theme preferences.",
      },
    ],
  });
  ```

  ```python Python SDK theme={"dark"}
  client.upload.add_memory(
      tenant_id="acme_corp",
      sub_tenant_id="user_john_123",
      memories=[
          {
              "text": (
                  "User opened the app 14 times in the last week. "
                  "Switched to dark mode on first session. "
                  "Toggled back to light once on 2026-04-12 at 3pm, "
                  "then switched back to dark within 4 minutes. "
                  "Has not changed theme since."
              ),
              "infer": True,
              "user_name": "John",
              "custom_instructions": "Focus on display and theme preferences.",
          }
      ],
  )
  ```
</CodeGroup>

When `recall_preferences` runs with a query like `"what UI settings does the user prefer?"`, HydraDB returns the *inferred* preference (`"prefers dark mode"`), not the raw log. Your application never had to write the extraction logic.

The same input with `infer: false` would index the raw text verbatim - useful only if you actually want to retrieve the event log itself.

***

## 10. Common mistakes

| Mistake                                           | What goes wrong                                                         | Fix                                                                                                |
| ------------------------------------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| Sending `"memory": "..."` (singular, string)      | Validation fails                                                        | Use `"memories": [ { "text": "..." } ]` - always an array of objects                               |
| Omitting `tenant_id`                              | Request validation error                                                | Required on every call                                                                             |
| Omitting `sub_tenant_id` for per-user data        | The memory lands in the default sub-tenant and may surface across users | Use the user's ID as `sub_tenant_id` for B2C separation                                            |
| Setting `custom_instructions` with `infer: false` | Instructions are ignored                                                | Set `infer: true` if you need them to take effect                                                  |
| Doing extraction yourself before sending raw logs | Wastes effort that `infer: true` already handles                        | Send the raw signal with `infer: true` and let HydraDB derive the preference                       |
| Sending pre-extracted facts with `infer: true`    | HydraDB may re-derive and lose the original phrasing                    | Use `infer: false` for already-structured facts                                                    |
| Treating `user_name` as cosmetic                  | It feeds inference, so the wrong name biases what HydraDB extracts      | Pass the actual user's name                                                                        |
| Storing shared docs as memories                   | They won't surface in `full_recall`                                     | Use [Knowledge ingestion](/api-reference/endpoint/upload-knowledge) instead                        |
| Expecting immediate searchability                 | Ingestion is async                                                      | Poll [`/ingestion/verify_processing`](/api-reference/endpoint/verify-processing) until `completed` |
| Forgetting `infer` for conversational input       | Raw dialogue gets stored without preference extraction                  | Set `infer: true` explicitly for conversations                                                     |

***

## Related

* [Knowledge](/essentials/knowledge) - shared, tenant-wide document context
* [Recall](/essentials/recall) - how memories are retrieved at query time
* [Multi-tenant support](/essentials/multi-tenant) - scoping memories per user or workspace
* [Metadata](/essentials/metadata) - designing filterable fields
