docs/api/authentication.md

Authentication

The API uses tenant-scoped Bearer tokens. One token = one organization = one tenant context.

The dashboard and oRPC tier authenticate with auth.js (next-auth) — email + password by default (sign in at /sign-in). The only required auth env var is AUTH_SECRET (alongside DATABASE_URL); generate it with openssl rand -base64 32. Clerk is an optional VOCION_AUTH_PROVIDER=clerk path for hosted/cloud builds, not the default.

Issuing a token

Dashboard: Admin → API tokens → Create token

A token carries:

FieldNotes
nameHuman-readable label (e.g. "field-notes-mobile-app")
scopeDefault read:all,run:invoke. Can narrow to e.g. read:skills,run:invoke
expiresAtOptional. Default: no expiry. Rotate routinely for production systems

The token is shown once at creation. Store it in your secret manager; the dashboard only keeps a vcn_live_... prefix and a hash after that.

Format

vcn_live_<token-id>_<secret>

The <token-id> is a stable public handle (safe to log / show in the dashboard); the <secret> is stored only as a SHA-256 hash. Prefix signals environment: vcn_live_ for production, vcn_test_ for non-production.

Sending requests

Authorization: Bearer vcn_live_...

Tokens are principals

A token isn't just an authentication credential — it resolves to a permission principal: a role, a set of action grants, and an org scope. Every write the token makes is authorized against the same model as a human user and lands in the same review queue. There is no separate "service account" authorization path. Deciding a review (POST /api/v1/reviews/decide), for instance, requires the approve capability — a token without it is rejected before anything happens.

Scopes

ScopeWhat it permits
read:allGET on any resource or run within the tenant
read:skillsNarrow: only GET /agents, /skills
write:resourcesPOST/PATCH on resource CRUD (equivalent to workspace-as-code writes)
run:invokePOST /skills/:slug/runs, POST /workflows/:slug/runs
run:approveApprove/reject pending runs

Default new token: read:all,run:invoke. Widen or narrow per use case.

Rate limits

  • Default: 60 requests/minute per token
  • Bursts up to 120 tolerated
  • X-RateLimit-Remaining + X-RateLimit-Reset headers on every response
  • 429 with Retry-After when exceeded

Enterprise tier: raise limits or disable entirely.

Rotation

# Create new token, test, then revoke old
curl -H "Authorization: Bearer $NEW_TOKEN" https://.../api/v1/agents
# If good:
curl -X DELETE -H "Authorization: Bearer $NEW_TOKEN" \
  https://.../api/v1/tokens/$OLD_TOKEN_ID

Revocation is instant — no grace period.

Token audit

Every API call records api_token_id on the skill_run / workflow_run / audit_event it produced. Full trail: who (token) triggered what, when, and what happened.