Management API
Everything your backend needs to provision a tenant without the admin dashboard: create users and orgs, define roles and packages, wire access, and stream audit events. All server-to-server, authenticated with a machine token.
Get a machine token
Open API and credentials → Generate credential in the dashboard and give it a name, a target environment, a purpose, and scopes:
- Purpose decides which scopes even exist in the picker — this is the first decision, and it can't be changed later. Choose Product runtimeif this credential's backend also calls the auth endpoints your product uses at request time (minting tokens, reading
/me, step-up, account-security). Choose Management automation onlyif it exclusively provisions tenant data (users, roles, packages) and never touches a live user session. Pick Product runtime if you host your own login pages — you almost certainly need both families on one credential, and “Management automation only” hides the runtime scopes from the picker entirely rather than merely leaving them unselected. - Scopes are frozen at creation, along with purpose. Getting the purpose and scope list right the first time avoids creating a replacement credential and cutting every caller over — see the full scope catalogue below.
You receive a client_id (ac_…) and client_secret (as_…) with exactly the scopes you granted. Exchange them for a short-lived, EdDSA-signed machine token at the token endpoint:
curl -X POST https://auth.example.com/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=ac_xxxxxxxx" \
-d "client_secret=as_xxxxxxxx" \
-d "scope=users:write roles:read"
# → { "access_token":"…", "token_type":"Bearer", "expires_in":900,
# "scope":"users:write roles:read" }Send it as Authorization: Bearer <access_token> on every management call. The token is audience-bound to authreads-management-api and tenant-scoped — it can only touch your own tenant.
expires_inelapses, then refresh proactively. Requesting a fresh token on every API call is the fastest way to hit the token endpoint's rate limit.Scopes
Scopes are granted per credential — request only what the caller needs. There are nineteen scopes in total: ten Management scopes (available to any purpose) and nine runtime scopes (available only on a Product runtime credential — see purpose, above). This is the complete catalogue; nothing is granted implicitly.
Management scopes — any purpose
| Resource | Read | Write |
|---|---|---|
| Users | users:read | users:write |
| Roles | roles:read | roles:write (dangerous) |
| Packages | packages:read | packages:write (dangerous) |
| Entitlement policy | entitlements:read | entitlements:write (dangerous) |
| Organizations / workspaces | workspaces:read | workspaces:write (dangerous) |
Runtime scopes — Product runtime purpose only
These authenticate the auth endpoints your backend calls at request time, not tenant provisioning. Most are marked dangerousin the dashboard because they can read or end a live user's session.
| Scope | Grants | Dangerous |
|---|---|---|
| tokens:issue | Mint a user access token from a session (POST /auth/token). | Yes |
| identity:read | Read session identity and full memberships (GET /auth/me). | Yes |
| sessions:reverify | Step-up re-verification (POST /auth/session/reverify). | Yes |
| sessions:validate | Check whether a direct session id is still valid (POST /auth/session/validate). | No |
| sessions:invalidate | Force-end a direct session by id (POST /auth/session/invalidate). | Yes |
| users:password_change | Change a signed-in user's password (POST /auth/password/change). | Yes |
| account-security:read | Read a user's sessions, trusted devices, and renderable policy. | No |
| account-security:manage | Revoke a user's sessions and trusted devices. | Yes |
| audit:write | Ingest a product-owned event (POST /api/v1/audit). Gated here, not in the Management table above. | Yes |
tokens:issue, identity:read, sessions:reverify, and the account-security scopes are covered in request/response detail in Sessions & tokens. sessions:validate and sessions:invalidate operate on a raw session_iddirectly — for a backend that manages its own direct sessions rather than going through account-security — and aren't covered elsewhere on this site.What you can manage
| Area | Endpoints |
|---|---|
| Users | GET/POST /management/users · GET/PATCH/DELETE /management/users/{id} · PATCH …/{id}/email-verification · POST …/suspend · POST …/reactivate |
| Organizations | POST /management/organizations · POST …/{id}/disable · POST …/{id}/enable |
| Roles | GET/POST /management/roles · PATCH/DELETE /management/roles/{id} · GET/POST …/{id}/apps |
| Packages | GET/POST /management/packages · PATCH/DELETE /management/packages/{id} · POST …/{id}/apps |
| Apps & entitlements | GET /management/apps · PATCH …/apps/{id}/package-policy · GET/PATCH /management/entitlement-policy |
| Workspaces | PATCH …/workspaces/{org}/package · POST …/{org}/environment · PATCH …/{org}/organization-type |
| Audit | POST /api/v1/audit — ingest a product-owned audit event |
/api/v1. Full request/response schemas — every field, every status — live in the API reference.Create a user
The only required field is email. Add organization and role together when the identity should immediately join a workspace:
curl -X POST https://auth.example.com/api/v1/management/users \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{
"email": "user@acme.com",
"organization_id": "<org uuid>",
"role_id": "<role uuid>",
"display_name": "Ada Lovelace"
}'
# → { "id":"<user id>", "email":"…", "organization_id":"…", "role_id":"…",
# "status":"active", "email_verified":false,
# "set_password": { "token":"…", "link":"…" } | null }password to receive a set_password link the user follows to choose their own — pass password to set one directly. The returned id is the user's authreads id; your product resolves it automatically on first login.Email verification
New users are unverified unless you explicitly assert evidence. Omit email_verified during creation to assert nothing. Authreads automatically records first-hand proof when the user redeems a login or step-up code, invite/onboarding link, or reset link delivered by Authreads email. A raw set-password link returned through this API does not verify the address.
curl -X PATCH \
https://auth.example.com/api/v1/management/users/<user-id>/email-verification \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{"email_verified":true}'
# → { "id":"…", "email_verified":true,
# "email_verified_method":"tenant_asserted", "email_verified_at":"…" }false request revokes only your own tenant_asserted evidence; it cannot downgrade authreads_otp or authreads_token proof. Every transition is tenant-scoped and durably audited.Conventions & limits
- Errors are RFC 7807
application/problem+json, and rate limits returnRetry-After— see Errors & rate limits. - Not idempotent by header. There is no
Idempotency-Key; guard retries in your own code (creating a user that already exists preserves the existing record rather than duplicating it). - List endpoints are unpaginated today— they return the full set. Don't assume a cursor/limit contract; treat large lists defensively.
- No general webhooks. Management events are not delivered outbound; poll the API, or push your own events in with
POST /api/v1/audit. OIDC back-channel logout is the narrow standards-based exception for ending RP sessions.