> ## 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.

# SDKs – Node and Python

> Official TypeScript/Node.js and Python SDKs for the HydraDB platform.

## Installation

<CodeGroup>
  ```bash TypeScript / Node.js theme={"dark"}
  npm install @hydradb/sdk
  ```

  ```bash Python SDK theme={"dark"}
  pip install hydradb-sdk
  ```
</CodeGroup>

## Client setup

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

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

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

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

  ```python Python (Async) theme={"dark"}
  import os
  from hydra_db import AsyncHydraDB

  async_client = AsyncHydraDB(token=os.environ["HYDRA_DB_API_KEY"])
  ```
</CodeGroup>

<Info>
  **Python:** Both synchronous (`HydraDB`) and asynchronous (`AsyncHydraDB`) clients are available. They share an identical surface – choose based on your application's concurrency model.
</Info>

## Naming conventions

The REST API uses **snake\_case** for all request and response fields. The SDKs adapt this to native idioms:

|                     | Method naming | Parameter field naming | Example                                                       |
| ------------------- | ------------- | ---------------------- | ------------------------------------------------------------- |
| **Raw HTTP / cURL** | -             | snake\_case            | `tenant_id`, `sub_tenant_id`                                  |
| **TypeScript SDK**  | camelCase     | snake\_case            | method: `fullRecall()`, params: `tenant_id`, `sub_tenant_id`  |
| **Python SDK**      | snake\_case   | snake\_case            | method: `full_recall()`, params: `tenant_id`, `sub_tenant_id` |

<Info>
  **TypeScript SDK uses snake\_case for all parameter field names.** Only method names are camelCase (e.g., `client.recall.fullRecall({tenant_id: "...", max_results: 5})`). This applies to every request object passed to an SDK method.
</Info>

The TypeScript SDK transparently maps camelCase parameters to snake\_case on the wire. You can rely on your IDE's autocomplete and type checking – if a field is optional in the API, it's optional in the SDK.

## SDK method structure

SDK methods are grouped by capability, not by URL path. The mapping is mostly intuitive but **a few groups span multiple URL prefixes**:

| URL prefix            | SDK group       | Why                                           |
| --------------------- | --------------- | --------------------------------------------- |
| `/tenants/*`          | `client.tenant` | Tenant lifecycle                              |
| `/ingestion/*`        | `client.upload` | Content upload + verification                 |
| `/memories/*`         | `client.upload` | Memories share the upload group (add, delete) |
| `/recall/*`           | `client.recall` | All retrieval modes                           |
| `/list/*`, `/fetch/*` | `client.fetch`  | Browse and retrieve                           |
| `/knowledge/*`        | `client.data`   | Bulk knowledge delete                         |

Additional clients available: `client.key` for API key management, `client.ingestionPipeline` for pipeline operations, `client.graphHealth` for health checks, and `client.metrics` for metrics, `client.passthrough` for passthrough operations.

## Method reference

Method names map to the URL by removing the leading slash and replacing path separators:

### Tenants

| Method                            | Endpoint                      |
| --------------------------------- | ----------------------------- |
| `client.tenant.create()`          | `POST /tenants/create`        |
| `client.tenant.getInfraStatus()`  | `GET /tenants/infra/status`   |
| `client.tenant.monitor()`         | `GET /tenants/monitor`        |
| `client.tenant.getSubTenantIds()` | `GET /tenants/sub_tenant_ids` |
| `client.tenant.getTenantIds()`    | `GET /tenants/tenant_ids`     |
| `client.tenant.deleteTenant()`    | `DELETE /tenants/delete`      |

### Ingestion + Memories (under `upload`)

| Method                             | Endpoint                            |
| ---------------------------------- | ----------------------------------- |
| `client.upload.knowledge()`        | `POST /ingestion/upload_knowledge`  |
| `client.upload.verifyProcessing()` | `POST /ingestion/verify_processing` |
| `client.upload.addMemory()`        | `POST /memories/add_memory`         |
| `client.upload.deleteMemory()`     | `DELETE /memories/delete_memory`    |

