Skip to main content
POST
The single retrieval endpoint for everything. Use it any time you need to feed an LLM with grounded context, surface user preferences, or fetch chunks ranked by relevance. Three independent dimensions control behavior:
  • type picks what to query: "knowledge", "memory", or "all" (both, merged and re-ranked together).
  • query_by picks how to match: "hybrid" (semantic + BM25, the default) or "text" (BM25 only - pair with operator).
  • mode picks how to rank results: "fast" (single-pass, low-latency), "thinking" (expands query, reranks, and can include forceful-relation context), or "auto" (scores the query and routes to "fast" or "thinking" automatically, defaulting to "thinking" when the signal is inconclusive - the default if mode is omitted).
Read more about choosing the perfect mode for your use case here.
database and collection are the current field names (formerly tenant_id and sub_tenant_id). The old names remain accepted as deprecated aliases for full backward compatibility.

Querying multiple collections

Use collections when one query should fan out across multiple user, workspace, or team scopes. The field accepts either a list or a weighted object:
Equal weighting
Weighted ranking
A list gives every collection equal normalized weight. An object treats values as positive relative ranking weights with at most one decimal place and normalizes them server-side. You can send at most 100 collections. When max_results is omitted, HydraDB uses up to 10 results per collection, capped at 1000 fanout candidates before the final ranked response is shaped. When max_results is set, it is the final global response cap across the merged fanout result set.
Caching tip: collections list order is not semantically significant for fanout selection. Sort list values before constructing cache keys; for weighted objects, sort keys and keep weights at the documented one-decimal precision so equivalent calls share the same cache entry.

Transforming the response into LLM context

Use build_string / buildString from the SDK. It takes any POST /query result and returns a formatted plain string.

Common use-cases and their configurations

HydraDB scores the query before retrieval and routes it to "fast" or "thinking" - a query naming several distinct entities like this one is likely to route to "thinking". Use "auto" for traffic where query complexity varies call-to-call and you don’t want to hand-pick per request. This is also the default: an omitted mode field behaves exactly like mode: "auto". Set mode to "fast" or "thinking" explicitly if you want a deterministic pipeline instead.

Request body

Tuning heuristics.
  • alpha: start at 0.8. Lower toward 0.3–0.5 when the query contains literal tokens (error codes, SKUs, product names). Raise toward 0.9 for conceptual questions. Use “auto” when query shape varies.
  • recency_bias: leave at 0 for static reference material. Set 0.2–0.4 for mixed content, 0.6–0.8 for changelogs, news, or status updates.
  • max_results: start at 10. Drop to 5 for tight context windows; raise to 20 if you rerank downstream.

Decision matrix

For query_by: "hybrid":"auto"’s resolved pipeline isn’t reported back in the response, so budget latency as thinking-level in the worst case. Omitting mode behaves exactly like mode: "auto" - set it explicitly to "fast" or "thinking" if you want a deterministic pipeline instead.
metadata_filters are hard exact-match constraints applied before ranking and re-checked after hydration. The shape combines two filter scopes:
Separate keys are ANDed. Each metadata (top-level) key takes an operator object naming the comparison:
Adding values to contains_any widens the result set. There is no ALL/AND operator within a single key, and range and fuzzy operators are not supported; run multiple queries or post-process client-side for those cases.Operators apply to metadata (top-level keys) only. Inside additional_metadata, use a bare scalar for an exact match or a bare array to match any listed value.
An operator used inside additional_metadata is not rejected. It is read as an exact-match filter against a stored object, so on a normal field it matches nothing and the request returns 200 with an empty result rather than an error.
The bare forms still work and are unchanged, but are deprecated in favour of the operators, because the comparison they perform is inferred from the JSON shape rather than stated. A bare scalar behaves as equals, a bare array as contains_any, and a bare single-element array as contains - so {"emails": "a@x"} and {"emails": ["a@x"]} differ by one character and return different results.
contains, contains_any and lists are supported on VARCHAR fields only: any of them passed for a declared field of another type is rejected with 400 VALIDATION_ERROR. equals works on every declared type, so {"priority": {"equals": 7}} is valid on an INT64 field.A known operator given the wrong operand type, or several operators in one object, is rejected with 400 VALIDATION_ERROR. A misspelled operator is not: {"contian": "x"} is indistinguishable from a filter for a stored object with that key, so it is left alone and matches nothing.contains, contains_any and equals are reserved key names: an object built only from them is read as an operator and can no longer exact-match a stored object, and an object whose keys are ALL operator names is rejected with 400. Mixing an operator name with any other key ({"contains": "a", "other": 1}) is unaffected. A caller needing the reserved shape must rename the nested key or the field.
A zero-result query returns empty arrays/maps rather than an error, as shown in the Zero results tab.

