Curiosity

Token scopes

Curiosity Workspace issues three kinds of bearer tokens. Choosing the right one — and scoping it tightly — is the single biggest lever for getting external integration security right.

Token type Issued by Used by Scope dimension
User session JWT the login flow (password, SSO, SAML) the built-in front-end and custom interfaces the user's permissions (ReBAC)
API token Settings → API Tokens data connectors, CLI scripts, CI jobs named scopes (ingestion, read, …)
Endpoint token Settings → Custom Endpoints → Tokens external systems calling specific endpoints path-scoped to one or more endpoints

All three are sent the same way:

Authorization: Bearer <token>

User session JWT

Issued automatically when a user signs in. Carries the user's UID and team memberships. The graph and search engines use it to compute permission filters via the ReBAC model.

You should rarely need to handle session JWTs explicitly. The built-in chat, search, and graph views attach them automatically; custom interfaces written with Tesserae do the same.

If you're scripting against a Workspace as a specific user (for example, in a test), call /api/login/create with credentials and reuse the returned token.

API tokens

Long-lived tokens issued from Settings → API Tokens. Each token is bound to one or more scopes. A scope is a logical capability — not a path — and is enforced by the controllers behind the matching endpoints.

Use case Recommended scopes Why
Data connector that only writes data ingestion Lets the connector create/update nodes and edges. No read access to other tenants' data.
Read-only export script read, search Can run graph queries and searches but cannot mutate.
Backup job that snapshots and restores admin:backup Limited admin capability without full admin rights.
Operator script triaging tasks admin:tasks Limited admin capability for scheduled-task management.
CI smoke test on staging read, endpoints:run Lets CI hit custom endpoints during deploy validation.

Least-privilege recipes

Ingestion-only connector
  1. Create a token named after the connector (crm-sync-connector).
  2. Grant the ingestion scope. Do not grant admin or read on other types.
  3. Store in your secret manager; rotate on a schedule.
  4. If the connector also needs to ingest ACLs (RestrictAccessTo*), add the ingestion:acl scope. ===
Read-only export to a data warehouse
  1. Create a token named after the consumer (bi-warehouse-export).
  2. Grant read and search.
  3. The token will only return content the workspace would show to the system — it does not bypass per-user ACLs. If you need per-user data, use endpoint tokens with CreateSearchAsUserAsync. ===
External system calling one custom endpoint

Use an endpoint token (next section) instead of an API token. Endpoint tokens are path-scoped and cannot be repurposed.

Endpoint tokens

Issued per custom-endpoint or per group of endpoints. They are the right tool when an external system needs to call a specific endpoint and nothing else.

Properties:

  • Path-scoped: a token valid for /api/endpoints/run/similar-tickets cannot call /api/endpoints/run/admin-rebuild-index.
  • Optionally act-as a specific user, in which case CurrentUser inside the endpoint is that user and ReBAC applies.
  • Issued from the endpoint editor; rotated independently of API tokens.

Choosing between API and endpoint tokens

Question Use API token Use endpoint token
Does the caller need access to many resources (graph, search, ingestion)? yes
Does the caller only need to invoke one or a few endpoints? yes
Does the caller need to run as a specific Workspace user? yes
Will the credential be embedded in a third-party system you don't control? yes (smaller blast radius)

Rotation and revocation

  • Rotation: create the new token, deploy it to consumers, then revoke the old one. Workspace tokens are stateless JWTs, so revocation lists are checked on every call.
  • Compromise response: from Settings → API Tokens, click Revoke. The token is rejected starting on the next request.
  • JWT key rotation: setting a new MSK_JWT_KEY invalidates every outstanding token. Use this only when responding to a key-material compromise; you'll need to reissue all tokens afterward.

Sending tokens correctly

Always use the Authorization: Bearer … header. Avoid query-string tokens — they leak into proxy logs and browser histories.

curl -X POST "https://workspace.example.com/api/endpoints/run/similar-tickets" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"query":"screen flicker"}'

Errors

HTTP status Meaning What to do
401 Unauthorized Missing or malformed token Add the Authorization header.
401 Unauthorized with WWW-Authenticate: Bearer error="invalid_token" Signature invalid (wrong MSK_JWT_KEY) or expired Re-issue the token.
403 Forbidden Token is valid but missing the scope or path required Re-issue with the correct scope or endpoint path.
404 Not Found on a custom endpoint Endpoint doesn't exist, or token's path scope excludes it Check the endpoint name and the token's allowed paths.

See the full list in Error codes.

  • API Usage — the request shape and auth flow.
  • Custom Endpoints — how to write the server side that consumes these tokens.
  • Security — the surrounding operational practices.
© 2026 Curiosity. All rights reserved.
Powered by Neko