Authentication & API Keys

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 Behaviour

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

  • Never commit a key. Keep nams_… values in environment variables or a secrets manager; commit only nams_xxxx placeholders in examples and .env.example files.

  • Scope per environment. Use separate keys (and workspaces) for dev, staging, and production so one leak is contained.

  • Rotate on exposure. If a key leaks, rotate (or revoke + create); revocation is effectively immediate via the blocklist.

  • Prefer short-lived tokens for browser contexts. Use the Auth0 JWT exchange for dashboard/user sessions rather than embedding a long-lived API key in a client.