Skip to content

TypeScript

The xmemory npm package is a lightweight TypeScript client for the xmemory API. Zero dependencies — it uses native fetch only.

For MCP-based integration, see the MCP guide. For framework-specific setup, see Mastra AI.

API key: To use xmemory APIs or integrations, you need an API key. Get one from the xmemory console. Copy and securely store the key. Never share your API key publicly.

The full method, data-type, enum, and exception listing is in the Reference at the bottom of this page.


Terminal window
npm install xmemory

import { XmemoryClient, SchemaType } from "xmemory";
// Connect (reads XMEM_API_URL and XMEM_API_KEY from env)
const xm = new XmemoryClient({ apiKey: "your-api-key" });
// Get a handle for an existing instance
const inst = xm.instance("your-instance-id");
// Write some information
await inst.write("Alice Johnson works at Acme Corp. Her email is alice@acme.com.");
await inst.write("Bob Lee is a designer at Globex. He joined last Monday.");
// Read it back
const result = await inst.read("What is Alice's email?");
console.log(result.reader_result);
// → {"answer": "alice@acme.com"}

Three options, depending on whether you want a health check:

import { XmemoryClient, xmemoryInstance } from "xmemory";
// Factory function — runs a health check automatically
const xm = await xmemoryInstance({ url: "https://api.xmemory.ai", apiKey: "..." });
// Static method — same behavior
const xm = await XmemoryClient.create({ url: "https://api.xmemory.ai", apiKey: "..." });
// Constructor — no health check
const xm = new XmemoryClient({ url: "https://api.xmemory.ai", apiKey: "..." });
Option Env var Default Description
apiKey XMEM_API_KEY undefined API key for authentication
url XMEM_API_URL https://api.xmemory.ai API base URL
timeoutMs 60000 Default request timeout in milliseconds

All three forms resolve apiKey and url from environment variables when not passed. Only xmemoryInstance() and XmemoryClient.create() perform a health check on construction.

Every method also accepts an options.timeoutMs (a per-call timeout in milliseconds that overrides the client default); it’s omitted from the per-method options in the Reference to keep them focused on the method-specific options.

Use xm.instance(id) to get a scoped handle for data operations:

const inst = xm.instance("your-instance-id");
// All data operations go through the handle
await inst.write("Carol is a senior engineer at Initech.");
const result = await inst.read("Who works at Initech?");
console.log(result.reader_result);

Send free-form text — xmemory extracts structured objects according to your schema and merges them into the knowledge graph.

const resp = await inst.write("Carol is a senior engineer at Initech.");
console.log(resp.write_id);
console.log(resp.changes); // what the write created / updated / deleted
console.log(resp.trace_id); // request trace id when available
console.log(resp.console_url); // this operation's trace in the console

write(), read(), writeAsync(), writeStatus() and extract() responses all carry console_url, a direct link to that operation’s trace in the console; every one except writeStatus() also carries trace_id, which is useful for correlating client calls with API logs. The link points at the call rather than at an individual record, and is null when the deployment has no console configured. Requires xmemory@3.6.0 or later.

Control the speed/accuracy tradeoff:

await inst.write("...", { extractionLogic: "deep" });
Value When to use
"fast" High-volume, low-stakes writes (default)
"deep" Important or complex information

For latency-sensitive code, enqueue a write and return immediately:

const { write_id } = await inst.writeAsync("Dave manages the London office.");
console.log(write_id); // use this to poll for completion

The result carries trace_id and console_url like every other call — see Writing.

Then check the status:

const status = await inst.writeStatus(write_id);
console.log(status.write_status);
// → "queued" | "processing" | "extracting" | "extracted" | "applying" | "completed" | "failed" | "not_found"

Do not call read immediately after writeAsync — the data may not be committed yet. Poll with writeStatus until "completed", or use write (synchronous) when you need to read right after.

When you already know exactly what to store, skip extraction: pass a WriteMutation[] instead of text (an empty array throws). Each mutation is a deterministic, LLM-free create / update / delete of an object or relation, applied in array order — later mutations may reference objects created earlier in the same batch.

const resp = await inst.write([
{
object_mutation: {
object_type: "person",
create: { key: { email: "carol@initech.com" }, values: { name: "Carol" } },
},
},
{
relation_mutation: {
relation_type: "works_at",
create: {
endpoints: [
{ object_name: "person", key: { email: "carol@initech.com" } },
{ object_name: "company", key: { name: "Initech" } },
],
},
},
},
]);
console.log(resp.changes); // exactly what was created / updated / deleted

An object_mutation carries exactly one of create / update / delete (enforced at compile time). key identifies an object by its user-defined primary-key fields, or (for update/delete) by { xuid: ... }. A null value in object values clears that field. A relation_mutation creates or deletes a relation through its endpoints; this caller-addressing format does not mean object primary keys are required for relations extracted from text. XMD v1 relations do not have scalar fields:

await inst.write([
{
object_mutation: {
object_type: "person",
update: { key: { email: "carol@initech.com" }, values: { role: null } },
},
},
]);

writeAsync accepts a WriteMutation[] identically — poll with writeStatus as usual. See the API reference for the full wire format, relation addressing (endpoints, allow_bulk_delete), and in-batch ordering rules.


