Skip to main content
AgentFlow API routes use bearer-token authentication. In production, the backend verifies Auth0-issued JWTs directly, binds the request to a tenant, and uses that tenant context for databases, tools, knowledge bases, artifacts, conversations, LLM settings, usage tracking, and background jobs.

Human requests

Browser or user-facing clients send an Auth0 access token in the Authorization header.

Machine requests

Services use Auth0 client credentials plus explicit tenant and acting-user headers.

Local development

DEV_AUTH_BYPASS=true allows unauthenticated local requests when no bearer token is sent.

SDK profiles

The Python SDK stores device, M2M, or dev auth settings in named profiles.

Runtime contract

Every authenticated request resolves to:
FieldSourcePurpose
User IDJWT sub, Auth0 /userinfo, snc-userid, or local dev envUser attribution, memory, usage, and audit context.
TenantNamespaced Auth0 tenant claim, M2M tenant claim, or local dev envSelects the tenant database and tenant-scoped resources.
Principal typeUser token, client-credentials token, or local dev userSeparates human and machine usage attribution.
Customer-facing REST routes live under /api/v1. System probes such as /health, /ready, /docs, and /openapi.json stay unversioned.

Human bearer tokens

Human clients send an Auth0 access token:
curl -H "Authorization: Bearer $AUTH0_ACCESS_TOKEN" \
  https://api.example.com/api/v1/agents
The backend verifies:
  • iss against AUTH0_ISSUER, or https://$AUTH0_DOMAIN/ when AUTH0_ISSUER is unset.
  • aud against AUTH0_AUDIENCE.
  • Signature through the issuer JWKS.
  • Allowed algorithms from AUTH0_ALGORITHMS (RS256 by default).
After token verification, AgentFlow reads user profile and tenant metadata from Auth0 /userinfo, with a DynamoDB-backed cache when configured.

Tenant claim parsing

Tenant metadata is read from the configured Auth0 custom claim namespace. Defaults match the Syncly claim shape:
AUTH0_CLAIM_NAMESPACE=https://syncly.us/
AUTH0_CLAIM_KEY=syncly
The backend accepts namespaced custom claims, then normalizes them under AUTH0_CLAIM_KEY. A typical token or /userinfo payload contains tenant data like this:
{
  "sub": "auth0|user_123",
  "https://syncly.us/tenant": "agentflow"
}
or this normalized shape:
{
  "sub": "auth0|user_123",
  "syncly": {
    "tenant": "agentflow"
  }
}
The resolved tenant must be a valid tenant/database name.

Machine clients

Machine-to-machine requests use Auth0 client credentials. The token must verify against the M2M issuer and audience, and it must include the required scope when AUTH0_M2M_REQUIRED_SCOPE is set.
AUTH0_M2M_AUDIENCE=https://api.example.com
AUTH0_M2M_ISSUER=https://your-tenant.us.auth0.com/
AUTH0_M2M_REQUIRED_SCOPE=agentflow:m2m
If the M2M-specific issuer or audience is unset, AgentFlow falls back to AUTH0_ISSUER and AUTH0_AUDIENCE. Request a token from Auth0:
curl -X POST https://your-tenant.us.auth0.com/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "$AUTH0_CLIENT_ID",
    "client_secret": "$AUTH0_CLIENT_SECRET",
    "audience": "https://api.example.com",
    "scope": "agentflow:m2m"
  }'
Then call AgentFlow with the token plus explicit tenant and acting-user headers:
curl -H "Authorization: Bearer $AUTH0_M2M_ACCESS_TOKEN" \
  -H "snc-tenant: agentflow" \
  -H "snc-userid: svc-agentflow" \
  https://api.example.com/api/v1/agents
Machine-token requests are rejected unless:
  • The token is a client-credentials token.
  • The token tenant claim exists.
  • The token tenant claim matches the snc-tenant header.
  • snc-userid is present and non-empty.
  • The required M2M scope is present when configured.
Machine tokens that act on behalf of a user must include explicit delegated-user scopes in addition to snc-tenant and snc-userid headers. Conversation APIs require agentflow:conversations; cancellation APIs require agentflow:cancellations. Without those scopes, AgentFlow rejects the request instead of trusting arbitrary on-behalf-of headers.

Local development auth

For local backend work, set:
DEV_AUTH_BYPASS=true
DEV_TENANT_NAME=agentflow
DEV_USER_ID=local-dev
When DEV_AUTH_BYPASS=true and no Authorization header is sent, AgentFlow uses the local dev tenant and user. If a bearer token is sent, the backend verifies it normally. Example local smoke test:
curl http://localhost:8001/api/v1/agents
DEV_AUTH_BYPASS=true is rejected when ENVIRONMENT is production-like. Keep it unset outside local development.

Python SDK

Install the private SDK from your onboarding package source:
uv add agentflow --default-index https://<package-index>/simple/
# or
python -m pip install --index-url https://<package-index>/simple/ agentflow
Create a user profile with the Auth0 device flow:
agentflow login \
  --endpoint https://api.example.com \
  --profile prod \
  --method device \
  --client-id <auth0-client-id> \
  --audience <auth0-api-audience> \
  --auth0-domain <auth0-domain>
Create a local dev profile:
agentflow login \
  --endpoint http://localhost:8001 \
  --profile local \
  --method dev
The M2M login flow prompts interactively for the client secret, tenant, and acting user:
agentflow login \
  --endpoint https://api.example.com \
  --profile service \
  --method m2m \
  --client-id <auth0-client-id> \
  --audience <auth0-api-audience> \
  --auth0-domain <auth0-domain>
For non-interactive services and CI, construct the client from environment variables or secrets:
from agentflow import AgentFlow

client = AgentFlow(
    endpoint="https://api.example.com",
    client_id="<auth0-client-id>",
    client_secret="<auth0-client-secret>",
    tenant="agentflow",
    on_behalf_of="svc-agentflow",
    scope="agentflow:conversations agentflow:cancellations",
    audience="<auth0-api-audience>",
    auth0_domain="<auth0-domain>",
)
Use a saved profile in application code:
from agentflow import AgentFlow

with AgentFlow.from_profile("prod") as client:
    agent_id = client.agents.by_name("MainAgent").id
    result = client.agents.run(agent_id=agent_id, message="What can you do?")
    print(result.text)
If you already have a bearer access token, pass it directly:
from agentflow import AgentFlow

client = AgentFlow(endpoint="https://api.example.com", access_token="<access-token>")
export AGENTFLOW_ENDPOINT=https://api.example.com
export AGENTFLOW_ACCESS_TOKEN=<access-token>
Do not invent an AgentFlow-specific af_... API key. SDK bearer-token examples use access_token / AGENTFLOW_ACCESS_TOKEN.

Required environment

AUTH0_DOMAIN=your-tenant.us.auth0.com
AUTH0_AUDIENCE=https://api.example.com
AUTH0_ISSUER=https://your-tenant.us.auth0.com/
AUTH0_ALGORITHMS=RS256

AUTH0_M2M_AUDIENCE=https://api.example.com
AUTH0_M2M_ISSUER=https://your-tenant.us.auth0.com/
AUTH0_M2M_REQUIRED_SCOPE=agentflow:m2m

AUTH0_CLAIM_NAMESPACE=https://syncly.us/
AUTH0_CLAIM_KEY=syncly
For local-only development, use DEV_AUTH_BYPASS=true instead of Auth0.

Multi-tenancy

See how tenant context maps to isolated AgentFlow data and runtime state.

API authentication

See the bearer-token contract used by REST clients.