Skip to content
Get started

Authentication & scopes

#Two ways to authenticate

Every /v1/* route accepts one of two credentials:

Credential Header Who uses it Resolves to
API key Authorization: Bearer afb_live_... Servers, scripts, the SDK { workspaceId, keyId, scopes, actor: "api_key" }
Session cookie Cookie: afb_session=... The console web app { userId, workspaceId, role, actor: "session" }

Routes under /v1/console/* are session-only (they exist for the console's own UI); every other route accepts either. A session request resolves its workspace from the X-Workspace-Id header, or your first workspace if that header is absent.

#Scopes

An API key is issued with one or more scopes, checked per route. There is no partial-scope wildcard — request exactly the scopes an integration needs:

Scope Grants
runs:read List/get runs, steps, output, artifacts, live events
runs:write Create runs; submit input/approve/review/cancel
agents:read List/get agents and their versions; list presets and models
agents:write Create agents and agent versions; manage evals
files:write Upload files
feedback:write Submit run feedback
webhooks:manage Manage webhook subscriptions and save-output connections
usage:read Read usage totals and balance
keys:manage Create/list/revoke API keys

A request with a valid key but a missing scope gets 403 { "error": { "code": "forbidden", ... } }, not 401 — the key itself is valid, it just isn't allowed to do that particular thing. A session request is instead checked against the caller's workspace role (owner/admin/member) for the closest matching action; a role that can't perform it also gets 403.

#Creating and revoking keys

bash
# Create
curl -X POST "https://api.agentflowbind.com/v1/api-keys" \
  -H "Authorization: Bearer afb_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Nightly reconciliation job", "scopes": ["runs:read", "runs:write", "files:write"] }'

# List (plaintext key is never returned again after creation)
curl "https://api.agentflowbind.com/v1/api-keys" -H "Authorization: Bearer afb_live_xxxxxxxxxxxx"

# Revoke
curl -X DELETE "https://api.agentflowbind.com/v1/api-keys/8f1e2b0a-...-c9d3" \
  -H "Authorization: Bearer afb_live_xxxxxxxxxxxx"

DELETE returns 204 No Content; revoking an already-revoked or unknown key id returns 404.

#Rate limits

Every key-authenticated request is rate-limited per minute, tracked per key. The limit itself comes from the calling workspace's plan (entitlements.apiRateLimitPerMin) — indicative Phase 0 values:

Plan Requests/min
Free trial 30
Starter 60
Business 300
Scale 1,200

A request over the limit gets:

text
HTTP/1.1 429 Too Many Requests
Retry-After: 37

{ "error": { "code": "rate_limited", "message": "Rate limit exceeded" } }

Retry-After is in seconds — back off at least that long before retrying. See Errors & rate limits for the full error-code reference, including the separate, lower rate limits on /v1/console/auth/sign-in and /sign-up.

#Unverified accounts

A session-authenticated (console) user whose email isn't verified yet can still use runs:write for a limited number of runs (AFB_UNVERIFIED_RUN_LIMIT, default 3) before POST /v1/runs starts returning:

json
{ "error": { "code": "auth.email_verification_required", "message": "Verify your email to create more runs (the unverified limit of 3 has been reached)" } }

at 403. This limit only applies to session callers — an API key request is never subject to it.