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
POST /databases(client.databases.create()): Provision an isolated database workspace.GET /databases/status(client.databases.status()): Poll untilinfra.ready_for_ingestionis true.POST /context/ingest(client.context.ingest()): Ingest documents, app records, or memories undertype="knowledge"ortype="memory".GET /context/status(client.context.status()): Poll status usingidsuntilindexing_statusiscompletedorgraph_creation.POST /query(client.query()): Retrieve context viatype: "knowledge","memory", or"all".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: 2automatically. - Use
HYDRA_DB_API_KEYas 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
datafield (e.g.response.data). On failure they raise a typed exception rather than returning an envelope witherrorset. - Log
meta.request_idfor failed requests. metamay also carry an optionaldeprecationlist (with aDeprecation: trueresponse header) when a request uses a legacy/tenantsroute or a deprecated field (tenant_id/sub_tenant_id, orsub_tenant_idson/query). Every entry hasdeprecated,message, anddeprecated_since.deprecated_fieldandpreferred_fieldare optional: thesub_tenant_idsnotice on/querycarries them, thetenant_id/sub_tenant_idnotice does not. Readmessagefor 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/databasesroutes anddatabase/collection/collectionsfields to avoid it. Treatmetaas 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:- Database creation: after
POST /databases, pollGET /databases/statusuntil infrastructure is ready. - Ingestion: after
POST /context/ingest, pollGET /context/statusuntil the content is searchable or fully complete.
graph_creation: searchable, graph may still be incomplete.completed: fully indexed and graphed.
- Docs use
errored; OpenAPI also exposesfailedin one enum. Treat both as terminal failures.
Search parameter naming
The canonical request field forPOST /query is type:
type: "knowledge"type: "memory"type: "all"
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 themetadataschema. Also a top-level request field.document_metadata: on file uploads only, binds a sourceidto each uploaded file. It is not a metadata envelope - do not nestmetadataoradditional_metadatainside it.metadata_filterstop-level keys match themetadataschema.metadata_filters.additional_metadatascopes 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 withPOST /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.
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_idfrom the query’s own response - never invent one. It is inmeta.request_id, and in theX-Request-IDresponse 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.
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:
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(formerlytenant_id) is the hard isolation boundary.collection(formerlysub_tenant_id) is a logical partition inside a database, typically user/workspace/team.- Use the same
collectionon writes and reads. Data written under one collection should not be expected to appear from another. - Do not use
metadata_filtersas a substitute forcollection.
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"ortype: "all". - Always pass the same
collectionused at ingestion. - Use
infer: truewhen the input is raw signal and HydraDB should extract the durable preference/fact. - Use
infer: falsewhen 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"
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. Whengraph_context: true (default), search can return:
graph_context.query_pathsgraph_context.chunk_relationsgraph_context.chunk_id_to_group_ids
chunks remain the primary search output.
Forceful relations
At ingestion, sources can declare explicit relations: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
TypeScript SDK
- 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()
- Database creation is async.
databaseshould be stable; docs recommend lowercase letters, numbers, and underscores for portability.database_metadata_schemais 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 /databasesmay return409 DATABASE_ALREADY_EXISTSfor duplicate database IDs and403 FORBIDDENwhen 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_statusinfra.graph_statusinfra.vectorstore_status.knowledgeinfra.vectorstore_status.memories
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
Useapp_knowledge when your connector already extracted text from Slack, Gmail, Jira, Linear, Zendesk, Notion, Confluence, Salesforce, webpages, etc.
App-source object kinds in OpenAPI include:
emailmessageticketknowledge_basecommentcustom
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
Usetype="memory" and a JSON-stringified memories array.
7. Context status and lifecycle
Poll indexing
GET /context/status · client.context.status()
Parameters:
databaserequiredidsrequired arraycollection- required if you ingested into one. The lookup is scoped: omittingcollection, or sending the wrong one, returnsindexing_status: "errored"witherror_code: "FILE_NOT_FOUND"andmessage: "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 readerror_code.
Pattern:
Webhooks instead of polling
Use/webhooks/indexing to receive terminal indexing events.
Endpoints:
Supported event:
indexing.status_changed
X-HydraDB-Delivery-IDX-HydraDB-EventX-HydraDB-Signaturewhen a signing secret is configured
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/indexingwith{"generate_signing_secret": true}registers and enables signing in one call, returning the secret once. Mutually exclusive withsigning_secret; sending both is a422.POST /webhooks/indexing/signing-secretwith no body generates one and returns the secret once. Send{"signing_secret": "..."}to supply your own (minimum 16 characters).DELETE /webhooks/indexing/signing-secretdisables signing.- Omitting
signing_secretonPOST /webhooks/indexingpreserves the existing secret. It does not disable signing.
- Webhook URL must be public HTTPS; localhost/private networks are blocked.
- Store
delivery_idto deduplicate retries. - Verify
X-HydraDB-Signaturewith a constant-time comparison. Useverify_webhook_signature(Python SDK) orverifyWebhookSignature(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
Recommended configurations
Search examples
Knowledge RAG:Search response
The successfuldata object / SDK return is a RetrievalResult:
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 fromessentials/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_contextto attach forcefully related chunks bychunk_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.
essentials/v2/api-results.mdx:
- Python:
build_string(result)fromhydra_db.helpers - TypeScript:
buildString(result)from@hydradb/sdk
10. Metadata guide
Database schema
Declared atPOST /databases:
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
idandupsert: 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()
databasecollectiontype: "knowledge" | "memory"idspage,page_size(1–100)filters.tenant_metadatafilters.additional_metadatafilters.source_fieldsinclude_fieldsfor projection
Fetch original content
GET /context/inspect · client.context.inspect()
Inspect graph relations
GET /context/relations · client.context.relations()
cursor.
Delete sources or memories
DELETE /context · client.context.delete()
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:
- Create database and schema for hot filters.
- Ingest shared knowledge/app sources with stable IDs.
- Ingest user/session memories with
collection. - Poll status or use webhooks.
- Query
/querywithtype: "knowledge","memory", or"all". - Format chunks, graph paths, and additional context into an LLM prompt.
- Cite sources and handle missing context explicitly.
14. Common mistakes checklist
- Forgetting
API-Version: 2in 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.statusor using webhooks. - Treating
processing/queuedcontent as searchable. - Not handling both
erroredandfailedas failure statuses. - Omitting
collectiononcontext.statusand reading the resultingFILE_NOT_FOUNDas a genuine indexing failure. - Using
??onerrorMessage, which is""rather than null on some failures, so the fallback never fires. - Omitting
collectionfor user memories. - Writing with one
collectionand reading with another. - Using metadata filters for user partitioning instead of
collection. - Using undeclared
tenant_metadatakeys inmetadata_filters. - Putting hot filters in free-form metadata instead of database schema.
- Expecting
query_forceful_relationsto work inmode: "fast". - Setting
graph_context: falseand expecting graph fields. - Re-sorting chunks before prompting without a deliberate reranker.
- Passing too many chunks to the LLM.
- Using
operatorwithoutquery_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
Usetype 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.
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]
