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

# 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 (PDF, DOCX, CSV, MD, TXT) that HydraDB should parse.
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={"dark"}
  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={"dark"}
  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={"dark"}
  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={"dark"}
  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={"dark"}
  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={"dark"}
  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. (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>

## Common use-cases and their configurations

### Document metadata

Per-document metadata (`id`, `metadata`, `additional_metadata`, `title`, etc.) can be passed alongside each uploaded document to control indexing, filtering, and display. See the field reference below.

<Accordion title="Knowledge documents - upload PDFs, DOCX, CSV, markdown, or text">
  <CodeGroup>
    ```python Python SDK theme={"dark"}
    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={"dark"}
    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={"dark"}
    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="title" type="string" />               | Override the source title shown in search results. (default=filename)                                                                                                                                   |
  | <Field name="type" type="string" />                | Override the source type shown in search results. (default=inferred)                                                                                                                                    |
  | <Field name="url" type="string" />                 | Override the canonical URL.                                                                                                                                                                             |
  | <Field name="timestamp" type="string" />           | ISO-8601 timestamp override. (default=upload time)                                                                                                                                                      |
  | <Field name="relations" type="object" />           | Declare forceful relations to other sources. Shape: `{ "ids": ["...", "..."] }`. Surfaced via `additional_context` in `mode: "thinking"` search.                                                        |
</Accordion>

<Accordion title="App sources - index text from your workspace or personal apps">
  <CodeGroup>
    ```python Python SDK theme={"dark"}
    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={"dark"}
    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={"dark"}
    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={"dark"}
    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={"dark"}
    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={"dark"}
    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={"dark"}
    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={"dark"}
    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={"dark"}
    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={"dark"}
    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={"dark"}
    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={"dark"}
    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 knowledge documents or memories for a tenant.
      requestBody:
        content:
          multipart/form-data:
            schema:
              properties:
                app_knowledge:
                  title: app_knowledge
                  type: string
                collection:
                  title: collection
                  type: string
                database:
                  title: database
                  type: string
                document_metadata:
                  title: document_metadata
                  type: string
                documents:
                  format: binary
                  title: documents
                  type: string
                graph_payload:
                  title: graph_payload
                  type: string
                memories:
                  title: memories
                  type: string
                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:
                  default: knowledge
                  enum:
                    - knowledge
                    - memory
                  title: type
                  type: string
                upsert:
                  default: 'true'
                  title: upsert
                  type: string
              required:
                - database
              type: object
        description: >-
          Content type: 'knowledge' or 'memory' | 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) | Knowledge
          files to ingest (repeatable; type=knowledge) | Per-document metadata
          as a JSON array (type=knowledge) | App-knowledge items as a JSON array
          (type=knowledge) | Memory items as a JSON array (type=memory) |
          Optional bring-your-own-graph payload as JSON
        required: true
      responses:
        '202':
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/handler.Envelope-ingestion_V2SourceUploadResponse
          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
        '422':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/handler.ErrorResponse'
          description: Unprocessable Entity
      security:
        - BearerAuth: []
components:
  schemas:
    handler.Envelope-ingestion_V2SourceUploadResponse:
      properties:
        data:
          $ref: '#/components/schemas/ingestion.V2SourceUploadResponse'
          example:
            failed_count: 0
            message: Success
            results:
              - error: ''
                filename: policy.pdf
                id: HydraDoc1234
                relations_created: 5
                status: queued
            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:
        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
            success: true
      type: object
    ingestion.V2SourceUploadResponse:
      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
              relations_created: 5
              status: queued
          items:
            $ref: '#/components/schemas/ingestion.V2SourceUploadResultItem'
          type: array
          uniqueItems: false
        success:
          description: Whether the request succeeded.
          example: true
          type: boolean
        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:
        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:
          description: Always false for error responses.
          example: true
          type: boolean
      type: object
    ingestion.V2SourceUploadResultItem:
      properties:
        error:
          description: Error message for this file, empty string on success.
          example: ''
          type: string
        error_code:
          description: Machine-readable error classification code.
          type: string
        filename:
          description: Original filename as submitted.
          example: policy.pdf
          type: string
        id:
          description: Unique identifier for this resource.
          example: HydraDoc1234
          type: string
        relations_created:
          description: Number of graph relations extracted from this file.
          example: 5
          type: integer
        relations_error:
          description: Error message from relation extraction, if any.
          type: string
        status:
          $ref: '#/components/schemas/ingestion.SourceStatus'
          description: Current lifecycle or processing state.
      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:
      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

````