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.
Installation
Section titled “Installation”npm install xmemoryQuick start
Section titled “Quick start”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 instanceconst inst = xm.instance("your-instance-id");
// Write some informationawait 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 backconst result = await inst.read("What is Alice's email?");console.log(result.reader_result);// → {"answer": "alice@acme.com"}Creating a client
Section titled “Creating a client”Three options, depending on whether you want a health check:
import { XmemoryClient, xmemoryInstance } from "xmemory";
// Factory function — runs a health check automaticallyconst xm = await xmemoryInstance({ url: "https://api.xmemory.ai", apiKey: "..." });
// Static method — same behaviorconst xm = await XmemoryClient.create({ url: "https://api.xmemory.ai", apiKey: "..." });
// Constructor — no health checkconst 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.
Instance handle
Section titled “Instance handle”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 handleawait inst.write("Carol is a senior engineer at Initech.");const result = await inst.read("Who works at Initech?");console.log(result.reader_result);Writing
Section titled “Writing”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 / deletedconsole.log(resp.trace_id); // request trace id when availableconsole.log(resp.console_url); // this operation's trace in the consolewrite(), 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.
Extraction logic
Section titled “Extraction logic”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 |
Async writes
Section titled “Async writes”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 completionThe 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.
Structured writes
Section titled “Structured writes”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 / deletedAn 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.
Reading
Section titled “Reading”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);Read modes
Section titled “Read modes”// Plain-text answer (default)const resp = await inst.read("What is Alice's email?", { readMode: "single-answer" });
// Structured objects and relationsconst resp = await inst.read("Show all contacts", { readMode: "xresponse" });
// Raw SQL result setsconst resp = await inst.read("List all contacts", { readMode: "raw-tables" });Scoped reads
Section titled “Scoped reads”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);Multiple objects with relations
Section titled “Multiple objects with relations”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 });Scope options
Section titled “Scope options”"no_relations"(default): Only the specified objects."all_relations": Include junction tables between objects.
Connecting an instance elsewhere
Section titled “Connecting an instance elsewhere”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.
Error handling
Section titled “Error handling”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; }}Async writes
Section titled “Async writes”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 completionThe 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.
Extracting (without writing)
Section titled “Extracting (without writing)”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.
Describing (agent tool discovery)
Section titled “Describing (agent tool discovery)”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 promptconsole.log(desc.asText());
// Anthropic tool-use formatconst tools = desc.asAnthropicTools();
// OpenAI function-calling formatconst 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, verbatimdesc.usageBrief; // generated from the schema; null until generatedasText() 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.
Cluster and instance management
Section titled “Cluster and instance management”All management operations live on xm.admin: discovering clusters, generating schemas, creating instances, and evolving them safely.
Clusters
Section titled “Clusters”const clusters = await xm.admin.listClusters();const clusterId = clusters[0].id;Schema generation
Section titled “Schema generation”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.
Instances
Section titled “Instances”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);Agent-facing instance metadata
Section titled “Agent-facing instance metadata”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,});Schema evolution
Section titled “Schema evolution”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);}Error handling
Section titled “Error handling”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.
Reference
Section titled “Reference”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.
Client
Section titled “Client”Constructors and client-level accessors.
XmemoryClientCreate a client (no health check)
new XmemoryClient(options?)→XmemoryClientParameters
options.apiKeystringXMEM_API_KEY.options.urlstringXMEM_API_URL. Defaults to https://api.xmemory.ai.options.timeoutMsnumber60000.XmemoryClient.createCreate a client with health check
XmemoryClient.create(options?)→Promise<XmemoryClient>Parameters
options.apiKeystringXMEM_API_KEY.options.urlstringXMEM_API_URL. Defaults to https://api.xmemory.ai.options.timeoutMsnumber60000.xmemoryInstanceFactory with health check (same as .create())
xmemoryInstance(options?)→Promise<XmemoryClient>Parameters
options.apiKeystringXMEM_API_KEY.options.urlstringXMEM_API_URL. Defaults to https://api.xmemory.ai.options.timeoutMsnumber60000.adminControl-plane accessor — clusters, schema generation, instances, schema evolution
xm.admin→AdminNamespaceReturns
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)→InstanceHandleParameters
instanceIdstringrequiredReturns
A bound InstanceHandle — call write / read / extract / … on it. (Synchronous; not a Promise.)
checkHealthThrows XmemoryHealthCheckError if the API is unreachable
xm.checkHealth()→Promise<void>Admin methods
Section titled “Admin methods”Control-plane methods on xm.admin — clusters, schema generation, instances, and schema evolution.
listClustersList clusters
Parameters
options.idsstring[]Returns
getClusterGet a cluster by ID
Parameters
clusterIdstringrequiredgenerateSchemaGenerate a schema from a description
Parameters
clusterIdstringrequiredschemaDescriptionstringrequiredoptions.currentYmlSchemastringReturns
GenerateSchemaResult — { data_schema }.
enhanceSchemaEvolve a schema → new schema + migration plan
enhanceSchema(clusterId, schemaDescription, currentYmlSchema, options?)→Promise<EnhanceSchemaResult>Parameters
clusterIdstringrequiredschemaDescriptionstringrequiredcurrentYmlSchemastringrequiredReturns
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>Parameters
clusterIdstringrequirednamestringrequiredschemaTextstringrequiredSchemaType.YML or SchemaType.JSON).options.descriptionstringoptions.schemaDescriptionstringReturns
A bound InstanceHandle for the new instance — read inst.id for its ID.
listInstancesList instances
Parameters
options.idsstring[]Returns
getInstanceGet an instance by ID
Parameters
instanceIdstringrequiredgetInstanceSchemaGet an instance's schema
Parameters
instanceIdstringrequiredupdateInstanceSchemaUpdate a schema; pass migrationPlan for non-additive changes
Parameters
instanceIdstringrequiredschemaTextstringrequiredSchemaType.YML or SchemaType.JSON).options.migrationPlanMigrationPlanoptions.confirmDestructivebooleanfalse.Returns
An InstanceInfo — also carries migration_id, prior_version, new_version, migration_warnings when a migration ran.
Errors
Throws XmemoryAPIError with .code non_additive_change_requires_plan (no plan) or destructive_confirmation_required (drops data without confirmDestructive).
updateInstanceMetadataUpdate instance name and description
Parameters
instanceIdstringrequirednamestringrequireddescriptionstringrequiredoptions.agentOwnerInstructionsstring | nullnull to clear it. Max 2000 characters.options.expectedOwnerInstructionsEpochnumberagent_owner_instructions_epoch your edit was composed from; a save that raced someone else's edit is then refused rather than overwriting it.Returns
The updated InstanceInfo. Omitting an option sends no such key at all, so a rename never disturbs the standing preference.
Errors
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
Parameters
instanceIdstringrequiredoptions.namestringoptions.descriptionstring | nullnull to clear).options.agentSurfacesreadonly string[] | nullAgentSurface. Advisory: it orders the connect steps and grants nothing.options.agentDefaultBindingTierstring | nullBindingTier. Advisory.options.agentEngagementHintsreadonly string[] | null"a convention is learned or corrected". At most 16, each at most 200 characters.options.agentOwnerInstructionsstring | nullupdateInstanceMetadata for this field — it is the only call that accepts expectedOwnerInstructionsEpoch, so an edit sent here cannot be guarded against a concurrent one.Returns
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>Parameters
instanceIdstringrequiredoptions.formatSetupFormatValueSetupFormat.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.Details
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[]>Parameters
instanceIdstringrequiredReturns
string[] — the deleted instance IDs.
dryRunMigrationPreview a migration's DDL
Parameters
instanceIdstringrequiredschemaTextstringrequiredSchemaType.YML or SchemaType.JSON).options.migrationPlanMigrationPlanoptions.confirmDestructivebooleanfalse.Returns
DryRunResult — { status, instance_id, current_version, statements: string[] (planned DDL), warnings, plan_summary: PlanSummary, requires_metadata_sync }.
listMigrationsList applied migrations (newest first)
Parameters
instanceIdstringrequiredoptions.limitnumber50.options.beforeIdstringnext_before_id.options.includeYamlbooleanReturns
ListMigrationsResult — { items: MigrationRecord[], next_before_id: string | null, has_more }.
getMigrationGet a single migration record
Parameters
instanceIdstringrequiredmigrationIdstringrequiredoptions.includeYamlbooleanReturns
MigrationRecord — { id, applied_at, source, prior_version, new_version, ops, ops_summary, yaml_before, yaml_after }.
Instance methods
Section titled “Instance methods”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>Parameters
textstringmutations.mutationsWriteMutation[]options.extractionLogic"fast" | "deep""fast".options.diffEnginebooleanReturns
write_idstringchangesunknowncreated / updated / deleted.trace_idstring | nullwriteAsyncEnqueue a write; resolves with write_id immediately
writeAsync(text | mutations, options?)→Promise<AsyncWriteResult>Parameters
textstringmutations.mutationsWriteMutation[]write.options.extractionLogic"fast" | "deep""fast".options.diffEnginebooleanReturns
write_idstringwriteStatus.writeStatusPoll the status of an async write
writeStatus(writeId, options?)→Promise<WriteStatusResult>Parameters
writeIdstringrequiredwriteAsync.Returns
write_idstringwrite_statusWriteQueueStatuserror_detailstring | nullcompleted_atstring | nullreadQuery the instance in natural language
read(query, options?)→Promise<ReadResult>Parameters
querystringrequiredoptions.readMode"single-answer" | "raw-tables" | "xresponse""single-answer".options.traceIdstringReturns
reader_resultunknownreadMode (e.g. { answer } for single-answer).trace_idstring | nullextractExtract objects without writing them
extract(text, options?)→Promise<ExtractResult>Parameters
textstringrequiredoptions.extractionLogic"fast" | "deep""fast".Returns
objects_extractedunknownobjects / relations).trace_idstring | nulldescribeGet agent-facing tool descriptions (cached 5 min)
describe(options?)→Promise<DescribeResult>Parameters
options.includeHttpbooleanReturns
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()→voidgetSchemaGet this instance's schema
setupInstructionsHow to connect *this* instance somewhere else, most likely surface first
setupInstructions(options?)→Promise<AgentSetupResult>Parameters
options.formatSetupFormatValueSetupFormat.AGENT (default) returns the steps to run now; SetupFormat.PROJECT also returns the files a team commits once.Details
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
Parameters
options.sessionIdstringReturns
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
Parameters
proposalVersionstringrequiredreviewSuggestions.{ item_fingerprint, decision, edits? }.options.sessionIdstringReturns
DecideSuggestionsResult — { decisions_recorded, warnings, next_proposal_version }. Pass next_proposal_version to applyPendingDecisions.
Errors
Throws XmemoryAPIError with .code stale_proposal_version (token out of date) or invalid_decision_input.
applyPendingDecisionsApply accepted decisions as one migration
Parameters
proposalVersionstringrequirednext_proposal_version from decideSuggestions.options.sessionIdstringReturns
ApplyPendingDecisionsResult — { status ("ok" | "nothing_to_apply"), migration_id, prior_version, new_version, applied_items, summary, warnings, notes }.
Errors
Throws XmemoryAPIError with .code stale_proposal_version, dependency_closure_failed, or apply_failed.
Data types
ClusterInfoA cluster.
Fields
idstringorg_idstringnamestringdescriptionstring | nullInstanceInfoAn instance's metadata.
Fields
idstringcluster_idstringnamestringdescriptionstring | nulldata_schemaobject | nullagent_surfacesstring[] | nullnull 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 | nullautoload / available). Advisory.agent_engagement_hintsstring[] | nullagent_owner_instructionsstring | nullagent_owner_instructions_epochnumberexpectedOwnerInstructionsEpoch to make an edit conflict-safe.InstanceSchemaInfoAn instance's schema.
Fields
data_schemaobjectGenerateSchemaResultA generated schema.
Fields
data_schemaobjectEnhanceSchemaResultAn evolved schema plus an executor-ready migration plan.
Fields
data_schemaobjectmigration_planMigrationPlan | nullupdateInstanceSchema / dryRunMigration.summarystring | nullwarningsobject[]repair_logobject[]DryRunResultPlanned migration DDL, with nothing applied.
Fields
status"ok""ok" for a successful dry run.instance_idstringcurrent_versionnumberstatementsstring[]warningsstring[]plan_summaryPlanSummaryrequires_metadata_syncbooleanMigrationRecordOne applied-migration history row.
Fields
idstringapplied_atstringsourcestringdirect or suggestion_engine.decided_bystring | nullprior_versionnumbernew_versionnumberopsobject[]ops_summaryPlanSummarynotesstring | nullyaml_beforestring | nullincludeYaml: true).yaml_afterstring | nullincludeYaml: true).ListMigrationsResultA page of migration records, newest first.
Fields
status"ok""ok".instance_idstringitemsMigrationRecord[]next_before_idstring | nullnull at the tail.has_morebooleanReviewSuggestionsResultThe rolling schema-improvement proposal (or an in-progress status).
Fields
status"ok" | "evolution_in_progress"evolution_in_progress means a migration is in flight.instance_idstringproposalConsolidatedProposal | nullstatus is "ok".retry_after_secondsnumber | nullevolution_in_progress.DecideSuggestionsResultResult of recording a batch of decisions.
Fields
status"ok""ok".instance_idstringdecisions_recordedRecordedDecision[]warningsDependencyWarning[]next_proposal_versionstringapplyPendingDecisions.ApplyPendingDecisionsResultResult of applying accepted decisions as one migration.
Fields
status"ok" | "nothing_to_apply"nothing_to_apply when no accepted items remained.instance_idstringmigration_idstring | nullprior_versionnumbernew_versionnumberapplied_itemsstring[]summarystringwarningsstring[]notesstring[]MigrationPlanAn ordered sequence of migration ops — the wire format emitted by `enhanceSchema` and consumed by `updateInstanceSchema` / `dryRunMigration`.
Fields
opsMigrationOp[]MigrationOpOne migration operation — a discriminated union keyed on `op_type`.
Fields
op_typestringadd_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.
Fields
count_by_op_typeRecord<string, number>op_type.totalnumberConsolidatedProposalThe single rolling improvement proposal for an instance.
Fields
instance_idstringproposal_versionstringdecideSuggestions / applyPendingDecisions.schema_versionnumberitemsProposalItem[]generated_atstringnotesstring[]ProposalItemOne decide-able item inside a proposal.
Fields
item_fingerprintstringDecisionInput.opobjectMigrationOp when needed).evidence_feedback_idsstring[]evidence_query_samplesstring[]frequencynumberdepends_onstring[]current_decisionstring | nullrationalestringDecisionInputOne decision in a `decideSuggestions` batch.
Fields
item_fingerprintstringProposalItem to decide on.decision"accept" | "reject" | "defer"editsobject | nullop_type).RecordedDecisionA persisted decision.
Fields
item_fingerprintstringdecision_idstringDependencyWarningAn advisory (non-blocking) dependency warning from `decideSuggestions`.
Fields
kindstringitem_fingerprintstringrelated_fingerprintsstring[]related_summariesstring[]guidancestringToolDescriptionOne agent-callable tool in a DescribeResult.
Fields
namestringdescriptionstringwhen_to_usestringparametersToolParameterDescription[]http_methodstringhttp_pathstringToolParameterDescriptionOne parameter of a ToolDescription.
Fields
namestringtypestringdescriptionstringrequiredbooleanenumstring[] | undefineddefaultstring | undefinedEnums
const SchemaType = { YML: 0, JSON: 1 } as const;| Enum | Values |
|---|---|
SchemaType | SchemaType.YML, SchemaType.JSON |
ExtractionLogic | "fast", "deep" |
ReadMode | "single-answer", "raw-tables", "xresponse" |
WriteQueueStatus | "queued", "processing", "extracting", "extracted", "applying", "completed", "failed", "not_found" |
Exceptions
| Exception | Parent | Attributes |
|---|---|---|
XmemoryAPIError | Error | .status, .code, .details, .retryAfter (each undefined when absent) |
XmemoryHealthCheckError | XmemoryAPIError | .status (HTTP status code or undefined) |