### Recall

| Method                              | Endpoint                          |
| ----------------------------------- | --------------------------------- |
| `client.recall.fullRecall()`        | `POST /recall/full_recall`        |
| `client.recall.recallPreferences()` | `POST /recall/recall_preferences` |
| `client.recall.booleanRecall()`     | `POST /recall/boolean_recall`     |

### List + Fetch (under `fetch`)

| Method                                    | Endpoint                          |
| ----------------------------------------- | --------------------------------- |
| `client.fetch.listData()`                 | `POST /list/data`                 |
| `client.fetch.graphRelationsBySourceId()` | `GET /list/graph_relations_by_id` |
| `client.fetch.content()`                  | `POST /fetch/content`             |

### Knowledge Deletion

| Method                                                                                                                      | Endpoint                           |
| --------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
| `client.data.delete()`                                                                                                      | `POST /knowledge/delete_knowledge` |
| In TypeScript, snake\_case method names use camelCase: `client.tenant.getInfraStatus()`, `client.recall.fullRecall()`, etc. |                                    |

In Python, all method names use snake\_case: `client.tenant.get_infra_status()`, `client.recall.full_recall()`, etc.

### Additional clients

| Method                                    | Endpoint                            |
| ----------------------------------------- | ----------------------------------- |
| `client.graphHealth.getSuperNodes()`      | `GET /graph/super_nodes`            |
| `client.ingestionPipeline.ingestMemory()` | `POST /memories/ingestion_pipeline` |
| `client.metrics.getMetrics()`             | `GET /metrics`                      |
| `client.passthrough.fetch()`              | `POST /passthrough/fetch`           |

## Getting started

### Create a tenant

A tenant is an isolated workspace. Most organizations create one tenant total, with sub-tenants for users or teams. See [Multi-Tenant](/essentials/multi-tenant) for the full model.

<CodeGroup>
  ```typescript TypeScript SDK theme={"dark"}
  const response = await client.tenant.create({
    tenant_id: "my_first_tenant"
  });
  ```

  ```python Python SDK theme={"dark"}
  response = client.tenant.create(
      tenant_id="my_first_tenant",
      tenant_metadata_schema={"department": "string"},
  )
  ```

  ```python Python (Async) theme={"dark"}
  response = await async_client.tenant.create(tenant_id="my_first_tenant")
  ```
</CodeGroup>

Tenant creation is asynchronous. Poll [`getInfraStatus`](/api-reference/endpoint/infra-status) until provisioning completes:

<CodeGroup>
  ```typescript TypeScript SDK theme={"dark"}
  while (true) {
    const status = await client.tenant.getInfraStatus({
      tenant_id: "my_first_tenant"
    });
    const { graph_status, vectorstore_status } = status.infra;
    if (graph_status && vectorstore_status.every(Boolean)) break;
    await new Promise(r => setTimeout(r, 2000));
  }
  ```

  ```python Python SDK theme={"dark"}
  import time

  while True:
      status = client.tenant.get_infra_status(tenant_id="my_first_tenant")
      if (status.infra.graph_status
          and all(status.infra.vectorstore_status)):
          break
      time.sleep(2)
  ```
</CodeGroup>

### Ingest knowledge

Upload documents, app sources, or both via [`upload.knowledge`](/api-reference/endpoint/upload-knowledge):

<CodeGroup>
  ```typescript TypeScript SDK theme={"dark"}
  import { openAsBlob } from "node:fs";

  const result = await client.upload.knowledge({
    tenant_id: "my_first_tenant",
    files: [
      { path: "contract.pdf", filename: "contract.pdf", contentType: "application/pdf" },
      { path: "policy.pdf", filename: "policy.pdf", contentType: "application/pdf" },
    ],
    file_metadata: JSON.stringify([
      {
        file_id: "contract_q4",
        metadata: { category: "legal" },
        additional_metadata: { author: "Alice" }
      },
      {
        file_id: "policy_2025",
        metadata: { category: "legal" },
        additional_metadata: { author: "Bob" }
      }
    ])
  });
  ```

  ```python Python SDK theme={"dark"}
  import json

  with open("contract.pdf", "rb") as f1, open("policy.pdf", "rb") as f2:
      result = client.upload.knowledge(
          tenant_id="my_first_tenant",
          files=[
              ("contract.pdf", f1, "application/pdf"),
              ("policy.pdf", f2, "application/pdf"),
          ],
          file_metadata=json.dumps([
              {
                  "file_id": "contract_q4",
                  "metadata": {"category": "legal"},
                  "additional_metadata": {"author": "Alice"},
              },
              {
                  "file_id": "policy_2025",
                  "metadata": {"category": "legal"},
                  "additional_metadata": {"author": "Bob"},
              },
          ]),
      )
  ```
