CortexDB Docs
Docs for CortexDB 0.12.8, the version running on CortexDB Cloud (api.cortexdb.ai).
SDKs & Clients

TypeScript SDK

Reference for cortexdbai — V1Client and typed errors.

The official TypeScript / JavaScript client for CortexDB v1. Single V1Client class, isomorphic (Node + browser).

npm install cortexdbai

Requires Node 18+ or any runtime with fetch + AbortController. npm package: cortexdbai Module: cortexdbai/v1. The SDK version is independent of the server version — see Versioning and SDK compatibility.

Install cortexdbai, not the bare cortexdb name

An unrelated, unaffiliated package squats the bare cortexdb name on npm (it self-describes as an "official" client but is not published by the CortexDB project). Always install cortexdbai.

The one-liner: V1Client.signup()

The shortest path from npm install to stored-and-retrieved memory:

import { V1Client } from "cortexdbai/v1";

const client = await V1Client.signup();
await client.experience(client.actor, {
  text: "hello memory",
  observedAt: new Date().toISOString(),
  idempotencyKey: "first-001",
});
const pack = await client.recall(client.actor, {
  query: "hello",
  diagnostics: "none",
});

signup() posts to /v1/auth/signup, gets a token/actor/scope, and returns a fully-wired client. Free tier — 7-day TTL, no email, no card.

Explicit construction

import { V1Client } from "cortexdbai/v1";

const client = new V1Client({
  apiUrl: "https://api-v1.cortexdb.ai",
  actor:  "user:alice",
  bearer: process.env.CORTEX_TOKEN!,
  timeoutMs: 30_000,
});
OptionDefaultNotes
apiUrl— (required)v1 surface URL.
actor— (required)Sent as X-Cortex-Actor; must match the token's sub.
bearerundefinedPASETO v4 public token — or, self-hosted, the server's CORTEX_API_KEY value. Optional only when the server runs with CORTEX_INSECURE_NO_AUTH=1.
timeoutMs60000Per-request timeout.
fetchglobalThis.fetchOverride for tracing / retries / custom transport.

Auth

const me = await client.whoami();
// → { caller, tenant_id, deployment_preset, effective_capabilities, token }

const minted = await client.mintToken({
  subject:    "user:alice",
  ttlSeconds: 3600,
  scopes:     ["org:acme/user:alice"],
});
// → { token: "v4.public...", expires_at: "2026-05-15T11:42:00Z" }

The capability list is returned under effective_capabilities (snake_case).

Write path

const r = await client.experience(
  "org:acme/user:alice",
  {
    text: "Acme upgraded to 200 seats.",
    role: "user",
    observedAt: "2026-05-15T10:42:00Z",
    idempotencyKey: "alice-chat-001",
  },
  { wait: "indexed" },   // optional: "captured" | "indexed" | "consolidated"
);

const bulk = await client.experienceBulk(
  "org:acme/user:alice",
  [
    { text: "Acme upgraded to 200 seats.", role: "user", observedAt: "2026-05-15T10:42:00Z", idempotencyKey: "k1" },
    { text: "Renewal is due in March.",    role: "user", observedAt: "2026-05-15T10:43:00Z", idempotencyKey: "k2" },
  ],
);

experienceBulk options are { wait? } only

The options object is { wait? } — there is no ordering field (passing one is a TS type error). Items are ingested in list order; encode temporal order in each item's observedAt. ordering is a REST body field, not an SDK option. Items are ExperienceItems: camelCase flattened fields (exactly one of text / jsonData / blobId / triple, plus optional role, observedAt, idempotencyKey, labels, modality, source); the SDK builds the snake_case wire envelope. Passing content / context is a type error and throws at runtime; ordering in the options is a type error too.

With wait omitted, a write returns captured (202) once the WAL append succeeds — not fully indexed; pass { wait: "indexed" } to block on indexing. For full envelope shape see Experience Envelope.

Read path

const pack = await client.recall("org:acme/user:alice", {
  view:    "holistic",
  query:   "What did we decide about Acme?",
  include: ["beliefs", "facts", "episodes"],
  budgets: { max_tokens: 4000 },
  temporal:{ natural: "last 30 days" },
});
// pack is a fully-typed StratifiedPack

const ans = await client.answer("org:acme/user:alice", {
  question: "Did Acme renew?",
  view:     "holistic",
  answerModel: "gpt-5.6-terra",   // echoed in diagnostics only; the configured model answers (see below)
  temporal: { natural: "last 30 days" },
});
// ans is a fully-typed AnswerResponse

answer_model is a cloud default

claude-opus-4-6 is the cloud default. Self-hosted, /v1/answer routes through the configured answer lane (CORTEX_ANSWER_*) and is disabled until that lane is set. The field is answerModel (camelCase; in plain JavaScript an answer_model key is dropped). The configured model answers and diagnostics.answer_model echoes the request (details). See Self-hosting defaults.

Layer reads

await client.events("org:acme/user:alice", { view: "local", limit: 50 });
await client.episodes("org:acme/user:alice", "local");
await client.facts("org:acme/user:alice");
await client.beliefs("org:acme/user:alice");
await client.beliefWhy("belief_01HX...");
await client.understanding("org:acme/user:alice");