Ask questions in natural language:

const resp = await inst.read("Who works at Acme Corp?");
console.log(resp.reader_result);
console.log(resp.trace_id);
// Plain-text answer (default)
const resp = await inst.read("What is Alice's email?", { readMode: "single-answer" });
// Structured objects and relations
const resp = await inst.read("Show all contacts", { readMode: "xresponse" });
// Raw SQL result sets
const resp = await inst.read("List all contacts", { readMode: "raw-tables" });

Restrict a query to a specific set of objects. Useful for multi-tenant systems, privacy controls, and compliance requirements.

import type { ReadScope } from "xmemory";
const scope: ReadScope = {
objects: [
{
type: "Person",
key: { name: "Alice Johnson" },
},
],
relations_scope: "no_relations",
};
const resp = await inst.read("What is their email?", { scope });
console.log(resp.reader_result);
const scope: ReadScope = {
objects: [
{ type: "Person", key: { email: "alice@example.com" } },
{ type: "Person", key: { email: "bob@example.com" } },
],
relations_scope: "all_relations",
};
const resp = await inst.read(
"Which projects do these people work on together?",
{ scope }
);
  • "no_relations" (default): Only the specified objects.
  • "all_relations": Include junction tables between objects.

admin.getSetupInstructions(instanceId) and instance.setupInstructions() both return an AgentSetupResult: how to reach the same memory from another agent surface, ordered most-likely-first. On either handle, because the MCP instance connection serves the same tool.

const setup = await inst.setupInstructions();
for (const surface of setup.surfaces) {
console.log(surface.label);
for (const step of surface.steps) console.log(" ", step.description, step.command ?? "");
}

The default SetupFormat.AGENT answers what do I run right now, here. SetupFormat.PROJECT also returns the files a team commits once:

import { SetupFormat } from "xmemory";
const setup = await inst.setupInstructions({ format: SetupFormat.PROJECT });
if (setup.format === SetupFormat.PROJECT) {
for (const f of setup.project?.fragments ?? []) {
console.log(f.path, f.merge); // a merge, never a file to overwrite
}
}

Read setup.format rather than assuming. A server older than that parameter ignores it, answers 200, and names no format at all — so undefined means this deployment predates the project rendering. That differs deliberately from the Python client, which applies an AGENT default in its model; there is no runtime normalization point here, and inventing one would report a format the server never claimed.

Nothing returned carries a credential: the steps tell a reader to sign in themselves, out of band, so an instance id stays an identifier rather than a key.

Advisory values — step.kind, fragment.merge, format — are widened to admit a value added to the server after this release, so an additive change does not become a breaking one. A step.kind you do not recognise is not something to execute.

import { XmemoryAPIError } from "xmemory";
try {
const resp = await inst.read("...", { scope });
} catch (e) {
if (e instanceof XmemoryAPIError && e.status === 400) {
console.error(`Invalid scope: ${e.message}`);
} else {
throw e;
}
}

For latency-sensitive code, enqueue a write and return immediately:

const { write_id } = await inst.writeAsync("Dave manages the London office.");
console.log(write_id); // use this to poll for completion

The result carries trace_id and console_url like every other call — see Writing.

Then check the status:

const status = await inst.writeStatus(write_id);
console.log(status.write_status);
// → "queued" | "processing" | "completed" | "failed" | "not_found"

Do not call read immediately after writeAsync — the data may not be committed yet. Poll with writeStatus until "completed", or use write (synchronous) when you need to read right after.


Preview what xmemory would extract from a piece of text, without storing anything:

const resp = await inst.extract("Dave manages the London office.");
console.log(resp.objects_extracted);
console.log(resp.trace_id);

Accepts the same extractionLogic option as write.


The describe() method returns the agent-facing tool descriptions for an instance, with its schema in schemaSummary. Use it to tell an LLM what tools are available and how to call them.

const desc = await inst.describe();
// Plain text — inject into a system prompt
console.log(desc.asText());
// Anthropic tool-use format
const tools = desc.asAnthropicTools();
// OpenAI function-calling format
const tools = desc.asOpenaiTools();

Results are cached locally for 5 minutes. To force a refresh (e.g. after updating the schema):

inst.clearDescribeCache();
const desc = await inst.describe();

asText() shows tools as method signatures by default. Pass { includeHttp: true } to also show HTTP method and path for raw REST callers.

describe() also carries what the instance is for:

desc.purpose; // what the memory is for (the instance description)
desc.ownerInstructions; // the standing preference set for it, verbatim
desc.usageBrief; // generated from the schema; null until generated

asText() includes the first two — the purpose under the instance line, the standing preference above the schema summary. usageBrief is left out of it, since it restates the schema summary already there; read the property if you want it.

Both are free text set by anyone holding edit permission on the instance, so asText() labels each with where it came from rather than presenting it as the library’s own words. Those labels state provenance; they are not a security boundary. If you inject this into a system prompt you are still handling text you do not control.


All management operations live on xm.admin: discovering clusters, generating schemas, creating instances, and evolving them safely.

const clusters = await xm.admin.listClusters();
const clusterId = clusters[0].id;