Behavior notes

Default Behaviors
  • mode defaults to "auto". Omitting mode entirely behaves exactly like mode: "auto" - set it explicitly to "fast" or "thinking" if you want a deterministic pipeline.
  • graph_context is on by default. Set it to false if you only need ranked chunks and want to drop the graph slice from the response.
  • recency_bias is off by default. Defaults to 0.0 - no recency boost is applied unless you set it.
Important Considerations & Common Mistakes
  • query_forceful_relations requires mode to resolve to "thinking". In fast mode the flag is silently ignored. The server does not error or warn - your additional_context will simply be empty. Under mode: "auto" this depends on that request’s routing decision, not on what you asked for.
  • mode: "auto" overrides graph_context. Whatever you send for graph_context is replaced to match the resolved mode - true if auto escalates to thinking, false if it resolves to fast. This also applies when mode is omitted, since it defaults to "auto". Set graph_context explicitly only when calling "fast" or "thinking" directly.
  • Want a deterministic pipeline instead of automatic routing? Set mode explicitly to "fast" or "thinking" - an omitted mode field now defaults to "auto", not "fast".
  • Relation timestamp is a Unix epoch float here. In the graph_context slice returned by /query - and in the passthrough relations returned by List Documents with include_fields: ["relations"] - each relation’s timestamp is a Unix epoch value in seconds (a float, e.g. 1778573640.0). The dedicated Context Relations endpoint returns the same field as an ISO-8601 string instead. Normalize before comparing relation timestamps across endpoints.
  • Use the right metadata namespace. Top-level metadata_filters keys match metadata; free-form per-document fields must be nested under additional_metadata (document_metadata is only a legacy alias). Declare hot top-level filter fields in database_metadata_schema with enable_match: true.
  • Common mistakes. Check Ingestion Status for recently ingested documents before querying. If you omit collection, HydraDB queries the default collection; use List Collections to discover available IDs.

Errors

Common codes: 400 INVALID_PARAMETERS (empty query), 404 DATABASE_NOT_FOUND, 422 VALIDATION_ERROR, 500 INTERNAL_ERROR. See Error Responses for the full list. 400 also covers oversized filters: a metadata_filters list above 500 values, or a metadata_filters object above 64 KiB of compact JSON. The message names the offending key or reports the actual byte count. See Filter size limits.
Related Resources

Authorizations

Authorization
string
header
required

API key sent as a Bearer token: "Bearer prefix.secret"

Body

application/json

Unified query request

acl
string[]

ACL scopes retrieval to documents the given principals may access (PRO-1684 document ACLs): a document matches when its stored ACL is empty (unrestricted, pre-RBAC content and connectors without permission support), contains public, or intersects these principals. Entries are bare emails or prefixed principals (user_email:/group:/domain:). Omitted, empty, or ["*"] disables ACL filtering entirely, today's behavior. Like IDs, the resulting clause survives the metadata zero-result retry. An entry that is not a known principal fails CLOSED: it matches only public and unrestricted documents, never restricted.

additional_context
string

Optional context string prepended to the query to improve retrieval relevance.

Example:

"The user is a senior engineer onboarding to the platform."

alpha
any

Weighting balance between dense and sparse retrieval in hybrid mode. "auto" lets HydraDB choose; a number from 0 (full BM25) to 1 (full dense) sets it explicitly.

attributes
object

Attributes is the go-forward metadata filter: a MongoDB-like operator query ($eq/$ne/$gt/$gte/$lt/$lte/$in/$nin/$and/$or/$not/$exists) over the database attributes, translated to a safe Milvus scalar pre-filter by BuildAttributesFilterExpr (PRO-1618). It composes (AND) with the deprecated metadata_filters while both exist. Field names are allowlisted and values escaped, so it is injection-safe.

It is applied everywhere metadata_filters is, and nowhere else: the chunks a query returns, the additional context and forceful-relation chunks (the fail-closed post-filter net in the service), and the graph paths, which the graph lane prunes by resolving every source a path cites and dropping the paths that touch one failing the predicate (disallowedGraphSources). Product decision 2026-09-04: attributes behaves like metadata_filters on every part of the response.

CodeSearch forces the repository code-search branch on (true) or off (false) for this query, overriding the classifier. Nil = let the classifier decide. Only meaningful where the branch is enabled.

Example:

true

collection
string

