Bearer API keys, session cookies, scopes and the default rate limit: how a request to the Agent Flow Bind API is authenticated.
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.
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, report, dashboard, live events; recent run input files; local-runner batches; workspace search |
runs:write |
Create runs; submit input/approve/review/cancel; share/unshare a run's dashboard; start, continue and end tutor sessions |
agents:read |
List/get agents, their versions, evals and improvement proposals; an agent's definition (its instructions only with runner:run); knowledge documents; projects and their members; presets, models, tools, price lists and pricing rules; agent-copilot and tutor sessions |
agents:write |
Create/update/delete agents and agent versions; run evals; create/accept/reject improvement proposals; the agent copilot; knowledge documents; price lists and pricing rules; projects and their members |
files:read |
List the file library and its folders; download a file |
files:write |
Upload files; rename, move and delete files; create, rename and delete folders |
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; set a key's monthly spend cap |
apps:manage |
Create/update/publish/pause/delete published apps; custom domains; per-app usage; app templates |
phone:read |
List/get phone agents, numbers and calls; voices and models; the booking calendar |
phone:manage |
Create/update/delete phone agents and numbers; rotate their secrets; place outbound calls; change the booking calendar. Also required, beside agents:write, to create, change or delete an agent built on a phone preset |
finance:read |
List transactions, imports, rules and categories; the summary; export |
finance:write |
Import bank exports; edit transactions; delete rules; start reconciliation and report runs |
data_sources:read |
List/get data sources; browse their objects and schema; read from them; list/get and refresh saved tables; the data-source copilot |
data_sources:manage |
Create/update/delete/test data sources; upload files to a file source; create/delete saved tables |
flows:read |
List/get flows, their runs and the flow templates |
flows:manage |
Create flows (also from a template), update and delete them; run one manually; regenerate its webhook token; answer a flow run's approval step |
whatsapp:read |
List WhatsApp accounts; read conversations |
whatsapp:manage |
Connect, update and remove WhatsApp accounts |
runner:run |
Report a local runner batch (POST /v1/local-runs); with agents:read, adds the agent's instructions and its linked tables' saved reads to GET /v1/agents/:id/definition |
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.
# 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.
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:
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.
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:
{ "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.