API
The xmemory REST API is what the Python and TypeScript SDKs call under the hood. You can also call it directly with curl, fetch, or any HTTP client.
For MCP-based access (no HTTP calls needed), see the MCP guide.
All endpoints live under https://api.xmemory.ai, accept JSON, and return JSON. Every request except /healthz requires a Bearer token in the Authorization header.
Please register your interest at https://xmemory.ai and we will reach out to give access. When you receive your API key, copy and securely store the key. Never share your API key publicly.
Try it
Section titled “Try it”Write something, then read it back — two curl calls:
# Writecurl -X POST https://api.xmemory.ai/instances/your-instance-id/write \ -H "Authorization: Bearer $XMEM_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "text": "Alice Johnson works at Acme Corp. Her email is alice@acme.com.", "extraction_logic": "deep" }'
# Readcurl -X POST https://api.xmemory.ai/instances/your-instance-id/read \ -H "Authorization: Bearer $XMEM_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "What is Alice'\''s email?" }'# → {"reader_result": {"answer": "alice@acme.com"}, ...}The rest of this page covers every endpoint in detail.
Clusters
Section titled “Clusters”GET /clusters
Section titled “GET /clusters”List all clusters accessible to the authenticated user.
curl https://api.xmemory.ai/clusters \ -H "Authorization: Bearer $XMEM_API_KEY"GET /clusters/{cluster_id}
Section titled “GET /clusters/{cluster_id}”Get a single cluster by ID.
curl https://api.xmemory.ai/clusters/your-cluster-id \ -H "Authorization: Bearer $XMEM_API_KEY"Instance management
Section titled “Instance management”Before you can write or read, you need an instance — a memory store with a typed schema inside a cluster.
POST /clusters/{cluster_id}/instances/generate_schema
Section titled “POST /clusters/{cluster_id}/instances/generate_schema”Generate an xmemory schema from a plain-language description:
{ "schema_description": "Track contacts with name, email, company, and notes."}To refine an existing schema, pass current_yml_schema. The server runs the enhance path and returns the new schema plus a structured migration plan so the change can be applied safely (renames preserve data, etc.):
{ "data_schema": { "...": "..." }, "migration_plan": { "ops": [ { "op_type": "rename_field", "object_name": "person", "old_name": "mail", "new_name": "email" } ] }, "summary": "Rename person.mail to person.email.", "warnings": [], "repair_log": []}When current_yml_schema is omitted (create-from-scratch), only data_schema is returned. Pass the migration_plan to the dry-run and update endpoints below. See Schema evolution.
POST /clusters/{cluster_id}/instances
Section titled “POST /clusters/{cluster_id}/instances”Create an instance from a generated schema:
{ "name": "contacts", "description": "Optional description", "instance_schema": { "yml": { "value": "..." } }}Use "yml": { "value": "..." } for YAML schemas, or "json_schema": { "value": "..." } for JSON.
Returns instance metadata including the id. Save it — you’ll pass it with every subsequent call.
GET /instances
Section titled “GET /instances”List all instances accessible to the authenticated user.
curl https://api.xmemory.ai/instances \ -H "Authorization: Bearer $XMEM_API_KEY"GET /instances/{instance_id}
Section titled “GET /instances/{instance_id}”Get a single instance by ID.
GET /instances/{instance_id}/schema
Section titled “GET /instances/{instance_id}/schema”Get the current schema of an instance.
PUT /instances/{instance_id}/schema
Section titled “PUT /instances/{instance_id}/schema”Update the schema of an existing instance:
{ "instance_schema": { "yml": { "value": "..." } }}A purely additive change (new objects/fields/relations) needs only instance_schema. For a non-additive change (rename, remove, type change) also pass the migration_plan from generate_schema, and set confirm_destructive: true for any op that drops data:
{ "instance_schema": { "yml": { "value": "..." } }, "migration_plan": { "ops": [ { "op_type": "remove_field", "object_name": "person", "field_name": "legacy_id" } ] }, "confirm_destructive": true}When a migration runs, the response carries migration_id, prior_version, new_version, and migration_warnings. A non-additive change sent without a plan is rejected with non_additive_change_requires_plan. See Schema evolution.
PUT /instances/{instance_id}
Section titled “PUT /instances/{instance_id}”Update instance metadata (name and description):
{ "name": "new-name", "description": "Updated description"}DELETE /instances/{instance_id}
Section titled “DELETE /instances/{instance_id}”Delete an instance. Returns a list of deleted instance IDs.
POST /instances/{instance_id}/write
Section titled “POST /instances/{instance_id}/write”Extract structured objects from text and persist them. Blocks until committed.
{ "text": "Bob Lee is a designer at Globex. He joined last Monday.", "extraction_logic": "deep"}| Field | Required | Description |
|---|---|---|
text | yes | Free-form text to extract from |
extraction_logic | no | "deep" (default) or "fast" |
Returns:
{ "write_id": "...", "cleaned_objects": { ... }, "trace_id": "..."}cleaned_objects contains the stored objects.
POST /instances/{instance_id}/write_async
Section titled “POST /instances/{instance_id}/write_async”Same extraction pipeline, but returns immediately instead of blocking:
{ "text": "Carol is a senior engineer at Initech.", "extraction_logic": "deep"}Returns { "write_id": "..." }.
Important: do not read immediately after an async write — the data may not be committed yet. Poll with write_status first.
POST /instances/{instance_id}/write_status
Section titled “POST /instances/{instance_id}/write_status”Check whether an async write has finished:
{ "write_id": "..." }Returns:
{ "write_id": "...", "write_status": "completed", "error_detail": null, "completed_at": "2025-03-16T12:34:56Z"}write_status is one of: queued, processing, completed, failed, not_found.
POST /instances/{instance_id}/read
Section titled “POST /instances/{instance_id}/read”Query the instance in natural language:
{ "query": "Who joined the team recently?", "mode": "single-answer"}| Field | Required | Description |
|---|---|---|
query | yes | Natural-language question |
mode | no | "single-answer" (default), "xresponse", or "raw-tables" |
The response shape depends on the mode:
| Mode | reader_result | Best for |
|---|---|---|
single-answer | {"answer": "Bob Lee joined last Monday."} | Natural-language answers |
xresponse | {"objects": [...], "relations": [...]} | Structured data |
raw-tables | {"tables": [...]} | Raw SQL results |
Extract
Section titled “Extract”POST /instances/{instance_id}/extract
Section titled “POST /instances/{instance_id}/extract”Extract structured objects from text without storing them — useful for previewing what a write would produce:
{ "text": "Dave manages the London office.", "extraction_logic": "deep"}Returns { "objects_extracted": { ... }, "trace_id": "..." }.
Same parameters as write.
Describe
Section titled “Describe”GET /instances/{instance_id}/describe
Section titled “GET /instances/{instance_id}/describe”Returns agent-facing tool descriptions enriched with the instance’s actual schema. Use this to tell an LLM what tools are available and how to call them.
curl https://api.xmemory.ai/instances/your-instance-id/describe \ -H "Authorization: Bearer $XMEM_API_KEY"Returns:
{ "instance_id": "...", "instance_name": "contacts", "schema_summary": "This instance tracks contacts with name, email, ...", "tools": [ { "name": "write", "description": "Extract structured data from text and persist it.", "when_to_use": "When you need to store new facts ...", "parameters": [ { "name": "text", "type": "string", "description": "...", "required": true }, { "name": "extraction_logic", "type": "string", "description": "...", "required": false, "enum": ["fast", "deep"], "default": "deep" } ], "http_method": "POST", "http_path": "/instances/.../write" } ]}The response only includes tools the caller has permission to use. The schema_summary is a human-readable description of the instance’s schema, suitable for including in an LLM system prompt.
Schema evolution
Section titled “Schema evolution”xmemory can change a live schema without losing data. There are two paths, both ending in an atomic migration with an audit record: a direct migration you drive yourself, and a suggestion engine that proposes improvements from real read traffic. Additive-only callers are unaffected — the endpoints above keep working as before.
POST /instances/{instance_id}/migrations/dry_run
Section titled “POST /instances/{instance_id}/migrations/dry_run”Preview a migration without applying it. Same body as the schema update (instance_schema, optional migration_plan, confirm_destructive); runs the full apply-path validation and returns the planned DDL:
{ "status": "ok", "instance_id": "...", "current_version": 4, "statements": ["ALTER TABLE person RENAME COLUMN mail TO email"], "warnings": [], "plan_summary": { "count_by_op_type": { "rename_field": 1 }, "total": 1 }, "requires_metadata_sync": true}GET /instances/{instance_id}/migrations
Section titled “GET /instances/{instance_id}/migrations”List applied migrations, newest first. Query params: limit (default 50, range 1–200), before_id (UUID pagination cursor), include_yaml (default false — adds before/after schema snapshots).
{ "status": "ok", "items": [ { "id": "...", "applied_at": "...", "source": "suggestion_engine", "decided_by": "...", "prior_version": 3, "new_version": 4, "ops": [ ... ], "ops_summary": { ... } } ], "next_before_id": null, "has_more": false}source is direct or suggestion_engine.
GET /instances/{instance_id}/migrations/{migration_id}
Section titled “GET /instances/{instance_id}/migrations/{migration_id}”Get a single migration record. include_yaml=true adds the before/after snapshots.
Suggestion engine — review → decide → apply
Section titled “Suggestion engine — review → decide → apply”The engine surfaces a single rolling proposal per instance, derived on demand from accumulated read-gap signals. The minimum flow is three calls.
POST /instances/{instance_id}/suggestions/review
Section titled “POST /instances/{instance_id}/suggestions/review”Returns the consolidated proposal and a proposal_version concurrency token (body may be {} or { "session_id": "..." }):
{ "status": "ok", "instance_id": "...", "proposal": { "proposal_version": "ab12...", "schema_version": 4, "items": [ { "item_fingerprint": "fp1", "op": { "op_type": "add_field", "object_name": "person", "field_name": "phone", "field_type": "str" }, "rationale": "queried but missing", "frequency": 3, "depends_on": [], "evidence_query_samples": ["what is bob's phone"] } ] }}When a migration is already running, the response is { "status": "evolution_in_progress", "retry_after_seconds": 5 } — back off and retry.
POST /instances/{instance_id}/suggestions/decide
Section titled “POST /instances/{instance_id}/suggestions/decide”Record an accept / reject / defer per item in one batch. Requires the proposal_version from review:
{ "proposal_version": "ab12...", "decisions": [ { "item_fingerprint": "fp1", "decision": "accept" } ]}Returns the recorded decisions, advisory dependency warnings, and a next_proposal_version you can pass straight to apply.
POST /instances/{instance_id}/suggestions/apply
Section titled “POST /instances/{instance_id}/suggestions/apply”Commit accepted decisions as one migration:
{ "proposal_version": "cd34..." }Returns { "status": "ok", "migration_id": "...", "prior_version": 4, "new_version": 5, "applied_items": ["fp1"], "summary": "..." }, or { "status": "nothing_to_apply" } if no accepted items remained.
Structured errors
Section titled “Structured errors”Schema-evolution endpoints return a structured error body with a 4xx/5xx status:
{ "status": "error", "error_type": "stale_proposal_version", "error_message": "...", "details": { ... } }error_type is one of: non_additive_change_requires_plan, destructive_confirmation_required, stale_schema_version, migration_precondition_failed, migration_execution_failed, migration_not_found, stale_proposal_version, dependency_closure_failed, invalid_decision_input, apply_failed, instance_not_initialised. The SDKs surface error_type as a structured error code.
Limits
Section titled “Limits”No backfill of historical data into new fields; no rollback of a committed migration (atomic abort on failure is the safety story); identity tightening (change_object.new_primary_key, change_relation.new_keys) is relax-only.
Health check
Section titled “Health check”GET /healthz
Section titled “GET /healthz”Returns 200 if the API is reachable. No authentication required.
curl https://api.xmemory.ai/healthzErrors
Section titled “Errors”All endpoints return an error response on failure with the appropriate HTTP status code (400, 401, 404, 500).
Endpoint summary
Section titled “Endpoint summary”| Endpoint | Method | Auth | Description |
|---|---|---|---|
/healthz | GET | — | Health check |
/clusters | GET | Yes | List clusters |
/clusters/{cluster_id} | GET | Yes | Get a cluster |
/clusters/{cluster_id}/instances/generate_schema | POST | Yes | Generate a schema from a description |
/clusters/{cluster_id}/instances | POST | Yes | Create a new instance |
/instances | GET | Yes | List instances |
/instances/{instance_id} | GET | Yes | Get an instance |
/instances/{instance_id} | PUT | Yes | Update instance metadata |
/instances/{instance_id} | DELETE | Yes | Delete an instance |
/instances/{instance_id}/schema | GET | Yes | Get instance schema |
/instances/{instance_id}/schema | PUT | Yes | Update instance schema (optional migration_plan) |
/instances/{instance_id}/migrations/dry_run | POST | Yes | Preview a migration |
/instances/{instance_id}/migrations | GET | Yes | List applied migrations |
/instances/{instance_id}/migrations/{migration_id} | GET | Yes | Get a migration record |
/instances/{instance_id}/suggestions/review | POST | Yes | Get the schema-improvement proposal |
/instances/{instance_id}/suggestions/decide | POST | Yes | Record accept/reject/defer decisions |
/instances/{instance_id}/suggestions/apply | POST | Yes | Apply accepted decisions |
/instances/{instance_id}/write | POST | Yes | Write (synchronous) |
/instances/{instance_id}/write_async | POST | Yes | Write (async, returns write_id) |
/instances/{instance_id}/write_status | POST | Yes | Poll async write status |
/instances/{instance_id}/read | POST | Yes | Query the instance |
/instances/{instance_id}/extract | POST | Yes | Extract without writing |
/instances/{instance_id}/describe | GET | Yes | Get agent-facing tool descriptions |