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

# Upload Knowledge

> Ingest documents (files) and/or app-generated content (app knowledge) into a tenant.

## When to use it

Use this endpoint to add knowledge that your agents will recall later:

* **Files** – PDFs, DOCX, Markdown, CSVs, and other documents you want HydraDB to parse and chunk
* **App knowledge** – structured content from connected apps (Slack messages, Notion pages, Gmail threads, tickets, CRM records, etc.) where you already have the text, IDs, metadata, and relations, sent as the `app_knowledge` multipart field (JSON string)

For user-specific context (preferences, conversations), use [`POST /memories/add_memory`](/api-reference/endpoint/add-memory) instead.

## Endpoint

* **Auth:** Bearer token
* **Content type:** `multipart/form-data` (always – even when sending only `app_knowledge`)
* **Idempotency:** Controlled by `upsert` (default `true` – overwrites existing items with the same ID)
* **Async:** Yes – returns **`200`** with a JSON body (`SourceUploadResponse` in OpenAPI) including one `source_id` per queued item. Use [`POST /ingestion/verify_processing`](/api-reference/endpoint/verify-processing) to check status.

<Note>
  The interactive **Try it** panel is generated from [`api-reference/openapi.json`](/api-reference/openapi.json) and uses the multipart field name **`app_knowledge`**, matching the OpenAPI spec.
</Note>

## Two ingestion paths

This endpoint accepts two content formats in one request. Send either or both:

| Path            | Form field                                                | Content type                         |
| --------------- | --------------------------------------------------------- | ------------------------------------ |
| **Files**       | `files` (binary) + optional `file_metadata` (JSON string) | Documents to be parsed               |
| **App sources** | `app_knowledge` (JSON string)                             | Pre-parsed app content with metadata |

## Example: Upload files

