> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hydradb.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Multi-Tenant Support

> How HydraDB scopes data using tenants and sub-tenants, and how scoping affects writes and recall.

## 1. What it is

HydraDB scopes data using two identifiers:

* **`tenant_id`** - the top-level scoping identifier. Use it for customers, environments, or other primary data boundaries.
* **`sub_tenant_id`** - an optional scoping identifier within a tenant. Use it for users, workspaces, teams, or other logical partitions.

HydraDB write and recall operations are scoped by `tenant_id`. When `sub_tenant_id` is provided, it further narrows the scope for that operation. If you omit `sub_tenant_id`, HydraDB uses the tenant's default sub-tenant.

Use the same scoping values consistently across writes and reads. If you write data with one `sub_tenant_id` and later recall with a different one, that data may not appear in the recall results.

***

## 2. When to use each

| Goal                                            | Use                                                         |
| ----------------------------------------------- | ----------------------------------------------------------- |
| Separate customers on your platform             | A different `tenant_id` per customer                        |
| Separate environments (`prod` vs `staging`)     | A different `tenant_id` per environment                     |
| Separate per-user state within one customer     | One `tenant_id`, `sub_tenant_id = user_id`                  |
| Separate per-workspace data within one customer | One `tenant_id`, `sub_tenant_id = workspace_id`             |
| Store broadly shared Knowledge                  | Omit `sub_tenant_id` to use the tenant's default sub-tenant |

Two practical rules:

* Use a separate **tenant** whenever data must not mix across customers or environments.
* Use a **sub-tenant** for logical partitions inside a tenant, such as users, workspaces, teams, or projects.

Do not use `sub_tenant_id` as a substitute for separate production and staging tenants. Environments should usually be separated at the `tenant_id` level.

***

## 3. Recommended patterns

### B2C application

Use one tenant for the application or customer account, and use each end-user as a sub-tenant.

```text theme={"dark"}
tenant_id      = "acme_app"
sub_tenant_id  = "user_123"
```

Use this when each user has private Memories, preferences, or conversation history.

Typical flow:

* Write user Memories with `sub_tenant_id = user_id`.
* Recall user Memories with the same `sub_tenant_id`.
* Keep shared Knowledge outside the user-specific scope.

### B2B SaaS

Use one tenant per customer organization. Use `sub_tenant_id` for the workspace, team, project, or user scope inside that customer.

```text theme={"dark"}
tenant_id      = "acme_corp"
sub_tenant_id  = "workspace_42"
```

Typical flow:

* Customer-level Knowledge uses the customer `tenant_id`.
* Workspace-specific data uses a workspace `sub_tenant_id`.
* User-specific Memories use a user-level `sub_tenant_id`.

### Shared Knowledge + user personalization

For personalized answers grounded in shared Knowledge, keep the two retrieval paths explicit:

* Use `full_recall` for shared Knowledge - this hits the Knowledge vector store (`vectorstore_status` index `1`).
* Use `recall_preferences` for user-specific Memories - this hits the Memories vector store (`vectorstore_status` index `0`).
* Merge the two results in your application before prompting the LLM.

This mirrors the pattern used throughout the Recall and Memories pages: shared context and personal context are retrieved separately, then combined at prompt time.

***

## 4. How scoping works

### Writes

Writes include `tenant_id`, and may include `sub_tenant_id`.

Use `sub_tenant_id` 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 tenant's default sub-tenant.

Examples:

* A memory about John's preferences should be written with John's `sub_tenant_id`.
* Workspace-specific runbooks should be written with that workspace's `sub_tenant_id`.
* Broadly shared Knowledge should use the same scope you plan to use when recalling it.

### Reads

Recall requests include `tenant_id`, and may include `sub_tenant_id`. If `sub_tenant_id` is omitted, recall uses the tenant's default sub-tenant.

Use the same scoping values on recall that you used when writing the data. A recall request with one `sub_tenant_id` should not be expected to retrieve data written under a different `sub_tenant_id`.

If your application needs to combine data from multiple scopes, make separate recall calls and merge the results in your application.

Use `sub_tenant_id` for partitioning data. Use `metadata_filters` for narrowing results within that scope.

### Memories and Knowledge

Memory recall and Knowledge recall are separate retrieval paths:

* `recall_preferences` retrieves **Memories** (user-scoped, personal).
* `full_recall` retrieves **Knowledge** (shared documents and app sources).

For a personalized answer, a common pattern is:

1. Recall shared Knowledge with `full_recall`.
2. Recall user Memories with `recall_preferences`.
3. Format and merge both results into one LLM prompt.

<Warning>
  <strong>Use the same <code>sub\_tenant\_id</code> on writes and reads for the same logical scope.</strong>
  <p>Data written under one sub-tenant should not be expected to appear when recalling from another. Keep scope identifiers stable and consistent in your application.</p>
</Warning>

***

## 5. Minimal working example