Collection scope. Defaults to the default collection when omitted. Formerly sub_tenant_id; the sub_tenant_id alias is still accepted (deprecated).

Example:

"team_docs"

collections

Preferred /query scope selector. Send either a list of collection IDs for equal normalized weighting, or an object mapping collection ID to a positive relative ranking weight with at most one decimal place. Do not send together with the deprecated sub_tenant_ids or sub_tenant_id.

Required array length: 1 - 100 elements
Example:
database
string

Database is the canonical v2 name for the tenant scope. TenantID is its deprecated alias and remains fully accepted. The TenantAliases middleware reconciles the two before binding, so TenantID is always populated and the handler reads it; Database/Collection are carried only for docs/OpenAPI.

Example:

"acme_corp"

follow_forceful_relations
boolean

Whether to follow the relations the author declared at ingest (forceful_relations) and return the related contexts. Defaults to true when omitted.

Example:

true

graph_context
boolean

Whether to include graph context in the response. Defaults to true for /query when omitted.

Example:

true

graph_vector_prune
boolean

GraphVectorPrune switches the graph-connected-chunks lane from "fetch graph-selected chunks and let the fusion reranker sort them out" to "fetch a wider graph-selected candidate pool, then rank that pool by Milvus vector similarity, fully replacing the final chunk list." Works in either fast or thinking mode. Default false preserves existing behavior. Also gated server-side by a repo-level config flag (SearchService's graphVectorPruneEnabled) — if that flag is off, this is forced to false regardless of what the request sets, so a deployment can disable the mechanism without any client-side change.

Example:

true

graph_vector_prune_spacy_entities
boolean

GraphVectorPruneSpacyEntities: when GraphVectorPrune is also set, swaps the graph lane's entity-extraction source from the default LLM-based extractor to a local spaCy subprocess (faster, no network round trip, but a narrower/mismatched entity vocabulary versus the graph's own LLM-extracted node names). No-op if GraphVectorPrune is false (including when forced false by the server-level flag) or no spaCy extractor was configured at startup.

Example:

true

ids
string[]

IDs optionally scopes retrieval to specific source ids. The v2 wire field is ids (matching /context/list); empty means search the whole corpus. Applied as a Milvus source_id in [...] pre-filter that is preserved across the metadata zero-result retry, so a source-scoped search that matches nothing returns nothing rather than silently widening to the whole corpus.

Example:
max_results
integer

Maximum number of chunks to return.

Example:

10

metadata_filters
object
deprecated

DEPRECATED: use attributes, which is an operator language pushed into the vector search rather than bare equality applied after it. metadata_filters keeps working, and is still the only way to filter on per-context custom_attributes, which attributes does not cover yet. Filters results by context metadata. Top-level keys target tenant metadata (for example department, priority, active, or tags). Nested additional_metadata keys target document metadata. Separate keys are ANDed. Each top-level key accepts an operator object naming the comparison: {"contains": value} matches sources whose field holds that value (multi-value fields are stored comma-joined, so this matches one member); {"contains_any": [values]} matches sources holding ANY one of the listed values; {"equals": value} matches sources whose field is exactly that value. The bare forms remain supported and unchanged but are deprecated in favour of the operators, because the comparison they perform is inferred from the JSON shape rather than stated: a bare scalar behaves as equals, a bare array as contains_any, and a bare single-element array as contains. Operators apply to top-level keys only; inside additional_metadata use the bare scalar or array forms. An operator used inside additional_metadata is NOT rejected - it is read as an exact-match filter against a stored object, so on a normal field it matches nothing and the request returns 200 with an empty result rather than an error. A known operator given the wrong operand type, or several operators in one object, is rejected with 400 VALIDATION_ERROR rather than silently matching nothing. A MISSPELLED operator is not: {"contian": "x"} is indistinguishable from a filter for a stored object with that key, so it is left alone and matches nothing. An object whose keys are not operator names is likewise treated as an exact-match filter against a stored object, unchanged. RESERVED NAMES: contains, contains_any and equals are reserved as the keys of a top-level filter object, so an object built only from them is read as an operator and is no longer available for exact object matching -- {"f": {"contains": "x"}} is read as the operator, and an object whose keys are ALL operator names is rejected with 400. A caller matching such an object in a JSON-typed field must rename the nested key or the field. Mixing an operator name with any other key ({"contains": "a", "other": 1}) is unaffected and still exact-matches. There is no ALL/AND operator within a single key. contains, contains_any and arrays are supported on VARCHAR fields only: any of them passed for a declared field of another type is rejected with 400 VALIDATION_ERROR. equals works on every declared type, so {"priority": {"equals": 7}} is valid on an INT64 field. Size limits: each list may hold at most 500 values, and the whole metadata_filters object is capped at 64 KiB measured on its compact JSON encoding in UTF-8 bytes AFTER operator objects are reduced to their values, so {"contains": "x"} is measured as ["x"] and the operator keyword itself costs nothing. The cap bounds the cost of the resulting vector-store expression, which the operator spelling does not change. Field names and punctuation count. Exceeding either returns 400 naming the offending key or the actual byte count.

