> ## Documentation Index
> Fetch the complete documentation index at: https://cortex-e852fafe-t3code-rewrite-docs-declutter.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# App Sources

> How to ingest Slack, Gmail, Jira, Notion, CRM, and other app data using the field-based app-source model.

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

An **app source** is a record from an application, such as a Slack message, support ticket, wiki page, or email. Use this format when your application already has the text and fields, rather than a file HydraDB needs to read. Each record is stored as knowledge.

Send them to [`POST /context/ingest`](/api-reference/v2/endpoint/ingest-context) in the `app_knowledge` multipart field. Each item carries `kind`, `provider`, `external_id`, `fields`, metadata, attachments, comments, and typed relations.

<Info>
  App sources land in the [Knowledge](/essentials/v2/knowledge) store. Query them with [`POST /query`](/api-reference/v2/endpoint/query), `type: "knowledge"` or `type: "all"`, and **`query_apps: true`** (the default). Use `mode: "thinking"` when you want app relation expansion and connected context.
</Info>

## 1. What it is

| | App source | File upload |
| - | - | - |
| **Content** | Pre-extracted app fields: Slack message body, Jira description, Notion page body, email body, etc. | Binary or text file (PDF, DOCX, MD, CSV, TXT) |
| **Sent in** | `app_knowledge` as a stringified JSON object or array | `documents` plus optional `document_metadata` |
| **HydraDB does** | Normalizes app identity, extracts searchable text from `fields`, indexes attachments/comments, and builds app graph links | Parses the file, chunks extracted text, and indexes it |
| **Best for** | Connectors and live app syncs | Documents in their original file format |

Both paths eventually feed the same Knowledge retrieval stack, but app sources give HydraDB extra structure for app-aware recall: provider IDs, object kinds, actors, threads, parents, and explicit relations.

***

## 2. How an app source is structured

Every app-source item becomes one HydraDB Knowledge source. The identity of the item comes from `id`, `kind`, `provider`, and `external_id`. The searchable app content lives in `fields`.

| Category | Fields | Why it matters |
| - | - | - |
| **Ingestion envelope** | `id`, `database` (formerly `tenant_id`), `collection` (formerly `sub_tenant_id`) | Routes the item and gives it a stable upsert/delete/status ID. |
| **Display / source metadata** | `title`, `type`, `url`, `timestamp` | Improves citations, display, chronology, and source rendering. |
| **App identity** | `kind`, `provider`, `external_id` | Enables exact ID lookup, provider scoping, and relation resolution. |
| **Searchable structure** | `fields` | Holds the app-native body and typed fields: message body, ticket description, email actors, status, parent IDs, etc. |
| **Filtering / bookkeeping** | `metadata`, `additional_metadata` | Deterministic scoping and free-form display metadata. |
| **Connected context** | `relations`, `attachments`, `comments` | Adds explicit links and extra searchable text connected to the parent app item. |

Do **not** put the primary app text in a top-level `content` object for app-native ingestion. Top-level `content` is the older generic knowledge payload; app sources should put text in the correct typed field:

| `kind` | Main searchable text field |
| - | - |
| `email` | `fields.subject`, `fields.body` |
| `message` | `fields.body` |
| `ticket` | `fields.title`, `fields.description` |
| `knowledge_base` | `fields.title`, `fields.body` |
| `comment` | `fields.body` |
| `meeting` | `fields.title`, `fields.body` |
| `custom` | `fields.data` |

`fields.kind` must match the top-level `kind`; a mismatch is rejected.

***

## 3. Ingest an app source

