Teams & RBAC
Driftstack supports team accounts where multiple users share a single billing entity. This page covers the invite flow, the member vs. admin roles, and how a team member calls API endpoints against the owner's resources.
Vocabulary
- Owner account — the account that pays the bill. Sessions, profiles, webhooks, and billing all live on the owner account.
- Member — a user invited to the owner
account with read access to its resources via
X-Driftstack-Account. - Admin — a member who additionally has write access (create + modify sessions, profiles, webhooks) on the owner account.
Invite flow
# 1. Owner sends an invite
POST /v1/team/invites
Authorization: Bearer ds_live_owner_…
Content-Type: application/json
{ "email": "[email protected]", "role": "admin" }
→ 202 Accepted
{ "message": "Invite sent. The invitee can accept via the email link." }
# 2. Owner can list pending invites
GET /v1/team/invites
Authorization: Bearer ds_live_owner_…
→ 200 OK
{
"data": [
{
"id": "inv_…",
"owner_account_id": "acc_owner_…",
"invitee_email": "[email protected]",
"role": "admin",
"expires_at": "2026-05-19T13:00:00.000Z",
"invited_by_account_id": "acc_owner_…",
"accepted_at": null,
"created_at": "2026-05-12T13:00:00.000Z"
}
]
}
# 3. Invitee redeems the token from the email
POST /v1/team/invites/accept
Authorization: Bearer ds_live_invitee_…
Content-Type: application/json
{ "token": "<plaintext token from the email link>" }
→ 200 OK
{
"membership": {
"id": "mem_…",
"owner_account_id": "acc_owner_…",
"member_account_id": "acc_invitee_…",
"member_email": "[email protected]",
"role": "admin",
"invited_at": "2026-05-12T13:00:00.000Z",
"accepted_at": "2026-05-12T13:15:00.000Z",
"invited_by_account_id": "acc_owner_…"
}
}
Acceptance posts the plaintext token from the email —
there is no /v1/team/invites/<id>/accept
endpoint. The invite row itself can't be revoked via the API
today; the invite simply expires.
Listing + removing members
# Owner lists their team
GET /v1/team/members
Authorization: Bearer ds_live_owner_…
→ 200 OK
{
"data": [
{
"id": "mem_…",
"owner_account_id": "acc_owner_…",
"member_account_id": "acc_member_…",
"member_email": "[email protected]",
"role": "admin",
"invited_at": "2026-05-12T13:00:00.000Z",
"accepted_at": "2026-05-12T13:15:00.000Z",
"invited_by_account_id": "acc_owner_…"
}
]
}
# Owner removes a membership by membership id (mem_…, NOT account id)
DELETE /v1/team/members/mem_…
Authorization: Bearer ds_live_owner_…
→ 204 No Content
The DELETE route takes the mem_ membership id, not
an acc_ account id. Transferring ownership of an
account is a support-ticket operation today.
Teams I am on
GET /v1/team/owners
Authorization: Bearer ds_live_member_…
→ 200 OK
{
"data": [
{
"owner_account_id": "acc_owner_…",
"role": "admin",
"membership_id": "mem_…"
}
]
}
The mirror of /v1/team/members — the latter
lists members of my team, this one lists teams
I am on. Use the returned owner_account_id
with the X-Driftstack-Account header on subsequent
requests to act on that team's resources.
X-Driftstack-Account: acting on a team owner's resources
Team members read + write the owner's resources by passing the
owner's account id in the X-Driftstack-Account
request header. The server validates the caller has a valid
membership on that owner before routing the request.
# List the team owner's sessions
GET /v1/sessions
Authorization: Bearer ds_live_member_…
X-Driftstack-Account: acc_owner_… SDK shortcut: effectiveAccount
The official SDKs send the header for you — construct a client scoped to a workspace and every request carries it. Omit it for your own account.
// TypeScript
const client = new Driftstack({ apiKey, effectiveAccount: 'acc_owner_…' });
# Python
client = Driftstack(api_key, effective_account="acc_owner_…")
// Go
client := driftstack.New(apiKey, driftstack.WithEffectiveAccount("acc_owner_…")) The desktop app exposes the same control as a workspace switcher in the profiles hub. Note: reads resolve against the workspace, but session launch against an owner's profile is an owner-only action today.
Role enforcement: reads are allowed for both
member and admin. Writes
(e.g. POST /v1/sessions,
POST /v1/webhooks,
POST /v1/webhooks/<id>/rotate-secret) require
admin; members get 403. See
/api-reference for the per-endpoint
gate.
What admins cannot do
- Change the owner's tier or initiate checkout — those are owner-only operations.
- Manage the team itself — invite, remove members, accept on behalf of someone else.
- See or modify other members' API keys; each member's keys are private to their account.
Audit trail
Team mutations (invite sent, invite accepted, member removed)
write entries on the owner's audit log. Read via
GET /v1/account/audit-log?action=team.* — see
/docs/audit-log.
Support
Team setup or ownership transfers: [email protected]. RBAC + audit questions: [email protected].