The example below shows a common personalized-answer flow: write a user Memory, recall that user's preferences, recall shared Knowledge, and merge the two contexts in your application.

<CodeGroup>
  ```bash cURL theme={"dark"}
  # 1. Write a per-user memory under the user's sub_tenant_id.
  curl -X POST 'https://api.hydradb.com/memories/add_memory' \
    -H "Authorization: Bearer $HYDRA_DB_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "tenant_id": "acme_corp",
      "sub_tenant_id": "user_123",
      "upsert": true,
      "memories": [
        {
          "text": "Prefers dark mode and short answers.",
          "infer": true,
          "user_name": "John"
        }
      ]
    }'

  # 2. Recall that user's memories.
  curl -X POST 'https://api.hydradb.com/recall/recall_preferences' \
    -H "Authorization: Bearer $HYDRA_DB_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "tenant_id": "acme_corp",
      "sub_tenant_id": "user_123",
      "query": "UI and answer style preferences"
    }'

  # 3. Recall shared knowledge from the default sub-tenant.
  curl -X POST 'https://api.hydradb.com/recall/full_recall' \
    -H "Authorization: Bearer $HYDRA_DB_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "tenant_id": "acme_corp",
      "query": "refund policy"
    }'
  ```

  ```typescript TypeScript SDK theme={"dark"}
  import { HydraDBClient } from "@hydradb/sdk";

  const client = new HydraDBClient({
    token: process.env.HYDRA_DB_API_KEY,
  });

  // 1. Write a per-user memory under the user's sub_tenant_id.
  await client.upload.addMemory({
    tenant_id: "acme_corp",
    sub_tenant_id: "user_123",
    upsert: true,
    memories: [
      {
        text: "Prefers dark mode and short answers.",
        infer: true,
        user_name: "John",
      },
    ],
  });

  // 2. Recall that user's memories using the same sub_tenant_id.
  const prefs = await client.recall.recallPreferences({
    tenant_id: "acme_corp",
    sub_tenant_id: "user_123",
    query: "UI and answer style preferences",
  });

  // 3. Recall shared knowledge from the default sub-tenant by omitting sub_tenant_id.
  const knowledge = await client.recall.fullRecall({
    tenant_id: "acme_corp",
    query: "refund policy",
  });

  // 4. Merge prefs + knowledge in your application before prompting the LLM.
  ```

  ```python Python SDK theme={"dark"}
  import os
  from hydra_db import HydraDB

  client = HydraDB(token=os.environ["HYDRA_DB_API_KEY"])

  # 1. Write a per-user memory under the user's sub_tenant_id.
  client.upload.add_memory(
      tenant_id="acme_corp",
      sub_tenant_id="user_123",
      upsert=True,
      memories=[
          {
              "text": "Prefers dark mode and short answers.",
              "infer": True,
              "user_name": "John",
          }
      ],
  )

  # 2. Recall that user's memories using the same sub_tenant_id.
  prefs = client.recall.recall_preferences(
      tenant_id="acme_corp",
      sub_tenant_id="user_123",
      query="UI and answer style preferences",
  )

  # 3. Recall shared knowledge from the default sub-tenant by omitting sub_tenant_id.
  knowledge = client.recall.full_recall(
      tenant_id="acme_corp",
      query="refund policy",
  )

  # 4. Merge prefs + knowledge in your application before prompting the LLM.
  ```
</CodeGroup>

***

## 6. Common mistakes

**Mismatched `sub_tenant_id` between write and read.**
Data written under one sub-tenant should not be expected to surface when recalled under another. Use the same `sub_tenant_id` consistently for the same logical scope.

**Sharing a `tenant_id` across environments.**
Do not use one tenant for both production and staging. Create separate tenants for separate environments.

**Confusing `tenant_id` with `sub_tenant_id`.**
Use `tenant_id` for primary boundaries such as customers or environments. Use `sub_tenant_id` for partitions inside a tenant, such as users or workspaces.

**Assuming recall automatically merges scopes.**
A recall call uses the scope you provide. If your application needs data from multiple scopes, make multiple calls and merge the results yourself.

**Writing shared Knowledge under a user scope by accident.**
If broadly shared Knowledge is written with a user-specific `sub_tenant_id`, it may not appear where other users expect it. Choose the write scope based on where the content should be recalled later.

**Using metadata filters as a substitute for sub-tenants.**
Metadata filters narrow results inside a scope. They are not a replacement for choosing the right `tenant_id` and `sub_tenant_id`.

**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`.

***

## Related

* [Memories](/essentials/memories) - user-scoped context
* [Knowledge](/essentials/knowledge) - shared document context
* [Recall](/essentials/recall) - how scoping is applied at query time
* [How to Use API Results](/essentials/api-results) - merging recall results into a prompt
* [Create Tenant](/api-reference/endpoint/create-tenant) - defining tenants and their metadata schema