Pass a detailed plain-language memory description to the generator when that is easier than authoring XMD directly. Both paths are supported; the generator saves work while producing the same contract, whose descriptions fine-tune extraction and permit grounded derivation rather than merely defining domain terminology:

const schema = await xm.admin.generateSchema(
clusterId,
"Track contacts with name, email, company, and notes.",
);
console.log(schema.data_schema);

To evolve an existing schema (rename / remove / type change) rather than create one, retrieve the current XMD and use enhanceSchema — it returns a structured migration plan; see Schema evolution.

Create an instance from a generated schema:

import { SchemaType } from "xmemory";
const inst = await xm.admin.createInstance(
clusterId,
"contacts",
schemaYml,
SchemaType.YML,
{ description: "User contacts" },
);
// inst is a bound InstanceHandle — use inst.write(), inst.read(), etc.
await inst.write("Alice joined the team.");

For a purely additive change (new objects/fields/relations), update the schema directly — existing data is preserved:

await xm.admin.updateInstanceSchema(instanceId, newSchemaYml, SchemaType.YML);

List and delete instances:

const instances = await xm.admin.listInstances();
const info = await xm.admin.getInstance(instanceId);
await xm.admin.deleteInstance(instanceId);

An instance can carry metadata that shapes how agents connect to it and what they do with it. Use patchInstanceMetadata to set the advisory hints: every option is independent — omitting one leaves the stored value untouched, and passing null clears it.

import { AgentSurface, BindingTier } from "xmemory";
await xm.admin.patchInstanceMetadata(instanceId, {
// Advisory hints — they seed what a connect flow proposes, and grant nothing.
agentSurfaces: [AgentSurface.CLAUDE_CODE, AgentSurface.CODEX],
agentDefaultBindingTier: BindingTier.AUTOLOAD,
agentEngagementHints: ["a convention is learned or corrected"],
});

Concurrent edits to these three are last-writer-wins by design: they only seed what a connect flow proposes, so the loser of a race re-applies a suggestion. agent_owner_instructions is not like that.

Read it back from InstanceInfo. The hints come back as plain strings, so a value your server knows and your library version does not is returned rather than making the instance unreadable:

const info = await xm.admin.getInstance(instanceId);
info.agent_owner_instructions;
info.agent_surfaces; // e.g. ["claude_code", "codex"]
info.agent_default_binding_tier; // e.g. "autoload"

Set agentOwnerInstructions through updateInstanceMetadata, not patchInstanceMetadata. It is rendered to agents verbatim and a second writer edits it from the same screen, so a silently lost edit is a rule that stops being enforced — and updateInstanceMetadata is the only call that carries expectedOwnerInstructionsEpoch. Pass the epoch you read the value at and the losing save is refused instead of applied:

const info = await xm.admin.getInstance(instanceId);
await xm.admin.updateInstanceMetadata(instanceId, info.name, info.description ?? "", {
agentOwnerInstructions: `${info.agent_owner_instructions ?? ""}\nAlso: never paraphrase a rule.`,
expectedOwnerInstructionsEpoch: info.agent_owner_instructions_epoch,
});

Non-additive changes (rename / remove / type change) preserve data via a structured migration plan. Use enhanceSchema to get the plan, dryRunMigration to preview the DDL, then updateInstanceSchema to apply it. confirmDestructive: true is required for ops that drop data.

import yaml from "js-yaml";
const current = (await xm.admin.getInstanceSchema(instanceId)).data_schema;
// 1. Enhance — new schema + an executor-ready migration plan.
const enhanced = await xm.admin.enhanceSchema(
clusterId,
"Rename Person.mail to Person.email.",
yaml.dump(current),
);
console.log(enhanced.summary, enhanced.migration_plan?.ops);
const newYaml = yaml.dump(enhanced.data_schema);
// 2. Dry-run — preview the DDL, apply nothing.
const preview = await xm.admin.dryRunMigration(instanceId, newYaml, SchemaType.YML, {
migrationPlan: enhanced.migration_plan ?? undefined,
});
console.log(preview.statements);
// 3. Update — apply. A rename is non-destructive, so confirmDestructive stays false.
const info = await xm.admin.updateInstanceSchema(instanceId, newYaml, SchemaType.YML, {
migrationPlan: enhanced.migration_plan ?? undefined,
confirmDestructive: false,
});
console.log(info.migration_id, info.prior_version, "->", info.new_version);

Review applied migrations:

const page = await xm.admin.listMigrations(instanceId, { limit: 20 });
for (const r of page.items) console.log(r.id, r.source, r.prior_version, "->", r.new_version);
const detail = await xm.admin.getMigration(instanceId, page.items[0].id, { includeYaml: true });
console.log(detail.yaml_before, detail.yaml_after);

Migration ops are a discriminated union on op_type (MigrationPlan, AddField, RenameField, RemoveObject, …). ProposalItem.op and MigrationRecord.ops stay raw for forward compatibility — narrow them to MigrationOp when needed.

Suggestion engine (review → decide → apply)

Section titled “Suggestion engine (review → decide → apply)”

xmemory watches read traffic and, on demand, surfaces a single rolling proposal of schema improvements. The flow is three calls — never wrap them; the checkpoints are the point.

