> ## Documentation Index
> Fetch the complete documentation index at: https://cortex-e852fafe-t3code-rewrite-docs-declutter.mintlify.site/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={null}
{
  "text": "Prefers detailed technical explanations and works in PST.",
  "infer": true
}
```

### Markdown

```json theme={null}
{
  "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={null}
{
  "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={null}
{
  "relations": {
    "cortex_source_ids": ["memory-id-1", "memory-id-2"]
  }
}
```

<CodeGroup>
  ```bash cURL theme={null}
  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={null}
  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={null}
  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={null}
  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={null}
  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={null}
  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={null}
{
  "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={null}
  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={null}
  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={null}
  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


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