Authentication and API keys

This page describes the service contract used by this checkout. The SDK source alone does not verify a deployed service, dashboard, quota or authentication configuration. Check the authenticated service response and current workspace settings when following operational instructions.

How the hosted NAMS backend authenticates requests, where API keys come from, and how to handle them safely. This page is language-neutral; for SDK-specific setup see Use NAMS (Python) and Authentication (TypeScript).

Authentication applies to the NAMS (hosted) backend only. The self-hosted bolt backend authenticates to your own Neo4j with NEO4J_PASSWORD and never uses a NAMS API key.

Methods

NAMS accepts three kinds of credential:

Method Use

API key (nams_…)

Programmatic access from the SDKs, REST clients, and the MCP server. Sent directly as a Bearer token. This is what you’ll use in code.

Auth0 JWT

Dashboard / browser sessions. An Auth0 login is exchanged for a short-lived (≈1 h, RS256) internal JWT via POST /v1/auth/exchange. Validate against the JWKS at /.well-known/jwks.json.

MCP OAuth

Interactive MCP clients (e.g. Claude Desktop) authenticate with an OAuth 2.0 Authorization-Code + PKCE flow. See MCP tools.

Key format & the wire

API keys are prefixed nams_ (legacy gylrd_ keys are still accepted). Send the key as a Bearer token on every request:

Authorization: Bearer nams_xxxxxxxxxxxxxxxxxxxx
X-Workspace-Id: ws_xxxxxxxx          # see "Key categories" below

Static API keys expire roughly 90 days after creation — rotate before expiry.

Key categories

Every key is created in one of two categories, which fix its scopes and how the target workspace is resolved:

Category Behavior

Workspace key
(category: "workspace", workspaceId required at creation)

Data-plane scopes only (memory / entities / reasoning / ontology / skills), permanently bound to one workspace. X-Workspace-Id is optional; sending a different workspace id is rejected with 403 — a bound key is never silently retargeted. Cannot mint or manage keys. This is the key you put in application code.

Admin key
(category: "admin", the default when category is omitted)

Account-wide, all scopes including workspace:admin. Not bound to a workspace — name the target per request with X-Workspace-Id. Use for administrative automation, never in a shipped app.

The service also checks that the key’s owner is a member of the resolved workspace; non-members get 403. See Tenancy & scoping.

Configuring the SDK

The SDKs consume an existing key — set it once and the backend auto-selects NAMS:

Variable Purpose

MEMORY_API_KEY

Your nams_… key. When set (and NAM_BACKEND is unspecified) the backend defaults to NAMS.

MEMORY_ENDPOINT

Override the service URL (default https://memory.neo4jlabs.com/v1).

MEMORY_WORKSPACE_ID

Workspace id, lifted into NamsConfig.workspace_id and sent as X-Workspace-Id.

NAM_BACKEND

Force bolt or nams.

NAM_NAMS__*

Nested NamsConfig overrides (timeout, retries, …).

See Environment variables and Configuration for the full list.

Key lifecycle

Keys are created and managed from the dashboard at memory.neo4jlabs.com or directly over REST:

Operation REST

Create

POST /v1/auth/api-keys — body {label, category, workspaceId?}. Returns the raw key once; also stored encrypted-at-rest so it can be revealed later.

List

GET /v1/auth/api-keys

Reveal

GET /v1/auth/api-keys/{id}/reveal — owner-only (non-owners get 404; revoked or pre-encryption keys get 410).

Rotate

POST /v1/auth/api-keys/{id}/rotate — mints a new key and revokes the old one.

Revoke

DELETE /v1/auth/api-keys/{id} — added to a blocklist for 30 days; effective on the next request.

Key management requires a user token or an admin key, so a leaked workspace data key cannot mint or manage keys. Keys are owner-private: only the user who created a key can list, reveal, rotate, or revoke it — they are invisible even to a workspace owner.

Managing keys from the SDK

Both SDKs expose the lifecycle on a client.auth accessor (Python NamsAuth, TypeScript AuthClient). On the bolt backend client.auth is a NotSupportedError sentinel — bolt uses NEO4J_PASSWORD, not a NAMS key.

# All async; NAMS only. `.key` (plaintext) is populated only on create/reveal.
keys     = await client.auth.list_api_keys(workspace_id="ws_123")   # metadata only
revealed = await client.auth.reveal_api_key(key_id, workspace_id="ws_123")
rotated  = await client.auth.rotate_api_key(key_id)   # mints a replacement, revokes the old
await client.auth.revoke_api_key(key_id)              # effective on the next request
print(rotated.key)                                    # shown once

TypeScript mirrors these as client.auth.listApiKeys / revealApiKey / rotateApiKey / revokeApiKey, plus refreshAccessToken for the JWT flow.

create_api_key(label, , scopes=…​, workspace_id=…​) exists on the SDK, but it does not send a category, and the service defaults an un-categorised create to an *admin key (which rejects a workspaceId). To create a workspace key — the usual case for application code — use the dashboard, or POST /v1/auth/api-keys with category: "workspace" directly. See REST API.

Handling keys safely

  • nams_… values are the credential; the service stores them encrypted-at-rest but transmits and displays them as plaintext, so examples and .env.example files in this repository use nams_xxxx placeholders, never a real key.

  • A workspace key is bound to one workspace (see "Key categories" above); using a separate key per environment (dev/staging/production) confines a single leak to one workspace instead of the whole account.

  • Rotation (POST /v1/auth/api-keys/{id}/rotate) mints a replacement and revokes the original; revocation alone (DELETE /v1/auth/api-keys/{id}) also works. Both take effect via the 30-day blocklist described under "Key lifecycle" above.

  • The Auth0 JWT exchange issues a short-lived (≈1h) token for dashboard/browser sessions; a nams_… API key carries the longer ~90-day static-key window noted under "Key format & the wire" above, so it is the higher-exposure credential to embed in a browser context.

See also