Skip to main content
App sources are pre-parsed records from business applications: emails, chat messages, tickets, wiki pages, CRM objects, comments, and attachments. Use app sources when you already know the item’s text and structured fields. HydraDB uses that structure to build better search context than raw text alone.
App-source search is enabled at recall time with search_apps: true. In thinking mode, HydraDB can use app IDs, actors, threads, parent links, and explicit relations to expand the returned context.

Page map

The mental model

Every app-source item becomes one HydraDB source. HydraDB stores three kinds of information: Do not model every provider-specific field as a first-class field. Put app-specific scope values like Slack channel, Confluence space, Jira project, Salesforce pipeline, or HubSpot list in metadata.

Ingest app sources

Send app sources in the app_knowledge form field as a single JSON object or an array. Each item is one HydraDB source and must include the ingestion envelope fields id, tenant_id, and sub_tenant_id. The app-specific part of each item uses the public app-source shape: For typed app sources, put the main searchable item text in fields (email.body, message.body, ticket.description, knowledge_base.body, comment.body, or custom.data). The top-level content object is the older generic knowledge payload and is not required for app-native search. The example below is a valid app_knowledge item for a Slack message:

Field categories

Identity fields

Identity fields tell HydraDB what this item is. Use external_id for the item itself. For example:

Thread and parent fields

For hierarchy fields, parent_id should be the parent’s provider external_id, not the HydraDB source id. The child and parent should use the same provider namespace so HydraDB can safely connect them. For example, a Salesforce deal with parent_id: "account_456" should have a parent account source with provider: "salesforce" and external_id: "account_456". For Slack-like threads, it is OK for thread_id and parent_id to be the same when the replied-to parent is the thread root.
For a direct reply where the provider gives the immediate parent:

Incremental ingestion

Use the same POST /ingestion/upload_knowledge endpoint for both initial sync and live updates. The important choice is whether the provider object is the same source being updated or a new source that arrived later.
upsert replaces the existing HydraDB source with the same id. Do not send only a new embedded comments[] entry on a ticket upsert unless you intend to replace the ticket snapshot. For live webhooks, model each new ticket comment as kind: "comment" instead.

First-class ticket comment

A first-class comment is one HydraDB source. Use a stable source id you can reproduce from the provider, and use the provider’s comment ID as external_id.
When parent_id is present on a comment, HydraDB derives a comment_on relation to the parent source in the same provider namespace. In the example above, HydraDB links the comment to the source with provider: "linear" and external_id: "UI-001". If no parent is present, the comment is still indexed as a standalone source. Use explicit relations when the parent is in a different provider namespace or when you already know the HydraDB source_id:

New Slack messages and email replies

Slack messages and email replies are not modeled as comments on an existing source. Ingest each new message or email as a new source:
  • For Slack or Teams, use kind: "message", a new external_id for the provider message ID, thread_id for the thread root, and parent_id for the replied-to message when available.
  • For Gmail or Outlook, use kind: "email", a new external_id for the provider message ID, thread_id for the email thread, and reply_to_id for the parent email when available.
This preserves message-level authorship, timestamps, URLs, and search results without re-indexing the whole conversation.

New knowledge-base pages

A new Notion, Confluence, Google Docs, or CMS page is a new knowledge_base source. The space, workspace, collection, or database is container metadata, not the source identity.

Container metadata

Use metadata for provider-specific scope and fields that help organize the source but do not identify the source itself. A container is the place that holds many app items: a Slack channel contains many messages, a Jira project contains many tickets, a Confluence space contains many pages, and a CRM pipeline contains many deals.
Examples: Think of metadata as a flexible bag for scope, grouping, and provider-specific details. HydraDB stores it with the source so results can show where the item came from and so your integration can keep provider context without forcing every app to have the same fields. Do not use a container ID as external_id. external_id should identify the individual item being ingested; container IDs belong in metadata.
Put fields in metadata when they describe where the item lives, such as channel, project, space, workspace, pipeline, region, or team. Put fields in fields when they are part of the item’s searchable content or structure, such as message body, ticket status, parent ID, assignee, or thread ID.

Actors

Actors are people or bots involved with an app source. HydraDB infers actors from kind-specific fields: Actors improve queries such as “emails from Alice”, “tickets assigned to Bob”, “messages mentioning Carol”, or “comments by the support team”.

Relations

Relations tell HydraDB how app sources connect. Use relations to explicitly connect two HydraDB sources when the relationship matters for search. For example, connect a ticket to a release it blocks, a CRM deal to a related support ticket, or a document page to a source it references.
The target can be either:
If you use target.external_id, always include target.provider. External IDs are only unique inside a provider namespace.
linked_ticket_ids is not a first-class field. Use linked_issue_ids for ticket-like items, or use explicit relations with predicate: "linked_to" for generic links.

Explicit relations

Use relations for source-to-source connections that are not already represented by typed fields:
Useful predicates include: The vocabulary is extensible. If you use a custom predicate, use a stable lowercase string and keep direction consistent. In thinking mode, related sources found through these connections can appear in additional_context.

Kind-specific fields

Kind-specific fields are the structured fields inside fields; they tell HydraDB how to read each app item as an email, message, ticket, knowledge-base page, or custom record.

Email

Use email for individual email messages. Put the subject and body in fields, plus sender, recipients, thread, reply, and forward IDs when you have them.

Message

