1. What it is
Multi-tenancy means serving several customers or users while keeping their data separate. In HydraDB, databases separate customer data and collections organize content within a database. Your backend chooses the database and collections each caller may use. HydraDB scopes data using two identifiers:database: the name of a separate workspace for data. Use it for customers, environments, or other primary data boundaries.collection: the name of a group of content within a database. Use it for users, workspaces, teams, or other logical partitions.
database. When collection is provided, it further narrows the scope for that operation. If you omit collection, HydraDB uses the database’s default collection, which is created on the first write.
Use the same scoping values consistently across writes and reads. If you write data with one collection and later query with a different one, that data may not appear in the query results.
2. When to use each
Do not use
collection as a substitute for separate production and staging databases. Environments should usually be separated at the database level.
3. Recommended patterns
B2C application
Use one database for the application or customer account, and use each end-user as a collection.- Write user Memories with
collection = user_id. - Query user Memories with the same
collection. - Keep shared Knowledge outside the user-specific scope.
B2B SaaS
Use one database per customer organization. Usecollection for the workspace, team, project, or user scope inside that customer.
- Customer-level Knowledge uses the customer
database. - Workspace-specific data uses a workspace
collection. - User-specific Memories use a user-level
collection.
Shared Knowledge + user personalization
For personalized answers grounded in shared Knowledge, use thetype parameter on POST /query:
type: "knowledge"retrieves shared Knowledge (Knowledge vector store,vectorstore_status.knowledge).type: "memory"retrieves user-specific Memories (Memories vector store,vectorstore_status.memories).type: "all"runs both in parallel and returns one merged, re-ranked result set, usually what you want for personalized answers.
type: "all" reads both stores from the same scope. If shared Knowledge lives in its own collection (for example company_docs), send collections: ["company_docs", "user_123"] with type: "all". HydraDB runs the query in each collection and merges the results. Every listed collection must already exist.
When you need different formatting for shared vs personal context in the LLM prompt, call POST /query twice (once with type: "knowledge", once with type: "memory") and combine in your application.
4. How scoping works
Writes
Writes includedatabase, and may include collection.
Use collection when the data belongs to a specific user, workspace, team, or other logical partition. Omit it when you intentionally want the data written to the database’s default collection.
Examples:
- A memory about John’s preferences should be written with John’s
collection. - Workspace-specific runbooks should be written with that workspace’s
collection. - Broadly shared Knowledge should use the same scope you plan to use when querying it.
Reads
Query requests includedatabase, and may include collection. If collection is omitted, query uses the database’s default collection.
A query with one collection does not read data written under another, including the default collection.
If your application needs to combine data from multiple scopes, prefer one query call with collections unless you need separate response formatting or client-side treatment per scope.
Use collection for partitioning data. Use metadata_filters for narrowing results within that scope.
5. Minimal working example
The example below shows a common personalized-answer flow: write a user Memory with a singlecollection, then query Knowledge and Memories together with type: "all". The query reads only user_123. For shared Knowledge in another collection, see Recommended patterns.
6. Common mistakes
Mismatchedcollection between write and read:
Data written under one collection should not be expected to surface when queried under another. Use the same collection consistently for the same logical scope.
Sharing a database across environments:
Do not use one database for both production and staging. Create separate databases for separate environments.
Confusing database with collection:
Use database for primary boundaries such as customers or environments. Use collection for partitions inside a database, such as users or workspaces.
Assuming query automatically searches every collection:
A query call uses the scope you provide. If your application needs data from multiple scopes, pass collections as a list or weighted object.
Writing shared Knowledge under a user scope by accident:
If broadly shared Knowledge is written with a user-specific collection, it may not appear where other users expect it. Choose the write scope based on where the content should be queried later.
Using metadata filters as a substitute for collections:
Metadata filters narrow results inside a scope. They are not a replacement for choosing the right database and collection.
Using unstable identifiers:
Avoid display names, emails that may change, or user-provided labels as long-term scope identifiers. Prefer stable internal IDs such as user_123, workspace_42, or org_acme.
Deleting a collection:
DELETE /databases/collections permanently removes one collection and all of its data. The parent database stays. Use List Collections to confirm the name first. On the dashboard, click a database to expand its collections, then delete from the inline list.
7. Migrating from the legacy tenant and sub-tenant fields
The request fields are
database (formerly tenant_id) and collection (formerly sub_tenant_id). The old names and the /tenants routes still work as deprecated aliases, so existing integrations keep running. New code should use the new names. See Migrating from tenant_id and sub_tenant_id.database/collection are the canonical v2 names for what were historically called tenant_id/sub_tenant_id. The rename is user-facing only: internally HydraDB still uses the historical names, so nothing about your data changes. The old names are still accepted, and migration is optional and non-breaking.
Both names work on every surface:
Both route prefixes use the same handlers. Legacy fields are accepted in the query string, JSON body, and multipart form.
On
/query, collections is the canonical way to scope to one or more collections (send a list, or an object mapping collection ID to a relative ranking weight). sub_tenant_ids remains accepted as its deprecated alias, and the singular sub_tenant_id still works too. See Query.
Deprecation signals
When a request uses a legacy route or a legacy field, HydraDB adds a non-breaking migration nudge (never an error, and the status code is unchanged):Deprecation: trueresponse header, plus aWarningheader carrying the migration message.meta.deprecation: a list of structured notices in the response envelope’smetaobject (for endpoints that return the envelope). Each entry carriesdeprecated, a human-readablemessage, anddeprecated_since(e.g."2.0.1"); field-level notices (such assub_tenant_idstocollections) also includedeprecated_fieldandpreferred_field, while route-level notices omit them. Fully-migrated requests omit the list entirely.
/databases routes, the header and meta.deprecation disappear.
Sending both names
If you send both a canonical field and its deprecated alias:- Same value (for example
databaseandtenant_idboth"acme"): accepted. HydraDB uses the canonical value. - Different values (for example
database: "acme"andtenant_id: "other"): rejected with400, since the two names refer to the same thing and must agree. Send only one. The same rule applies tocollection/sub_tenant_idand tocollections/sub_tenant_idson/query.
Related
- Memories: user-scoped context
- Knowledge: shared document context
- Query: how scoping is applied at query time
- How to Use API Results: merging query results into a prompt
- Databases API: creating databases and their metadata schema