<CodeGroup>
  ```bash cURL theme={"dark"}
  curl -X POST 'https://api.hydradb.com/ingestion/upload_knowledge' \
    -H "Authorization: Bearer <your_api_key>" \
    -F "tenant_id=my_first_tenant" \
    -F "files=@/path/to/contract.pdf" \
    -F "files=@/path/to/policy.pdf" \
    -F 'file_metadata=[
      { "file_id": "contract_q4", "metadata": { "category": "legal" } },
      { "file_id": "policy_2025", "metadata": { "category": "legal" } }
    ]'
  ```

  ```typescript TypeScript SDK theme={"dark"}
  const response = await client.upload.knowledge({
    tenant_id: "my_first_tenant",
    files: [
      { path: "/path/to/contract.pdf", filename: "contract.pdf", contentType: "application/pdf" },
      { path: "/path/to/policy.pdf", filename: "policy.pdf", contentType: "application/pdf" },
    ],
    file_metadata: JSON.stringify([
      { file_id: "contract_q4", metadata: { category: "legal" } },
      { file_id: "policy_2025", metadata: { category: "legal" } },
    ]),
  });
  ```

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

  with open("/path/to/contract.pdf", "rb") as f1, \
       open("/path/to/policy.pdf", "rb") as f2:
      response = client.upload.knowledge(
          tenant_id="my_first_tenant",
          files=[
              ("contract.pdf", f1, "application/pdf"),
              ("policy.pdf", f2, "application/pdf"),
          ],
          file_metadata=json.dumps([
              {"file_id": "contract_q4", "metadata": {"category": "legal"}},
              {"file_id": "policy_2025", "metadata": {"category": "legal"}},
          ]),
      )
  ```
</CodeGroup>

## Example: Upload app knowledge

<CodeGroup>
  ```bash cURL theme={"dark"}
  curl -X POST 'https://api.hydradb.com/ingestion/upload_knowledge' \
    -H "Authorization: Bearer <your_api_key>" \
    -F "tenant_id=my_first_tenant" \
    -F 'app_knowledge=[
      {
        "tenant_id": "my_first_tenant",
        "sub_tenant_id": "default",
        "id": "slack_msg_12345",
        "title": "Q4 planning standup notes",
        "type": "slack",
        "url": "https://acme.slack.com/archives/C01/p1234567890",
        "timestamp": "2025-10-15T14:30:00Z",
        "kind": "message",
        "provider": "slack",
        "external_id": "1234567890.000100",
        "fields": {
          "kind": "message",
          "body": "Today we discussed the Q4 roadmap...",
          "author": "alex",
          "thread_id": "1234567890.000100",
          "url": "https://acme.slack.com/archives/C01/p1234567890"
        },
        "metadata": {
          "container_type": "channel",
          "container_id": "C01",
          "container_name": "planning",
          "category": "engineering"
        },
        "attachments": [
          {
            "id": "att-1",
            "title": "roadmap-notes.txt",
            "content_type": "text/plain",
            "content": { "text": "Attached notes mention owners for the Q4 roadmap." }
          }
        ]
      }
    ]'
  ```

  ```typescript TypeScript SDK theme={"dark"}
  const response = await client.upload.knowledge({
    tenant_id: "my_first_tenant",
    app_knowledge: JSON.stringify([
      {
        tenant_id: "my_first_tenant",
        sub_tenant_id: "default",
        id: "slack_msg_12345",
        title: "Q4 planning standup notes",
        type: "slack",
        url: "https://acme.slack.com/archives/C01/p1234567890",
        timestamp: "2025-10-15T14:30:00Z",
        kind: "message",
        provider: "slack",
        external_id: "1234567890.000100",
        fields: {
          kind: "message",
          body: "Today we discussed the Q4 roadmap...",
          author: "alex",
          thread_id: "1234567890.000100",
          url: "https://acme.slack.com/archives/C01/p1234567890",
        },
        metadata: {
          container_type: "channel",
          container_id: "C01",
          container_name: "planning",
          category: "engineering",
        },
        attachments: [
          {
            id: "att-1",
            title: "roadmap-notes.txt",
            content_type: "text/plain",
            content: { text: "Attached notes mention owners for the Q4 roadmap." },
          },
        ],
      },
    ]),
  });
  ```

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

  response = client.upload.knowledge(
      tenant_id="my_first_tenant",
      app_knowledge=json.dumps([
          {
              "tenant_id": "my_first_tenant",
              "sub_tenant_id": "default",
              "id": "slack_msg_12345",
              "title": "Q4 planning standup notes",
              "type": "slack",
              "url": "https://acme.slack.com/archives/C01/p1234567890",
              "timestamp": "2025-10-15T14:30:00Z",
              "kind": "message",
              "provider": "slack",
              "external_id": "1234567890.000100",
              "fields": {
                  "kind": "message",
                  "body": "Today we discussed the Q4 roadmap...",
                  "author": "alex",
                  "thread_id": "1234567890.000100",
                  "url": "https://acme.slack.com/archives/C01/p1234567890",
              },
              "metadata": {
                  "container_type": "channel",
                  "container_id": "C01",
                  "container_name": "planning",
                  "category": "engineering",
              },
              "attachments": [
                  {
                      "id": "att-1",
                      "title": "roadmap-notes.txt",
                      "content_type": "text/plain",
                      "content": {"text": "Attached notes mention owners for the Q4 roadmap."},
                  }
              ],
          }
      ]),
  )
  ```
</CodeGroup>

## Form fields