Send `app_knowledge` as a **JSON-stringified** form value in a `multipart/form-data` request. Scope fields inside an item (`database`, `collection`) are optional; if you include them, they must match the form-level values.

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

  result = client.context.ingest(
      type="knowledge",
      database="acme_corp",
      collection="default",
      app_knowledge=json.dumps([
          {
              "id": "slack_C01_1716213600_000100",
              "database": "acme_corp",
              "collection": "default",
              "title": "Auth rollback - thread root",
              "kind": "message",
              "provider": "slack",
              "external_id": "1716213600.000100",
              "url": "https://acme.slack.com/archives/C01/p1716213600000100",
              "timestamp": "2026-05-20T10:00:00Z",
              "fields": {
                  "kind": "message",
                  "body": "The auth rollback notes are attached here.",
                  "author": "alice",
                  "thread_id": "1716213600.000100",
                  "created_at": "2026-05-20T10:00:00Z",
                  "url": "https://acme.slack.com/archives/C01/p1716213600000100",
              },
              "metadata": {"channel": "engineering", "workspace": "acme"},
              "additional_metadata": {"slack_ts": "1716213600.000100"},
              "attachments": [
                  {
                      "id": "F012AUTH",
                      "title": "auth-rollback-notes.md",
                      "url": "https://files.slack.com/files-pri/T01-F012AUTH/auth-rollback-notes.md",
                      "content_type": "text/markdown",
                      "content": {
                          "markdown": "# Auth rollback\nRollback starts by disabling the new token refresh path."
                      },
                  }
              ],
          }
      ]),
  )
  ```

  ```typescript TypeScript SDK theme={"dark"}
  const result = await client.context.ingest({
    type: "knowledge",
    database: "acme_corp",
    collection: "default",
    appKnowledge: JSON.stringify([
      {
        id: "slack_C01_1716213600_000100",
        database: "acme_corp",
        collection: "default",
        title: "Auth rollback - thread root",
        kind: "message",
        provider: "slack",
        external_id: "1716213600.000100",
        url: "https://acme.slack.com/archives/C01/p1716213600000100",
        timestamp: "2026-05-20T10:00:00Z",
        fields: {
          kind: "message",
          body: "The auth rollback notes are attached here.",
          author: "alice",
          thread_id: "1716213600.000100",
          created_at: "2026-05-20T10:00:00Z",
          url: "https://acme.slack.com/archives/C01/p1716213600000100",
        },
        metadata: { channel: "engineering", workspace: "acme" },
        additional_metadata: { slack_ts: "1716213600.000100" },
        attachments: [
          {
            id: "F012AUTH",
            title: "auth-rollback-notes.md",
            url: "https://files.slack.com/files-pri/T01-F012AUTH/auth-rollback-notes.md",
            content_type: "text/markdown",
            content: {
              markdown: "# Auth rollback\nRollback starts by disabling the new token refresh path.",
            },
          },
        ],
      },
    ]),
  });
  ```

  ```bash cURL theme={"dark"}
  curl -X POST 'https://api.hydradb.com/context/ingest' \
    -H "Authorization: Bearer $HYDRA_DB_API_KEY" \
    -H "API-Version: 2" \
    -F "type=knowledge" \
    -F "database=acme_corp" \
    -F "collection=default" \
    -F 'app_knowledge=[
      {
        "id": "slack_C01_1716213600_000100",
        "database": "acme_corp",
        "collection": "default",
        "title": "Auth rollback - thread root",
        "kind": "message",
        "provider": "slack",
        "external_id": "1716213600.000100",
        "url": "https://acme.slack.com/archives/C01/p1716213600000100",
        "timestamp": "2026-05-20T10:00:00Z",
        "fields": {
          "kind": "message",
          "body": "The auth rollback notes are attached here.",
          "author": "alice",
          "thread_id": "1716213600.000100",
          "created_at": "2026-05-20T10:00:00Z",
          "url": "https://acme.slack.com/archives/C01/p1716213600000100"
        },
        "metadata": { "channel": "engineering", "workspace": "acme" },
        "additional_metadata": { "slack_ts": "1716213600.000100" },
        "attachments": [
          {
            "id": "F012AUTH",
            "title": "auth-rollback-notes.md",
            "url": "https://files.slack.com/files-pri/T01-F012AUTH/auth-rollback-notes.md",
            "content_type": "text/markdown",
            "content": { "markdown": "# Auth rollback\nRollback starts by disabling the new token refresh path." }
          }
        ]
      }
    ]'
  ```
</CodeGroup>

**Response:**

```json theme={"dark"}
{
  "success": true,
  "data": {
    "success": true,
    "message": "Knowledge uploaded successfully",
    "results": [
      { "id": "slack_C01_1716213600_000100", "filename": null, "status": "queued", "error": null }
    ],
    "success_count": 1,
    "failed_count": 0
  },
  "error": null,
  "meta": { "request_id": "...", "latency_ms": 8.4 }
}
```

The call returns `202 Accepted`; the source is not queryable until [`GET /context/status`](/api-reference/v2/endpoint/source-status) reports `indexing_status: "graph_creation"` or `"completed"`.

***

## 4. Incremental ingestion

Use the same endpoint for initial backfill and live updates. The important modeling choice is whether the provider event updates an existing source or creates a new source.

| Provider event | What to send | Why |
| - | - | - |
| Ticket title/description/status changed | Re-ingest the same `kind: "ticket"` item with the same `id` and `external_id`, `upsert: true` | The ticket snapshot changed. |
| New Linear/Jira/Zendesk ticket comment | Ingest a **new** `kind: "comment"` source with its own `id` and `external_id`; put the parent ticket provider ID in `fields.parent_id` | The comment is a distinct provider object. |
| Edited ticket comment | Re-ingest the comment source with the same `id` and `external_id` | Same comment changed. |
| Deleted ticket comment | Call [`DELETE /context`](/api-reference/v2/endpoint/delete-source) with the comment source `id` | The comment should no longer surface. |
| New Slack/Teams message | Ingest a **new** `kind: "message"` source with `fields.thread_id` and, when available, `fields.parent_id` | Messages are standalone events linked by provider IDs. |
| New email reply | Ingest a **new** `kind: "email"` source with `fields.thread_id` and `fields.reply_to_id` | Email replies have their own provider IDs, actors, body, and timestamp. |
| New Notion/Confluence page | Ingest a **new** `kind: "knowledge_base"` source | A page is a standalone document; the space belongs in metadata. |
| Existing Notion/Confluence page edited | Re-ingest the same page source with the same `id` and `external_id` | The page document changed. |

<Warning>
  `upsert: true` replaces the existing source with the same `id`. Do not upsert a parent ticket/page with only a new `comments[]` item unless you intend to replace the whole snapshot. For live webhook events, model each new comment as its own `kind: "comment"` source.
</Warning>

***

## 5. Per-app patterns

### Slack message with thread reply and attachment

A Slack thread is multiple `message` sources connected by `thread_id`, `parent_id`, and optional explicit relations. The thread root is the item from [section 3](#3-ingest-an-app-source), attachment included.

For a reply, ingest a second source. `fields.parent_id` points to the parent message's provider `external_id`; an explicit relation can point to either the parent HydraDB ID or the provider ID.

```json theme={"dark"}
{
  "id": "slack_C01_1716213700_000200",
  "database": "acme_corp",
  "collection": "default",
  "title": "Re: Auth rollback - reply",
  "kind": "message",
  "provider": "slack",
  "external_id": "1716213700.000200",
  "timestamp": "2026-05-20T10:01:40Z",
  "fields": {
    "kind": "message",
    "body": "Looking now - will pair with Bob in 10.",
    "author": "carol",
    "thread_id": "1716213600.000100",
    "parent_id": "1716213600.000100",
    "created_at": "2026-05-20T10:01:40Z"
  },
  "metadata": { "channel": "engineering", "workspace": "acme" },
  "relations": [
    {
      "predicate": "reply_to",
      "target": { "external_id": "1716213600.000100", "provider": "slack" }
    }
  ]
}
```

### Jira ticket with linked issues

The ticket body lives in `fields.description`. Workflow fields belong in `fields`; scope/filter values like project and environment belong in `metadata`.

```json theme={"dark"}
{
  "id": "jira_AUTH-123",
  "database": "acme_corp",
  "collection": "default",
  "title": "Login page returns 500",
  "type": "jira",
  "kind": "ticket",
  "provider": "jira",
  "external_id": "AUTH-123",
  "url": "https://jira.example.com/browse/AUTH-123",
  "timestamp": "2026-05-20T10:00:00Z",
  "fields": {
    "kind": "ticket",
    "title": "Login page returns 500",
    "description": "Invalid credentials return HTTP 500 instead of 401. Reproduces on staging and prod.",
    "status": "open",
    "priority": "high",
    "assignee": "alice",
    "reporter": "bob",
    "parent_id": "AUTH-100",
    "linked_issue_ids": ["AUTH-124", "AUTH-125"],
    "created_at": "2026-05-20T10:00:00Z",
    "url": "https://jira.example.com/browse/AUTH-123"
  },
  "metadata": { "project": "AUTH", "environment": "production" },
  "relations": [
    { "predicate": "child_of", "target": { "external_id": "AUTH-100", "provider": "jira" } },
    { "predicate": "linked_to", "target": { "external_id": "AUTH-124", "provider": "jira" } },
    { "predicate": "linked_to", "target": { "external_id": "AUTH-125", "provider": "jira" } }
  ]
}
```

### Linear ticket comment

Comments are first-class app sources. Use `fields.parent_id` for the parent ticket's provider ID. Use `relations[]` when you need a cross-provider link or want to declare the relation explicitly.

```json theme={"dark"}
{
  "id": "linear_comment_c123",
  "database": "acme_corp",
  "collection": "default",
  "title": "Comment on AUTH-123",
  "type": "linear_comment",
  "kind": "comment",
  "provider": "linear",
  "external_id": "c123",
  "url": "https://linear.app/acme/issue/AUTH-123#comment-c123",
  "timestamp": "2026-05-25T07:00:00Z",
  "fields": {
    "kind": "comment",
    "body": "I found the repro steps. Attaching HAR.",
    "author": "alice@acme.com",
    "parent_id": "AUTH-123",
    "thread_id": "AUTH-123",
    "created_at": "2026-05-25T07:00:00Z",
    "url": "https://linear.app/acme/issue/AUTH-123#comment-c123"
  },
  "metadata": { "project": "AUTH" },
  "relations": [
    {
      "predicate": "comment_on",
      "target": { "external_id": "AUTH-123", "provider": "linear" }
    }
  ]
}
```

### Notion / Confluence page

A page is one `knowledge_base` source. Space/workspace/database values belong in metadata, not in `external_id`.

```json theme={"dark"}
{
  "id": "notion_page_abc",
  "database": "acme_corp",
  "collection": "default",
  "title": "Incident response runbook",
  "kind": "knowledge_base",
  "provider": "notion",
  "external_id": "page_abc",
  "url": "https://notion.so/page_abc",
  "timestamp": "2026-05-25T07:00:00Z",
  "fields": {
    "kind": "knowledge_base",
    "title": "Incident response runbook",
    "body": "Steps to triage production incidents...",
    "parent_id": "space_eng_root",
    "created_by": "alice@acme.com",
    "updated_by": "alice@acme.com",
    "created_at": "2026-05-25T07:00:00Z",
    "updated_at": "2026-05-25T07:00:00Z",
    "url": "https://notion.so/page_abc"
  },
  "metadata": { "space": "Engineering", "doc_type": "runbook" }
}
```

### Gmail email with reply

Each email message is its own `email` source. Link replies with `fields.reply_to_id` and, when useful, a `reply_to` relation.

```json theme={"dark"}
{
  "id": "gmail_msg_19a2b3c",
  "database": "acme_corp",
  "collection": "default",
  "title": "Q4 launch plan",
  "kind": "email",
  "provider": "gmail",
  "external_id": "19a2b3c",
  "url": "https://mail.google.com/mail/u/0/#inbox/19a2b3c",
  "timestamp": "2026-05-20T09:00:00Z",
  "fields": {
    "kind": "email",
    "subject": "Q4 launch plan",
    "body": "Here is the updated plan...",
    "from": "alice@acme.com",
    "to": ["team@acme.com"],
    "cc": ["pm@acme.com"],
    "thread_id": "thread_19a2",
    "reply_to_id": "19a1a00",
    "created_at": "2026-05-20T09:00:00Z",
    "url": "https://mail.google.com/mail/u/0/#inbox/19a2b3c"
  },
  "metadata": { "mailbox": "team@acme.com" },
  "relations": [
    {
      "predicate": "reply_to",
      "target": { "external_id": "19a1a00", "provider": "gmail" }
    }
  ]
}
```

### Salesforce opportunity linked to a support ticket

CRM records usually use `kind: "custom"`. Use `fields.parent_id` for same-provider hierarchy, and `relations[]` for cross-app context.

```json theme={"dark"}
{
  "id": "salesforce_opp_789",
  "database": "acme_corp",
  "collection": "default",
  "title": "Acme enterprise renewal",
  "type": "salesforce",
  "kind": "custom",
  "provider": "salesforce",
  "external_id": "opp_789",
  "url": "https://acme.lightning.force.com/lightning/r/Opportunity/opp_789/view",
  "timestamp": "2026-05-21T08:45:00Z",
  "fields": {
    "kind": "custom",
    "parent_id": "account_456",
    "thread_id": "opp_789_activity",
    "data": {
      "record_type": "opportunity",
      "name": "Acme enterprise renewal",
      "stage": "negotiation",
      "amount": "50000",
      "owner": "morgan"
    }
  },
  "metadata": { "pipeline": "enterprise-renewals", "region": "north-america" },
  "relations": [
    {
      "predicate": "linked_to",
      "target": { "external_id": "AUTH-123", "provider": "linear" },
      "properties": { "reason": "Customer renewal depends on auth fix" }
    }
  ]
}
```

***

## 6. Relations

Use `relations[]` to declare explicit source-to-source connections when the relationship matters for search and cannot be safely inferred from text alone.

```json theme={"dark"}
{
  "relations": [
    {
      "predicate": "blocks",
      "target": {
        "external_id": "RELEASE-2026-05",
        "provider": "jira"
      },
      "properties": {
        "reason": "release blocker"
      }
    },
    {
      "predicate": "related_to",
      "target": {
        "id": "src_existing_source"
      }
    }
  ]
}
```

The target can use either shape:

| Target shape | Use when |
| - | - |
| `{ "id": "..." }` | You already know the HydraDB ID. |
| `{ "external_id": "...", "provider": "..." }` | You know the provider ID and want HydraDB to resolve it in the provider namespace. |

Useful predicates include:

| Predicate | Direction |
| - | - |
| `reply_to` | reply/message/email to parent message/email |
| `comment_on` | comment to parent ticket/page/source |
| `forwarded_from` | forwarded email to original email |
| `child_of` | child ticket/page/custom record to parent |
| `linked_to` | source to related peer source |
| `blocks` | blocker to blocked item |
| `caused_by` | effect to cause |
| `related_to` | generic association |

<Warning>
  If you use `target.external_id`, include `target.provider`. External IDs are only unique inside a provider namespace, and a target without `target.provider` is rejected.
</Warning>

<Note>
  For app sources, prefer the app-native `relations[]` shape above. `relations.ids` is the file/legacy forceful-relation shortcut and does not carry predicate or provider information.
</Note>

***

## 7. Attachments and comments

Attachments and embedded `comments[]` add searchable child content connected to the app source.

Use `attachments[].content` only when you already extracted the attachment text. HydraDB treats an attachment URL as metadata; it does not fetch arbitrary URLs from app-source payloads.

```json theme={"dark"}
{
  "attachments": [
    {
      "id": "att-har-1",
      "title": "staging-login.har",
      "url": "https://...",
      "content_type": "application/json",
      "content": { "text": "HAR trace shows repeated 302 redirects after token refresh." },
      "properties": { "source": "linear", "comment_id": "c123" }
    }
  ],
  "comments": [
    {
      "id": "c-1",
      "author": "carol",
      "body": "Confirmed on staging.",
      "created_at": "2026-05-20T11:00:00Z"
    }
  ]
}
```

Use embedded `comments[]` for full backfill snapshots. For live incremental webhooks, prefer first-class `kind: "comment"` sources so a single new/edited/deleted comment can be handled without replacing the parent ticket or page.

***

## 8. Metadata: scoping vs display

For v2 Knowledge ingestion, `metadata` is the canonical filterable metadata field and `additional_metadata` is the canonical free-form metadata field.

| Use `metadata` when | Use `additional_metadata` when |
| - | - |
| You want deterministic query-time filtering via `metadata_filters` | The field is for display, citation, debugging, or per-source bookkeeping |
| The key is declared in `database_metadata_schema` | The key is unique or not worth adding to the database schema |
| Examples: `department`, `channel`, `project`, `space`, `pipeline`, `environment`, `region` | Examples: `slack_ts`, `ticket_key`, `comment_id`, `raw_provider_payload_id`, `golden_run` |

Legacy aliases are accepted for compatibility in some request shapes:

| Canonical v2 field | Legacy alias |
| - | - |
| `metadata` | `tenant_metadata` |
| `additional_metadata` | `document_metadata` |

Filterable database metadata keys must be declared in the database schema. Query-time filters use top-level keys for `metadata` and nested `additional_metadata` for free-form fields:

```json theme={"dark"}
{
  "query": "auth rollback discussion",
  "metadata_filters": {
    "channel": "engineering",
    "additional_metadata": { "slack_ts": "1716213600.000100" }
  }
}
```

See [Scoping using metadata](/essentials/v2/metadata) for the full schema and filter rules.

***

## 9. How app sources affect query

When `query_apps: true` (the default), HydraDB runs an app-aware retrieval lane alongside normal semantic and lexical retrieval. It can use app-specific signals that are not available from raw text alone.

| Query signal | Uses |
| - | - |
| `kind`, `provider` | Routes queries such as "Slack messages", "Jira tickets", or "ticket comments". |
| `external_id` | Exact ID lookup like `AUTH-123`, `1716213600.000100`, or `page_abc`. |
| Workflow fields | Uses `status`, `priority`, `assignee`, `reporter`, etc. when the query implies them. |
| Actors | Answers queries like "emails from Alice", "tickets assigned to Bob", or "comments by Carol". |
| Thread and parent fields | Expands around replies, parents, children, epics, subtasks, accounts, deals, and pages. |
| `relations[]` | Traverses explicit links like `linked_to`, `blocks`, `comment_on`, `reply_to`, and `child_of`. |
| Attachments / comments | Searches extracted attachment text and embedded comment bodies. |

### Parent and child query

Parent/child query is based on provider-scoped external IDs. When a child source has `fields.parent_id`, HydraDB can match that value to another source's `external_id` under the same `provider`.

At query time, `query_apps: true` lets the app-aware lane find an initial source by text, exact ID, actor, provider, or kind. In `thinking` mode, hierarchy language such as "parent", "children", "epic", "subtask", "account", or "deal" can trigger expansion around the matched source.

```json theme={"dark"}
{
  "database": "acme_corp",
  "query": "show me tickets linked to AUTH-123 and comments from Alice",
  "mode": "thinking",
  "query_apps": true,
  "max_results": 10
}
```

***

## 10. Field reference

### Top-level app source fields

| Field | Why send it | Query / ingestion effect |
| - | - | - |
| <Field name="id" type="string" required /> | Stable HydraDB ID for this item. | Upsert key, delete/status/list ID, and direct relation target. |
| <Field name="database" type="string" recommended /> | Database that owns the item. | Must match the form-level `database` when present. |
| <Field name="collection" type="string" recommended /> | Logical partition. Use `"default"` if you do not partition. | Must match the form-level `collection` when present. |
| <Field name="title" type="string" recommended /> | Human-readable subject, page name, ticket title, or message label. | Improves display, citations, and semantic matching. |
| <Field name="kind" type="email, message, ticket, knowledge_base, comment, meeting, custom" recommended /> | App object kind. | Selects the app parser and query behavior. |
| <Field name="provider" type="string" recommended /> | App namespace such as `slack`, `gmail`, `jira`, `notion`, `salesforce`, or `linear`. | Prevents ID collisions and scopes relation resolution. |
| <Field name="external_id" type="string" recommended /> | Stable provider ID for this exact item. | Exact lookup and relation/hierarchy resolution. |
| <Field name="url" type="string" /> | Canonical link back to the provider item. | Improves citations and source navigation. |
| <Field name="timestamp" type="ISO-8601 datetime" /> | Creation, sent, or last-updated time. | Helps chronology, display, and recency-aware ranking. |
| <Field name="fields" type="object" required /> | Structured kind-specific content. | Primary app-native searchable payload. |
| <Field name="metadata" type="object" /> | Schema-aligned filterable fields. | Queryable with top-level `metadata_filters` keys when declared in database schema. |
| <Field name="additional_metadata" type="object" /> | Free-form display/bookkeeping metadata. | Queryable under `metadata_filters.additional_metadata`. |
| <Field name="relations" type="Relation[]" /> | Explicit app-source links. | Enables connected-context retrieval and app graph expansion. |
| <Field name="acl" type="string[]" /> | Who may retrieve this item. Emails, `group:<provider>:<id>`, `domain:<domain>`, or `__public__`. | Omit to leave it unrestricted. Enforcement is opt-in on the query side: a query that carries `acl` sees this item only with a matching principal, and a query without `acl` is not filtered at all. See [Access Control](/essentials/v2/access-control). |
| <Field name="attachments" type="Attachment[]" /> | Pre-extracted files attached to the item. | Attachment text is indexed and connected to the source. |
| <Field name="comments" type="Comment[]" /> | Embedded snapshot/backfill comments. | Comment text and authors become searchable. |

### `email` fields

| Field | Why send it | Search / ingestion effect |
| - | - | - |
| <Field name="kind" type="email" required /> | Discriminator inside `fields`. | Should match top-level `kind`. |
| <Field name="subject" type="string" recommended /> | Email subject. | Improves title/snippet matching. |
| <Field name="body" type="string" recommended /> | Main email content. | Primary searchable text. |
| <Field name="from" type="string" recommended /> | Sender. | Actor queries like "emails from Alice". |
| <Field name="to" type="string[]" /> | Recipients. | Actor and recipient matching. |
| <Field name="cc" type="string[]" /> | CC recipients. | Additional actor context. |
| <Field name="thread_id" type="string" recommended /> | Email thread ID. | Same-thread expansion. |
| <Field name="reply_to_id" type="string" /> | Parent email provider ID. | Reply traversal. |
| <Field name="forwarded_from_id" type="string" /> | Original forwarded email provider ID. | Forward traversal. |
| <Field name="created_at" type="ISO-8601 datetime" /> | Sent/created time. | Chronology and recency. |
| <Field name="url" type="string" /> | Link back to the email. | Citations/navigation. |

### `message` fields

| Field | Why send it | Search / ingestion effect |
| - | - | - |
| <Field name="kind" type="message" required /> | Discriminator inside `fields`. | Should match top-level `kind`. |
| <Field name="body" type="string" recommended /> | Message text. | Primary searchable text. |
| <Field name="author" type="string" recommended /> | Sender. | Actor queries like "messages from Alice". |
| <Field name="thread_id" type="string" recommended /> | Thread/root conversation ID. | Same-thread expansion. |
| <Field name="parent_id" type="string" /> | Parent/replied-to message provider ID. | Reply/parent traversal. |
| <Field name="mentions" type="string[]" /> | Mentioned people or handles. | Mention queries. |
| <Field name="created_at" type="ISO-8601 datetime" /> | Message timestamp. | Chronology and recency. |
| <Field name="url" type="string" /> | Link back to the message. | Citations/navigation. |

### `ticket` fields

| Field | Why send it | Search / ingestion effect |
| - | - | - |
| <Field name="kind" type="ticket" required /> | Discriminator inside `fields`. | Should match top-level `kind`. |
| <Field name="title" type="string" recommended /> | Ticket title. | Display and matching. |
| <Field name="description" type="string" recommended /> | Ticket body. | Primary searchable text. |
| <Field name="status" type="string" recommended /> | Workflow state. | Queries like "open tickets". |
| <Field name="priority" type="string" /> | Priority. | Priority-aware queries. |
| <Field name="assignee" type="string" /> | Owner. | Actor queries. |
| <Field name="reporter" type="string" /> | Reporter/creator. | Actor queries. |
| <Field name="parent_id" type="string" /> | Parent epic/ticket external ID. | Parent/child traversal. |
| <Field name="linked_issue_ids" type="string[]" /> | Related issue external IDs. | Linked-ticket context. |
| <Field name="created_at" type="ISO-8601 datetime" /> | Creation time. | Chronology. |
| <Field name="updated_at" type="ISO-8601 datetime" /> | Last update time. | Freshness. |
| <Field name="url" type="string" /> | Link back to the ticket. | Citations/navigation. |

### `knowledge_base` fields

| Field | Why send it | Search / ingestion effect |
| - | - | - |
| <Field name="kind" type="knowledge_base" required /> | Discriminator inside `fields`. | Should match top-level `kind`. |
| <Field name="title" type="string" recommended /> | Page title. | Display and matching. |
| <Field name="body" type="string" recommended /> | Page body. | Primary searchable text. |
| <Field name="parent_id" type="string" /> | Parent page external ID. | Page hierarchy traversal. |
| <Field name="created_by" type="string" /> | Creator. | Actor queries. |
| <Field name="updated_by" type="string" /> | Last editor. | Actor queries. |
| <Field name="created_at" type="ISO-8601 datetime" /> | Creation time. | Chronology. |
| <Field name="updated_at" type="ISO-8601 datetime" /> | Last update time. | Freshness. |
| <Field name="url" type="string" /> | Link back to the page. | Citations/navigation. |

### `comment` fields

| Field | Why send it | Search / ingestion effect |
| - | - | - |
| <Field name="kind" type="comment" required /> | Discriminator inside `fields`. | Should match top-level `kind`. |
| <Field name="body" type="string" recommended /> | Comment body. | Primary searchable text. |
| <Field name="author" type="string" recommended /> | Comment author. | Actor queries. |
| <Field name="parent_id" type="string" recommended /> | Parent source external ID in the same provider namespace. | `comment_on` relation derivation. |
| <Field name="thread_id" type="string" /> | Shared discussion/activity thread ID. | Same-thread expansion. |
| <Field name="created_at" type="ISO-8601 datetime" /> | Creation time. | Chronology. |
| <Field name="updated_at" type="ISO-8601 datetime" /> | Last update time. | Freshness. |
| <Field name="url" type="string" /> | Link back to the comment. | Citations/navigation. |
| <Field name="properties" type="object" /> | Provider-specific comment details. | Preserved in payload. |

### `custom` fields

| Field | Why send it | Search / ingestion effect |
| - | - | - |
| <Field name="kind" type="custom" required /> | Discriminator inside `fields`. | Should match top-level `kind`. |
| <Field name="parent_id" type="string" /> | Parent external ID for same-provider hierarchy. | Parent/child traversal. |
| <Field name="thread_id" type="string" /> | Activity stream or conversation ID. | Same-thread expansion. |
| <Field name="data" type="object" recommended /> | App-specific structured payload. | Searchable business/system details. |

### Relation fields

| Field | Why send it | Search / ingestion effect |
| - | - | - |
| <Field name="predicate" type="string" required /> | Relationship name such as `linked_to`, `blocks`, `child_of`, `reply_to`, or `comment_on`. | Lets query expansion understand relation type and direction. |
| <Field name="target.id" type="string" /> | Direct HydraDB ID target. | Connects to a known source. |
| <Field name="target.external_id" type="string" /> | Provider ID target. | Resolves by app namespace. |
| <Field name="target.provider" type="string" /> | Provider namespace for `external_id`. | Prevents false links across apps. |
| <Field name="properties" type="object" /> | Optional relation metadata. | Preserves why the link exists. |

### Attachment fields

| Field | Why send it | Search / ingestion effect |
| - | - | - |
| <Field name="id" type="string" /> | Stable attachment ID. | Deduplication and display. |
| <Field name="title" type="string" /> | File name/title. | Matching and display. |
| <Field name="url" type="string" /> | Link back to attachment. | Citations/navigation. |
| <Field name="content_type" type="string" /> | MIME type. | Classification. |
| <Field name="size_bytes" type="integer" /> | Attachment size. | Display/debugging. |
| <Field name="content.text" type="string" /> | Pre-extracted plain text. | Indexed as attachment text. |
| <Field name="content.markdown" type="string" /> | Pre-extracted Markdown. | Indexed as attachment text. |
| <Field name="properties" type="object" /> | Provider-specific attachment metadata. | Preserved in payload. |

***

## 11. Common mistakes

| Mistake | Why it breaks | Fix |
| - | - | - |
| Using container IDs as `external_id` | Relations and exact lookup point to the channel/project/space instead of the item | Use the individual item ID as `external_id`; put channel/project/space in `metadata`. |
| Re-ingesting a whole Slack thread for every reply | Loses message-level authorship/timestamps and wastes indexing | Ingest each message as its own `kind: "message"` source with `thread_id` / `parent_id`. |
| Putting filterable fields only in `additional_metadata` | Filters are slower and require nested filter syntax | Put hot filters in `metadata` and declare them in the database schema. |
| Sending `query_apps: false` | You only get normal text/vector retrieval, not app-aware ID/relation expansion | Leave `query_apps` at its default (`true`); use `mode: "thinking"` for relation-heavy queries. |

***

## Related

* [Knowledge](/essentials/v2/knowledge): the broader ingestion path
* [Memories](/essentials/v2/memories): the per-user counterpart
* [Query](/essentials/v2/query): retrieving app-source context
* [Scoping using metadata](/essentials/v2/metadata): designing filterable metadata
* [Context Graphs](/essentials/v2/context-graphs): graph enrichment and traversal
* [Access Control](/essentials/v2/access-control): restricting who may retrieve an item
* [Ingest Content (API Reference)](/api-reference/v2/endpoint/ingest-context)
* [Ingestion Status (API Reference)](/api-reference/v2/endpoint/source-status)


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