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

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

App sources are pre-parsed records from business applications: emails, chat messages, tickets, wiki pages, CRM objects, comments, and attachments. Use them when your connector already extracted the item's text and structured fields.

App sources are ingested through [`POST /context/ingest`](/api-reference/v2/endpoint/ingest-context) with the `app_knowledge` multipart field. The app-source payload is **not** a generic top-level `content` object. It uses the same app-native model as v1: `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`**. 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. The mental model

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`                        |
| `custom`         | `fields.data`                        |

***

## 3. Ingest an app source

Send `app_knowledge` as a **JSON-stringified** form value. Each item should include the request-level scope fields inside the item too; if supplied, they must match the form-level `database` and `collection`.

<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",
              "type": "slack",
              "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",
        type: "slack",
        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",
        "type": "slack",
        "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"`.

<Info>
  **Multipart, not JSON.** `app_knowledge` is nested JSON inside a `multipart/form-data` request. Stringify the object or array before sending it.
</Info>

***

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

```json theme={"dark"}
{
  "id": "slack_C01_1716213600_000100",
  "database": "acme_corp",
  "collection": "default",
  "title": "Auth rollback - thread root",
  "type": "slack",
  "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" },
  "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." }
    }
  ]
}
```

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",
  "type": "slack",
  "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",
  "type": "notion",
  "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",
  "type": "gmail",
  "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 → parent message/email |
| `comment_on`     | comment → parent ticket/page/source        |
| `forwarded_from` | forwarded email → original email           |
| `child_of`       | child ticket/page/custom record → parent   |
| `linked_to`      | source → related peer source               |
| `blocks`         | blocker → blocked item                     |
| `caused_by`      | effect → cause                             |
| `related_to`     | generic association                        |

<Warning>
  If you use `target.external_id`, include `target.provider`. External IDs are only unique inside a provider namespace.
</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` with `enable_match: true`                | 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 with `enable_match: true`. 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`, 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 recall 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. TLDR 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" required />                                                 | Database that owns the item.                                                                   | Must match the form-level `database` when present.                                 |
| <Field name="collection" type="string" required />                                               | 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="type" type="string" recommended />                                                  | Source category such as `slack`, `gmail`, `jira`, `notion`, `salesforce`, or `linear_comment`. | Preserved for source rendering and analytics; not the app discriminator.           |
| <Field name="kind" type="email, message, ticket, knowledge_base, comment, 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="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                                                                                                    |
| -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| Sending a generic `content: { "text": "..." }` payload for app-native data | App-specific parsing cannot see `kind`, `provider`, `external_id`, actors, thread IDs, parent IDs, or typed body fields | Use `kind`, `provider`, `external_id`, and `fields`.                                                   |
| Putting the main text outside `fields`                                     | App-source extraction and query expansion lose structure                                                                | Put text in `fields.body`, `fields.description`, `fields.title`, or `fields.data` depending on `kind`. |
| 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`.                  |
| Omitting `provider` on a relation target                                   | External IDs collide across apps                                                                                        | Include `target.provider` whenever you use `target.external_id`.                                       |
| 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`.                |
| Sending only a new ticket comment in `comments[]` during an upsert         | Replaces the parent snapshot and may drop existing content                                                              | Ingest live comments as first-class `kind: "comment"` sources.                                         |
| 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.                                 |
| Forgetting `query_apps: true`                                              | You only get normal text/vector retrieval, not app-aware ID/relation expansion                                          | Set `query_apps: 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
* [Ingest Content - API Reference](/api-reference/v2/endpoint/ingest-context)
* [Ingestion Status - API Reference](/api-reference/v2/endpoint/source-status)