await client.buildEpisodes("org:acme/user:alice");
await client.buildBeliefs("org:acme/user:alice");
await client.synthesize("org:acme/dept:eng", ["sales_process"]);

Facts / Beliefs need enrichment on self-host

On a content-only self-hosted instance the Facts and Beliefs layers stay empty — they are LLM-derived. See Self-hosting defaults.

Forget

await client.forget("org:acme/user:alice", {
  layers:   ["beliefs"],
  selector: { predicate: "is_likely_to_renew" },
  cascade:  "derived_only",
  reason:   "User retracted speculation",
});

The audit-note field is reason

ForgetParams = { layers, selector, cascade, confirmAll, reason, fromPreviewId }. The audit note field is reason, not audit_note.

A selector of { memory_ids: [...] } purges every listed id. If a storage backend fails mid-delete the server returns 502 FORGET_BACKEND_FAILED rather than reporting success — deletes are idempotent, so retry the same call.

The two SDKs' forget APIs differ

The TypeScript forget takes a selector object; the Python forget does not — it targets by flat keyword (predicate=, memory_ids=, about_subject=). See the Python SDK.

Audit + Admin

await client.audit({ actor: "user:alice", capability: "forget.gdpr", limit: 100 });
await client.layerStats();    // experimental

Typed errors

import {
  V1Error,
  V1APIError,
  V1AuthError,
  V1PolicyDeniedError,
  V1RateLimitError,
  V1NotConfiguredError,
  V1InvalidRequestError,
  V1ConnectionError,
  V1TimeoutError,
} from "cortexdbai/v1";

try {
  await client.experience("org:acme/user:alice", { ... });
} catch (e) {
  if (e instanceof V1PolicyDeniedError) {
    console.error(`policy denied: ${e.message}`);
  } else if (e instanceof V1RateLimitError) {
    await new Promise(r => setTimeout(r, 1000));
  } else {
    throw e;
  }
}

What 0.10.0 sees from the server

Checked with cortexdbai 0.10.0 against the current server (no client change is needed): a { wait: "indexed" } write that runs out of the server's wait budget rejects with V1APIError after about 30 s — before the client's own 60 s timeout — with status 408, errorCode WAIT_TIMEOUT, retriable true and details.status_url (details.event_ids from a bulk). The write is durable: poll its status, or re-send with the same idempotencyKey (see Experience). A forget or erasure answered 409 AUTHORIZATION_STATE_CHANGED is a retriable V1APIError, not V1PolicyDeniedError. The SDK has no method for GET /v1/events/{id}, so call the route directly to use its ?scope= filter.

Types

The SDK exports the full type set used by the v1 surface:

import type {
  StratifiedPack,
  AnswerResponse,
  ExperienceItem,
  RecallParams,
  AnswerParams,
  ForgetParams,
} from "cortexdbai/v1";
// Citation / Provenance are not exported (0.10.0); derive them if needed,
// e.g. type Citation = AnswerResponse["citations"][number];
// RecallDiagnostics and RecallItemDiagnostics are exported.

tsc --noEmit reports zero errors in the v1 surface — the SDK is suitable for strict TypeScript projects (strict: true, skipLibCheck: false).

Method index

Capabilities, chunks and language fields

  • version() and capabilities() (GET /v1/admin/version). Gate new request fields on the capability list: an older server refuses them with 422 INVALID_BODY.
  • eventChunks(eventId) — how a long event was chunked (GET /v1/admin/events/{id}/chunks).
  • Exported helpers matchedFragments(pack) and fragmentText(...) read the new matched_fragments spans of long documents in a recall pack.
  • ExperienceItem takes language, scripts, timezone and refersTo; RecallParams takes language, scripts and query_variants (snake_case, like the rest of RecallParams, which mirrors the REST body), and its temporal block takes natural_mode, timezone and refers_during.
  • deleteBlob(id, { missingOk: true }) tolerates a blob that's already gone.

Beyond the paths shown above, the client also exposes:

AreaMethods
Composecompose → POST /v1/compose
Bi-temporal recordsqueryClaims · claimHistory · listConflicts · getConflict · resolveConflict → Claims & Conflicts
Layer historybeliefHistory · conceptHistory
ErasureerasurePreview · forgetPreview → /v1/erasures
ImportimportData → /v1/import
BlobsuploadBlob · deleteBlob → /v1/blobs
Beliefs (write)createBelief
VocabulariespatchVocabulary → /v1/vocabularies
Auditaudit · auditChain · verifyAuditChain
Serverversion · capabilities → /v1/admin/version
DocumentseventChunks → GET /v1/admin/events/{id}/chunks
Write statuswriteStatus — "did my write land?" by idempotency key or event id

The TypeScript surface is not identical to Python

Beyond the obvious snake_case to camelCase shift, three methods are named differently: Python's audit_list, audit_chain_verify and update_vocabulary are audit, verifyAuditChain and patchVocabulary here.

Twelve Python methods have no TypeScript equivalent: erase (the execute step — TypeScript ships erasurePreview only), import_status, audit_verify, index_audit, close, and the retention / WAL operator methods (retention_*, wal_status, wal_compact). Use the REST endpoints for those. signup() is a static on V1Client, not an instance method.

On this page