import type { DecisionInput } from "xmemory";
const inst = xm.instance(instanceId);
// 1. Review — proposal + optimistic-concurrency token.
const review = await inst.reviewSuggestions();
if (review.status === "evolution_in_progress") {
console.log(`Migration in flight; retry in ${review.retry_after_seconds}s`);
} else if (review.proposal) {
for (const item of review.proposal.items) {
console.log(item.item_fingerprint, item.rationale, item.op);
}
// 2. Decide — accept / reject / defer per item, in bulk.
const decisions: DecisionInput[] = review.proposal.items.map((i) => ({
item_fingerprint: i.item_fingerprint,
decision: "accept",
}));
const decided = await inst.decideSuggestions(review.proposal.proposal_version, decisions);
// 3. Apply — commit accepted decisions as one migration.
const applied = await inst.applyPendingDecisions(decided.next_proposal_version);
console.log(applied.status, applied.summary);
}

All errors throw XmemoryAPIError with optional .status (HTTP status), .code (structured error code, when the server returned one), .details, and .retryAfter (number of seconds parsed from the Retry-After header, or undefined).

import { XmemoryAPIError, XmemoryHealthCheckError } from "xmemory";
try {
const xm = await XmemoryClient.create({ apiKey: "..." });
} catch (e) {
if (e instanceof XmemoryHealthCheckError) {
console.error("Server unreachable:", e.message);
}
}
try {
await inst.write("...");
} catch (e) {
if (e instanceof XmemoryAPIError) {
console.error(`Error (HTTP ${e.status}): ${e.message}`);
}
}

The schema-evolution methods set .code to a structured error type you can match on — e.g. stale_proposal_version, destructive_confirmation_required, non_additive_change_requires_plan:

try {
await inst.applyPendingDecisions(token);
} catch (e) {
if (e instanceof XmemoryAPIError && e.code === "stale_proposal_version") {
const review = await inst.reviewSuggestions(); // re-review and retry
}
}

The same .code branching separates a spent allowance from a genuine rate limit — match on .code, not on the status:

try {
await inst.write("...");
} catch (e) {
if (e instanceof XmemoryAPIError) {
if (e.code === "QUOTA_EXCEEDED") {
// Plan/usage allowance exhausted; e.details.kind is
// "daily_quota_exceeded" or "monthly_quota_exceeded".
// Not retryable now — surface "limit reached / upgrade your plan".
// You may retry after e.details.retry_after_seconds (if not null).
} else if (e.code === "RATE_LIMITED") {
// Genuine rate limit — back off and retry, honoring Retry-After.
}
}
}

For both RATE_LIMITED (429) and a resettable QUOTA_EXCEEDED (402), use e.retryAfter — the seconds parsed from the Retry-After header (a number, or undefined when the header was absent) — as your backoff delay.


The complete method, data-type, enum, and exception listing. Methods are grouped by where they live; expand a row for its parameters, returns, and details.

Constructors and client-level accessors.

XmemoryClientCreate a client (no health check)
new XmemoryClient(options?)XmemoryClient
options.apiKeystring
API key for authentication. Falls back to XMEM_API_KEY.
options.urlstring
API base URL. Falls back to XMEM_API_URL. Defaults to https://api.xmemory.ai.
options.timeoutMsnumber
Default request timeout in milliseconds. Defaults to 60000.
XmemoryClient.createCreate a client with health check
XmemoryClient.create(options?)Promise<XmemoryClient>
options.apiKeystring
API key for authentication. Falls back to XMEM_API_KEY.
options.urlstring
API base URL. Falls back to XMEM_API_URL. Defaults to https://api.xmemory.ai.
options.timeoutMsnumber
Default request timeout in milliseconds. Defaults to 60000.
xmemoryInstanceFactory with health check (same as .create())
xmemoryInstance(options?)Promise<XmemoryClient>
options.apiKeystring
API key for authentication. Falls back to XMEM_API_KEY.
options.urlstring
API base URL. Falls back to XMEM_API_URL. Defaults to https://api.xmemory.ai.
options.timeoutMsnumber
Default request timeout in milliseconds. Defaults to 60000.
adminControl-plane accessor — clusters, schema generation, instances, schema evolution
xm.adminAdminNamespace

xm.admin — the receiver for the admin / control-plane methods (clusters, schema generation, instances, schema evolution).

instanceGet a handle for data operations on an instance (synchronous)
xm.instance(instanceId)InstanceHandle
instanceIdstringrequired
The instance to scope data operations to.

A bound InstanceHandle — call write / read / extract / … on it. (Synchronous; not a Promise.)

checkHealthThrows XmemoryHealthCheckError if the API is unreachable
xm.checkHealth()Promise<void>

Control-plane methods on xm.admin — clusters, schema generation, instances, and schema evolution.

listClustersList clusters
listClusters(options?)Promise<ClusterInfo[]>
options.idsstring[]
Filter to specific cluster IDs.

Each item is a ClusterInfo.

getClusterGet a cluster by ID
getCluster(clusterId, options?)Promise<ClusterInfo>
clusterIdstringrequired
The cluster to fetch.

A ClusterInfo.

generateSchemaGenerate a schema from a description
generateSchema(clusterId, schemaDescription, options?)Promise<GenerateSchemaResult>
clusterIdstringrequired
The cluster to generate the schema for.
schemaDescriptionstringrequired
Natural-language description of what to track.
options.currentYmlSchemastring
Existing schema to extend additively.