</CodeGroup>

For full options including app sources and forceful relations, see [Upload knowledge](/api-reference/endpoint/upload-knowledge).

### Add user memories

Store user preferences and conversation history via [`upload.addMemory`](/api-reference/endpoint/add-memory):

<CodeGroup>
  ```typescript TypeScript SDK theme={"dark"}
  // Plain text memory
  const result = await client.upload.addMemory({
    tenant_id: "my_first_tenant",
    memories: [
      {
        text: "User prefers detailed technical explanations and dark mode",
        infer: true,
        user_name: "Alex",
        metadata: { team: "engineering" },
        additional_metadata: { source: "onboarding" }
      }
    ]
  });

  // Conversation pairs
  const conversation = await client.upload.addMemory({
    tenant_id: "my_first_tenant",
    memories: [
      {
        user_assistant_pairs: [
          { user: "What are my preferences?", assistant: "You prefer dark mode." },
          { user: "How do I like reports?", assistant: "Weekly summaries." }
        ],
        infer: true,
        user_name: "Alex"
      }
    ]
  });
  ```

  ```python Python SDK theme={"dark"}
  # Plain text memory
  result = client.upload.add_memory(
      tenant_id="my_first_tenant",
      memories=[
          {
              "text": "User prefers detailed technical explanations and dark mode",
              "infer": True,
              "user_name": "Alex",
              "metadata": {"team": "engineering"},
              "additional_metadata": {"source": "onboarding"},
          }
      ],
  )

  # Conversation pairs
  conversation = client.upload.add_memory(
      tenant_id="my_first_tenant",
      memories=[
          {
              "user_assistant_pairs": [
                  {"user": "What are my preferences?", "assistant": "You prefer dark mode."},
                  {"user": "How do I like reports?", "assistant": "Weekly summaries."},
              ],
              "infer": True,
              "user_name": "Alex",
          }
      ],
  )
  ```
</CodeGroup>

<Info>
  **`infer` defaults to `false`.** Set `infer: true` for conversational content where you want HydraDB to extract implicit preferences. Use `infer: false` for content that should be stored verbatim.
</Info>

### Recall

Three retrieval modes for different use cases:

<CodeGroup>
  ```typescript TypeScript SDK theme={"dark"}
  // Hybrid recall over knowledge
  const knowledge = await client.recall.fullRecall({
    tenant_id: "my_first_tenant",
    query: "What are the pricing tiers?",
    mode: "thinking",
    graph_context: true
  });

  // Hybrid recall over user memories
  const prefs = await client.recall.recallPreferences({
    tenant_id: "my_first_tenant",
    sub_tenant_id: "user_alex",
    query: "What are this user's display preferences?",
    mode: "thinking"
  });

  // Exact-match search
  const exact = await client.recall.booleanRecall({
    tenant_id: "my_first_tenant",
    query: "ERROR_429 rate limit",
    operator: "and"
  });
  ```

  ```python Python SDK theme={"dark"}
  # Hybrid recall over knowledge
  knowledge = client.recall.full_recall(
      tenant_id="my_first_tenant",
      query="What are the pricing tiers?",
      mode="thinking",
      graph_context=True,
  )

  # Hybrid recall over user memories
  prefs = client.recall.recall_preferences(
      tenant_id="my_first_tenant",
      sub_tenant_id="user_alex",
      query="What are this user's display preferences?",
      mode="thinking",
  )

  # Exact-match search
  exact = client.recall.boolean_recall(
      tenant_id="my_first_tenant",
      query="ERROR_429 rate limit",
      operator="and",
  )
  ```
