REST API Reference

The hosted service at https://memory.neo4jlabs.com/v1 exposes a REST API. This page is a curated companion to the machine-readable contract — it maps every endpoint to its bridge-protocol equivalent and the corresponding method on each language client.

The authoritative, always-current contract is the OpenAPI document at memory.neo4jlabs.com/openapi.json (production). Request/response schemas live there; this page covers the cross-cutting rules — auth, versioning, errors, pagination — and the SDK-method mapping the spec doesn’t show.

Bridge-method names are what the agent-memory-tck cross-language conformance suite uses. SDK methods take language-idiomatic casing.

Base URL & versioning

All endpoints are served under a major-version prefix: the production base URL is https://memory.neo4jlabs.com/v1. The SDK appends paths to the configured MEMORY_ENDPOINT (default the URL above). The /v{n} prefix also selects the REST transport; endpoints without it use the TCK bridge protocol (see Bolt vs NAMS).

The active workspace is resolved from the API key (and/or the X-Workspace-Id header), not from the URL path — so endpoints like read-only Cypher are POST /v1/query, scoped to the caller’s workspace. Treat the OpenAPI spec as the source of truth for exact paths, and prefer the SDK accessors (e.g. client.query.cypher(…​)) over hand-built URLs.

Request headers

Every request carries Authorization: Bearer <token>, where the token is either your API key (nams_…​) used directly, or a short-lived JWT obtained from the Auth0 exchange endpoints (POST /v1/auth/exchange, POST /v1/auth/refresh).

The target workspace depends on the key’s category (see Authentication & API Keys): a workspace key is already bound to one workspace, so X-Workspace-Id is optional (and a different value is rejected with 403); an admin key names the workspace per request with X-Workspace-Id: <workspace_id>. The SDK sends the header automatically when workspace_id / workspaceId (or MEMORY_WORKSPACE_ID) is configured. See Use NAMS.

Conversations

HTTP Path Bridge TS Go Py C#

GET

/conversations

list_conversations

listConversations

ListConversations

list_conversations

ListConversationsAsync

POST

/conversations

create_conversation

createConversation

CreateConversation

create_conversation

CreateConversationAsync

GET

/conversations/{id}

get_conversation_metadata

getConversationMetadata

GetConversationMetadata

get_conversation_metadata

GetConversationMetadataAsync

DELETE

/conversations/{id}

delete_conversation

deleteConversation

DeleteConversation

delete_conversation

DeleteConversationAsync

GET

/conversations/{id}/messages

get_conversation

getConversation

GetConversation

get_conversation

GetConversationAsync

POST

/conversations/{id}/messages

add_message

addMessage

AddMessage

add_message

AddMessageAsync

POST

/conversations/{id}/messages/bulk

bulk_add_messages

bulkAddMessages

BulkAddMessages

bulk_add_messages

BulkAddMessagesAsync

POST

/conversations/{id}/search

search_messages

searchMessages

SearchMessages

search_messages

SearchMessagesAsync

GET

/conversations/{id}/context

get_context

getContext

GetContext

get_context

GetContextAsync

GET

/conversations/{id}/observations

get_observations

getObservations

GetObservations

get_observations

GetObservationsAsync

GET

/conversations/{id}/reflections

get_reflections

getReflections

GetReflections

get_reflections

GetReflectionsAsync

GET

/conversations/{id}/extraction-status

get_extraction_status

getExtractionStatus

GetExtractionStatus

get_extraction_status

GetExtractionStatusAsync

Because extraction runs asynchronously on NAMS, get_extraction_status (short-term memory) is the authoritative "has the pipeline finished?" signal for a conversation — long_term.wait_for_extraction(session_id=…​) polls it for you.

Entities

HTTP Path Bridge TS Go Py C#

GET

/entities

list_entities

listEntities

ListEntities

list_entities

ListEntitiesAsync

POST

/entities

add_entity

addEntity

AddEntity

add_entity

AddEntityAsync

GET

/entities/{id}

get_entity

getEntity

GetEntity

get_entity

GetEntityAsync

PUT

/entities/{id}

update_entity

updateEntity

UpdateEntity

update_entity

UpdateEntityAsync

DELETE

/entities/{id}

delete_entity

deleteEntity

DeleteEntity

delete_entity

DeleteEntityAsync

PUT

/entities/{id}/feedback

set_entity_feedback

setEntityFeedback

SetEntityFeedback

set_entity_feedback

SetEntityFeedbackAsync

GET

/entities/{id}/history

get_entity_history

getEntityHistory

GetEntityHistory

get_entity_history

GetEntityHistoryAsync

POST

/entities/{id}/merge

merge_entities

mergeEntities

MergeEntities

merge_entities

MergeEntitiesAsync

GET

/entities/graph

get_entity_graph

getEntityGraph

GetEntityGraph

get_entity_graph

GetEntityGraphAsync

POST

/entities/search

search_entities

searchEntities

SearchEntities

search_entities

SearchEntitiesAsync

POST

/graph/expand

expand_graph

expandGraph

ExpandGraph

expand_graph

ExpandGraphAsync

POST /graph/expand returns the 1-hop neighbourhood of a node ({nodes, edges}), excluding any loaded_ids you already have — it backs incremental graph-visualization ("expand this node"). Exposed as long_term.expand_graph / longTerm.expandGraph.

Reasoning

HTTP Path Bridge TS Go Py C#

POST

