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

> Save what you learn about each user, so your agent remembers it next time.

export const Field = ({name, type, required, recommended}) => {
  const label = required ? 'required' : recommended ? 'recommended' : null;
  const typeLabel = typeof type === 'string' ? type : null;
  const ariaParts = [name, typeLabel && `${typeLabel}`, label].filter(Boolean);
  return <span aria-label={ariaParts.join(', ')} className={label ? 'field-wrap has-field-tip' : 'field-wrap'} style={{
    position: 'relative',
    cursor: label ? 'default' : undefined
  }} tabIndex={label ? 0 : undefined}>
      <span className="field-name-row">
        <code>{name}</code>
        {required && <span className="field-req"> *</span>}
        {recommended && <span className="field-rec"> ●</span>}
      </span>
      {type && <span className="field-type">{type}</span>}
      {label && <span className="field-tip" role="tooltip">
          {label}
        </span>}
    </span>;
};

A memory is what your agent knows about one user: a preference, a decision, something they already tried, or a conversation worth keeping. Your application sends it to [`POST /context/ingest`](/api-reference/v2/endpoint/ingest-context) with `type=memory`, and gets it back with [`POST /query`](/api-reference/v2/endpoint/query). HydraDB does not capture memories on its own.

Good moments to save one: a user states a preference, makes a decision, finishes a task, or corrects the agent. Saving "this customer already tried restarting" lets a support agent skip that suggestion next time.

Material your users read, such as product docs, policies, and wiki pages, is [Knowledge](/essentials/v2/knowledge), not a memory.

This guide builds up in order:

