> ## Documentation Index
> Fetch the complete documentation index at: https://cortex-e852fafe-auto-update-openapi-6a9e3873a7492d091ac8c1f.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Ingest Context

> Ingestion endpoint for knowledge (documents, app sources) and user memories.

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>;
};

When context is of `type=knowledge`:

1. **Documents** - Use the `documents` field for binary uploads that HydraDB should parse: PDFs, Office and iWork files, spreadsheets, images and plain text. See [Supported file formats](#supported-file-formats) for the full list.
2. **App Sources** - Use `app_knowledge` for pre-extracted JSON content (Slack threads, Notion pages, emails, tickets). Read more about [ingesting knowledge from your apps](/essentials/v2/app-sources).

When context is of `type=memory`

Use `memories` for per-user content, scoped with `collection`. Set `infer: true` to let HydraDB extract preferences from raw signals, or `infer: false` to store the text verbatim. Read more about [ingesting memories](/essentials/v2/memories).

<Note>
  `database` and `collection` are the current field names (formerly `tenant_id` and `sub_tenant_id`). The old names remain accepted as deprecated aliases for full backward compatibility.
</Note>

<RequestExample>
  ```python Python SDK theme={null}
  import json

  with open("/path/to/policy.pdf", "rb") as policy:
      knowledge_result = client.context.ingest(
          # Knowledge requests can include documents, app_knowledge, or both.
          type="knowledge",
          database="acme_corp",
          collection="team_docs",
          upsert=True,

          # Use documents when HydraDB should parse PDFs, DOCX, CSV, Markdown, or text files.
          documents=[("policy.pdf", policy, "application/pdf")],
          document_metadata=json.dumps([
              {
                  "id": "policy_main",
                  "metadata": {"department": "legal"},
                  "additional_metadata": {"source": "policy"},
              }
          ]),

          # Use app_knowledge when your app already extracted the source text/metadata.
          app_knowledge=json.dumps([
              {
                  "id": "slack_thread_001",
                  "database": "acme_corp",
                  "collection": "team_docs",
                  "title": "Pricing discussion",
                  "type": "slack",
                  "content": {"text": "We agreed on three tiers..."},
                  "metadata": {"department": "product"},
                  "additional_metadata": {"channel": "pricing"},
              }
          ]),
      )

  text_memory_result = client.context.ingest(
      type="memory",
      database="acme_corp",
      collection="user_alex",
      memories=json.dumps([
          {
              # Use text for raw notes or signals.
              "text": "Prefers concise answers and dark mode.",
              "infer": True,
              "user_name": "Alex",
          }
      ]),
  )

  conversation_memory_result = client.context.ingest(
      type="memory",
      database="acme_corp",
      collection="user_alex",
      memories=json.dumps([
          {
              # Use user_assistant_pairs for conversation history instead of text.
              "title": "Support conversation about refunds",
              "user_assistant_pairs": [
                  {"user": "Can I get a refund?", "assistant": "Refunds are available within 30 days."},
              ],
              "infer": False,
          }
      ]),
  )
  ```

  ```typescript TypeScript SDK theme={null}
  const knowledgeResult = await client.context.ingest({
    // Knowledge requests can include documents, app_knowledge, or both.
    type: "knowledge",
    database: "acme_corp",
    collection: "team_docs",
    upsert: true,

    // Use documents when HydraDB should parse PDFs, DOCX, CSV, Markdown, or text files.
    documents: [
      { path: "/path/to/policy.pdf", filename: "policy.pdf", contentType: "application/pdf" },
    ],
    documentMetadata: JSON.stringify([
      {
        id: "policy_main",
        metadata: { department: "legal" },
        additional_metadata: { source: "policy" },
      },
    ]),

    // Use app_knowledge when your app already extracted the source text/metadata.
    appKnowledge: JSON.stringify([
      {
        id: "slack_thread_001",
        database: "acme_corp",
        collection: "team_docs",
        title: "Pricing discussion",
        type: "slack",
        content: { text: "We agreed on three tiers..." },
        metadata: { department: "product" },
        additional_metadata: { channel: "pricing" },
      },
    ]),
  });

  const textMemoryResult = await client.context.ingest({
    type: "memory",
    database: "acme_corp",
    collection: "user_alex",
    memories: JSON.stringify([
      {
        // Use text for raw notes or signals.
        text: "Prefers concise answers and dark mode.",
        infer: true,
        user_name: "Alex",
      },
    ]),
  });

  const conversationMemoryResult = await client.context.ingest({
    type: "memory",
    database: "acme_corp",
    collection: "user_alex",
    memories: JSON.stringify([
      {
        // Use user_assistant_pairs for conversation history instead of text.
        title: "Support conversation about refunds",
        user_assistant_pairs: [
          { user: "Can I get a refund?", assistant: "Refunds are available within 30 days." },
        ],
        infer: false,
      },
    ]),
  });
  ```

  ```bash cURL theme={null}
  curl -X POST 'https://api.hydradb.com/context/ingest' \
    -H "Authorization: Bearer <your_api_key>" \
    -H "API-Version: 2" \
    -F "type=knowledge" \
    -F "database=acme_corp" \
    -F "collection=team_docs" \
    -F "upsert=true" \
    -F "documents=@/path/to/policy.pdf" \
    -F 'document_metadata=[
      {
        "id": "policy_main",
        "metadata": { "department": "legal" },
        "additional_metadata": { "source": "policy" }
      }
    ]' \
    -F 'app_knowledge=[
      {
        "id": "slack_thread_001",
        "database": "acme_corp",
        "collection": "team_docs",
        "title": "Pricing discussion",
        "type": "slack",
        "content": { "text": "We agreed on three tiers..." },
        "metadata": { "department": "product" },
        "additional_metadata": { "channel": "pricing" }
      }
    ]'

  # OR: memory from raw text.
  curl -X POST 'https://api.hydradb.com/context/ingest' \
    -H "Authorization: Bearer <your_api_key>" \
    -H "API-Version: 2" \
    -F "type=memory" \
    -F "database=acme_corp" \
    -F "collection=user_alex" \
    -F 'memories=[
      {
        "text": "Prefers concise answers and dark mode.",
        "infer": true,
        "user_name": "Alex"
      }
    ]'

  # OR: memory from conversation pairs.
  curl -X POST 'https://api.hydradb.com/context/ingest' \
    -H "Authorization: Bearer <your_api_key>" \
    -H "API-Version: 2" \
    -F "type=memory" \
    -F "database=acme_corp" \
    -F "collection=user_alex" \
    -F 'memories=[
      {
        "title": "Support conversation about refunds",
        "user_assistant_pairs": [
          { "user": "Can I get a refund?", "assistant": "Refunds are available within 30 days." }
        ],
        "infer": false
      }
    ]'
  ```
</RequestExample>

## Upload in-memory text as a `.txt` file

If you already have text in memory, create a file-like object and upload it through `documents` as a `text/plain` `.txt` file.

<CodeGroup>
  ```python Python SDK theme={null}
  import io
  import json

  text = """Q4 planning notes

  - Launch checklist is owned by Priya.
  - Legal review is due by Friday.
  """

  # Create a file-like object in memory. No local .txt file is required.
  txt_file = io.BytesIO(text.encode("utf-8"))

  response = client.context.ingest(
      type="knowledge",
      database="acme_corp",
      collection="team_docs",
      documents=[("meeting-notes.txt", txt_file, "text/plain")],
      document_metadata=json.dumps([
          {"id": "meeting_notes_q4", "metadata": {"department": "product"}}
      ]),
  )
  ```

  ```typescript TypeScript SDK theme={null}
  const text = `Q4 planning notes

  - Launch checklist is owned by Priya.
  - Legal review is due by Friday.
  `;

  const response = await client.context.ingest({
    type: "knowledge",
    database: "acme_corp",
    collection: "team_docs",
    documents: [
      {
        filename: "meeting-notes.txt",
        contentType: "text/plain",
        data: Buffer.from(text, "utf-8"),
      },
    ],
    documentMetadata: JSON.stringify([
      { id: "meeting_notes_q4", metadata: { department: "product" } },
    ]),
  });
  ```

  ```python API theme={null}
  import io
  import json
  import requests

  text = """Q4 planning notes

  - Launch checklist is owned by Priya.
  - Legal review is due by Friday.
  """

  # Create a file-like object in memory. No local .txt file is required.
  txt_file = io.BytesIO(text.encode("utf-8"))

  response = requests.post(
      "https://api.hydradb.com/context/ingest",
      headers={
          "Authorization": "Bearer <your_api_key>",
          "API-Version": "2",
      },
      data={
          "type": "knowledge",
          "database": "acme_corp",
          "collection": "team_docs",
          "document_metadata": json.dumps([
              {"id": "meeting_notes_q4", "metadata": {"department": "product"}}
          ]),
      },
      files={
          "documents": ("meeting-notes.txt", txt_file, "text/plain"),
      },
  )
  response.raise_for_status()
  ```
</CodeGroup>

## Important form fields

| Name                                                                     | Description                                                                                                                                                                                                                                                                                                                  |
| ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <Field name="type" type="&#x22;knowledge&#x22; or &#x22;memory&#x22;" /> | Use singular `"memory"` when writing memories. (default=`"knowledge"`)                                                                                                                                                                                                                                                       |
| <Field name="database" type="string" required />                         | Target database. Formerly `tenant_id`; the `tenant_id` alias is still accepted (deprecated).                                                                                                                                                                                                                                 |
| <Field name="collection" type="string" />                                | Logical partition inside the database. Formerly `sub_tenant_id`; the `sub_tenant_id` alias is still accepted (deprecated). (default=`""`  -  default collection)                                                                                                                                                             |
| <Field name="upsert" type="boolean" />                                   | Replace existing sources with the same ID. Set to `false` to error on conflict. (default=`true`)                                                                                                                                                                                                                             |
| <Field name="documents" type="file[]" />                                 | Binary uploads, **knowledge only**. Required when `type=knowledge` and you want HydraDB to parse documents. Omit when ingesting memories. See [Supported file formats](#supported-file-formats). (default=`[]`)                                                                                                              |
| <Field name="document_metadata" type="string (JSON array)" />            | One entry per file in `documents`, in the same order. If omitted, documents index with inferred defaults such as filename/title. See the item shape below.                                                                                                                                                                   |
| <Field name="app_knowledge" type="string (JSON object or array)" />      | Pre-extracted source objects (Slack, Notion, web pages, etc.), **knowledge only**. See the `app_knowledge` item shape below.                                                                                                                                                                                                 |
| <Field name="graph_payload" type="string (JSON map)" />                  | Map of source id → your own entities + relations - replaces LLM graph extraction for each keyed source. Works for `type=knowledge` (key = a `document_metadata` id or `app_knowledge` item id) and `type=memory` (key = a memory `id`). See [Bring Your Own Graph](/essentials/v2/bring-your-own-graph) and the shape below. |
| <Field name="memories" type="string (JSON array)" />                     | Memory items, **memory only**. Required and non-empty when `type=memory`. Use plural `memories` for the form field, even though `type` is singular `memory`. See the `memories` item shape below.                                                                                                                            |

<Note>
  1. **`id` must not contain a comma (`,`).** The comma is reserved as the id separator on [Ingestion Status](/api-reference/v2/endpoint/source-status) (`GET /context/status?ids=a,b`), so an `id` containing a comma cannot be looked up unambiguously. This applies to every `id` you supply  -  `document_metadata`, `app_knowledge`, and `memories` items. Ingesting an item whose `id` contains a comma is rejected with a `400`.

  2. **`202 Accepted` means queued, not indexed.** Ingestion is asynchronous. A successful response only confirms your sources were accepted, not that they are ready to query. Before querying, poll [`GET /context/status`](/api-reference/v2/endpoint/source-status) with the returned IDs until each source reaches `completed` or `errored`. Alternatively, register a webhook for `indexing.status_changed` events (see [Webhooks](/essentials/v2/webhooks)).
</Note>

## Supported file formats

Anything in this table can be uploaded through `documents` and HydraDB will read it.

| Type          | Formats                                                           |
| ------------- | ----------------------------------------------------------------- |
| Documents     | `.pdf` `.doc` `.docx` `.docm` `.dot` `.dotx` `.odt` `.ott` `.rtf` |
| Spreadsheets  | `.xls` `.xlsx` `.xlsm` `.xlsb` `.ods` `.ots` `.csv` `.tsv`        |
| Presentations | `.ppt` `.pptx` `.pptm` `.odp` `.otp`                              |
| Apple iWork   | `.pages` `.numbers` `.key`                                        |
| Images        | `.png` `.jpg` `.jpeg` `.tif` `.tiff` `.webp` `.gif` `.bmp`        |
| Plain text    | `.txt` `.md` `.markdown` `.json`                                  |

What HydraDB extracts differs by type, and it is worth knowing which one you are uploading:

| Type                        | What gets indexed                                                                                                      |
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Documents and presentations | The text, in reading order. Slide titles and body text both come through.                                              |
| Spreadsheets                | Cell values rendered as a table, one per sheet. Formulas are indexed as their computed result, not the formula itself. |
| Images and scans            | Text read out of the picture with OCR. A photo of a page and a scan of a page both work.                               |
| Plain text                  | The file as-is.                                                                                                        |

A scanned page with nothing readable on it, or a photo that happens to contain no text, will index as an empty document rather than fail.

### Formats we cannot read

These are rejected the moment you upload them, before anything is queued. You get the answer in the upload response rather than minutes later.

| Format                  | Upload this instead                                  |
| ----------------------- | ---------------------------------------------------- |
| `.heic` `.heif` `.avif` | Export the photo as JPEG or PDF                      |
| `.svg` `.ico`           | Export as PNG or JPEG                                |
| `.html` `.htm` `.xml`   | Save the page as PDF                                 |
| `.eml` `.msg`           | Save the message as PDF, or send it as an app source |
| `.epub` `.wpd`          | Convert to PDF or DOCX                               |
| `.zip`                  | Upload the files individually                        |

<Note>
  `.heic` is the format iPhones use for photos by default, so it is the one people hit most often without realizing. On iOS you can change this under **Settings > Camera > Formats > Most Compatible**, which makes the camera save JPEGs instead. Or export the photo as JPEG or PDF before uploading.
</Note>

### How we decide the format

The file extension is what counts. `report.pdf` is treated as a PDF because of the `.pdf`, not because of what is inside it.

If the filename has no extension at all, the `Content-Type` you send with that part is used instead, so a file with no extension in its name, sent as `application/pdf`, is accepted.

<Note>
  Renaming a file does not convert it. A `.heic` photo renamed to `photo.pdf` passes this check, because the check reads the name, then fails later during parsing and comes back as `errored` on [`GET /context/status`](/api-reference/v2/endpoint/source-status). Upload files under their real extension.
</Note>

### Size limit

Each file in `documents` can be up to **50 MB**. A larger file is rejected with `413` and a message naming the file, and no part of the request is processed. Split large documents, or upload them separately.

## When a file is not supported

A rejected file does not stop the rest of your upload. Each file is checked on its own, so a batch of ten with one bad file still indexes the other nine.

The request returns `202` as usual. The rejected file comes back with `status: "failed"` and `error_code: "E1002"`, and every other file is queued normally:

```json theme={null}
{
  "success": true,
  "data": {
    "success": false,
    "message": "Upload completed with some failures.",
    "results": [
      { "id": "6cc7bcd185b044cd", "filename": "report.pdf", "status": "queued", "error": null, "error_code": null },
      { "id": "f47411c736447126", "filename": "photo.heic", "status": "failed", "error": "This file format isn't supported. Please upload a PDF, Office document (Word, Excel, PowerPoint), image, CSV or text file.", "error_code": "E1002" },
      { "id": "d0c919bdec196a8d", "filename": "notes.txt", "status": "queued", "error": null, "error_code": null }
    ],
    "success_count": 2,
    "failed_count": 1
  },
  "error": null,
  "meta": { "request_id": "bcd03673-174d-4a73-83e6-73bfcdc16061", "api_version": "2.0.1" }
}
```

<Note>
  `results`, `success_count` and `failed_count` sit inside `data`, not at the top level, like every other v2 response. The outer `success: true` means the request was accepted; `data.success` is what tells you whether every file in it was queued.
</Note>

<Note>
  **Do not poll [`GET /context/status`](/api-reference/v2/endpoint/source-status) for a file rejected with `E1002`.** The file never entered the pipeline, so it has no status record, and looking it up returns `FILE_NOT_FOUND` rather than the format error you were given. The upload response is the only place `E1002` appears. Read `error_code` on each item in `data.results` and act on it there.
</Note>

## Common use-cases and their configurations

### Document metadata

Per-document metadata (`id`, `metadata`, `additional_metadata`, `relations`) can be passed alongside each uploaded document to control indexing, filtering, and display. The key list is closed  -  an item carrying any other key is rejected with a `400` naming it, rather than being silently dropped. See the field reference below.

<Accordion title="Knowledge documents - upload PDFs, DOCX, CSV, markdown, or text">
  <CodeGroup>
    ```python Python SDK theme={null}
    import json

    with open("/path/to/policy.pdf", "rb") as f1, open("/path/to/runbook.pdf", "rb") as f2:
        result = client.context.ingest(
            type="knowledge",
            database="acme_corp",
            documents=[
                ("policy.pdf", f1, "application/pdf"),
                ("runbook.pdf", f2, "application/pdf"),
            ],
            document_metadata=json.dumps([
                {"id": "policy_main", "metadata": {"department": "legal"}},
                {"id": "runbook_deploy", "metadata": {"department": "ops"}},
            ]),
        )
    ```

    ```typescript TypeScript SDK theme={null}
    const result = await client.context.ingest({
      type: "knowledge",
      database: "acme_corp",
      documents: [
        { path: "/path/to/policy.pdf", filename: "policy.pdf", contentType: "application/pdf" },
        { path: "/path/to/runbook.pdf", filename: "runbook.pdf", contentType: "application/pdf" },
      ],
      documentMetadata: JSON.stringify([
        { id: "policy_main", metadata: { department: "legal" } },
        { id: "runbook_deploy", metadata: { department: "ops" } },
      ]),
    });
    ```

    ```bash cURL theme={null}
    curl -X POST 'https://api.hydradb.com/context/ingest' \
      -H "Authorization: Bearer <your_api_key>" \
      -H "API-Version: 2" \
      -F "type=knowledge" \
      -F "database=acme_corp" \
      -F "documents=@/path/to/policy.pdf" \
      -F "documents=@/path/to/runbook.pdf" \
      -F 'document_metadata=[
        { "id": "policy_main", "metadata": { "department": "legal" } },
        { "id": "runbook_deploy", "metadata": { "department": "ops" } }
      ]'
    ```
  </CodeGroup>

  | Field                                              | Description                                                                                                                                                                                             |
  | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | <Field name="id" type="string" />                  | Optional context ID. If set, becomes the `id` for this document (use your app's document ID for parity). Must not contain a comma (`,`) - it is reserved as the id separator on `/context/status?ids=`. |
  | <Field name="metadata" type="object" />            | Database-schema fields for filtering and search. Keys must be declared in `database_metadata_schema`. (default=`{}`)                                                                                    |
  | <Field name="additional_metadata" type="object" /> | Free-form per-document fields for display or bookkeeping. To filter on these at search time, nest under `metadata_filters.additional_metadata`. (default=`{}`)                                          |
  | <Field name="relations" type="object" />           | Declare forceful relations to other sources. Shape: `{ "ids": ["...", "..."] }`. Surfaced via `additional_context` in `mode: "thinking"` search.                                                        |

  <Note>
    Those four are the only keys accepted. Anything else  -  including `title`, `type`, `url` and `timestamp`  -  is rejected with a `400` naming the unsupported key, rather than being silently dropped.

    In particular, a document's **title is derived, not settable**. It defaults to the uploaded filename and is returned as `source_title` on query results and `title` on `/context/list`. It cannot be overridden at ingest, and [`PATCH /context/{id}/metadata`](/api-reference/v2/endpoint/update-source-metadata) only merges `additional_metadata` and `database_metadata`. Use `additional_metadata` for your own display fields, or ingest through `app_knowledge`, whose items carry an explicit `title`.
  </Note>
</Accordion>

<Accordion title="App sources - index text from your workspace or personal apps">
  <CodeGroup>
    ```python Python SDK theme={null}
    import json

    client.context.ingest(
        type="knowledge",
        database="acme_corp",
        collection="team_docs",
        app_knowledge=json.dumps([
            {
                "id": "slack_thread_001",
                "database": "acme_corp",
                "collection": "team_docs",
                "title": "Pricing discussion",
                "type": "slack",
                "content": {"text": "We agreed on three tiers..."},
                "metadata": {"channel": "product"},
            }
        ]),
    )
    ```

    ```typescript TypeScript SDK theme={null}
    await client.context.ingest({
      type: "knowledge",
      database: "acme_corp",
      collection: "team_docs",
      appKnowledge: JSON.stringify([
        {
          id: "slack_thread_001",
          database: "acme_corp",
          collection: "team_docs",
          title: "Pricing discussion",
          type: "slack",
          content: { text: "We agreed on three tiers..." },
          metadata: { channel: "product" },
        },
      ]),
    });
    ```

    ```bash cURL theme={null}
    curl -X POST 'https://api.hydradb.com/context/ingest' \
      -H "Authorization: Bearer <your_api_key>" \
      -H "API-Version: 2" \
      -F "type=knowledge" \
      -F "database=acme_corp" \
      -F "collection=team_docs" \
      -F 'app_knowledge=[
        {
          "id": "slack_thread_001",
          "database": "acme_corp",
          "collection": "team_docs",
          "title": "Pricing discussion",
          "type": "slack",
          "content": { "text": "We agreed on three tiers..." },
          "metadata": { "channel": "product" }
        }
      ]'
    ```
  </CodeGroup>

  | Field                                              | Description                                                                                                                                                                                |
  | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
  | <Field name="id" type="string" required />         | Context ID. Treated as the upsert key. Send an empty string to have one generated upstream. Must not contain a comma (`,`) - it is reserved as the id separator on `/context/status?ids=`. |
  | <Field name="database" type="string" required />   | Target database. Must match the form-level `database`. Formerly `tenant_id`; the `tenant_id` alias is still accepted (deprecated).                                                         |
  | <Field name="collection" type="string" required /> | Logical partition inside the database. Must match the form-level `collection`. Formerly `sub_tenant_id`; the `sub_tenant_id` alias is still accepted (deprecated).                         |
  | <Field name="title" type="string" />               | Short title or subject shown in search results.                                                                                                                                            |
  | <Field name="type" type="string" />                | Source category (`slack`, `notion`, `gmail`, `webpage`, etc.). Used for filtering and display.                                                                                             |
  | <Field name="description" type="string" />         | Optional long-form description.                                                                                                                                                            |
  | <Field name="url" type="string" />                 | Canonical URL or reference link.                                                                                                                                                           |
  | <Field name="timestamp" type="string" />           | ISO-8601 timestamp (creation or last-updated).                                                                                                                                             |
  | <Field name="content" type="object" required />    | Content payload. Use `{ "text": "..." }` for plain text. Required for app sources.                                                                                                         |
  | <Field name="metadata" type="object" />            | Database-schema fields. (default=`{}`)                                                                                                                                                     |
  | <Field name="additional_metadata" type="object" /> | Free-form per-document fields. (default=`{}`)                                                                                                                                              |
  | <Field name="attachments" type="array" />          | Optional related attachments. (default=`[]`)                                                                                                                                               |
  | <Field name="relations" type="object" />           | Forceful relations, same shape as on `metadata`.                                                                                                                                           |
</Accordion>

<Accordion title="3. User memories - store notes or infer user preferences">
  <CodeGroup>
    ```python Python SDK theme={null}
    import json

    client.context.ingest(
        type="memory",
        database="acme_corp",
        collection="user_alex",
        memories=json.dumps([
            {
                "text": "Prefers dark mode and short answers.",
                "infer": True,
                "user_name": "Alex",
            }
        ]),
    )
    ```

    ```typescript TypeScript SDK theme={null}
    await client.context.ingest({
      type: "memory",
      database: "acme_corp",
      collection: "user_alex",
      memories: JSON.stringify([
        {
          text: "Prefers dark mode and short answers.",
          infer: true,
          user_name: "Alex",
        },
      ]),
    });
    ```

    ```bash cURL theme={null}
    curl -X POST 'https://api.hydradb.com/context/ingest' \
      -H "Authorization: Bearer <your_api_key>" \
      -H "API-Version: 2" \
      -F "type=memory" \
      -F "database=acme_corp" \
      -F "collection=user_alex" \
      -F 'memories=[
        {
          "text": "Prefers dark mode and short answers.",
          "infer": true,
          "user_name": "Alex"
        }
      ]'
    ```
  </CodeGroup>

  | Field                                                            | Description                                                                                                                                                                                             |
  | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | <Field name="id" type="string" />                                | Optional unique ID. Acts as the upsert key. Must not contain a comma (`,`) - it is reserved as the id separator on `/context/status?ids=`. (default=auto-generated)                                     |
  | <Field name="title" type="string" />                             | Short label for display in `/context/list` and search hits as `source.title`. (default=truncated `text`)                                                                                                |
  | <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 `{ user, assistant }`. Required unless `text` is provided.                                                                                                                           |
  | <Field name="is_markdown" type="boolean" />                      | Treat `text` as markdown for chunking. (default=`false`)                                                                                                                                                |
  | <Field name="infer" type="boolean" />                            | When `true`, HydraDB extracts the underlying preference from raw signal. (default=`false`)                                                                                                              |
  | <Field name="custom_instructions" type="string" />               | Guides extraction when `infer: true`. Ignored when `infer: false`.                                                                                                                                      |
  | <Field name="user_name" type="string" />                         | The user's name. Feeds inference. (default=`"User"`)                                                                                                                                                    |
  | <Field name="expiry_time" type="integer" />                      | TTL in seconds. Memory stops surfacing after expiry.                                                                                                                                                    |
  | <Field name="metadata" type="string (JSON object)" />            | Database-schema fields as a **JSON-stringified** object (e.g. `"{\"department\":\"legal\"}"`). Unlike `metadata` and `app_knowledge`, memory items take this as a string, not an object. (default=`""`) |
  | <Field name="additional_metadata" type="string (JSON object)" /> | Free-form per-document fields as a **JSON-stringified** object. Same string-vs-object difference as `metadata` above. (default=`""`)                                                                    |
  | <Field name="relations" type="object" />                         | Forceful relations within the Memories store. Shape: `{ "ids": ["...", "..."] }`.                                                                                                                       |
</Accordion>

<Accordion title="4. Chat/LLM conversation pairs - store chat history or support context ">
  <CodeGroup>
    ```python Python SDK theme={null}
    import json

    client.context.ingest(
        type="memory",
        database="acme_corp",
        collection="user_alex",
        memories=json.dumps([
            {
                "title": "Support conversation about refunds",
                "user_assistant_pairs": [
                    {"user": "Can I get a refund?", "assistant": "Refunds are available within 30 days."},
                ],
                "infer": False,
            }
        ]),
    )
    ```

    ```typescript TypeScript SDK theme={null}
    await client.context.ingest({
      type: "memory",
      database: "acme_corp",
      collection: "user_alex",
      memories: JSON.stringify([
        {
          title: "Support conversation about refunds",
          user_assistant_pairs: [
            { user: "Can I get a refund?", assistant: "Refunds are available within 30 days." },
          ],
          infer: false,
        },
      ]),
    });
    ```

    ```bash cURL theme={null}
    curl -X POST 'https://api.hydradb.com/context/ingest' \
      -H "Authorization: Bearer <your_api_key>" \
      -H "API-Version: 2" \
      -F "type=memory" \
      -F "database=acme_corp" \
      -F "collection=user_alex" \
      -F 'memories=[
        {
          "title": "Support conversation about refunds",
          "user_assistant_pairs": [
            { "user": "Can I get a refund?", "assistant": "Refunds are available within 30 days." }
          ],
          "infer": false
        }
      ]'
    ```
  </CodeGroup>
</Accordion>

<Accordion title="Bring your own graph - supply entities and relations">
  `graph_payload` is a **map of source id → graph** that **replaces LLM graph extraction** for each keyed source. For `type=knowledge`, the key is a `document_metadata` id or an `app_knowledge` item id; for `type=memory`, the key is a memory `id`. Keyed sources are still chunked and embedded, so they stay searchable. See [Bring Your Own Graph](/essentials/v2/bring-your-own-graph) for the full guide.

  <CodeGroup>
    ```bash cURL theme={null}
    curl -X POST 'https://api.hydradb.com/context/ingest' \
      -H "Authorization: Bearer <your_api_key>" \
      -H "API-Version: 2" \
      -F "type=knowledge" \
      -F "database=acme_corp" \
      -F "documents=@/path/to/policy.pdf" \
      -F 'document_metadata=[{ "id": "billing-policy-doc" }]' \
      -F 'graph_payload={
        "billing-policy-doc": {
          "entities": {
            "alice":   { "name": "Alice Carter",  "type": "PERSON", "namespace": "employees" },
            "billing": { "name": "Billing Policy", "type": "POLICY", "namespace": "policies" }
          },
          "relations": [
            { "source": "alice", "target": "billing", "predicate": "OWNS",
              "context": "Alice Carter owns the billing policy." }
          ]
        }
      }'
    ```

    ```python Python SDK theme={null}
    import json

    with open("/path/to/policy.pdf", "rb") as f:
        client.context.ingest(
            type="knowledge",
            database="acme_corp",
            documents=[("policy.pdf", f, "application/pdf")],
            document_metadata=json.dumps([{"id": "billing-policy-doc"}]),
            graph_payload=json.dumps({
                "billing-policy-doc": {
                    "entities": {
                        "alice":   {"name": "Alice Carter",  "type": "PERSON", "namespace": "employees"},
                        "billing": {"name": "Billing Policy", "type": "POLICY", "namespace": "policies"},
                    },
                    "relations": [
                        {"source": "alice", "target": "billing", "predicate": "OWNS",
                         "context": "Alice Carter owns the billing policy."},
                    ],
                }
            }),
        )
    ```

    ```typescript TypeScript SDK theme={null}
    await client.context.ingest({
      type: "knowledge",
      database: "acme_corp",
      documents: [{ path: "/path/to/policy.pdf", filename: "policy.pdf", contentType: "application/pdf" }],
      documentMetadata: JSON.stringify([{ id: "billing-policy-doc" }]),
      graphPayload: JSON.stringify({
        "billing-policy-doc": {
          entities: {
            alice:   { name: "Alice Carter",  type: "PERSON", namespace: "employees" },
            billing: { name: "Billing Policy", type: "POLICY", namespace: "policies" },
          },
          relations: [
            { source: "alice", target: "billing", predicate: "OWNS",
              context: "Alice Carter owns the billing policy." },
          ],
        },
      }),
    });
    ```
  </CodeGroup>

  | Field                                                         | Description                                                                                                                                                                                        |
  | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | <Field name="<source_id>" type="object" />                    | Top-level key: a `document_metadata` id or `app_knowledge` item id for `type=knowledge`, or a memory `id` for `type=memory`. Value is that source's graph. A key matching no source returns `400`. |
  | <Field name="entities" type="object (map)" />                 | Map keyed by a caller-local id; each value is an entity. The key is only a handle for `relations` to reference - it is not stored.                                                                 |
  | <Field name="entities[].name" type="string" required />       | Entity name. Normalized (lowercased) server-side so it matches at query time. ≤ 256 chars.                                                                                                         |
  | <Field name="entities[].type" type="string" />                | Entity type (e.g. `PERSON`, `POLICY`). Stored as supplied.                                                                                                                                         |
  | <Field name="entities[].namespace" type="string" />           | Logical grouping for the entity. Stored as supplied.                                                                                                                                               |
  | <Field name="entities[].identifier" type="string" />          | Optional external id (email, URL, etc.) - display only.                                                                                                                                            |
  | <Field name="relations" type="array" />                       | Edges referencing entity-map keys.                                                                                                                                                                 |
  | <Field name="relations[].source" type="string" required />    | Entity-map key of the source entity.                                                                                                                                                               |
  | <Field name="relations[].target" type="string" required />    | Entity-map key of the target entity.                                                                                                                                                               |
  | <Field name="relations[].predicate" type="string" required /> | Relationship label, any plain string. ≤ 256 chars.                                                                                                                                                 |
  | <Field name="relations[].context" type="string" />            | Optional sentence supporting the edge. ≤ 2,000 chars.                                                                                                                                              |
  | <Field name="relations[].temporal_details" type="string" />   | Optional timing info (e.g. "since 2021", "in Q3").                                                                                                                                                 |

  <Note>
    **Per-source replace mode.** Each top-level key must match a `document_metadata` id or `app_knowledge` item id for `type=knowledge`, or a memory `id` for `type=memory`, in the same request; attach graphs to multiple sources at once. Extraction is skipped for keyed sources. Caps per graph: ≤ 5,000 entities, ≤ 10,000 relations, ≤ 500 relations per entity; over-cap returns `400`. Graphs survive re-ingest (re-upload or connector re-sync re-applies the stored graph).
  </Note>
</Accordion>

## Some important notes

* **Async indexing.** `202 Accepted` means HydraDB queued the work, not that content is searchable. Poll [Ingestion Status](/api-reference/v2/endpoint/source-status) until `indexing_status` reaches `graph_creation` (searchable) or `completed` (graph-ready).
* **Multipart, not JSON.** This endpoint uses `multipart/form-data`. Stringify all JSON arrays (`metadata`, `app_knowledge`, `memories`) before placing them in the form field.
* **Declare hot schema fields upfront.** Put frequently filtered fields in `metadata`, define them in `database_metadata_schema` with `enable_match: true`, and use `additional_metadata` for free-form display/bookkeeping fields. Define filterable fields when creating the database via [Create Database](/api-reference/v2/endpoint/create-tenant). Additive schema updates exist, but newly added dense/sparse metadata lanes are not backfilled into existing Milvus collections.
* **Memory vs knowledge.** Use `type: "memory"` for memory ingestion, listing, and deletion. Use `type: "all"` on `POST /query` when results should combine both. The multipart field name for memories is always `memories`.
* **Collection defaulting.** Omitting `collection` writes to the default collection. List available collections with [List Collections](/api-reference/v2/endpoint/list-sub-tenants).

<div className="api-before-related-resources" />

<Tip>
  **Related Resources**

  * **Always check** [ingestion status](/api-reference/v2/endpoint/source-status) to ensure context is ready to be retrieved
  * [Query](/api-reference/v2/endpoint/query) once context is ready
  * **Inspect:** [List Documents](/api-reference/v2/endpoint/list-documents) helps you fetch titles and descriptions of ingested context
  * **Inspect:** [Fetch Content](/api-reference/v2/endpoint/fetch-content) helps you fetch full context of a document, memory, knowledge item
  * **Cleanup:** [Delete Context](/api-reference/v2/endpoint/delete-source)
</Tip>


## OpenAPI

````yaml api-reference/v2/openapi.json POST /context/ingest
openapi: 3.1.0
info:
  contact:
    email: support@hydradb.com
    name: HydraDB Support
  description: >-
    HydraDB Application API — knowledge ingestion, search, and memory
    management.
  license:
    name: Proprietary
  title: HydraDB Application API
  version: 0.1.0
servers:
  - description: Production server
    url: https://api.hydradb.com
security: []
externalDocs:
  description: ''
  url: ''
paths:
  /context/ingest:
    post:
      tags:
        - context
      summary: Ingest content
      description: >-
        Ingest content for a database. `context` is the preferred shape (text or
        a conversation per item); the deprecated `documents`, `app_knowledge`
        and `memories` fields are selected by `type`. The same `context` array
        may also be sent as an application/json body.
      requestBody:
        content:
          multipart/form-data:
            schema:
              properties:
                app_knowledge:
                  deprecated: true
                  description: >-
                    App-knowledge items as a JSON array (type=knowledge). Per
                    item, `metadata` is capped at 16 KiB and
                    `additional_metadata` at 1 KiB, measured on the compact JSON
                    encoding of the whole map in UTF-8 bytes (keys and
                    punctuation count). The deprecated `tenant_metadata` /
                    `document_metadata` spellings are accepted here and held to
                    the same caps. Over-cap returns 400 with the actual byte
                    count. Each item may also carry `acl`, a list of principals
                    (`user_email:<email>`, a bare email,
                    `group:<provider>:<id>`, `domain:<domain>`, or `__public__`)
                    restricting who may retrieve it; omit it to leave the
                    document unrestricted, and send an empty list to restrict it
                    to nobody. A malformed principal rejects the whole request
                    with 400. Items may also carry
                    `evidence_kind`/`evidence_subject` provenance labels (see
                    document_metadata); an unknown kind returns 400.
                  title: app_knowledge
                  type: string
                  x-deprecated: 'true'
                collection:
                  title: collection
                  type: string
                context:
                  description: >-
                    JSON-encoded array of contexts -- text or a conversation per
                    item. The same array may also be POSTed as an
                    application/json body under `context`; that variant is not
                    listed here so SDK generators emit this form, which carries
                    every field.
                  title: context
                  type: string
                database:
                  title: database
                  type: string
                document_metadata:
                  deprecated: true
                  description: >-
                    Per-document metadata as a JSON array (type=knowledge). Per
                    item, `metadata` is capped at 16 KiB and
                    `additional_metadata` at 1 KiB. Both caps are measured on
                    the compact JSON encoding of the whole map in UTF-8 bytes,
                    so keys, quotes, commas and braces count toward the budget.
                    Over-cap returns 400 with the actual byte count. Each item
                    may also carry evidence labels (`evidence_kind`: one of
                    assertion, record, said, done, third_party, inferred;
                    `evidence_subject`: a stable handle for who the evidence is
                    about, e.g. `user:kiran@acme.com`) declaring the content's
                    provenance for entity understanding; an unknown kind returns
                    400.
                  title: document_metadata
                  type: string
                  x-deprecated: 'true'
                documents:
                  deprecated: true
                  items:
                    format: binary
                    type: string
                  title: documents
                  type: array
                  x-deprecated: 'true'
                enrich:
                  default: 'true'
                  title: enrich
                  type: string
                graph_payload:
                  title: graph_payload
                  type: string
                instructions:
                  title: instructions
                  type: string
                memories:
                  deprecated: true
                  description: >-
                    Memory items as a JSON array (type=memory). Per item,
                    `metadata` is capped at 16 KiB and `additional_metadata` at
                    1 KiB, measured on the compact JSON encoding of the whole
                    map in UTF-8 bytes (keys and punctuation count). Over-cap
                    returns 400 with the actual byte count. Items may also carry
                    `evidence_kind`/`evidence_subject` provenance labels (see
                    document_metadata); an unknown kind returns 400.
                  title: memories
                  type: string
                  x-deprecated: 'true'
                sub_tenant_id:
                  deprecated: true
                  title: sub_tenant_id
                  type: string
                  x-deprecated: 'true'
                tenant_id:
                  deprecated: true
                  title: tenant_id
                  type: string
                  x-deprecated: 'true'
                type:
                  deprecated: true
                  enum:
                    - knowledge
                    - memory
                  title: type
                  type: string
                  x-deprecated: 'true'
                upsert:
                  default: 'true'
                  title: upsert
                  type: string
              required:
                - database
              type: object
        description: >-
          Context[] body: the application/json alternative to this form. |
          Deprecated: kept for split databases. Corpus to write to: 'knowledge'
          (default) or 'memory'. 'all' is refused here: an ingest must name the
          one corpus it writes to. | Database (canonical name for the tenant
          scope) | Collection (canonical name for the sub-tenant scope) |
          Deprecated alias for database | Deprecated alias for collection |
          Upsert existing content (true/false/1/0) | Deprecated: knowledge files
          to ingest (repeatable; type=knowledge, split databases only) |
          Deprecated: per-document metadata as a JSON array (type=knowledge,
          split databases only). Per item: metadata <= 16 KiB,
          additional_metadata <= 1 KiB. | Deprecated: app-knowledge items as a
          JSON array (type=knowledge, split databases only). Per item: metadata
          <= 16 KiB, additional_metadata <= 1 KiB, optional acl principal list
          (PRO-1684). | Deprecated: memory items as a JSON array (type=memory,
          split databases only); use context. Per item: metadata <= 16 KiB,
          additional_metadata <= 1 KiB. | Contexts as a JSON array, the same
          list a JSON body carries under `context`. Each is one of text |
          conversation ([{role, content}]), with optional context_id, title (<=
          1024 bytes), user_name, enrich, upsert, instructions (<= 4000 chars),
          happened_at, attributes, custom_attributes, context_category
          (auto|user_preference|business_knowledge|decision_trace),
          forceful_relations ({context_ids, properties}), acl. At most 100
          contexts, 1 MiB of text per context and 8 MiB per request. Unknown
          keys are refused. Contexts land in the memory corpus. | Request-level
          enrichment default for `context` (true/false/1/0) | Request-level
          enrichment instructions default for `context` (<= 4000 chars) |
          Optional bring-your-own-graph payload as JSON, keyed by context_id
          (context) or source_id (split paths)
        required: true
      responses:
        '202':
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/handler.Envelope-ingestion_V2IngestResponse
          description: Accepted
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/handler.ErrorResponse'
          description: Bad Request
        '413':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/handler.ErrorResponse'
          description: Request Entity Too Large
        '415':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/handler.ErrorResponse'
          description: Body is neither multipart/form-data nor application/json
        '422':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/handler.ErrorResponse'
          description: Unprocessable Entity
      security:
        - BearerAuth: []
components:
  schemas:
    handler.Envelope-ingestion_V2IngestResponse:
      properties:
        data:
          $ref: '#/components/schemas/ingestion.V2IngestResponse'
          example:
            failed_count: 0
            message: Success
            results:
              - error: ''
                filename: policy.pdf
                id: HydraDoc1234
                infer: true
                relations_created: 5
                status: queued
                title: Project Phoenix Overview
            success: true
            success_count: 2
        error:
          $ref: '#/components/schemas/handler.apiError'
          description: Error message, empty string on success.
          example:
            code: DATABASE_NOT_FOUND
            message: Database not found
        meta:
          $ref: '#/components/schemas/handler.responseMeta'
          example:
            collection: team_docs
            database: acme_corp
            latency_ms: 12.3
            request_id: 9d13aef4-02f4-4e73-8c62-4c2601d04f9d
            source_type: file
            sub_tenant_id: sub_tenant_4567
            tenant_id: tenant_1234
        success:
          description: Whether the request succeeded.
          example: true
          type: boolean
      type: object
    handler.ErrorResponse:
      properties:
        data: {}
        detail:
          $ref: '#/components/schemas/handler.ErrorDetail'
          description: Structured error detail with code, message, and deprecation hints.
          example:
            deprecated: true
            deprecated_field: tenant_id
            error_code: VALIDATION_ERROR
            message: Request validation failed
            preferred_field: database
        error:
          $ref: '#/components/schemas/handler.apiError'
          description: Error message, empty string on success.
          example:
            code: DATABASE_NOT_FOUND
            message: Database not found
        meta:
          $ref: '#/components/schemas/handler.ErrorMeta'
          example:
            latency_ms: 12.3
            request_id: 9d13aef4-02f4-4e73-8c62-4c2601d04f9d
        success:
          description: Whether the request succeeded.
          example: true
          type: boolean
      type: object
    ingestion.V2IngestResponse:
      properties:
        failed_count:
          description: Number of uploaded files that failed to queue.
          example: 0
          type: integer
        message:
          description: Human-readable result message.
          example: Success
          type: string
        results:
          description: Per-item results.
          example:
            - error: ''
              filename: policy.pdf
              id: HydraDoc1234
              infer: true
              relations_created: 5
              status: queued
              title: Project Phoenix Overview
          items:
            $ref: '#/components/schemas/ingestion.V2IngestResultItem'
          type: array
          uniqueItems: false
        success:
          deprecated: true
          description: >-
            Deprecated for API clients: whether the REQUEST was accepted is the
            HTTP

            status code (202) or equivalently the envelope's top-level
            `success`.

            Whether each SOURCE ingested is per-item — read results[].status and

            results[].error, then poll GET /context/status, since a 202 only
            means

            queued. This flag answers neither question independently: it always

            mirrors the envelope. Still emitted unchanged for existing clients

            (PRO-1208).
          example: true
          type: boolean
          x-deprecated: 'true'
        success_count:
          description: Number of files successfully queued for processing.
          example: 2
          type: integer
      type: object
    handler.apiError:
      properties:
        code:
          description: Machine-readable error code (e.g. `DATABASE_NOT_FOUND`).
          example: DATABASE_NOT_FOUND
          type: string
        message:
          description: Human-readable description of the error.
          example: Database not found
          type: string
      type: object
    handler.responseMeta:
      properties:
        api_version:
          description: >-
            APIVersion echoes the version of the API that served the request
            (PRO-1209),

            sourced from reqmeta.APIVersion — the same value carried by OpenAPI

            info.version and /health — so a client always knows which API
            version

            produced a response. Always present (no omitempty).
          type: string
        collection:
          description: >-
            Collection scope. Defaults to the default collection when omitted.
            Formerly `sub_tenant_id`; the `sub_tenant_id` alias is still
            accepted (deprecated).
          example: team_docs
          type: string
        database:
          description: >-
            Owning database. Formerly `tenant_id`; the `tenant_id` alias is
            still accepted (deprecated).
          example: acme_corp
          type: string
        deprecation:
          description: >-
            Deprecation lists any migration nudges that apply to this request —
            the

            caller used a legacy /tenants route, a legacy
            tenant_id/sub_tenant_id field,

            or the deprecated sub_tenant_ids selector. It is a non-breaking
            signal (the

            status code is unchanged); omitempty keeps it absent for
            fully-migrated

            requests. A list so independent deprecations coexist without
            clobbering.
          items:
            $ref: '#/components/schemas/handler.deprecationNotice'
          type: array
          uniqueItems: false
        latency_ms:
          description: Server-side processing time in milliseconds.
          example: 12.3
          type: number
        request_id:
          description: Unique identifier for this request, useful for support and tracing.
          example: 9d13aef4-02f4-4e73-8c62-4c2601d04f9d
          type: string
        source_type:
          description: Type of the parent source (e.g. `file`, `slack`, `notion`).
          example: file
          type: string
        sub_tenant_id:
          deprecated: true
          example: sub_tenant_4567
          type: string
          x-deprecated: 'true'
        tenant_id:
          deprecated: true
          example: tenant_1234
          type: string
          x-deprecated: 'true'
      type: object
    handler.ErrorDetail:
      properties:
        deprecated:
          description: Whether this response concerns a deprecated field or route.
          example: true
          type: boolean
        deprecated_field:
          description: The deprecated field name.
          example: tenant_id
          type: string
        error_code:
          description: Machine-readable error classification code.
          example: VALIDATION_ERROR
          type: string
        message:
          description: Human-readable description of the error.
          example: Request validation failed
          type: string
        preferred_field:
          description: The canonical replacement for the deprecated field.
          example: database
          type: string
        success:
          deprecated: true
          description: >-
            Deprecated for API clients: always false on this path, so it carries
            no

            information. To detect a failure read the HTTP status code; for what

            went wrong read the envelope's error.code and error.message, and

            meta.request_id when reporting it. The whole `detail` object is

            deprecated legacy — tagging the field individually so SDK users see
            it

            on the property, not just the container (PRO-1208).
          example: true
          type: boolean
          x-deprecated: 'true'
      type: object
    handler.ErrorMeta:
      properties:
        api_version:
          type: string
        latency_ms:
          example: 12.3
          type: number
        request_id:
          description: Unique identifier for this request, useful for support and tracing.
          example: 9d13aef4-02f4-4e73-8c62-4c2601d04f9d
          type: string
      type: object
    ingestion.V2IngestResultItem:
      properties:
        error:
          description: >-
            Error is the failure message for this item, null on success. Both
            modes.
          example: ''
          type: string
        error_code:
          description: >-
            ErrorCode is the machine-readable failure classification, null on
            success.

            Both modes; always null on the memory path, which produces no
            per-item code.
          type: string
        filename:
          description: Filename is the original filename as submitted. type=knowledge only.
          example: policy.pdf
          type: string
        id:
          description: ID is the source identifier assigned to this item. Both modes.
          example: HydraDoc1234
          type: string
        infer:
          description: >-
            Infer reports whether the memory was queued for inference.
            type=memory only.
          example: true
          type: boolean
        relations_created:
          description: >-
            RelationsCreated is the number of graph relations extracted from
            this file.

            type=knowledge only, and only for items that carried a `relations`
            payload.
          example: 5
          type: integer
        relations_error:
          description: |-
            RelationsError is the relation-extraction failure message, if any.
            type=knowledge only.
          type: string
        status:
          $ref: '#/components/schemas/ingestion.SourceStatus'
          description: Current lifecycle or processing state.
        title:
          description: Title is the memory's title. type=memory only.
          example: Project Phoenix Overview
          type: string
      type: object
    handler.deprecationNotice:
      properties:
        deprecated:
          description: Whether this response concerns a deprecated field or route.
          example: true
          type: boolean
        deprecated_field:
          description: The deprecated field name.
          example: tenant_id
          type: string
        deprecated_since:
          description: API version when the field was deprecated.
          example: 2.0.1
          type: string
        message:
          description: Migration guidance message.
          example: tenant_id is deprecated; use database instead.
          type: string
        preferred_field:
          description: The canonical replacement for the deprecated field.
          example: database
          type: string
      type: object
    ingestion.SourceStatus:
      description: |-
        Status is the item's initial lifecycle state. Both modes share this
        vocabulary — memory mode reuses the same values.
      enum:
        - queued
        - processing
        - completed
        - failed
      type: string
      x-enum-varnames:
        - SourceStatusQueued
        - SourceStatusProcessing
        - SourceStatusCompleted
        - SourceStatusFailed
  securitySchemes:
    BearerAuth:
      bearerFormat: API key
      description: 'API key sent as a Bearer token: "Bearer prefix.secret"'
      scheme: bearer
      type: http

````