| Name            | Type        | Required | Default | Description                                                                                                                              |
| --------------- | ----------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `tenant_id`     | string      | Yes      | –       | The tenant to ingest into.                                                                                                               |
| `files`         | binary\[]   | One of   | `[]`    | One or more files to upload.                                                                                                             |
| `file_metadata` | JSON string | No       | –       | Array of metadata objects, one per file. Length must match `files`. See [File metadata](#file-metadata).                                 |
| `app_knowledge` | JSON string | One of   | –       | A single app source object or an array. Each item **must include `tenant_id` and `sub_tenant_id`**. See [App knowledge](#app-knowledge). |
| `app_sources`   | JSON string | -        | –       | **Deprecated alias for `app_knowledge`.** Use `app_knowledge` instead.                                                                   |
| `sub_tenant_id` | string      | No       | default | Sub-tenant scope.                                                                                                                        |
| `upsert`        | boolean     | No       | `true`  | If `true`, existing sources with the same ID are overwritten.                                                                            |

You must provide at least one of `files` or `app_knowledge`. You can send both in the same request.

## File metadata

Each entry in `file_metadata` corresponds to the file at the same index in `files`:

| Field                 | Type   | Description                                                                                                                                                                                                                                                                                                                                                         |
| --------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `file_id`             | string | Optional. Server-generated if omitted. Use this to reference the file later (e.g., for `verify_processing`).                                                                                                                                                                                                                                                        |
| `metadata`            | object | **Tenant-level** metadata. Pre-filterable in recall via top-level keys in `metadata_filters` (fast). Each key must be declared in `tenant_metadata_schema` with `enable_match: true`.                                                                                                                                                                               |
| `additional_metadata` | object | **Document-level** metadata. Filterable in recall by nesting under `"additional_metadata"` (canonical) or `"document_metadata"` (legacy alias) inside `metadata_filters`. Post-retrieval, so slower than tenant-level filters (engine over-fetches \~3x to compensate). See [Full recall → Metadata filters](/api-reference/endpoint/full-recall#metadata-filters). |
| `relations`           | object | Forcefully connect this file to other sources. See [Forceful relations](/essentials/knowledge#7-forceful-relations).                                                                                                                                                                                                                                                |

## App knowledge

Each item in the `app_knowledge` JSON array represents a single piece of pre-parsed content. **`tenant_id` and `sub_tenant_id` are required inside each item.**

For app-aware retrieval, include the typed app-source fields below. See [App Sources](/essentials/app-sources) for field-by-field guidance, relation examples, and search behavior.

| Field                 | Type   | Description                                                                                                                                                                                                                                                                                                                            |
| --------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tenant_id`           | string | **Required.** The tenant to ingest into.                                                                                                                                                                                                                                                                                               |
| `sub_tenant_id`       | string | **Required.** Sub-tenant scope. Use `"default"` if not partitioning by user.                                                                                                                                                                                                                                                           |
| `id`                  | string | **Required.** Unique identifier for the source.                                                                                                                                                                                                                                                                                        |
| `title`               | string | Short title or subject.                                                                                                                                                                                                                                                                                                                |
| `type`                | string | Source category (e.g., `slack`, `notion`, `gmail`, `webpage`).                                                                                                                                                                                                                                                                         |
| `description`         | string | Optional long-form description.                                                                                                                                                                                                                                                                                                        |
| `url`                 | string | Canonical URL or reference link.                                                                                                                                                                                                                                                                                                       |
| `timestamp`           | string | ISO-8601 timestamp (creation or last-updated).                                                                                                                                                                                                                                                                                         |
| `content`             | object | Legacy/generic content payload. For typed app sources, prefer `fields` for item text. See [Content formats](#content-formats).                                                                                                                                                                                                         |
| `metadata`            | object | Tenant-level or app-specific metadata. Use this for category, channel, project, space, workspace, or other provider context. Filterable at recall via top-level keys in `metadata_filters`.                                                                                                                                            |
| `additional_metadata` | object | Document-level metadata. Stored per-source. Filterable at recall by nesting under `"additional_metadata"` (canonical) or `"document_metadata"` (legacy alias) in `metadata_filters`. Post-retrieval (the engine over-fetches and filters). See [Full recall → Metadata filters](/api-reference/endpoint/full-recall#metadata-filters). |
| `relations`           | array  | Typed app-source relations such as `reply_to`, `child_of`, `linked_to`, or `blocks`.                                                                                                                                                                                                                                                   |
| `kind`                | string | App object kind: `email`, `message`, `ticket`, `knowledge_base`, or `custom`.                                                                                                                                                                                                                                                          |
| `provider`            | string | Provider namespace such as `slack`, `gmail`, `jira`, `notion`, or `salesforce`.                                                                                                                                                                                                                                                        |
| `external_id`         | string | Stable provider ID for this source. Used for exact ID search and relation resolution.                                                                                                                                                                                                                                                  |
| `fields`              | object | Kind-specific structured fields. Include `kind` inside this object.                                                                                                                                                                                                                                                                    |
| `attachments`         | array  | Attachments connected to this app source. Include `content.text` or `content.markdown` when you already have extracted attachment text.                                                                                                                                                                                                |
| `comments`            | array  | Comments connected to this app source.                                                                                                                                                                                                                                                                                                 |

<Info>
  If your data comes from an app, prefer the typed app-source fields over flattening everything into Markdown. Structured IDs and relations let `search_apps: true` use graph expansion and exact provider IDs during Recall.
</Info>

### Content formats

For typed app sources, primary searchable text belongs in `fields`, such as `fields.body` for messages/emails or `fields.description` for tickets. The top-level `content` object remains available for legacy or untyped app knowledge payloads.

The `content` object supports multiple content types. Use the field that matches your source - only one needs to be set:

| Field         | Use for                    |
| ------------- | -------------------------- |
| `text`        | Plain text content         |
| `markdown`    | Markdown-formatted content |
| `html_base64` | Base64-encoded HTML        |
| `csv_base64`  | Base64-encoded CSV         |

```json theme={"dark"}
{ "content": { "text": "Plain text content." } }
```

Attachment objects can also include a `content` object. HydraDB indexes `attachments[].content.text` and, when `text` is omitted, `attachments[].content.markdown`; attachment URLs and titles are metadata and are not fetched or parsed automatically.

## Response

```json theme={"dark"}
{
  "success": true,
  "message": "Upload initiated successfully",
  "results": [
    {
      "source_id": "ef3ea754019855e2b39e9ab5c2d26096",
      "filename": "contract.pdf",
      "status": "queued",
      "error": null
    },
    {
      "source_id": "9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d",
      "filename": "policy.pdf",
      "status": "queued",
      "error": null
    }
  ],
  "success_count": 2,
  "failed_count": 0
}
```

| Field                            | Description                                                                                 |
| -------------------------------- | ------------------------------------------------------------------------------------------- |
| `success`                        | `true` if at least one item was queued.                                                     |
| `message`                        | Human-readable status.                                                                      |
| `results[]`                      | Per-item upload result.                                                                     |
| `results[].source_id`            | The ID assigned to this source. Use this for `verify_processing` and downstream operations. |
| `results[].filename`             | Original filename (for files) or `null` (for app knowledge payloads).                       |
| `results[].status`               | Initial status. Always `queued` on a successful response.                                   |
| `success_count` / `failed_count` | Aggregate counts across the request.                                                        |

## Behavior notes

<Info>
  **Processing is asynchronous.** This endpoint returns once items are queued. Use [`POST /ingestion/verify_processing`](/api-reference/endpoint/verify-processing) with the returned `source_id`s to check when content is fully indexed.
</Info>

<Info>
  **Always use multipart form data.** Even if you're sending only `app_knowledge` (no files), the endpoint expects `multipart/form-data`. Sending JSON directly will return `422 VALIDATION_ERROR`.
</Info>

<Info>
  **`file_metadata` and `app_knowledge` are JSON strings.** Both must be serialized before being sent as form fields. Most HTTP libraries don't auto-stringify nested JSON in multipart bodies.
</Info>

## Related endpoints

* **Next:** [Verify processing](/api-reference/endpoint/verify-processing) – poll for ingestion status
* **Next:** [Full recall](/api-reference/endpoint/full-recall) – retrieve ingested content
* **Alternative:** [Add memory](/api-reference/endpoint/add-memory) – for user memories instead of knowledge
* **Related:** [Delete knowledge](/api-reference/endpoint/delete-knowledge) – remove sources

## Errors

Common codes: `400 INVALID_PARAMETERS`, `404 TENANT_NOT_FOUND`, `422 VALIDATION_ERROR`. See [Error Responses](/api-reference/error-responses) for the full list.

Read more: [Essentials → Memories](/essentials/memories) · [Essentials → Metadata](/essentials/metadata)


## OpenAPI

````yaml POST /ingestion/upload_knowledge
openapi: 3.1.0
info:
  title: HydraDB API
  description: REST APIs for the HydraDB retrieval engine
  version: 0.0.1
servers:
  - url: https://api.hydradb.com
    description: Production
    x-fern-server-name: hydradb-prod
security: []
paths:
  /ingestion/upload_knowledge:
    post:
      tags:
        - ingestion
      summary: Upload Knowledge
      operationId: upload_knowledge_ingestion_upload_knowledge_post
      requestBody:
        content:
          multipart/form-data:
            schema:
              $ref: >-
                #/components/schemas/Body_upload_knowledge_ingestion_upload_knowledge_post
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceUploadResponse'
        '400':
          description: Bad Request - Invalid input parameters
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/cortex__models__response__commons__ActualErrorResponse
        '401':
          description: Unauthorized - Authentication required
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/cortex__models__response__commons__ActualErrorResponse
        '403':
          description: Forbidden - Access denied
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/cortex__models__response__commons__ActualErrorResponse
        '404':
          description: Not Found - Resource does not exist
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/cortex__models__response__commons__ActualErrorResponse
        '422':
          description: Unprocessable Entity - Validation failed
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/cortex__models__response__commons__ActualErrorResponse
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/cortex__models__response__commons__ActualErrorResponse
        '503':
          description: Service Unavailable
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/cortex__models__response__commons__ActualErrorResponse
      security:
        - HTTPBearer: []
components:
  schemas:
    Body_upload_knowledge_ingestion_upload_knowledge_post:
      properties:
        tenant_id:
          type: string
          title: Tenant Id
          description: Unique identifier for the tenant/organization
          example: tenant_1234
        sub_tenant_id:
          type: string
          title: Sub Tenant Id
          description: >-
            Optional sub-tenant identifier used to organize data within a
            tenant. If omitted, the default sub-tenant created during tenant
            setup will be used.
          default: ''
          example: sub_tenant_4567
        upsert:
          type: boolean
          title: Upsert
          description: If true, update existing sources with the same id.
          default: true
          example: true
        files:
          items:
            type: string
            format: binary
          type: array
          title: Files
          description: >-
            Files to upload (documents). Omit or leave empty when only sending
            app_knowledge.
          default: []
        file_metadata:
          anyOf:
            - type: string
            - type: 'null'
          title: File Metadata
          description: >-
            JSON array of file metadata objects; length must match files when
            provided. Each object may include: file_id (optional), metadata,
            additional_metadata, and relations (forceful relations to other
            HydraDB source IDs).
        app_knowledge:
          anyOf:
            - type: string
            - type: 'null'
          title: App Knowledge
          description: >-
            JSON: single source object or array of app-generated sources to
            index. Omit when only uploading files.
      type: object
      required:
        - tenant_id
      title: Body_upload_knowledge_ingestion_upload_knowledge_post
    SourceUploadResponse:
      properties:
        success:
          type: boolean
          title: Success
          default: true
          example: true
        message:
          type: string
          title: Message
          default: Upload initiated successfully
        results:
          items:
            $ref: '#/components/schemas/SourceUploadResultItem'
          type: array
          title: Results
          description: List of upload results for each source.
          example: []
        success_count:
          type: integer
          title: Success Count
          description: Number of sources successfully queued.
          default: 0
          example: 1
        failed_count:
          type: integer
          title: Failed Count
          description: Number of sources that failed to upload.
          default: 0
          example: 1
      type: object
      title: SourceUploadResponse
    cortex__models__response__commons__ActualErrorResponse:
      properties:
        detail:
          $ref: >-
            #/components/schemas/cortex__models__response__commons__ErrorResponse
      type: object
      required:
        - detail
      title: ActualErrorResponse
    SourceUploadResultItem:
      properties:
        source_id:
          type: string
          title: Source Id
          description: Unique identifier for the uploaded source.
          example: <source_id>
        filename:
          anyOf:
            - type: string
            - type: 'null'
          title: Filename
          description: Original filename if present.
        status:
          $ref: '#/components/schemas/SourceStatus'
          description: Initial processing status.
          default: queued
        error:
          anyOf:
            - type: string
            - type: 'null'
          title: Error
          description: Error message if upload failed.
      type: object
      required:
        - source_id
      title: SourceUploadResultItem
    cortex__models__response__commons__ErrorResponse:
      properties:
        success:
          type: boolean
          title: Success
          default: false
          example: true
        message:
          type: string
          title: Message
          default: Error occurred
        error_code:
          anyOf:
            - type: string
            - type: 'null'
          title: Error Code
      type: object
      title: ErrorResponse
    SourceStatus:
      type: string
      enum:
        - queued
        - processing
        - completed
        - failed
      title: SourceStatus
  securitySchemes:
    HTTPBearer:
      type: http
      scheme: bearer

````