Authentication
NexSpace supports three authentication methods. Choose based on your use case.API Keys
API keys are the simplest way to authenticate. Each key has scopes that control what it can access.Minting a key is currently restricted. All of
/api/api-keys
(create, list, revoke, rotate) sits behind a guard that requires a dashboard
session or JWT and either the super_admin role or an admin/manager role
resolved into the platform-internal NexSpace 365 organization. An ordinary
customer admin gets 403 FORBIDDEN —
“API key management is restricted to the NexSpace 365 organization”.If you’re integrating as a customer, use the
OAuth 2.1 connector flow or
nexspace login device-code login (both work for any dashboard user), or
email admin@nexspace365.com to request a key. See
Quickstart → Step 1 for the decision table.Key Prefixes
Sandbox (
nex_test_) keys share the production database but only see is_sandbox rows, skip real notifications and outbound platform webhooks, and are omitted from usage analytics totals. See Sandbox (test API keys) for isolation rules, TTL, and scope limits.
Scopes
Scopes follow the patternresource:action. Wildcard resource:* grants all
actions on a resource — crm:*, for example. The resource half must be a real
resource name; a bare * or *:* is rejected by the create validator with
400 VALIDATION_ERROR.
What a scope means depends on the surface
- MCP (
mcp.nexspace365.com) — scope only ever narrows. A tool call is first checked against the credential’s scopes, then executed through the governed path using the bound user’s role, so suite entitlements, RBAC, and AI autonomy rules still apply on top. A credential cannot exceed its user’s own access. - REST (
api.nexspace365.com) — scope is the check. For an API-key-authenticated request, the permission guard is satisfied by scopes alone and the bound user’s RBAC is never consulted. A key carryingpayroll:readpasses thepayroll.viewguard onGET /api/payroll/paymentseven if the user it belongs to has no payroll permission in their role.
nex_pat_ bound to a
super_admin still cannot do super_admin things unless that scope was
explicitly granted at key creation. Treat a REST key as its own privilege
grant, not a projection of its owner’s role, and grant the narrowest scope set
that works.
Core workforce scopes (gate the named MCP verbs and their REST
equivalents):
Category scopes (gate the platform tool registry over MCP, mirroring the
AI-configuration categories):
Connected apps & agents:
Permission names are not scope names
REST routes are gated on RBAC permission names (dot-separated, e.g.integrations.manage_webhooks). Credentials carry scopes
(colon-separated, e.g. integrations:write). They are different vocabularies,
and the error you get on a mismatch names the permission, not the scope you
need to grant. The resolution rules, in order:
- Exact match —
shifts:writesatisfiesshifts.write. The dot is normalized to a colon before comparison. - Resource wildcard —
crm:*satisfies anycrm.*permission. - Action aliasing — the coarse catalog actions
readandwritecover a set of finer RBAC verbs.readcoversview,view_credentials,read,list.writecoverscreate,edit,update,delete,deactivate,manage_credentials,manage_webhooks,write.
agents:run and agents:approve gate the headless run endpoints
(POST /api/agents/:id/runs and the approval resolution routes). They do
not unlock the agent-builder routes gated on agents.manage.Default connector scopes
MCP connector clients (claude.ai, ChatGPT, Gemini) typically omitscope
from the OAuth request because they don’t know a server’s catalog up front.
A scope-omitted request receives the default connector bundle: every
read scope in the catalog and nothing else — no writes, no apps:*, no
agents:*. The consent page shows exactly this list, and the issued token
carries it explicitly, so omitting scope can never escalate to an unscoped
token. To use write tools or connected-app actions from a connector, request
those scopes explicitly.
Key Rotation
Rotate keys with zero downtime — the old key keeps authenticating for 24 hours after the rotation, then stops. Rotation is a dashboard-session operation by design. An API key must never be able to mint or rotate another key: API-key requests authorize on their scopes rather than their owner’s role, so a low-scope token could otherwise issue itself a fully-scoped one. SendingAuthorization: Bearer nex_live_… to this endpoint returns 403 FORBIDDEN —
“API keys cannot be managed with an API key — sign in to the dashboard”.
Rotate from Settings → API Keys in the dashboard, or with a session cookie:
nexspace.sid on the shared-domain deployment;
a local instance without SESSION_COOKIE_DOMAIN set uses connect.sid.)
From the CLI, log in with the device-code flow first (nexspace login
without --token) so the stored credential is a session-class token rather
than an API key:
key. Swap it
into your deployment within the 24-hour window; after that the old key resolves
as revoked. Like every other /api/api-keys route, rotation additionally
requires NexSpace 365 organization membership (see the note above).
OAuth 2.1 + PKCE
For apps where users grant access through a browser flow.Discovery
Dynamic Client Registration (RFC 7591)
Clients can self-register without operator intervention:Authorization Flow
- Redirect user to
/oauth/authorizewith PKCEcode_challenge(S256) - User logs in and grants scopes
- Exchange
code+code_verifierat/oauth/token - Receive
access_token+refresh_token
Device Code Flow (RFC 8628)
For CLI tools and environments without a browser.nexspace login runs the
device-code flow by default (opening your browser); pass --token nex_pat_… to
store a personal access token instead:
~/.nexspace/config.json (mode 0600). The CLI refreshes the OAuth access
token automatically as it nears expiry, rotating the stored refresh token.
Verify identity
GET /api/auth/me returns the identity a credential authenticates as. It
accepts any supported credential — API key (nex_live_/nex_test_),
personal access token (nex_pat_), OAuth 2.1 access token, or first-party
session — so it’s the canonical “who am I” probe for SDKs, MCP clients, and the
CLI (nexspace whoami).
401.
Token Introspection (RFC 7662)
Resource servers can check whether a token is active and inspect its metadata. The caller authenticates as the client that owns the token — public clients (PKCE: CLIs, SPAs) presentclient_id alone; confidential clients also present
client_secret. Works for both access tokens (JWT) and refresh tokens.
{ "active": false } (never an error):
Token Revocation (RFC 7009)
Revoke a token when a session ends or a credential is compromised. Revoking a refresh token invalidates it and its entire rotation chain; revoking an access token invalidates the associated refresh chain so it can’t be renewed (the stateless access JWT itself remains valid until its short 1-hourexp).
Per RFC 7009 the response is always 200 with an empty body.
nexspace logout clears stored credentials locally (keychain + config file).
Rate Limits
Every API-key-authenticated response onapi.nexspace365.com includes
rate-limit headers:
Default: 60 requests/minute per API key. Configurable per key up to 6,000/min.
Session- and JWT-authenticated requests are excluded — they are governed by
a separate role/IP limiter and carry none of these headers, so don’t build a
throttle that depends on them when calling from a browser session.
MCP requests are governed separately:
mcp.nexspace365.com runs its own
limiter with IETF RateLimit-* headers and a higher default budget
(120/min unless the credential is an API key, which uses its own per-key
limit). See Rate Limits.

