> ## Documentation Index
> Fetch the complete documentation index at: https://cortex-e852fafe-docs-pro-2457-api-response-cleanup.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# SDKs: Python and TypeScript

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

The SDKs wrap every endpoint in the [API Reference](/api-reference/v2) with typed methods and IDE autocomplete. They automatically set the `API-Version: 2` header on every request, so you do not need to send it manually.

## Installation

The SDKs ship as major-version bumps of the same packages used for v1. Pin to `>=2.0.0` to use the latest method surface.

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

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

## Client setup

<CodeGroup>
  ```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"])
  ```

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

  const client = new HydraDBClient({
    token: process.env.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, and the Python SDK preserves those field names for request/response objects. TypeScript camelCases multi-word **method names** and all request and response **fields**.

| | Method naming | Parameter field naming | Example |
| - | - | - | - |
| **Raw HTTP / cURL** | N/A | snake\_case | `database`, `collection`, `query_by` |
| **Python SDK** | snake\_case | snake\_case | method: `query()`, params: `database`, `query_by` |
| **TypeScript SDK** | camelCase for multi-word methods | camelCase | method: `query()`, params: `database`, `queryBy` |

<Info>
  **Use camelCase request fields in TypeScript** (e.g., `client.context.list({ database: "...", pageSize: 50 })`). The SDK converts them to snake\_case on the wire and silently drops keys it does not recognize, such as `page_size: 50`. JSON-string values (`documentMetadata`, `memories`, `appKnowledge`) and free-form maps (`metadataFilters`) are sent as written, so keep snake\_case inside them.

  The Python SDK also uses snake\_case throughout (e.g., `client.context.list(database="...", page_size=50)`).

  `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.
</Info>

## SDK method structure

SDK methods are grouped under top-level namespaces, one per `/api-reference/v2` group:

| URL prefix | SDK group | Purpose |
| - | - | - |
| `/context/*` and `/context` | `client.context` | Ingest, status, fetch, list, delete, metadata updates, graph relations, subgraph |
| `/query` | `client.query` | Unified retrieval over knowledge, memories, or both |
| `/feedback` | `client.feedback` | Feedback on query results |
| `/databases/*` and `/databases` | `client.databases` | Create, list, delete, status, collections, stats, collection deletion, metadata schema. Legacy `/tenants/*` paths remain as deprecated aliases. |
| `/connectors/*` and `/connectors` | `client.connectors` | Create, configure, sync, and manage app connectors. `GET /connectors/providers` is `client.list_providers()`. |
| `/webhooks/indexing*` | `client.webhooks` | Register, inspect, test, and delete the indexing webhook; list and retry deliveries |

## Method reference

### Context

`client.context.*` covers every flow around content lifecycle: document uploads, app sources, memories, polling, fetching, listing, deletion, and graph inspection.

| Method | Endpoint |
| - | - |
| `client.context.ingest()` | `POST /context/ingest` |
| `client.context.status()` | `GET /context/status` |
| `client.context.inspect()` | `GET /context/inspect` |
| `client.context.list()` | `POST /context/list` |
| `client.context.delete()` | `DELETE /context` |
| `client.context.relations()` | `GET /context/relations` |
| `client.context.update_source_metadata()` | `PATCH /context/{id}/metadata` |
| `client.context.subgraph()` | `GET /context/{id}/subgraph` |

### Query

A single method covers all retrieval. Pick `type` (`"knowledge"`, `"memory"`, `"all"`) and `query_by` (`"hybrid"`, `"text"`) to control behavior.

| Method | Endpoint |
| - | - |
| `client.query()` | `POST /query` |

### Feedback

| Method | Endpoint |
| - | - |
| `client.feedback.submit()` | `POST /feedback` |

### Databases

| Method | Endpoint |
| - | - |
| `client.databases.create()` | `POST /databases` |
| `client.databases.list()` | `GET /databases` |
| `client.databases.delete()` | `DELETE /databases` |
| `client.databases.status()` | `GET /databases/status` |
| `client.databases.collections()` | `GET /databases/collections` |
| `client.databases.stats()` | `GET /databases/stats` |
| `client.databases.delete_collection()` | `DELETE /databases/collections` |
| `client.databases.update_metadata_schema()` | `PATCH /databases/{database}/metadata-schema` |
| `client.databases.get_metadata_schema()` | `GET /databases/{database}/metadata-schema` |

### Connectors

| Method | Endpoint |
| - | - |
| `client.list_providers()` | `GET /connectors/providers` |
| `client.connectors.create()` | `POST /connectors` |
| `client.connectors.discover()` | `GET /connectors/{id}/discover` |
| `client.connectors.configure()` | `POST /connectors/{id}/configure` |
| `client.connectors.status()` | `GET /connectors/{id}/status` |
| `client.connectors.list()` | `GET /connectors` |
| `client.connectors.get()` | `GET /connectors/{id}` |
| `client.connectors.list_resources()` | `GET /connectors/{id}/resources` |
| `client.connectors.sync()` | `POST /connectors/{id}/sync` |
| `client.connectors.pause()` | `POST /connectors/{id}/pause` |
| `client.connectors.resume()` | `POST /connectors/{id}/resume` |
| `client.connectors.update()` | `PATCH /connectors/{id}` |
| `client.connectors.update_resource_acl()` | `PATCH /connectors/{id}/resources/{resource_id}` |
| `client.connectors.create_resource()` | `POST /connectors/{id}/resources` |
| `client.connectors.delete_resource()` | `DELETE /connectors/{id}/resources/{resource_id}` |
| `client.connectors.delete()` | `DELETE /connectors/{id}` |

### Webhooks

| Method | Endpoint |
| - | - |
| `client.webhooks.register()` | `POST /webhooks/indexing` |
| `client.webhooks.get()` | `GET /webhooks/indexing` |
| `client.webhooks.delete()` | `DELETE /webhooks/indexing` |
| `client.webhooks.test()` | `POST /webhooks/indexing/test` |
| `client.webhooks.list_deliveries()` | `GET /webhooks/indexing/deliveries` |
| `client.webhooks.get_delivery()` | `GET /webhooks/indexing/deliveries/{delivery_id}` |
| `client.webhooks.retry_delivery()` | `POST /webhooks/indexing/deliveries/{delivery_id}/retry` |

The tables above use the Python method names. TypeScript camelCases multi-word ones (`list_resources()` is `listResources()`).

## Migrating from v1

The v2 SDKs are a deliberate consolidation. A few common v1 to v2 method swaps:

| v1 | v2 |
| - | - |
| `client.upload.knowledge(...)` | `client.context.ingest(type="knowledge", ...)` |
| `client.upload.addMemory(...)` | `client.context.ingest(type="memory", ...)` |
| `client.upload.verifyProcessing(...)` | `client.context.status(...)` |
| `client.search.fullRecall(...)` | `client.query(type="knowledge", ...)` |
| `client.search.recallPreferences(...)` | `client.query(type="memory", ...)` |
| `client.search.booleanRecall(...)` | `client.query(query_by="text", ...)` |
| `client.fetch.listData(...)` | `client.context.list(...)` |
| `client.fetch.graphRelationsBySourceId(...)` | `client.context.relations(...)` |
| `client.fetch.content(...)` | `client.context.inspect(...)` |
| `client.data.delete(...)` | `client.context.delete(type="knowledge", ...)` |
| `client.upload.deleteMemory(...)` | `client.context.delete(type="memory", ...)` |
| `client.tenant.create(...)` | `client.databases.create(...)` |
| `client.tenant.getTenantIds(...)` | `client.databases.list(...)` |
| `client.tenant.deleteTenant(...)` | `client.databases.delete(...)` |
| `client.tenant.getInfraStatus(...)` | `client.databases.status(...)` |
| `client.tenant.getSubTenantIds(...)` | `client.databases.collections(...)` |
| `client.tenant.monitor(...)` | `client.databases.stats(...)` |

The v1 SDK methods remain available on the `<2.0.0` releases of `hydradb-sdk` / `@hydradb/sdk` for as long as v1 routes are supported.

## Getting started

### Create a database

A database is an isolated workspace. B2C apps usually use one database with a collection per user; B2B apps use one database per customer. See [Multi-tenancy](/essentials/v2/multi-tenant) for the full model.

<CodeGroup>
  ```python Python SDK theme={"dark"}
  response = client.databases.create(
      database="my_first_database",
      database_metadata_schema=[
          {"name": "department", "data_type": "VARCHAR"},
      ],
  )
  ```

  ```python Python (Async) theme={"dark"}
  import asyncio


  async def create_database():
      return await async_client.databases.create(database="my_first_database")


  response = asyncio.run(create_database())
  ```

  ```typescript TypeScript SDK theme={"dark"}
  const response = await client.databases.create({
    database: "my_first_database",
    databaseMetadataSchema: [
      { name: "department", dataType: "VARCHAR" },
    ],
  });
  ```
</CodeGroup>

Database creation is asynchronous. Poll [`databases.status`](/api-reference/v2/endpoint/tenant-status) until provisioning completes:

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

  while True:
      status = client.databases.status(database="my_first_database")
      if status.data.infra.ready_for_ingestion:
          break
      time.sleep(2)
  ```

  ```typescript TypeScript SDK theme={"dark"}
  while (true) {
    const status = await client.databases.status({
      database: "my_first_database",
    });
    // Response fields are optional in the TypeScript types.
    if (status.data?.infra?.readyForIngestion) break;
    await new Promise((r) => setTimeout(r, 2000));
  }
  ```
</CodeGroup>

### Ingest knowledge

Upload documents, app sources, or both in one call:

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

  with open("contract.pdf", "rb") as f1, open("policy.pdf", "rb") as f2:
      result = client.context.ingest(
          type="knowledge",
          database="my_first_database",
          documents=[
              ("contract.pdf", f1, "application/pdf"),
              ("policy.pdf", f2, "application/pdf"),
          ],
          document_metadata=json.dumps([
              {
                  "id": "contract_q4",
                  "metadata": {"department": "legal"},
                  "additional_metadata": {"author": "Alice"},
              },
              {
                  "id": "policy_2025",
                  "metadata": {"department": "legal"},
                  "additional_metadata": {"author": "Bob"},
              },
          ]),
      )
  ```

  ```typescript TypeScript SDK theme={"dark"}
  const result = await client.context.ingest({
    type: "knowledge",
    database: "my_first_database",
    documents: [
      { path: "contract.pdf", filename: "contract.pdf", contentType: "application/pdf" },
      { path: "policy.pdf", filename: "policy.pdf", contentType: "application/pdf" },
    ],
    documentMetadata: JSON.stringify([
      {
        id: "contract_q4",
        metadata: { department: "legal" },
        additional_metadata: { author: "Alice" },
      },
      {
        id: "policy_2025",
        metadata: { department: "legal" },
        additional_metadata: { author: "Bob" },
      },
    ]),
  });
  ```
</CodeGroup>

For app sources (Slack, Notion, Gmail, webpages), pass `app_knowledge` instead of `documents`. You can combine both in a single request.

### Add user memories

Use the same `ingest` method with `type: "memory"`:

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

  result = client.context.ingest(
      type="memory",
      database="my_first_database",
      collection="user_alex",
      memories=json.dumps([
          {
              "text": "User prefers detailed technical explanations and dark mode",
              "infer": True,
              "user_name": "Alex",
              "metadata": {"team": "engineering"},
              "additional_metadata": {"source": "onboarding"},
          }
      ]),
  )
  ```

  ```typescript TypeScript SDK theme={"dark"}
  const result = await client.context.ingest({
    type: "memory",
    database: "my_first_database",
    collection: "user_alex",
    memories: JSON.stringify([
      {
        text: "User prefers detailed technical explanations and dark mode",
        infer: true,
        user_name: "Alex",
        metadata: { team: "engineering" },
        additional_metadata: { source: "onboarding" },
      },
    ]),
  });
  ```
</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>

### Verify processing

Poll `context.status` with the IDs from the ingest response:

<CodeGroup>
  ```python Python SDK theme={"dark"}
  status = client.context.status(
      database="my_first_database",
      ids=["contract_q4", "policy_2025"],
  )
  ```

  ```typescript TypeScript SDK theme={"dark"}
  const status = await client.context.status({
    database: "my_first_database",
    ids: ["contract_q4", "policy_2025"],
  });
  ```
</CodeGroup>

Wait until each `indexing_status` is `completed` (or `graph_creation` if you don't need full graph traversal). See [Context Overview](/api-reference/v2/endpoint/sources-overview) for the full status pipeline.

### Query

A single method covers every retrieval pattern: switch `type` and `query_by` to control behavior:

<CodeGroup>
  ```python Python SDK theme={"dark"}
  # Knowledge over documents
  knowledge = client.query(
      database="my_first_database",
      query="What are the pricing tiers?",
      type="knowledge",
      query_by="hybrid",
      mode="thinking",
      graph_context=True,
  )

  # Personalized: knowledge and this user's memories, from the same collection
  personalized = client.query(
      database="my_first_database",
      collection="user_alex",
      query="What are the pricing tiers, and how should I explain them to this user?",
      type="all",
      query_by="hybrid",
      mode="thinking",
  )

  # Exact-phrase BM25 search
  exact = client.query(
      database="my_first_database",
      query="ERROR_429 rate limit",
      type="knowledge",
      query_by="text",
      operator="phrase",
  )
  ```

  ```typescript TypeScript SDK theme={"dark"}
  // Knowledge over documents
  const knowledge = await client.query({
    database: "my_first_database",
    query: "What are the pricing tiers?",
    type: "knowledge",
    queryBy: "hybrid",
    mode: "thinking",
    graphContext: true,
  });

  // Personalized: knowledge and this user's memories, from the same collection
  const personalized = await client.query({
    database: "my_first_database",
    collection: "user_alex",
    query: "What are the pricing tiers, and how should I explain them to this user?",
    type: "all",
    queryBy: "hybrid",
    mode: "thinking",
  });

  // Exact-phrase BM25 search
  const exact = await client.query({
    database: "my_first_database",
    query: "ERROR_429 rate limit",
    type: "knowledge",
    queryBy: "text",
    operator: "phrase",
  });
  ```
</CodeGroup>

For the full parameter reference, see [Query: Overview](/api-reference/v2/endpoint/query-overview).

### Browse, fetch, and inspect

<CodeGroup>
  ```python Python SDK theme={"dark"}
  # List knowledge sources with pagination
  sources = client.context.list(
      database="my_first_database",
      type="knowledge",
      page=1,
      page_size=50,
  )

  # Inspect graph relationships for a specific source
  relations = client.context.relations(
      database="my_first_database",
      id="doc_12345",
  )

  # Inspect original source content or get a presigned URL
  file = client.context.inspect(
      database="my_first_database",
      id="doc_12345",
      mode="url",
  )

  # Delete a knowledge source
  client.context.delete(
      type="knowledge",
      database="my_first_database",
      ids=["doc_12345"],
  )
  ```

  ```typescript TypeScript SDK theme={"dark"}
  // List knowledge sources with pagination
  const sources = await client.context.list({
    database: "my_first_database",
    type: "knowledge",
    page: 1,
    pageSize: 50,
  });

  // Inspect graph relationships for a specific source
  const relations = await client.context.relations({
    database: "my_first_database",
    id: "doc_12345",
  });

  // Inspect original source content or get a presigned URL
  const file = await client.context.inspect({
    database: "my_first_database",
    id: "doc_12345",
    mode: "url",
  });

  // Delete a knowledge source
  await client.context.delete({
    type: "knowledge",
    database: "my_first_database",
    ids: ["doc_12345"],
  });
  ```
</CodeGroup>

## Response envelope

All responses, except connector endpoints and `PATCH /databases/{database}/metadata-schema`, are wrapped in a consistent envelope:

```json theme={"dark"}
{
  "success": true,
  "data": {
    "message": "Success"
  },
  "error": null,
  "meta": {
    "request_id": "<uuid>",
    "latency_ms": 12.3
  }
}
```

The SDKs return the full envelope. Read the payload from the `data` field (e.g. `response.data`); on failure the SDK raises a typed exception instead of returning an envelope with `error` populated.

## Error handling

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

<CodeGroup>
  ```python Python SDK theme={"dark"}
  from hydra_db.core.api_error import ApiError
  from hydra_db.errors import NotFoundError

  try:
      result = client.query(
          database="my_first_database",
          query="...",
          type="knowledge",
      )
  except NotFoundError:
      # Handle missing database / resource (HTTP 404)
      pass
  except ApiError as exc:
      if exc.status_code == 429:
          # Rate limit: retry with backoff
          pass
      else:
          raise
  ```

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

  try {
    const result = await client.query({
      database: "my_first_database",
      query: "...",
      type: "knowledge",
    });
  } catch (error) {
    if (error instanceof HydraDBError) {
      // error.body is typed `unknown`; it holds the raw snake_case error envelope.
      const code = (error.body as { error?: { code?: string } } | undefined)?.error?.code;

      if (code === "DATABASE_NOT_FOUND") {
        // Handle missing database
      } else if (error.statusCode === 429) {
        // Rate limit: retry with backoff
      } else {
        throw error;
      }
    } else {
      throw error;
    }
  }
  ```
</CodeGroup>

<Info>
  The Python SDK raises a typed exception per status from `hydra_db.errors`. Each subclasses `ApiError` and carries `status_code`, `headers`, and the parsed response `body`, from which you can read `body["error"]["code"]`.

  Not every status has its own class: `401` and `429` do not. Catch the base `ApiError` and branch on `status_code`, as above, whenever you need to handle those.
</Info>

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

## Related sections

* [API Reference](/api-reference/v2): complete endpoint documentation
* [Error Responses](/api-reference/v2/error-responses): HTTP codes, error codes, retry patterns
* [Quickstart](/get-started/v2/quickstart): build your first integration in five minutes
* [Query](/essentials/v2/query): conceptual overview of `POST /query`
* [Knowledge](/essentials/v2/knowledge) and [Memories](/essentials/v2/memories): ingestion deep-dives


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.