/reasoning/steps

record_step

recordStep

RecordStep

record_step

RecordStepAsync

GET

/reasoning/steps

list_steps

listSteps

ListSteps

list_steps

ListStepsAsync

GET

/reasoning/explain/{step}

explain_step

explainStep

ExplainStep

explain_step

ExplainStepAsync

POST

/reasoning/tool-calls

record_tool_call

recordToolCall

RecordToolCall

record_tool_call

RecordToolCallAsync

GET

/reasoning/trace/{conv}

get_trace_by_conversation

getTraceByConversation

GetTraceByConversation

get_trace_by_conversation

GetTraceByConversationAsync

GET

/reasoning/provenance/{entity}

get_entity_provenance

getEntityProvenance

GetEntityProvenance

get_entity_provenance

GetEntityProvenanceAsync

Cypher and Auth

HTTP Path Bridge TS Go Py C#

POST

/query

cypher_query

query.cypher

Query.Cypher

query.cypher

Query.CypherAsync

GET

/auth/api-keys

list_api_keys

auth.listApiKeys

Auth.ListAPIKeys

auth.list_api_keys

Auth.ListApiKeysAsync

POST

/auth/api-keys

create_api_key

auth.createApiKey

Auth.CreateAPIKey

auth.create_api_key

Auth.CreateApiKeyAsync

DELETE

/auth/api-keys/{id}

revoke_api_key

auth.revokeApiKey

Auth.RevokeAPIKey

auth.revoke_api_key

Auth.RevokeApiKeyAsync

GET

/auth/api-keys/{id}/reveal

reveal_api_key

auth.revealApiKey

Auth.RevealAPIKey

auth.reveal_api_key

Auth.RevealApiKeyAsync

POST

/auth/api-keys/{id}/rotate

rotate_api_key

auth.rotateApiKey

Auth.RotateAPIKey

auth.rotate_api_key

Auth.RotateApiKeyAsync

POST

/auth/refresh

refresh_access_token

auth.refreshAccessToken

Auth.RefreshAccessToken

auth.refresh_access_token

Auth.RefreshAccessTokenAsync

The per-language method names above follow the bridge-protocol/conformance naming. Both SDKs expose these on a client.auth accessor (Python NamsAuth, TypeScript AuthClient): list_api_keys / create_api_key / reveal_api_key / rotate_api_key / revoke_api_key / refresh_access_token. On the bolt backend client.auth is a NotSupportedError sentinel (bolt uses NEO4J_PASSWORD, not a NAMS key). You can also manage keys from the dashboard. See Authentication & API Keys.

Ontologies

Typed, versioned domain schemas. Workspace-scoped via X-Workspace-Id. Request bodies are snake_case. See Ontology API for the document schema and validation modes.

HTTP Path Python TypeScript

GET

/ontologies

ontology.list

ontology.list

GET

/ontologies/{id}

ontology.get

ontology.get

GET

/ontologies/active

ontology.get_active

ontology.getActive

POST

/ontologies/{name}/clone

ontology.clone

ontology.clone

POST

/ontologies

ontology.create

ontology.create

PUT

/ontologies/{id}

ontology.update

ontology.update

POST

/ontologies/active

ontology.activate

ontology.activate

DELETE

/ontologies/{id}

ontology.delete

ontology.delete

POST

/ontologies/import

ontology.import_

ontology.import

GET

/ontologies/{id}/diff

ontology.diff

ontology.diff

POST

/ontologies/{id}/migrate

ontology.migrate

ontology.migrate

GET

/ontologies/migrations/{job_id}

ontology.get_migration

ontology.getMigration

import, diff, and migrate are exposed on the client.ontology accessor in both SDKs (Python import_ — trailing underscore, since import is a keyword). migrate is asynchronous: it returns a MigrationJob you poll with get_migration(). The preview/dry-run path of import is rate-limited to 30 requests/hour/workspace (see NAMS Limits & Behavior). See Ontology API for method signatures and the accepted import formats.

Errors

Error responses use standard HTTP status codes with a JSON body of the shape {"error": "<message>"}. Some errors carry extra fields (e.g. message, limit on quota errors). The SDK maps them to typed exceptions:

Status Meaning Python SDK exception

400

Invalid request, write Cypher attempted, validation failure

ValidationError (carries details)

401, 403

Missing/invalid token, or workspace not accessible

AuthenticationError

404

Resource not found

MemoryError (base class)

405, 501

Operation not available on this backend

NotSupportedError

429

Rate limited

RateLimitError (carries retry_after; honors Retry-After)

5xx

Service error

TransportError (after retries exhausted)

Other 4xx (e.g. 409, 410, 413, 422)

Conflict, gone, payload too large, semantic validation

TransportError

All SDK exceptions subclass MemoryError. The SDK retries 429, 5xx, and network errors with exponential backoff, honoring any Retry-After header. See NAMS Limits & Behavior for rate-limit and quota specifics.

Pagination

List endpoints accept limit and offset query parameters for offset-based pagination (e.g. GET /v1/conversations?limit=50&offset=100). Defaults are service-configured; pass an explicit limit for deterministic page sizes.

Wire format differences

  • Hosted REST: camelCase fields (userId, conversationId, createdAt).

  • Bridge protocol: snake_case fields (user_id, conversation_id, created_at).

The clients translate transparently. If you’re writing a custom transport, see the bridge-protocol reference and the language-specific casing modules in the TCK repo.