GenerateSchemaResult{ data_schema }.

enhanceSchemaEvolve a schema → new schema + migration plan
enhanceSchema(clusterId, schemaDescription, currentYmlSchema, options?)Promise<EnhanceSchemaResult>
clusterIdstringrequired
The cluster the instance belongs to.
schemaDescriptionstringrequired
Natural-language description of the change.
currentYmlSchemastringrequired
The current schema as YAML.

EnhanceSchemaResult{ data_schema, migration_plan: MigrationPlan | null, summary, warnings, repair_log }. Pass migration_plan to dryRunMigration / updateInstanceSchema.

createInstanceCreate an instance; resolves to a bound handle
createInstance(clusterId, name, schemaText, schemaType, options?)Promise<InstanceHandle>
clusterIdstringrequired
The cluster to create the instance in.
namestringrequired
Instance name.
schemaTextstringrequired
The schema source.
schemaTypeSchemaTyperequired
Schema format (SchemaType.YML or SchemaType.JSON).
options.descriptionstring
Instance description.
options.schemaDescriptionstring
Plain-language schema description.

A bound InstanceHandle for the new instance — read inst.id for its ID.

listInstancesList instances
listInstances(options?)Promise<InstanceInfo[]>
options.idsstring[]
Filter to specific instance IDs.

Each item is an InstanceInfo.

getInstanceGet an instance by ID
getInstance(instanceId, options?)Promise<InstanceInfo>
instanceIdstringrequired
The instance to fetch.

An InstanceInfo.

getInstanceSchemaGet an instance's schema
getInstanceSchema(instanceId, options?)Promise<InstanceSchemaInfo>
instanceIdstringrequired
The instance to fetch the schema for.

An InstanceSchemaInfo.

updateInstanceSchemaUpdate a schema; pass migrationPlan for non-additive changes
updateInstanceSchema(instanceId, schemaText, schemaType, options?)Promise<InstanceInfo>
instanceIdstringrequired
The instance to update.
schemaTextstringrequired
The new schema source.
schemaTypeSchemaTyperequired
Schema format (SchemaType.YML or SchemaType.JSON).
options.migrationPlanMigrationPlan
Executor-ready plan for a non-additive change.
options.confirmDestructiveboolean
Required for ops that drop data. Defaults to false.

An InstanceInfo — also carries migration_id, prior_version, new_version, migration_warnings when a migration ran.

Throws XmemoryAPIError with .code non_additive_change_requires_plan (no plan) or destructive_confirmation_required (drops data without confirmDestructive).

updateInstanceMetadataUpdate instance name and description
updateInstanceMetadata(instanceId, name, description, options?)Promise<InstanceInfo>
instanceIdstringrequired
The instance to update.
namestringrequired
New instance name.
descriptionstringrequired
New instance description.
options.agentOwnerInstructionsstring | null
Standing preference for how agents should use this instance. Omit to leave it as it is; pass null to clear it. Max 2000 characters.
options.expectedOwnerInstructionsEpochnumber
The agent_owner_instructions_epoch your edit was composed from; a save that raced someone else's edit is then refused rather than overwriting it.

The updated InstanceInfo. Omitting an option sends no such key at all, so a rename never disturbs the standing preference.

When expectedOwnerInstructionsEpoch is sent and the stored instructions changed after you read them, the save is refused rather than applied: XmemoryAPIError with .status 409 and .code INVALID_INPUT. Re-read the instance, reapply your change, and save again.

patchInstanceMetadataChange some metadata fields, leaving the rest alone
patchInstanceMetadata(instanceId, options?)Promise<InstanceInfo>
instanceIdstringrequired
The instance to update.
options.namestring
New instance name.
options.descriptionstring | null
New instance description (pass null to clear).
options.agentSurfacesreadonly string[] | null
Agent surfaces this instance is expected to be used from — see AgentSurface. Advisory: it orders the connect steps and grants nothing.
options.agentDefaultBindingTierstring | null
Default prominence for a binding — see BindingTier. Advisory.
options.agentEngagementHintsreadonly string[] | null
Short routing phrases, e.g. "a convention is learned or corrected". At most 16, each at most 200 characters.
options.agentOwnerInstructionsstring | null
Standing preference for how agents should use this instance. Max 2000 characters. Prefer updateInstanceMetadata for this field — it is the only call that accepts expectedOwnerInstructionsEpoch, so an edit sent here cannot be guarded against a concurrent one.

The updated InstanceInfo. Every option is independent: omit one and the stored value is untouched, pass null to clear it. This is the only call that accepts the three agent* hints; updateInstanceMetadata is the only one that accepts the epoch guard.

getSetupInstructionsHow to connect this instance on an agent surface, most likely first
getSetupInstructions(instanceId, options?)Promise<AgentSetupResult>
instanceIdstringrequired
The instance to describe connect steps for.
options.formatSetupFormatValue
SetupFormat.AGENT (default) returns the steps to run now. SetupFormat.PROJECT additionally returns the files a team commits once, so nobody sets the instance up by hand.

