@hydradb/sdk
v0.0.3
Published
The official TypeScript SDK for the Hydra DB platform.
Readme
Hydra DB TypeScript SDK
The official TypeScript SDK for the Hydra DB platform.
Hydra DB provides memory, knowledge ingestion, retrieval, graph context, and raw-vector workflows for AI applications.
Features
- Upload files into a tenant knowledge base
- Add free-form memories, markdown, and user/assistant conversation memories
- Verify ingestion status after upload
- Search indexed knowledge and memories
- Fetch uploaded source content and graph relations
- Manage tenants and API keys
- Store, search, filter, and delete raw embeddings
- Use passthrough requests for endpoints not yet wrapped by the SDK
Installation
npm install @hydradb/sdk
# or
yarn add @hydradb/sdk
# or
pnpm add @hydradb/sdkClient setup
import { HydraDBClient } from "@hydradb/sdk";
const client = new HydraDBClient({
token: process.env.HYDRA_DB_API_KEY,
});
const TENANT_ID = process.env.HYDRA_TENANT_ID ?? "my-company";
const SUB_TENANT_ID = process.env.HYDRA_SUB_TENANT_ID ?? "my-sub-tenant";The default API base URL is:
https://api.hydradb.comFor local development, pass baseUrl:
const localClient = new HydraDBClient({
token: process.env.HYDRA_DB_API_KEY,
baseUrl: "http://localhost:8080",
});Important tenant and sub-tenant rule
Use the same tenant_id and sub_tenant_id across upload, verify, recall, fetch, and delete calls.
If you upload with a sub_tenant_id and then verify or search without it, you may check a different namespace. That can make statuses look inconsistent, for example queued in one namespace and graph_creation in another.
Tenant management
A tenant is the top-level isolated database. A sub-tenant is an optional isolated collection inside a tenant.
Create a standard tenant
await client.tenant.create({
tenant_id: TENANT_ID,
});Create an embeddings tenant
Raw embedding APIs require a tenant created with is_embeddings_tenant: true and a fixed embedding dimension.
await client.tenant.create({
tenant_id: "my-embeddings-tenant",
is_embeddings_tenant: true,
embeddings_dimension: 1536,
});Create a tenant with metadata schema
await client.tenant.create({
tenant_id: TENANT_ID,
tenant_metadata_schema: [
{
name: "department",
data_type: "VARCHAR",
enable_match: true,
enable_dense_embedding: false,
enable_sparse_embedding: false,
},
],
});List sub-tenant IDs
const subTenants = await client.tenant.getSubTenantIds({
tenant_id: TENANT_ID,
});
console.log(subTenants.sub_tenant_ids);List tenant IDs
const tenants = await client.tenant.getTenantIds();
console.log(tenants.tenant_ids);Check tenant infrastructure status
const infraStatus = await client.tenant.getInfraStatus({
tenant_id: TENANT_ID,
});
console.log(infraStatus);Monitor tenant stats
const stats = await client.tenant.monitor({
tenant_id: TENANT_ID,
});
console.log(stats);Delete a tenant
Warning: this permanently deletes the tenant and its data.
await client.tenant.deleteTenant({
tenant_id: TENANT_ID,
});Upload knowledge
Use client.upload.knowledge() to upload files to the knowledge base.
The SDK sends a multipart request to:
POST /ingestion/upload_knowledgeUpload one file
const uploadResult = await client.upload.knowledge({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
files: [
{
path: "./report.pdf",
filename: "report.pdf",
contentType: "application/pdf",
},
],
upsert: true,
});
const sourceId = uploadResult.results?.[0]?.source_id;
console.log("source_id:", sourceId);
console.log("initial_status:", uploadResult.results?.[0]?.status);Upload multiple files
const uploadResult = await client.upload.knowledge({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
files: [
{
path: "./a.pdf",
filename: "a.pdf",
contentType: "application/pdf",
},
{
path: "./notes.txt",
filename: "notes.txt",
contentType: "text/plain",
},
],
upsert: true,
});
console.log(uploadResult.results);You can also pass buffers, blobs, streams, or a file object with metadata:
import { readFileSync } from "node:fs";
await client.upload.knowledge({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
files: [
{
data: readFileSync("./report.pdf"),
filename: "report.pdf",
contentType: "application/pdf",
},
],
});Upload with metadata
file_metadata must be a JSON string. The array length should match the files array length.
Supported metadata object fields:
file_id: optional custom source IDmetadata: tenant-level metadataadditional_metadata: document-level metadatarelations: forceful relations to other Cortex/Hydra source IDs
const fileMetadata = [
{
file_id: "doc_a",
metadata: { department: "sales" },
additional_metadata: { author: "Alice" },
},
{
file_id: "doc_b",
metadata: { department: "marketing" },
additional_metadata: { author: "Bob" },
relations: {
cortex_source_ids: ["doc_a"],
properties: { relation: "same_upload_batch" },
},
},
];
const uploadResult = await client.upload.knowledge({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
files: [
{
path: "./a.pdf",
filename: "a.pdf",
contentType: "application/pdf",
},
{
path: "./b.pdf",
filename: "b.pdf",
contentType: "application/pdf",
},
],
file_metadata: JSON.stringify(fileMetadata),
upsert: true,
});
console.log(uploadResult.results);Do not use old metadata keys such as id, tenant_metadata, or document_metadata inside file_metadata for file upload. The current upload endpoint expects file_id, metadata, and additional_metadata.
Upload app-generated knowledge
You can index app-generated source objects without uploading files by passing app_knowledge as a JSON string.
const appKnowledge = [
{
id: "app_source_1",
title: "CRM Account Note",
content: "Acme is interested in the enterprise plan.",
type: "crm_note",
tenant_metadata: { department: "sales" },
document_metadata: { source: "crm" },
},
];
await client.upload.knowledge({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
app_knowledge: JSON.stringify(appKnowledge),
upsert: true,
});app_sources still exists in the generated SDK as a deprecated alias, but new code should use app_knowledge.
Verify ingestion status
Upload can return queued immediately. That means the file was accepted by the ingestion pipeline, not that indexing is complete.
Use client.upload.verifyProcessing() with the returned source_id values.
const status = await client.upload.verifyProcessing({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
file_ids: ["source-id-1", "source-id-2"],
});
console.log(status.statuses);Current processing status values:
queued
processing
graph_creation
completed
success
erroredMeaning:
| Status | Meaning |
|---|---|
| queued | Upload accepted and waiting for processing |
| processing | Parsing, chunking, embedding, or indexing is running |
| graph_creation | Vector indexing is done, graph creation is still running |
| completed | Ingestion finished |
| success | Alias for completed |
| errored | Ingestion failed |
Poll until ingestion finishes
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
async function waitForIngestion(fileIds: string[]) {
while (true) {
const status = await client.upload.verifyProcessing({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
file_ids: fileIds,
});
const statuses = status.statuses;
console.log(statuses);
const failed = statuses.find((item) => item.indexing_status === "errored");
if (failed) {
throw new Error(failed.error_message ?? `Ingestion failed for ${failed.file_id}`);
}
const done = statuses.every((item) =>
item.indexing_status === "completed" || item.indexing_status === "success"
);
if (done) {
return statuses;
}
await sleep(5000);
}
}
const fileIds = uploadResult.results?.map((item) => item.source_id).filter(Boolean) ?? [];
await waitForIngestion(fileIds);Add memories
Use client.upload.addMemory() to add text, markdown, or conversation memories.
The SDK sends JSON to:
POST /memories/add_memoryAdd plain text memory
await client.upload.addMemory({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
upsert: true,
memories: [
{
source_id: "memory_001",
text: "User prefers detailed technical explanations.",
title: "User preference",
infer: true,
user_name: "John",
metadata: { category: "preference" },
additional_metadata: { source: "chat" },
},
],
});Add markdown memory
await client.upload.addMemory({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
upsert: true,
memories: [
{
source_id: "meeting_notes_001",
title: "Meeting Notes",
text: "# Meeting Notes\n\n- Budget approved\n- Launch planned for Q2",
is_markdown: true,
infer: false,
},
],
});Add user/assistant conversation memory
await client.upload.addMemory({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
upsert: true,
memories: [
{
source_id: "conversation_001",
user_assistant_pairs: [
{
user: "How do I like reports?",
assistant: "You prefer weekly summary reports with charts.",
},
],
infer: true,
user_name: "John",
custom_instructions: "Extract durable user preferences.",
},
],
});Delete a memory
await client.upload.deleteMemory({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
memory_id: "memory_001",
});Search and retrieval
Full recall
Hybrid semantic and keyword retrieval across indexed content.
const results = await client.recall.fullRecall({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
query: "What did the account notes say about Acme?",
max_results: 10,
mode: "fast",
alpha: 0.8,
recency_bias: 0,
graph_context: true,
});
console.log(results.chunks);
console.log(results.sources);alpha can be a number from 0.0 to 1.0, or the string "auto".
Recall preferences
Search user memory and preference data.
const preferences = await client.recall.recallPreferences({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
query: "report format preference",
max_results: 5,
});
console.log(preferences.chunks);Boolean recall
Keyword, phrase, and BM25-style search.
const boolResults = await client.recall.booleanRecall({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
query: "enterprise plan",
operator: "phrase",
search_mode: "sources",
max_results: 10,
});
console.log(boolResults.chunks);Supported operator values:
or
and
phraseSupported search_mode values:
sources
memoriesFetch and inspect data
List knowledge sources
const sources = await client.fetch.listData({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
kind: "knowledge",
page: 1,
page_size: 50,
});
console.log(sources.data);
console.log(sources.pagination);List memories
const memories = await client.fetch.listData({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
kind: "memories",
page: 1,
page_size: 50,
});
console.log(memories.data);Filter listed data
const filtered = await client.fetch.listData({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
kind: "knowledge",
filters: {
tenant_metadata: { department: "sales" },
document_metadata: { author: "Alice" },
source_fields: { type: "crm_note" },
},
});
console.log(filtered.data);Reduce list payload size
For kind: "knowledge", you can use include_fields to return only selected source fields.
const slimSources = await client.fetch.listData({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
kind: "knowledge",
include_fields: ["title", "document_metadata", "timestamp"],
});
console.log(slimSources.data);Fetch source content
const source = await client.fetch.content({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
source_id: "source-id-1",
mode: "content",
});
console.log(source);Supported mode values:
content
url
bothFetch graph relations
const relations = await client.fetch.graphRelationsBySourceId({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
source_id: "source-id-1",
is_memory: false,
limit: 10,
});
console.log(relations);If you omit source_id, the endpoint can return relations across the sub-tenant.
Delete data
Use client.data.delete() to delete one or more source IDs.
await client.data.delete({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
ids: ["source-id-1", "source-id-2"],
});Graph health
Fetch high-degree graph nodes for a tenant or sub-tenant.
const superNodes = await client.graphHealth.getSuperNodes({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
degree_threshold: 50,
limit: 20,
});
console.log(superNodes);Raw embeddings
Raw embedding APIs are for bring-your-own-vector workflows. They require an embeddings tenant.
Insert raw embeddings
await client.embeddings.insert({
tenant_id: "my-embeddings-tenant",
sub_tenant_id: SUB_TENANT_ID,
upsert: true,
embeddings: [
{
source_id: "doc_001",
metadata: { category: "finance", year: 2026 },
embeddings: [
{
chunk_id: "doc_001_chunk_0",
embedding: [0.1, 0.2, 0.3],
},
],
},
],
});The vector length must match the embeddings_dimension used when creating the embeddings tenant.
Search raw embeddings
const rawResults = await client.embeddings.search({
tenant_id: "my-embeddings-tenant",
sub_tenant_id: SUB_TENANT_ID,
query_embedding: [0.1, 0.2, 0.3],
limit: 10,
});
console.log(rawResults);Filter raw embeddings
const bySource = await client.embeddings.filter({
tenant_id: "my-embeddings-tenant",
sub_tenant_id: SUB_TENANT_ID,
source_id: "doc_001",
limit: 50,
});
console.log(bySource);const byChunks = await client.embeddings.filter({
tenant_id: "my-embeddings-tenant",
sub_tenant_id: SUB_TENANT_ID,
chunk_ids: ["doc_001_chunk_0"],
});
console.log(byChunks);Delete raw embeddings
await client.embeddings.delete({
tenant_id: "my-embeddings-tenant",
sub_tenant_id: SUB_TENANT_ID,
source_id: "doc_001",
});await client.embeddings.delete({
tenant_id: "my-embeddings-tenant",
sub_tenant_id: SUB_TENANT_ID,
chunk_ids: ["doc_001_chunk_0"],
});API key management
This endpoint is meant for an authenticated dashboard/admin context. Do not expose admin credentials in client-side applications.
const apiKey = await client.key.createApiKey({
owner: "[email protected]",
scopes: ["ingest", "query"],
env: "live",
prefix: "sk",
});
console.log(apiKey.full_api_key);Platform metrics
const metrics = await client.metricsMetricsGet();
console.log(metrics);Passthrough fetch
Use client.passthroughFetch() for API endpoints that are not yet wrapped by the generated SDK. It uses the SDK's configured auth headers, base URL, timeout, retry, and fetch settings.
const response = await client.passthroughFetch("/metrics", {
method: "GET",
});
const data = await response.json();
console.log(data);Error handling
import { HydraDBError, HydraDBTimeoutError } from "@hydradb/sdk";
try {
await client.upload.knowledge({
tenant_id: TENANT_ID,
sub_tenant_id: SUB_TENANT_ID,
files: [{ path: "./missing.pdf" }],
});
} catch (error) {
if (error instanceof HydraDBTimeoutError) {
console.error("Request timed out", error);
} else if (error instanceof HydraDBError) {
console.error("Hydra DB API error", error.statusCode, error.body);
} else {
console.error("Unexpected error", error);
}
}SDK method reference
| SDK method | API path | Description |
|---|---|---|
| client.tenant.create() | POST /tenants/create | Create a standard or embeddings tenant |
| client.tenant.getSubTenantIds() | GET /tenants/sub_tenant_ids | List sub-tenant IDs |
| client.tenant.getTenantIds() | GET /tenants/tenant_ids | List tenant IDs |
| client.tenant.getInfraStatus() | GET /tenants/infra/status | Check tenant infrastructure |
| client.tenant.monitor() | GET /tenants/monitor | Get tenant stats |
| client.tenant.deleteTenant() | DELETE /tenants/delete | Delete a tenant |
| client.upload.knowledge() | POST /ingestion/upload_knowledge | Upload files or app knowledge |
| client.upload.verifyProcessing() | POST /ingestion/verify_processing | Check ingestion status |
| client.upload.addMemory() | POST /memories/add_memory | Add memories |
| client.upload.deleteMemory() | DELETE /memories/delete_memory | Delete one memory |
| client.recall.fullRecall() | POST /recall/search | Hybrid semantic and keyword recall |
| client.recall.recallPreferences() | POST /recall/preferences | Search preferences/memories |
| client.recall.booleanRecall() | POST /recall/boolean | Boolean/BM25 recall |
| client.fetch.listData() | POST /fetch/list | List knowledge or memories |
| client.fetch.content() | POST /fetch/source | Fetch source content or URL |
| client.fetch.graphRelationsBySourceId() | GET /fetch/graph_relations | Fetch graph relations |
| client.data.delete() | POST /data/delete | Delete sources |
| client.graphHealth.getSuperNodes() | GET /graph_health/super_nodes | Fetch graph super nodes |
| client.embeddings.insert() | POST /embeddings/insert_raw_embeddings | Insert raw embeddings |
| client.embeddings.search() | POST /embeddings/search_raw_embeddings | Search raw embeddings |
| client.embeddings.filter() | POST /embeddings/filter_raw_embeddings | Filter raw embeddings |
| client.embeddings.delete() | DELETE /embeddings/delete | Delete raw embeddings |
| client.key.createApiKey() | POST /api_keys/create | Create API key |
| client.metricsMetricsGet() | GET /metrics | Platform metrics |
| client.passthroughFetch() | any path | Authenticated passthrough request |
Links
- Homepage: hydradb.com
- Documentation: docs.hydradb.com
- API Reference: docs.hydradb.com/api-reference/introduction
Support
If you have any questions or need help, reach out at [email protected].
