Skip to content
Authreads Docs

Users & teams

You always create people in Authreads — it's the source of truth for identity. Where they end up depends on which surface they use.

Two kinds of user

 Operators (admin/management team)Product / end-users
UseYour admin / management surfaceYour product app
Access gateRole only (read live from the token)role ∩ org-type ∩ package
Local product rowNever — kept in Authreads onlyMirrored on first login

Create a user (Management API)

Get a machine token, then create the identity. Organization and role are optional; include both when the user should immediately join a workspace:

1 · get a machine token
curl -X POST https://auth.example.com/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=...&client_secret=..."
# → { "access_token": "...", ... }
2 · create the user
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","password":"..."}'
# → { "id": "<user id>", "email": "...", "email_verified": false,
#     "set_password": { "link": "..." } | null }
The returned id is the user's Authreads id. You don't copy it anywhere — your product resolves the user by that id automatically on login. Omit password to get a set-password link instead.

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.

Assert verification later
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":"…" }
Set this only when your tenant has actually verified control of the address. A 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.

Deactivate a workspace membership or offboard the person

These are deliberately different operations. Deactivating a membership ends that workspace's sessions and access, but retains the tenant-wide password so the person can still authenticate for another active workspace. Your integration must treat membership_inactive as a workspace-access denial, not as an authentication failure.

To lock the person out of the tenant entirely, use the tenant-user offboarding action. It disables the identity and revokes tenant-wide sessions, trusted devices, refresh grants, and credentials. Removing one named workspace membership does not delete the tenant-wide user.

The operator control-plane routes that back these Admin actions are private and are not part of the public Management API. Server integrations should use the published user lifecycle operations in the API reference instead of calling /api/v1/clients/....

Products can create users too

Your product's own backend can create users by calling the same Management API with its machine credential — so your users are added from your app, and your product sends the onboarding email. Either way the user is created in Authreads; product/end-users are then mirrored into your product on their first login (no manual sync).

Operators (admin team) stay in Authreads only — never stored in your product. Only product/end-users get a local record, created on first login.