Answers "how do I also reach this memory somewhere else" — a desktop client, another editor, a teammate's machine — rather than how to reach it from here. The same payload the get_setup_instructions MCP tool serves and xmemcli instance setup prints.

Also on an instance handle as instance.setupInstructions(), without the id.

Carries no credential. The steps tell a reader to sign in themselves, out of band, so nothing returned is a secret and an instance id stays an identifier rather than a key.

Read result.format rather than assuming. A server older than the format parameter ignores it, answers 200, and names no format at all — so undefined is the signal that the deployment predates the project rendering. This differs from the Python client, which applies an AGENT default in its model; there is no runtime normalization point here, and inventing one would report a format the server never claimed.

Advisory values are widened rather than closed: step.kind, fragment.merge and format admit a value added to the server after this release, so an additive change does not become a breaking one. A step.kind you do not recognise is not something to execute.

deleteInstanceDelete an instance
deleteInstance(instanceId, options?)Promise<string[]>
instanceIdstringrequired
The instance to delete.

string[] — the deleted instance IDs.

dryRunMigrationPreview a migration's DDL
dryRunMigration(instanceId, schemaText, schemaType, options?)Promise<DryRunResult>
instanceIdstringrequired
The instance to preview against.
schemaTextstringrequired
The proposed schema source.
schemaTypeSchemaTyperequired
Schema format (SchemaType.YML or SchemaType.JSON).
options.migrationPlanMigrationPlan
Executor-ready plan for a non-additive change.
options.confirmDestructiveboolean
Required for ops that drop data. Defaults to false.

DryRunResult{ status, instance_id, current_version, statements: string[] (planned DDL), warnings, plan_summary: PlanSummary, requires_metadata_sync }.

listMigrationsList applied migrations (newest first)
listMigrations(instanceId, options?)Promise<ListMigrationsResult>
instanceIdstringrequired
The instance to list migrations for.
options.limitnumber
Maximum number of records to return. Defaults to 50.
options.beforeIdstring
Page cursor: pass the previous page's next_before_id.
options.includeYamlboolean
Include before/after YAML in each record.

ListMigrationsResult{ items: MigrationRecord[], next_before_id: string | null, has_more }.

getMigrationGet a single migration record
getMigration(instanceId, migrationId, options?)Promise<MigrationRecord>
instanceIdstringrequired
The instance the migration belongs to.
migrationIdstringrequired
The migration record to fetch.
options.includeYamlboolean
Include before/after YAML in the record.

MigrationRecord{ id, applied_at, source, prior_version, new_version, ops, ops_summary, yaml_before, yaml_after }.

Data-plane methods on a bound instance handle (xm.instance(id)) — writing, reading, extracting, describing, and the suggestion engine.

writePersist data — extract objects from text, or apply structured mutations
write(text | mutations, options?)Promise<WriteResult>
textstring
Free-form text to extract from. Pass either this or mutations.
mutationsWriteMutation[]
Ordered list of deterministic, LLM-free object create/update/delete and relation create/delete mutations — see Structured writes. An empty array throws.
options.extractionLogic"fast" | "deep"
Speed/accuracy tradeoff. Text form only. Defaults to "fast".
options.diffEngineboolean
Override the server's diff-engine setting for this write. Text form only.
write_idstring
ID of the committed write.
changesunknown
What the write did, grouped into created / updated / deleted.
trace_idstring | null
Correlate the call with API logs.
writeAsyncEnqueue a write; resolves with write_id immediately
writeAsync(text | mutations, options?)Promise<AsyncWriteResult>
textstring
Free-form text to extract from. Pass either this or mutations.
mutationsWriteMutation[]
Same structured-mutation input as write.
options.extractionLogic"fast" | "deep"
Speed/accuracy tradeoff. Text form only. Defaults to "fast".
options.diffEngineboolean
Override the server's diff-engine setting for this write. Text form only.
write_idstring
Use this to poll for completion with writeStatus.
writeStatusPoll the status of an async write
writeStatus(writeId, options?)Promise<WriteStatusResult>
writeIdstringrequired
The write to poll, from writeAsync.
write_idstring
Echoes the polled write ID.
write_statusWriteQueueStatus
Current queue status of the write.
error_detailstring | null
Failure detail when the write failed.
completed_atstring | null
When the write finished, when available.
readQuery the instance in natural language
read(query, options?)Promise<ReadResult>
querystringrequired
Natural-language question.
options.readMode"single-answer" | "raw-tables" | "xresponse"
Response shape. Defaults to "single-answer".
options.traceIdstring
Correlate the call with API logs.
reader_resultunknown
The answer, shaped by readMode (e.g. { answer } for single-answer).
trace_idstring | null
Correlate the call with API logs.
extractExtract objects without writing them
extract(text, options?)Promise<ExtractResult>
textstringrequired
Free-form text to extract from.
options.extractionLogic"fast" | "deep"
Speed/accuracy tradeoff. Defaults to "fast".
objects_extractedunknown
The objects that would be stored (objects / relations).
trace_idstring | null
Correlate the call with API logs.
describeGet agent-facing tool descriptions (cached 5 min)
describe(options?)Promise<DescribeResult>
options.includeHttpboolean
Also show HTTP method and path for raw REST callers.

