Skip to main content

Authentication

Every binary accepts two credentials, and which one a route accepts is part of its contract. Both are sent the same way:

Authorization: Bearer <credential>

Session tokens​

A signed JWT, issued by POST /api/auth/login and by the signup verification endpoint. This is what the web console uses.

The token carries only two claims — the user id and the organization id:

{ "uid": 4, "org_id": 2, "exp": 1789459200 }

Everything else about the caller — their role, their email, whether they must change their password — is read from the database on each request. That is deliberate: a role change or an org approval takes effect immediately rather than at the next token issuance.

The token lifetime is set per deployment when the issuer is constructed.

API keys​

For server-to-server integration. Every key starts with nn_k_:

nn_k_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Keys are stored hashed. The plaintext is shown exactly once, at creation. See API keys for issuing and revoking them.

A key carries scopes; a session carries a role. They are not interchangeable, and a request never carries both credentials.

Roles​

Three org roles, ranked. A check asks for "manager or above" rather than listing acceptable roles.

RoleIntended for
adminRuns the account — team, API keys, org identity, telephony credentials
managerDay-to-day configuration, but cannot change who has access
memberDay-to-day work without changing how anything is configured

Superadmin is not in this list. It is a platform-level capability derived from the caller's email domain (SUPERADMIN_EMAIL_DOMAINS / SUPERADMIN_EMAIL), checked separately. A superadmin bypasses RequireRole entirely and can list and enter any organization on the deployment.

Use only these three values

admin, manager, and member are the roles the authorization layer recognises. Any other value carries no privileges and is refused by every role-gated route.

A member can never create an account above their own role.

Scopes​

Scopes are app-specific vocabulary. Nothing in any app's scope list grants member management, telephony credentials, or key issuance — those stay behind a human admin session on purpose.

nextneural_converse​

ScopeAllows
calls:readList calls, read transcripts and statistics
calls:writeTrigger and modify calls
customers:readList and read contacts
customers:writeCreate, update, and delete contacts
flows:readList campaigns and inbound flows
flows:writeCreate and modify flows
webhooks:readList webhooks and delivery logs
webhooks:writeRegister and delete webhooks

nextneural_assist​

ScopeAllows
customers:read / customers:writeRead / manage customers
calls:read / calls:writeRead live calls / start calls
agents:readList agents
kb:read / kb:writeRead / manage knowledge base documents

nextneural_intel​

ScopeAllows
sales:analyses:readRead submitted analyses and field labels
sales:analyses:writeSubmit analyses
sales:simulations:judgeScore a simulated run (used by nextneural_coach)

nextneural_coach​

Keys authenticate against the same routes as a session. Role-gated routes (manager and admin actions such as creating personas or scenarios) refuse key credentials outright.

Choosing a credential​

You areUse
The web console, or a script acting as a personSession token
A backend service, CRM sync, or scheduled jobAPI key
Performing an admin action (members, keys, telephony)Session token — keys are refused

Rejections​

StatusBodyCause
401{"detail": "missing bearer token"}No Authorization header
401{"detail": "invalid or expired token"}Bad or expired session JWT
401{"detail": "invalid or revoked API key"}Key not found or revoked
401{"detail": "this endpoint requires a signed-in user session, not an API key"}Key sent to a session-only route
403{"detail": "this action requires the admin role"}Role too low
403{"detail": "this action requires a signed-in admin, and is not available to API keys"}Key sent to a role-gated route
403{"detail": "insufficient scope", ...}Key lacks the scope the route requires
403{"detail": "organization setup is required before using the app", "needs_org_setup": true}Account has no organization
403{"detail": "your organization is awaiting approval", "needs_waitlist_approval": true}Organization is pending or rejected
403{"detail": "set your own password before using the app", ...}Invited user has not set a password

See Errors for the full list and the response shape.

WebSocket authentication​

Browser WebSocket clients cannot set an Authorization header, so live-stream endpoints take the token as a query parameter instead:

wss://your-host/api/voice/ws/live/<call_uuid>?token=<session-token>

The same applies to a handful of media endpoints loaded by plain <audio> and <img> tags, such as nextneural_intel's recording playback, which accept the token in either the header or the query string.