Skip to content

Authentication

Matt Dula edited this page Apr 18, 2026 · 2 revisions

Authentication

Two flavors, same header.

API keys — the agent path

Format: nk_<prefix>_<secret> (stored as SHA-256 hash; the plaintext is shown exactly once at creation).

Create:

curl -X POST http://localhost:8000/workspace/api-keys \
  -H "Authorization: Bearer <jwt-or-existing-key>" \
  -H "Content-Type: application/json" \
  -d '{"name":"sdr-agent","role":"member","rate_limit_per_minute":120}'

Response includes "key": "nk_..."save it now. There is no recovery flow. A new key mints in about a second if you lose one, but the lost one has to be revoked.

Use:

Authorization: Bearer nk_<prefix>_<secret>

On every request. The workspace is inferred from the key — no X-Workspace header needed.

Revoke:

curl -X DELETE http://localhost:8000/workspace/api-keys/<key-id> \
  -H "Authorization: Bearer <admin-key>"

This sets revoked_at; subsequent requests with the old key get 401.

User JWTs — the human path

Two endpoints:

Signup (creates workspace + owner)

curl -X POST http://localhost:8000/auth/signup \
  -H "Content-Type: application/json" \
  -d '{
    "email": "you@example.com",
    "password": "correct-horse-battery-staple",
    "workspace_name": "Acme",
    "workspace_slug": "acme"
  }'

Login (for existing users)

curl -X POST http://localhost:8000/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","password":"..."}'

Both return:

{
  "access_token": "eyJ...",
  "token_type": "bearer",
  "user_id": "...",
  "workspace_id": "...",
  "workspace_slug": "acme",
  "expires_in_seconds": 3600
}

Use:

Authorization: Bearer <jwt>
X-Workspace: acme         # slug or id; required when the token covers multiple workspaces

Roles

Every membership and every API key carries a role:

Role Can do
owner everything, including revoke other owners
admin everything except revoke the last owner
member read + write CRM; cannot mint API keys or edit workspace config
readonly read only

Assign your SDR agents member. Never give an agent owner.

Rate limits

API keys can carry a per-key rate_limit_per_minute. See Rate-Limiting.

Expiry

Set expires_at at create time to auto-expire a key. Useful for short-lived tokens you hand to a contractor's agent.

Security considerations

  • Keys are stored as SHA-256 digests, never plaintext.
  • Webhook secrets (separate from API keys) are random 64-char hex, also generated server-side and shown once.
  • Passwords use bcrypt (pinned to 4.0.1 for passlib compatibility).
  • SECRET_KEY signs JWTs — set it to openssl rand -hex 32 in production and never commit it.

See also: SECURITY.md.

Clone this wiki locally