Example:
mode
enum<string>
Available options:
fast,
thinking,
auto
Example:

"thinking"

Number of adjacent chunks to pull alongside each matched chunk for additional context.

Example:

3

operator
enum<string>
Available options:
or,
and,
phrase
Example:

"and"

profile_entity_type
string

ProfileEntityType/ProfileNamespace refine the subject's graph identity; defaults ("PERSON"/"users") cover the common case of a person subject.

profile_namespace
string
profile_subject
string

ProfileSubject names the entity whose compiled profile should ride the response as profile_context/profile_filter (PRO-1797). Payload-only: chunk ranking is never altered. Omitted = no profile block. Dark until the repo-level ENTITY_PROFILE_CONTEXT_ENABLED flag is on.

query
string

Natural-language search query.

Example:

"Which mode does the user prefer?"

query_apps
boolean

Whether to include app-aware knowledge retrieval. Applies to knowledge hybrid queries. Defaults to true when omitted; pass false to search files only.

Example:

true

query_by
enum<string>

Retrieval method to use for the query.

Available options:
hybrid,
text
Example:

"hybrid"

query_forceful_relations
boolean
deprecated

Deprecated alias for follow_forceful_relations. Ignored when follow_forceful_relations is sent.

Example:

true

recency_bias
number

Recency boost applied to ranking (0.0-1.0). Omit it to get the always-on default baseline of 0.40 (a bounded <=40% swing on normalized relevance — it reorders within a relevance gap of up to 0.40 but never buries a more strongly relevant result); send 0 to disable recency entirely; higher values favour more recent sources more strongly.

Example:

0.2

sub_tenant_id
string
deprecated

Deprecated for /query (since 2.0.1). Use collection for a single scope or collections for multiple. Backwards-compatible and will be removed in a future version. Do not send together with a multi-scope selector.

Example:

"sub_tenant_4567"

sub_tenant_ids
deprecated

Deprecated for /query (since 2.0.1). Use collections instead; it accepts the same list or weighted-object shape. Backwards-compatible and will be removed in a future version. Do not send together with collections.

Required array length: 1 - 100 elements
Example:
temporal_intent
object

TemporalIntent (EXPERIMENTAL) lets the caller supply the classification (mode/window/phrases) directly, bypassing the regex classifier — for agents whose own LLM already understands the query, and for non-English queries. Invalid overrides fall back to the classifier.

Example:
temporal_now
string

TemporalNow optionally anchors "now" for temporal reasoning (ISO-8601). Callers replaying past conversations (or backfilling) must supply it or to-now durations and recency windows resolve against the server's wall clock (LongMemEval measured 0 exact to-now durations from this alone).

temporal_reasoning
boolean

TemporalReasoning activates the temporal read path: the query is classified into a temporal mode (current/as-of/range/upcoming...), matching edge-level temporal facts are resolved from the edge_temporal store and ride back on the response (temporal_facts / temporal_duration / temporal_filter). CONTRACT: chunk ranking is NEVER altered — ON returns the same chunks as OFF; the layer is additive payload + computed answers only (rank shaping measured net-negative on BEAM/LongMemEval/TEMPO; see temporal_filters.go). Optional; ON by default — pass temporal_reasoning:false to disable. Resolved by GetTemporalReasoningOrDefault (ownership rule).

Example:

true

tenant_id
string
deprecated

deprecated: use database

Example:

"tenant_1234"

titles
string[]

Optional exact document-title filter. Values are matched case-insensitively and ORed, resolved to source IDs, then the normal query pipeline runs within that source scope. When ids is also supplied, the two filters are intersected.

type
enum<string>
deprecated

Deprecated: kept for split databases. Corpus to query: knowledge (the default), memory, or all (both, merged).

Available options:
knowledge,
memory,
all

Response

OK

data
object

The response body, in the shape the database answers with: the v2 body (chunks, graph_context, sources and related fields), or the four-key body (chunks, graph, forceful_relations, llm_prompt).

Example:
error
object

Error message, empty string on success.

Example:
meta
object
Example:
success
boolean

Whether the request succeeded.

Example:

true