DescribeResult — properties instanceId, instanceName, schemaSummary, tools, plus purpose (what this memory is for), ownerInstructions (the standing preference set for it, verbatim) and usageBrief (generated from the schema; null until generated); methods asText({ includeHttp? }), asAnthropicTools(), asOpenaiTools(). asText() includes the first two, each labelled with where it came from rather than as the library's own words, and leaves usageBrief out since it restates schemaSummary. (Class with camelCase props, unlike the snake_case wire results.)

clearDescribeCacheForce next describe() to fetch fresh data (synchronous)
clearDescribeCache()void
getSchemaGet this instance's schema
getSchema(options?)Promise<InstanceSchemaInfo>

An InstanceSchemaInfo.

setupInstructionsHow to connect *this* instance somewhere else, most likely surface first
setupInstructions(options?)Promise<AgentSetupResult>
options.formatSetupFormatValue
SetupFormat.AGENT (default) returns the steps to run now; SetupFormat.PROJECT also returns the files a team commits once.

The instance-scoped counterpart of admin.getSetupInstructions(), which takes an id. On both because the MCP instance connection serves the same tool, so a caller holding an instance handle need not reach through admin for it.

Computed from the instance's current metadata, so an owner who edits a surface hint expects the next call to show it.

reviewSuggestionsGet the rolling schema-improvement proposal
reviewSuggestions(options?)Promise<ReviewSuggestionsResult>
options.sessionIdstring
Session ID for tracing.

ReviewSuggestionsResult{ status, proposal: ConsolidatedProposal | null, retry_after_seconds }. When status is "evolution_in_progress", back off for retry_after_seconds — it's a status, not a thrown error.

decideSuggestionsRecord accept/reject/defer decisions in bulk
decideSuggestions(proposalVersion, decisions, options?)Promise<DecideSuggestionsResult>
proposalVersionstringrequired
Optimistic-concurrency token from reviewSuggestions.
decisionsDecisionInput[]required
Per-item decisions — { item_fingerprint, decision, edits? }.
options.sessionIdstring
Session ID for tracing.

DecideSuggestionsResult{ decisions_recorded, warnings, next_proposal_version }. Pass next_proposal_version to applyPendingDecisions.

Throws XmemoryAPIError with .code stale_proposal_version (token out of date) or invalid_decision_input.

applyPendingDecisionsApply accepted decisions as one migration
applyPendingDecisions(proposalVersion, options?)Promise<ApplyPendingDecisionsResult>
proposalVersionstringrequired
The next_proposal_version from decideSuggestions.
options.sessionIdstring
Session ID for tracing.

ApplyPendingDecisionsResult{ status ("ok" | "nothing_to_apply"), migration_id, prior_version, new_version, applied_items, summary, warnings, notes }.

Throws XmemoryAPIError with .code stale_proposal_version, dependency_closure_failed, or apply_failed.