1. [Save a memory](#1-save-a-memory): one request, shown with a user preference.
2. [Scope memories to one user](#2-scope-memories-to-one-user): the `collection` field.
3. [Let HydraDB infer the memory](#3-let-hydradb-infer-the-memory): the `infer` switch, with a behavior-log example.
4. [Send text or a conversation](#4-send-text-or-a-conversation): the two input shapes.
5. [Retrieve memories](#5-retrieve-memories): one user's memories, alone or together with knowledge.
6. [Update or delete a memory](#6-update-or-delete-a-memory).
7. [Field reference](#field-reference) and [common mistakes](#common-mistakes).

## 1. Save a memory

The example below saves one preference for the user `john_123`. Every memory uses this same request; only the items inside `memories` change.

The request is **multipart/form-data** with these fields:

| Form field | What to send |
| - | - |
| `type` | `memory` |
| `database` | An existing [database](/api-reference/v2/endpoint/create-tenant) |
| `collection` | The user's own collection, such as `user_john_123`. [Section 2](#2-scope-memories-to-one-user) explains why. |
| `memories` | One item or a list of items, as a JSON string |
| `upsert` | Leave it out. It defaults to `true`, so sending an item again with the same `id` replaces the saved memory. |

<Accordion title="Save a memory: Python, TypeScript, or cURL">
  Set up your [SDK client](/api-reference/v2/sdks#client-setup) before running the Python or TypeScript example.

  <CodeGroup>
    ```python Python SDK theme={"dark"}
    import json
    import os
    from hydra_db import HydraDB

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

    result = client.context.ingest(
        type="memory",
        database="acme_corp",
        collection="user_john_123",
        memories=json.dumps([
            {
                "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.context.ingest({
      type: "memory",
      database: "acme_corp",
      collection: "user_john_123",
      memories: JSON.stringify([
        {
          text: "Prefers short, direct answers with no preamble.",
          infer: true,
          user_name: "John",
        },
      ]),
    });
    ```

    ```bash cURL theme={"dark"}
    curl -X POST 'https://api.hydradb.com/context/ingest' \
      -H "Authorization: Bearer $HYDRA_DB_API_KEY" \
      -H "API-Version: 2" \
      -F "type=memory" \
      -F "database=acme_corp" \
      -F "collection=user_john_123" \
      -F 'memories=[
        {
          "text": "Prefers short, direct answers with no preamble.",
          "infer": true,
          "user_name": "John"
        }
      ]'
    ```
  </CodeGroup>

  The response lists each memory in `data.results` with its `id` and a `status` of `queued`. The payload is wrapped in the standard envelope, so read it from `response.data`:

  ```json theme={"dark"}
  {
    "success": true,
    "data": {
      "success": true,
      "message": "Memories queued for ingestion successfully",
      "results": [
        { "id": "mem_abc123", "status": "queued", "infer": true }
      ],
      "success_count": 1,
      "failed_count": 0
    },
    "error": null,
    "meta": {
      "request_id": "9d13aef4-02f4-4e73-8c62-4c2601d04f9d",
      "latency_ms": 12.3
    }
  }
  ```
</Accordion>

### What each property in the item means

| Property | What it is |
| - | - |
| `text` | The memory, in your words. Use `user_assistant_pairs` instead when the signal is in a conversation. [Section 4](#4-send-text-or-a-conversation) covers both. |
| `infer` | `true` asks HydraDB to extract the preference or fact from what you sent. `false`, the default, stores your text as written. [Section 3](#3-let-hydradb-infer-the-memory) explains the choice. |
| `user_name` | The user's name, used during inference. |
| `id` | Optional stable ID you choose. Send the same `id` later to replace the memory, or use it to check status and delete. Leave it out to have one generated; the response returns it either way. |
| `metadata`, `additional_metadata` | Values for filtering and bookkeeping, sent as objects. See [Metadata](/essentials/v2/metadata). |

### Status

Ingestion runs in the background: HydraDB parses the input, infers the memory if you asked it to, and indexes the result. Check [`GET /context/status`](/api-reference/v2/endpoint/source-status) with the returned `id` and the same `collection`. The memory is searchable once `indexing_status` reaches `graph_creation`; `completed` means the graph work is done too. If you would rather not poll, [register a webhook](/essentials/v2/webhooks).

## 2. Scope memories to one user

A collection is a partition inside your database. Send the user's own `collection` on every write, status check, and query, and that user's memories never mix with another user's. Your backend selects it from the authenticated session; the user does not choose it.

If you leave `collection` out, the memory lands in the database's default collection, and a query that names a user's collection does not read the default one. The memory type itself does not grant or enforce access; the collection does the scoping. See [Multi-tenancy](/essentials/v2/multi-tenant).

## 3. Let HydraDB infer the memory

`infer` controls how much extraction work your application does before sending content to HydraDB.

### Use `infer: true` when the input is raw signal

With `infer: true`, you give HydraDB messy or indirect evidence (dialogue, logs, behavior, feedback, or observations), and HydraDB extracts the useful preference, trait, or fact.

For example, instead of writing your own logic to decide whether a user prefers dark mode, send the raw stream of UI events and let HydraDB infer it. The example below does exactly this.

Use `infer: true` for:

* Dialogue where the preference is implicit.
* Behavior logs and event streams.
* Feedback like "the last summary was too long."
* Any input where the useful memory needs to be derived from raw context.

`custom_instructions` only takes effect when `infer: true`. Use it to guide extraction, for example: `"Focus on UI and notification preferences only"`.

### Use `infer: false` when the input is already the memory

With `infer: false` (the default), HydraDB stores and indexes exactly what you send. There is no extraction step.

Use `infer: false` for:

* Facts you already captured, such as `"User's plan tier is Pro"`.
* Pre-structured notes.
* Content you want to retrieve exactly as written.

**Rule of thumb:** `infer: false` is faster and deterministic. `infer: true` is better when your input is raw, noisy, or indirect.

<Accordion title="Infer a preference from a behavior log: Python, TypeScript, or cURL">
  A user has been toggling dark mode in your app. Instead of extracting the preference yourself, send the raw behavior log and let HydraDB infer the useful memory.

  <CodeGroup>
    ```python Python SDK theme={"dark"}
    import json

    client.context.ingest(
        type="memory",
        database="acme_corp",
        collection="user_john_123",
        memories=json.dumps([
            {
                "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.context.ingest({
      type: "memory",
      database: "acme_corp",
      collection: "user_john_123",
      memories: JSON.stringify([
        {
          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.",
        },
      ]),
    });
    ```

    ```bash cURL theme={"dark"}
    curl -X POST 'https://api.hydradb.com/context/ingest' \
      -H "Authorization: Bearer $HYDRA_DB_API_KEY" \
      -H "API-Version: 2" \
      -F "type=memory" \
      -F "database=acme_corp" \
      -F "collection=user_john_123" \
      -F '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>

  Later, when [`POST /query`](/api-reference/v2/endpoint/query) runs with `type: "memory"` and a query like `"what UI settings does the user prefer?"`, HydraDB can return the inferred preference (for example, `"prefers dark mode"`) rather than the raw event log.

  If you sent the same input with `infer: false`, HydraDB would index the event log verbatim. That is useful only when you want to retrieve the log itself.
</Accordion>

## 4. Send text or a conversation

Each memory item takes either `text` or `user_assistant_pairs`, **not both** (sending both returns `400`). Choose the shape that matches the content you already have.

* **`text`:** any prose input: plain observations, captured facts, preference statements, meeting notes, memos, or semi-structured records. Set `is_markdown: true` when the content uses Markdown syntax (headings, lists, code blocks) and you want HydraDB to preserve that structure during chunking and embedding. Leave `is_markdown` unset for plain prose.
* **`user_assistant_pairs`:** dialogue, especially when the preference is implied rather than stated. Each pair is `{ "user": "...", "assistant": "..." }`.

<Accordion title="Input shapes: text, Markdown, and user-assistant pairs">
  Markdown notes, stored as written:

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

  A plain statement, with the preference extracted:

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

  A conversation, with the preference extracted:

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

## 5. Retrieve memories

Memories live in their own store, separate from [Knowledge](/essentials/v2/knowledge). [`POST /query`](/api-reference/v2/endpoint/query) reaches each store through the `type` parameter:

* `type: "memory"` queries the Memories store (`vectorstore_status.memories`).
* `type: "knowledge"` queries the [Knowledge](/essentials/v2/knowledge) store (`vectorstore_status.knowledge`).
* `type: "all"` runs both in parallel and returns a single merged, re-ranked result set.

Send the same `collection` you used when saving. Both stores are read from that collection, so if shared documents live in their own collection, list it next to the user's in `collections`. See [Query](/essentials/v2/query).

<Accordion title="Query one user's memories: Python, TypeScript, or cURL">
  <CodeGroup>
    ```python Python SDK theme={"dark"}
    result = client.query(
        database="acme_corp",
        collection="user_john_123",
        query="How does the user like answers written?",
        type="memory",
    )

    for chunk in result.data.chunks:
        print(chunk.chunk_content)
    ```

    ```typescript TypeScript SDK theme={"dark"}
    const result = await client.query({
      database: "acme_corp",
      collection: "user_john_123",
      query: "How does the user like answers written?",
      type: "memory",
    });

    for (const chunk of result.data.chunks) {
      console.log(chunk.chunkContent);
    }
    ```

    ```bash cURL theme={"dark"}
    curl -X POST 'https://api.hydradb.com/query' \
      -H "Authorization: Bearer $HYDRA_DB_API_KEY" \
      -H "API-Version: 2" \
      -H "Content-Type: application/json" \
      -d '{
        "database": "acme_corp",
        "collection": "user_john_123",
        "query": "How does the user like answers written?",
        "type": "memory"
      }'
    ```
  </CodeGroup>

  Matching memories come back in `data.chunks`, ranked by relevance. Pass their text to your model as context. [How to Use API Results](/essentials/v2/api-results) shows how to turn the response into a prompt.
</Accordion>

## 6. Update or delete a memory

To change a memory, send it again with the same `id` and the new content. `upsert` defaults to `true`, so the saved memory is replaced. Wait for the previous ingestion to reach `completed` or `errored` before sending the next update for the same `id`.

To remove a memory, call [`DELETE /context`](/api-reference/v2/endpoint/delete-source) with its `id` and the same `collection`.

## Field reference

<AccordionGroup>
  <Accordion title="Request fields">
    | Field | Default | Description |
    | - | - | - |
    | <Field name="type" type="memory" required /> | None | Selects the Memories store. Use singular `memory`. |
    | <Field name="database" type="string" required /> | None | Target database. The `database` field was formerly `tenant_id`. |
    | <Field name="collection" type="string" recommended /> | default collection | User, workspace, or session scope for the memory. The `collection` field was formerly `sub_tenant_id`. |
    | <Field name="upsert" type="boolean" /> | `true` | Replace existing memories with the same `id`. |
    | <Field name="memories" type="string (JSON array)" required /> | None | JSON-stringified array of memory items. |
  </Accordion>

  <Accordion title="Memory item fields">
    | Field | Description |
    | - | - |
    | <Field name="id" type="string" /> | Optional stable ID. Acts as the upsert key. |
    | <Field name="title" type="string" /> | Short label for display in `/context/list` and query results. |
    | <Field name="text" type="string" recommended /> | Raw text or Markdown content. Required unless `user_assistant_pairs` is provided. |
    | <Field name="user_assistant_pairs" type="array" recommended /> | Conversation pairs in the shape `{ "user": "...", "assistant": "..." }`. Required unless `text` is provided. |
    | <Field name="is_markdown" type="boolean" /> | Treat `text` as Markdown for chunking and indexing. |
    | <Field name="infer" type="boolean" /> | When `true`, HydraDB extracts the underlying preference, trait, or fact. When `false` (the default), it stores the input as written. |
    | <Field name="custom_instructions" type="string" /> | Guides extraction when `infer: true`. Ignored when `infer: false`. |
    | <Field name="user_name" type="string" /> | User name used during inference. |
    | <Field name="metadata" type="object" /> | Database-schema fields for filtering and query. Send this as an object inside each `memories[]` item, for example `"metadata": { "department": "support" }`. Up to 16 KiB as compact JSON. |
    | <Field name="additional_metadata" type="object" /> | Free-form per-memory fields for display or bookkeeping. Send this as an object. Up to 1 KiB as compact JSON. |

    `memories` is itself a JSON-stringified multipart field. Within each memory item, `metadata` and `additional_metadata` are plain objects; do not stringify them again.

    ```json theme={"dark"}
    {
      "id": "pref_dark_mode",
      "text": "User prefers dark mode and concise answers.",
      "infer": true,
      "metadata": { "department": "support", "workspace": "docs" },
      "additional_metadata": { "source": "onboarding" }
    }
    ```
  </Accordion>
</AccordionGroup>

## Common mistakes

| Mistake | What goes wrong | Fix |
| - | - | - |
| Sending `"memory": "..."` instead of `"memories": [...]` | Validation fails | Use `"memories": [ { "text": "..." } ]`, always an array of objects |
| Omitting `database` | `400` (`database is required`) | Include `database` on every call |
| Omitting `collection` for per-user data | The memory lands in the default collection and may surface in the wrong scope | Use the user's ID as `collection` for B2C separation |
| Extracting preferences yourself before sending raw logs | You duplicate work HydraDB can do for you | Send the raw signal with `infer: true` |
| Sending pre-extracted facts with `infer: true` | HydraDB may re-derive the memory and change the original phrasing | Use `infer: false` for already-structured facts |
| Treating `user_name` as cosmetic | It can influence inference, so the wrong name can bias extraction | Pass the actual user's name |
| Storing shared docs as memories | They will not surface when querying `type: "knowledge"` | Use [knowledge ingestion](/essentials/v2/knowledge) with `type=knowledge` on [`POST /context/ingest`](/api-reference/v2/endpoint/ingest-context) |

## Related

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


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