Use message for chat or collaboration messages from tools like Slack, Teams, Discord, or in-app comments that behave like messages.
Put channel or workspace information in metadata.

Ticket

Use ticket for issue-tracker or support-case style records: Jira issues, Linear issues, Zendesk tickets, GitHub issues, support cases, and similar workflow items.
Put project information in metadata.

Knowledge base

Use knowledge_base for wiki or documentation pages from tools like Notion, Confluence, Google Docs, or an internal CMS.
Put space information in metadata.

Comment

Use comment for incremental comments from ticketing, issue-tracking, support, CRM, or workflow systems. A first-class comment is its own source with its own stable id and external_id.
parent_id should point to the parent source’s provider external_id in the same provider namespace. HydraDB derives a comment_on relation from that field. If a comment belongs to a cross-provider source, use relations[] with predicate: "comment_on" instead.

Custom and CRM

Use custom for records that do not fit the built-in kinds yet, especially CRM and business-system objects such as accounts, deals, contacts, renewals, invoices, or internal workflow records.
Use: For CRM-style hierarchies, ingest both sides as app sources when you want traversal. For example, ingest the account as provider: "salesforce", external_id: "account_456", then ingest the deal or contact with fields.parent_id: "account_456". During app search, HydraDB can use that link to bring the account and its children into context.

Attachments and comments

Attachments and embedded comments[] are indexed separately from the main source text, but remain connected to the source. Use attachments[].content when you already have extracted text for the file. HydraDB indexes that text in attachment-specific vectors. A URL by itself is treated as metadata; HydraDB does not fetch and parse arbitrary attachment URLs from app-source payloads. Use embedded comments[] for initial backfills or full snapshots where you already have the complete current comment list for the parent source. For live incremental webhooks, prefer first-class kind: "comment" sources so you can add, update, or delete a single comment without re-ingesting the parent ticket or page.
Search can use comment authors, comment vectors, extracted attachment text, attachment vectors, and shared-attachment graph edges as extra recall signals. When search_apps: true, HydraDB runs an app-aware search lane alongside normal retrieval. The app-aware lane does more than search text. It first looks for app-specific clues in the query, such as a provider name, object type, exact ticket/message ID, person name, thread wording, or relation wording like “linked to”, “blocked by”, “parent”, or “reply”. It then uses those clues to build candidates and expand context around the best matches. This means a query like “show me Slack replies from Alice about AUTH-123” can combine text relevance with Slack scoping, actor matching, exact ticket ID matching, and reply/thread expansion. A query like “what is blocking the release?” can use explicit blocks or linked_to relations instead of relying only on whether the same words appear in both sources. This helps because app search can retrieve the surrounding work context, not just the closest text match. If a ticket, Slack thread, and release note are connected by IDs or relations, HydraDB can bring them together even when they use different wording. The result is fewer missed sources, better answers for workflow questions, and more useful context for agents that need to explain why something happened or what to do next. Parent/child search is based on provider-scoped external IDs. When HydraDB ingests a child source with parent_id, it stores that value as a pointer to the parent’s external_id in the same provider namespace. At recall time, search_apps: true lets the app-aware lane find an initial source by text, exact ID, actor, or provider/kind filters. In thinking mode, if the query asks for parents, children, ancestors, descendants, epics, subtasks, accounts, deals, or similar hierarchy wording, HydraDB expands around that matched source:
  • To find a parent, HydraDB matches the source’s parent_id to another source’s external_id with the same provider.
  • To find children, HydraDB looks for sources whose parent_id equals the matched source’s external_id with the same provider.
  • Expansion is bounded and can include one more level, such as grandparent or grandchild context, when useful.
The expanded sources are used as connected context for the answer. Use mode: "thinking" for best app-source behavior, because relation expansion and reranking have more room to improve context quality.

Common mistakes

End-to-end example

This example ingests a Jira ticket with a parent epic, linked issues, project metadata, embedded snapshot comments, and an explicit blocker relation. For new comments arriving after this ticket has already been indexed, use the first-class comment example in Incremental ingestion.
At ingestion time, HydraDB derives: At search time, search_apps: true lets queries like “what is blocking the release?”, “children of AUTH-100”, or “tickets linked to AUTH-123” use those graph edges and IDs instead of relying only on text similarity.

Common scenarios

Use these payload patterns when app data contains comments, attachments, threads, or cross-app links. Each example is one item in the app_knowledge array.

Slack message with an attachment

Model the Slack message as kind: "message", keep channel/workspace details in metadata, and put extracted file text in attachments[].content.

Incremental Linear ticket comment with attachment

Represent the Linear issue itself as a ticket. When a new comment arrives later, ingest the comment as a first-class comment source. Files mentioned or uploaded in that comment can go in the comment source’s attachments[].

Custom threaded record with comments and attachments

Use custom for internal systems or CRM/workflow records outside the built-in email, message, ticket, and knowledge-base shapes. thread_id groups activity around the record, comments make discussion searchable, and attachments add independent file recall.

CRM deal linked to a support ticket

Use parent_id for account -> deal/contact hierarchy and relations[] for cross-app context, such as a Salesforce opportunity linked to a Linear or Jira support issue.

Relation fields

TLDR field reference

Use this as the compact contract for app-source ingestion. The fields.kind value must match the top-level kind when you provide typed fields.

Top-level fields

email fields

message fields

ticket fields

knowledge_base fields

comment fields

custom fields

Attachment fields

Embedded comments[] fields