</CodeGroup>

For full parameter documentation, see [Recall](/api-reference/endpoint/recall-overview).

### Browse and inspect

<CodeGroup>
  ```typescript TypeScript SDK theme={"dark"}
  // List knowledge sources with pagination
  const sources = await client.fetch.listData({
    tenant_id: "my_first_tenant",
    kind: "knowledge",
    page: 1,
    page_size: 50
  });

  // Inspect graph relationships for a specific source
  const relations = await client.fetch.graphRelationsBySourceId({
    tenant_id: "my_first_tenant",
    source_id: "doc_12345"
  });

  // Fetch original file content or get a presigned URL
  const file = await client.fetch.content({
    tenant_id: "my_first_tenant",
    source_id: "doc_12345",
    mode: "url"
  });
  ```

  ```python Python SDK theme={"dark"}
  # List knowledge sources with pagination
  sources = client.fetch.list_data(
      tenant_id="my_first_tenant",
      kind="knowledge",
      page=1,
      page_size=50,
  )

  # Inspect graph relationships for a specific source
  relations = client.fetch.graph_relations_by_source_id(
      tenant_id="my_first_tenant",
      source_id="doc_12345",
  )

  # Fetch original file content or get a presigned URL
  file = client.fetch.content(
      tenant_id="my_first_tenant",
      source_id="doc_12345",
      mode="url",
  )
  ```
</CodeGroup>

## Type safety

<Check>
  Both SDKs are fully typed:

  * **Autocomplete** for all method names and parameters
  * **Type checking** for request and response objects
  * **Inline documentation** for each parameter
  * **Compile-time validation** for required vs optional fields
</Check>

The SDKs provide exact type parity with the API specification:

* **Request parameters** – every field documented in the API reference is reflected in method signatures
* **Response objects** – return types match the JSON schema for each endpoint
* **Error types** – exception structures mirror error response formats
* **Nested objects** – complex parameters and responses keep their full structure

## Error handling

Both SDKs throw exceptions for non-2xx responses. Error objects expose the API's `detail` payload:

<CodeGroup>
  ```typescript TypeScript SDK theme={"dark"}
  import { HydraDBError } from "@hydradb/sdk";

  try {
    const result = await client.recall.fullRecall({
      tenant_id: "my_first_tenant",
      query: "..."
    });
  } catch (error) {
    if (error instanceof HydraDBError) {
      const code = error.body?.detail?.error_code;

      if (code === "TENANT_NOT_FOUND") {
        // Handle missing tenant
      } else if (error.statusCode === 429) {
        // Rate limit - retry with backoff
      } else {
        throw error;
      }
    } else {
      throw error;
    }
  }
  ```

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

  try:
      result = client.recall.full_recall(
          tenant_id="my_first_tenant",
          query="...",
      )
  except HydraDBError as e:
      code = e.error_code
      status = e.status_code

      if code == "TENANT_NOT_FOUND":
          # Handle missing tenant
          pass
      elif status == 429:
          # Rate limit - retry with backoff
          pass
      else:
          raise
  ```
</CodeGroup>

For the full list of error codes and retry patterns, see [Error Responses](/api-reference/error-responses).

## IDE-driven discovery

Whether you're using TypeScript, Python, VS Code, PyCharm, or any modern IDE, the workflow is the same:

1. Type the method name → see all available methods
2. Open the parentheses → see all required and optional parameters
3. Press `Cmd+Space` (macOS) or `Ctrl+Space` (Windows/Linux) → get inline documentation

This works because the SDKs are fully typed with comprehensive parameter docs.

## Related sections

* [API Reference](/api-reference) – complete endpoint documentation
* [Error Responses](/api-reference/error-responses) – HTTP codes, error codes, retry patterns
* [Quickstart](/get-started/quickstart) – build your first integration in five minutes
