Skip to main content

HydraDB Agent Integration Guide

This document is a self-contained reference designed for AI coding agents. It covers everything needed to understand, install, configure, and integrate HydraDB into any project — from zero prior knowledge to production-ready usage.

TL;DR: Critical endpoints

  1. POST /databases (client.databases.create()): Provision an isolated database workspace.
  2. GET /databases/status (client.databases.status()): Poll until infra.ready_for_ingestion is true.
  3. POST /context/ingest (client.context.ingest()): Ingest documents, app records, or memories under type="knowledge" or type="memory".
  4. GET /context/status (client.context.status()): Poll status using ids until indexing_status is completed or graph_creation.
  5. POST /query (client.query()): Retrieve context via type: "knowledge", "memory", or "all".
  6. POST /feedback (client.feedback.submit()): Tell us when a query did not give you what you needed, and once the task is done, how the context held up. See Sending feedback.

1. Critical rules for LLMs

API versioning and auth

  • Base URL: https://api.hydradb.com
  • Raw HTTP calls must include:
    • Authorization: Bearer <api_key>
    • API-Version: 2
  • Official SDKs set API-Version: 2 automatically.
  • Use HYDRA_DB_API_KEY as the environment variable in examples.

Response envelope

Core raw HTTP responses (/databases, /context/*, and /query) are wrapped:
  • Parse core raw HTTP payloads from data.
  • The SDKs return the full envelope object; read the payload from its data field (e.g. response.data). On failure they raise a typed exception rather than returning an envelope with error set.
  • Log meta.request_id for failed requests.
  • meta may also carry an optional deprecation list (with a Deprecation: true response header) when a request uses a legacy /tenants route or a deprecated field (tenant_id/sub_tenant_id, or sub_tenant_ids on /query). Every entry has deprecated, message, and deprecated_since. deprecated_field and preferred_field are optional: the sub_tenant_ids notice on /query carries them, the tenant_id/sub_tenant_id notice does not. Read message for the migration, and do not key logic off the two optional fields. It is a non-breaking nudge. The status code is unchanged. Prefer the /databases routes and database/collection/collections fields to avoid it. Treat meta as an open object: ignore keys you do not recognize.
  • Exception: webhook management endpoints (/webhooks/indexing*) return their documented response object directly, without the { success, data, error, meta } envelope.

Core endpoints

Async lifecycle

Two operations are asynchronous:
  1. Database creation: after POST /databases, poll GET /databases/status until infrastructure is ready.
  2. Ingestion: after POST /context/ingest, poll GET /context/status until the content is searchable or fully complete.
Searchable status:
  • graph_creation: searchable, graph may still be incomplete.
  • completed: fully indexed and graphed.
Failure status:
  • Docs use errored; OpenAPI also exposes failed in one enum. Treat both as terminal failures.

Search parameter naming

The canonical request field for POST /query is type:
  • type: "knowledge"
  • type: "memory"
  • type: "all"
Use the same type enum across context lifecycle APIs and search. Search additionally accepts "all" to query knowledge and memories together.

Metadata naming

  • metadata: schema-aligned, declared at database creation, fast filter path. Sent as a top-level request field on /context/ingest.
  • additional_metadata: canonical free-form metadata; anything not present in the metadata schema. Also a top-level request field.
  • document_metadata: on file uploads only, binds a source id to each uploaded file. It is not a metadata envelope - do not nest metadata or additional_metadata inside it.
  • metadata_filters top-level keys match the metadata schema.
  • metadata_filters.additional_metadata scopes free-form metadata.

Sending feedback

If a query did not give you what you needed - nothing relevant came back, the top result was wrong, or you had to fall back to another source - please tell us with POST /feedback. You are the only party that knows whether the retrieved context actually answered the question, and that judgement is what tunes retrieval. It is entirely optional, and there is no penalty for skipping it. Send it when you have something specific to report; a report on a query that disappointed you is worth far more than a stream of routine ones. If a query worked well and you want to say so, rating: "positive" is welcome too - it tells us what to preserve. Once you have finished the task, a short report on how the context actually performed is recommended. The end of the task is when you know something you could not know at retrieval time: whether a chunk that looked plausible turned out to be stale, contradicted by another source, or never used at all. A single report then is worth more than one per query. Give it context. We see only a request_id and whatever prose you send - nothing about what you were doing - so a report that stands on its own is far more useful than one that assumes the session can be reconstructed. Worth a sentence each:
  • What the user was actually trying to do.
  • Which chunks carried the answer, and which you discarded and why.
  • Whether you had to go outside HydraDB to finish, and what was missing when you did.
At the end of a task rating: "positive" is as valuable as a negative one, because by then you know which part of the context did the work - and that is what tells us what to keep. Rules, when you do send it:
  • Take request_id from the query’s own response - never invent one. It is in meta.request_id, and in the X-Request-ID response header. Send it back verbatim: an id you made up, or one from another query, correlates to nothing.
  • Set source: "agent". It separates your reports from human ones, which fail in different ways.
  • Say what was wrong, not that something was wrong. “Returned the 2023 policy, current one is in the Q3 handbook” is actionable; “bad results” is not.
  • Do not block the user on it. Feedback is fire-and-forget: if the feedback call itself fails, drop it and carry on - never retry in a loop, and never surface that error to the user.
  • Feedback never changes the query’s result. It is a signal, not a correction, and reading it back is not possible.
Catch broadly. The point is that nothing escapes - narrowing to the SDK error type would still let a timeout or a DNS failure surface an error the user cannot act on, from a call they did not ask for. “Did not give me what I needed” is not the same as “errored”. A query that returned 200 with unhelpful results is exactly what this endpoint is for. A query that never returned - 4xx/5xx, or the SDK raised - is not: handle the error and move on rather than reporting it. Feedback is a judgement about retrieval quality, and a query that produced no results has no retrieval to judge. Fix the request instead: a 404 means the database name is wrong, a 429 means back off, a 400 means the body was malformed. If you know the right answer, send it as ground_truth. When you are running against a labelled set, or you know which document should have been returned, that is a far stronger signal than a comment - it can be scored without a human reading it. With ground_truth present, feedback prose is optional:
Send answer, source_ids, or both. Do not guess: only send ground truth you actually have. A fabricated answer key is worse than none, because it is scored as if it were true. Limit: 100 submissions per minute per organization - far above what reporting only the queries that fell short will ever reach. If you do hit 429, honour Retry-After or simply skip that report - never spin.

Do not mix scopes accidentally

  • database (formerly tenant_id) is the hard isolation boundary.
  • collection (formerly sub_tenant_id) is a logical partition inside a database, typically user/workspace/team.
  • Use the same collection on writes and reads. Data written under one collection should not be expected to appear from another.
  • Do not use metadata_filters as a substitute for collection.

2. Core primitives

Databases and collections

A database is an isolated workspace. A collection partitions data inside a database. Recommended patterns:

Knowledge

Knowledge is shared context: documents, PDFs, Markdown, CSVs, Slack threads, Notion pages, Gmail threads, tickets, webpages, etc.
  • Ingest with POST /context/ingest, type=knowledge.
  • Search with POST /query, type: "knowledge".
  • Use for content that should be reusable across users.
  • Mutability is explicit: re-ingest with the same ID and upsert: true, or delete.

Memories

Memories are user/workspace/session-scoped context: preferences, conversation history, behavioral signals, decisions, inferred traits.
  • Ingest with POST /context/ingest, type=memory.
  • Search with POST /query, type: "memory" or type: "all".
  • Always pass the same collection used at ingestion.
  • Use infer: true when the input is raw signal and HydraDB should extract the durable preference/fact.
  • Use infer: false when the memory is already structured and should be stored verbatim.

Query

POST /query is the single retrieval endpoint. It can search:
  • Knowledge only: type: "knowledge"
  • Memories only: type: "memory"
  • Both stores together: type: "all"
It supports:
  • query_by: "hybrid" for semantic + BM25 retrieval.
  • query_by: "text" for BM25 keyword/phrase search.
  • mode: "fast" for low latency.
  • mode: "thinking" for query expansion, reranking, richer graph traversal, and forceful-relation expansion.

Feedback

POST /feedback records how a query performed. It is the loop that closes retrieval quality, and it is open to agents whenever a query falls short - and, once a task is finished, for a short report on how the context held up - see Sending feedback. Each submission is its own record - sending a second report about the same query adds to it rather than replacing it, so refine as you learn more.

Context graph

HydraDB builds a graph of entity/relation triplets from ingested content. When graph_context: true (default), search can return:
  • graph_context.query_paths
  • graph_context.chunk_relations
  • graph_context.chunk_id_to_group_ids
Graph context augments retrieval; chunks remain the primary search output.

Forceful relations

At ingestion, sources can declare explicit relations:
At search time, set or rely on default query_forceful_relations: true with mode: "thinking" to pull related chunks into additional_context. Rules:
  • Forceful relation expansion only takes effect in mode: "thinking".
  • Use ids.
  • Relations are store-local: memory-to-memory or knowledge-to-knowledge. Cross-store relation lookups may not surface anything.

3. Installation and client setup

Python SDK

Async client:

TypeScript SDK

SDK naming:
  • Python methods and fields: snake_case, e.g. client.databases.collections(), database, collection, page_size, query_by.
  • TypeScript methods and fields: camelCase, e.g. client.databases.collections(), database, collection, pageSize, queryBy.
  • Both SDKs return a { success, data, error, meta } envelope; the payload is under .data (e.g. response.data.infra, response.data.statuses, response.data.results).
  • Values that are passed as JSON strings (memories, app_knowledge, document_metadata) keep snake_case keys inside the stringified payload in both SDKs, since that is raw wire data.

4. Minimal end-to-end flow

Python

TypeScript


5. Databases API

Create database

POST /databases · client.databases.create()
Notes:
  • Database creation is async.
  • database should be stable; docs recommend lowercase letters, numbers, and underscores for portability.
  • database_metadata_schema is effectively planned up front. If you need to change filterable metadata fields, expect to create a new database or re-ingest under a new schema.
  • POST /databases may return 409 DATABASE_ALREADY_EXISTS for duplicate database IDs and 403 FORBIDDEN when the API key or plan cannot create more databases.

Check readiness

GET /databases/status?database=... · client.databases.status() Ready when infrastructure reports usable status. Current docs show infra.ready_for_ingestion; SDK examples also check:
  • infra.scheduler_status
  • infra.graph_status
  • infra.vectorstore_status.knowledge
  • infra.vectorstore_status.memories
Use SDK autocomplete/returned object shape for the exact field spelling.

Other database endpoints


6. Source ingestion

POST /context/ingest is the unified write endpoint for knowledge and memories.

Common form fields

metadata and additional_metadata are top-level request fields. They are not nested inside document_metadata.

Knowledge from files

Use for PDFs, DOCX, Markdown, CSV, TXT, and other files HydraDB should parse.
metadata carries the schema-aligned fields declared on the database; top-level metadata_filters match these. Anything you want as metadata that is not present in the metadata schema goes in additional_metadata. document_metadata exists for one reason: when you upload a file there is otherwise no way to assign it a specific source ID. Use it to bind an id to each uploaded file. document_metadata item fields:

Knowledge from app sources

Use app_knowledge when your connector already extracted text from Slack, Gmail, Jira, Linear, Zendesk, Notion, Confluence, Salesforce, webpages, etc.
Recommended app-source fields: App-source object kinds in OpenAPI include:
  • email
  • message
  • ticket
  • knowledge_base
  • comment
  • custom
Modern docs show both a generic content shape and a richer app shape with kind, provider, external_id, fields, attachments, comments, and relations. Use the richer shape when building app-aware search; at minimum provide stable IDs, content, source type, metadata, and relations.

Memories

Use type="memory" and a JSON-stringified memories array.
Memory item fields:

7. Context status and lifecycle

Poll indexing

GET /context/status · client.context.status() Parameters:
  • database required
  • ids required array
  • collection - required if you ingested into one. The lookup is scoped: omitting collection, or sending the wrong one, returns indexing_status: "errored" with error_code: "FILE_NOT_FOUND" and message: "ID not found" for a source that exists and is fully searchable. That is a scope miss, not an indexing failure, and it is indistinguishable from one unless you read error_code.
Status meanings from docs: Pattern:

Webhooks instead of polling

Use /webhooks/indexing to receive terminal indexing events. Endpoints: Supported event:
  • indexing.status_changed
Payload shape:
Headers:
  • X-HydraDB-Delivery-ID
  • X-HydraDB-Event
  • X-HydraDB-Signature when a signing secret is configured
Signature scheme:
The sha256= prefix is part of the header value. The digest is lowercase hex, not base64. The HMAC covers the raw body bytes as received; re-serialising the parsed JSON produces different bytes and will not match. Managing the secret:
  • POST /webhooks/indexing with {"generate_signing_secret": true} registers and enables signing in one call, returning the secret once. Mutually exclusive with signing_secret; sending both is a 422.
  • POST /webhooks/indexing/signing-secret with no body generates one and returns the secret once. Send {"signing_secret": "..."} to supply your own (minimum 16 characters).
  • DELETE /webhooks/indexing/signing-secret disables signing.
  • Omitting signing_secret on POST /webhooks/indexing preserves the existing secret. It does not disable signing.
Rules:
  • Webhook URL must be public HTTPS; localhost/private networks are blocked.
  • Store delivery_id to deduplicate retries.
  • Verify X-HydraDB-Signature with a constant-time comparison. Use verify_webhook_signature (Python SDK) or verifyWebhookSignature (TypeScript SDK) from the SDK helpers module.
  • Fail closed: reject the request when the signing secret is absent from the environment, rather than skipping verification.

8. Query API

POST /query · client.query()

Request fields that matter

Search examples

Knowledge RAG:
Personalized answer using knowledge and memories:
Exact phrase:
Metadata-scoped search:

Search response

The successful data object / SDK return is a RetrievalResult:
Use chunks as the primary LLM context. Preserve server order; do not re-sort unless you have a deliberate reranking step.

9. Turning search results into LLM prompts

Practical guidance from essentials/v2/api-results.mdx:
  • Preserve the order of chunks; it is the server ranking.
  • Use graph context only when it helps the question.
  • Use additional_context to attach forcefully related chunks by chunk_uuid / extra_context_ids.
  • For personalized answers, type: "all" is simplest: one result set already merges knowledge and memories.
  • Do not pass raw JSON directly to the LLM if token budget matters; use the SDK formatting helper to produce a compact context string.
  • Include a grounding instruction: answer only from provided context, and say when context is insufficient.
Use the SDK helper from essentials/v2/api-results.mdx:
  • Python: build_string(result) from hydra_db.helpers
  • TypeScript: buildString(result) from @hydradb/sdk
Python:
TypeScript:
Common mistakes:

10. Metadata guide

Database schema

Declared at POST /databases:
Field options:

Where metadata belongs

Rules:
  • Plan hot filter fields before first ingest; undeclared scope keys can be ignored.
  • Use exact equality filters; range/contains/fuzzy matching should be in the query or downstream reranker.
  • To change metadata on an indexed context item, re-ingest with the same id and upsert: true.
  • Do not rename metadata keys without re-ingesting affected sources.

11. Browse, fetch, relations, delete

List documents or memories

POST /context/list · client.context.list()
Parameters:
  • database
  • collection
  • type: "knowledge" | "memory"
  • ids
  • page, page_size (1–100)
  • filters.tenant_metadata
  • filters.additional_metadata
  • filters.source_fields
  • include_fields for projection

Fetch original content

GET /context/inspect · client.context.inspect()
Modes:

Inspect graph relations

GET /context/relations · client.context.relations()
Use it for graph debugging and provenance inspection. It supports pagination via cursor.

Delete sources or memories

DELETE /context · client.context.delete()
Use type: "memory" to delete memory IDs.

12. Error handling

HTTP status codes

Common error codes

There is no INVALID_PARAMETERS and no SOURCE_NOT_FOUND - earlier revisions of this guide listed both, and no HydraDB response has ever carried either. Match on INVALID_INPUT and FILE_NOT_FOUND instead. Retry only 429, 500, 503; use bounded exponential backoff with jitter. SDK errors:

13. Cookbook patterns

The cookbooks show production patterns. Click the links below to read the raw Markdown guides with complete setup code and schemas: Common architecture across cookbooks:
  1. Create database and schema for hot filters.
  2. Ingest shared knowledge/app sources with stable IDs.
  3. Ingest user/session memories with collection.
  4. Poll status or use webhooks.
  5. Query /query with type: "knowledge", "memory", or "all".
  6. Format chunks, graph paths, and additional context into an LLM prompt.
  7. Cite sources and handle missing context explicitly.

14. Common mistakes checklist

  • Forgetting API-Version: 2 in raw HTTP calls.
  • Parsing raw HTTP response from the top level instead of data.
  • Assuming the SDK unwraps the envelope; it returns the full envelope, so read the payload from data.
  • Searching immediately after database creation without polling readiness.
  • Searching immediately after ingestion without polling context.status or using webhooks.
  • Treating processing/queued content as searchable.
  • Not handling both errored and failed as failure statuses.
  • Omitting collection on context.status and reading the resulting FILE_NOT_FOUND as a genuine indexing failure.
  • Using ?? on errorMessage, which is "" rather than null on some failures, so the fallback never fires.
  • Omitting collection for user memories.
  • Writing with one collection and reading with another.
  • Using metadata filters for user partitioning instead of collection.
  • Using undeclared tenant_metadata keys in metadata_filters.
  • Putting hot filters in free-form metadata instead of database schema.
  • Expecting query_forceful_relations to work in mode: "fast".
  • Setting graph_context: false and expecting graph fields.
  • Re-sorting chunks before prompting without a deliberate reranker.
  • Passing too many chunks to the LLM.
  • Using operator without query_by: "text".
  • Forgetting to verify webhook signatures when a signing secret is configured.

15. SDK Method Reference

15.1 TypeScript SDK

15.2 Python SDK

15.3 Minimal cURL example

Use type on raw /query requests to select knowledge, memory, or all. The raw API response is wrapped; read payloads from .data.

15.4 Raw HTTP: ingest app-source knowledge

Use this for connector output where your app already extracted the text. app_knowledge must be a JSON string in multipart form data.
Search it with app-aware retrieval:

16. Navigation and support

  • Docs root for v2: get-started/v2, essentials/v2, api-reference/v2, cookbooks/v2
  • API reference OpenAPI: api-reference/v2/openapi.json
  • Python package: hydradb-sdk>=2,<3
  • TypeScript package: @hydradb/sdk@^2
  • Dashboard/API keys: https://app.hydradb.com
  • Support email in docs: [email protected]