Data types
ClusterInfoA cluster.
idstring
Cluster ID.
org_idstring
Owning organization ID.
namestring
Cluster name.
descriptionstring | null
Cluster description.
InstanceInfoAn instance's metadata.
idstring
Instance ID.
cluster_idstring
The cluster this instance belongs to.
namestring
Instance name.
descriptionstring | null
Instance description.
data_schemaobject | null
The instance's data schema.
agent_surfacesstring[] | null
Agent surfaces this instance is expected to be used from. Advisory; null means no opinion. Typed as plain strings, so a value newer than your library version is returned rather than making the instance unreadable.
agent_default_binding_tierstring | null
Default prominence for a binding (autoload / available). Advisory.
agent_engagement_hintsstring[] | null
Short routing phrases seeding when an agent should engage this instance.
agent_owner_instructionsstring | null
The standing preference set for how agents should use this instance, rendered verbatim wherever it is shown.
agent_owner_instructions_epochnumber
Which edit of the instructions above this response describes. Pass it back as expectedOwnerInstructionsEpoch to make an edit conflict-safe.
InstanceSchemaInfoAn instance's schema.
data_schemaobject
The instance's data schema.
GenerateSchemaResultA generated schema.
data_schemaobject
The produced schema.
EnhanceSchemaResultAn evolved schema plus an executor-ready migration plan.
data_schemaobject
The produced schema.
migration_planMigrationPlan | null
Reconciled plan; pass to updateInstanceSchema / dryRunMigration.
summarystring | null
Human-readable summary of the change.
warningsobject[]
Non-fatal reconciler observations.
repair_logobject[]
Auto-repairs applied to the LLM output.
DryRunResultPlanned migration DDL, with nothing applied.
status"ok"
Always "ok" for a successful dry run.
instance_idstring
The instance previewed against.
current_versionnumber
Current schema version.
statementsstring[]
The planned DDL statements.
warningsstring[]
Non-fatal warnings.
plan_summaryPlanSummary
Per-op-type breakdown.
requires_metadata_syncboolean
Whether a metadata sync is needed after applying.
MigrationRecordOne applied-migration history row.
idstring
Migration record ID.
applied_atstring
When the migration was applied (ISO-8601).
sourcestring
direct or suggestion_engine.
decided_bystring | null
Who decided the migration, if recorded.
prior_versionnumber
Schema version before.
new_versionnumber
Schema version after.
opsobject[]
The raw ops applied (kept raw for forward-compat).
ops_summaryPlanSummary
Per-op-type breakdown.
notesstring | null
Free-text notes.
yaml_beforestring | null
Schema YAML before (only when includeYaml: true).
yaml_afterstring | null
Schema YAML after (only when includeYaml: true).
ListMigrationsResultA page of migration records, newest first.
status"ok"
Always "ok".
instance_idstring
The instance listed.
The records on this page.
next_before_idstring | null
Pagination cursor; null at the tail.
has_moreboolean
Whether more pages follow.
ReviewSuggestionsResultThe rolling schema-improvement proposal (or an in-progress status).
status"ok" | "evolution_in_progress"
evolution_in_progress means a migration is in flight.
instance_idstring
The instance reviewed.
proposalConsolidatedProposal | null
The proposal when status is "ok".
retry_after_secondsnumber | null
Back-off hint when evolution_in_progress.
DecideSuggestionsResultResult of recording a batch of decisions.
status"ok"
Always "ok".
instance_idstring
The instance decided on.
decisions_recordedRecordedDecision[]
One per recorded decision.
Advisory dependency warnings.
next_proposal_versionstring
Token to pass to applyPendingDecisions.
ApplyPendingDecisionsResultResult of applying accepted decisions as one migration.
status"ok" | "nothing_to_apply"
nothing_to_apply when no accepted items remained.
instance_idstring
The instance applied to.
migration_idstring | null
The committed migration's ID (when one ran).
prior_versionnumber
Schema version before.
new_versionnumber
Schema version after.
applied_itemsstring[]
Item fingerprints that were applied.
summarystring
Human-readable summary.
warningsstring[]
Non-fatal warnings.
notesstring[]
Additional notes.
MigrationPlanAn ordered sequence of migration ops — the wire format emitted by `enhanceSchema` and consumed by `updateInstanceSchema` / `dryRunMigration`.
The ordered ops.
MigrationOpOne migration operation — a discriminated union keyed on `op_type`.
op_typestring
Discriminator: one of add_object, remove_object, rename_object, change_object, add_field, remove_field, rename_field, change_field, add_relation, remove_relation, rename_relation, change_relation — each variant carries operation-specific parameters.
PlanSummaryPer-op-type breakdown of a migration plan.
count_by_op_typeRecord<string, number>
Op count keyed by op_type.
totalnumber
Total number of ops.
ConsolidatedProposalThe single rolling improvement proposal for an instance.
instance_idstring
The instance the proposal is for.
proposal_versionstring
Optimistic-concurrency token for decideSuggestions / applyPendingDecisions.
schema_versionnumber
Schema version the proposal targets.
The accept/reject/defer-able items.
generated_atstring
When the proposal was generated (ISO-8601).
notesstring[]
Proposal-level notes.
ProposalItemOne decide-able item inside a proposal.
item_fingerprintstring
Stable ID; pass in a DecisionInput.
opobject
The proposed op (raw; cast to MigrationOp when needed).
evidence_feedback_idsstring[]
Feedback IDs that motivated the item.
evidence_query_samplesstring[]
Sample queries that motivated the item.
frequencynumber
How often the supporting signal occurred.
depends_onstring[]
Fingerprints this item depends on.
current_decisionstring | null
Existing decision, if any.
rationalestring
Why the item was proposed.
DecisionInputOne decision in a `decideSuggestions` batch.
item_fingerprintstring
The ProposalItem to decide on.
decision"accept" | "reject" | "defer"
The decision for this item.
editsobject | null
Optional op override (must keep the same op_type).
RecordedDecisionA persisted decision.
item_fingerprintstring
The decided item.
decision_idstring
ID of the recorded decision.
DependencyWarningAn advisory (non-blocking) dependency warning from `decideSuggestions`.
kindstring
Warning kind.
item_fingerprintstring
The item the warning concerns.
related_fingerprintsstring[]
Related item fingerprints.
related_summariesstring[]
Human-readable summaries of the related items.
guidancestring
What to do about it.
ToolDescriptionOne agent-callable tool in a DescribeResult.
namestring
Tool name.
descriptionstring
What the tool does.
when_to_usestring
Guidance on when to call it.
The tool's parameters.
http_methodstring
Underlying HTTP method.
http_pathstring
Underlying HTTP path.
ToolParameterDescriptionOne parameter of a ToolDescription.
namestring
Parameter name.
typestring
Parameter type.
descriptionstring
What the parameter is.
requiredboolean
Whether it must be supplied.
enumstring[] | undefined
Allowed values, if constrained.
defaultstring | undefined
Default value, if any.
Enums
const SchemaType = { YML: 0, JSON: 1 } as const;
EnumValues
SchemaTypeSchemaType.YML, SchemaType.JSON
ExtractionLogic"fast", "deep"
ReadMode"single-answer", "raw-tables", "xresponse"
WriteQueueStatus"queued", "processing", "extracting", "extracted", "applying", "completed", "failed", "not_found"
Exceptions
ExceptionParentAttributes
XmemoryAPIErrorError.status, .code, .details, .retryAfter (each undefined when absent)
XmemoryHealthCheckErrorXmemoryAPIError.status (HTTP status code or undefined)