Team RBAC — invite, accept, act-as
This guide walks through the full lifecycle of a Driftstack team: the owner invites a member, the member accepts, and the member then runs sessions / manages resources scoped to the owner’s account.
You’ll need:
- An owner account (already paying the subscription).
- A second teammate’s email address.
Inviting, accepting and removing members happen in the Driftstack dashboard. Once a member is on the team, their programs work in the owner’s workspace through the API — see Team workspaces for the reference.
Step 1 — Invite a teammate (owner)
From the owner’s dashboard, navigate to Team in the sidebar + click Invite member. Fill in:
- Email: the teammate’s email address. They’ll be able to accept only when signed in to a Driftstack account on this address.
- Role:
member— read access to the owner’s persisted session metadata / profiles / audit log / etc. Live session state requiresadmin. Cannot make changes, and cannot list the owner’s API keys.admin— full read + write. Can create sessions, manage webhooks and create API keys on the owner’s behalf.
The teammate receives an email with a 7-day accept link.
Step 2 — Accept the invite (teammate)
The teammate signs up at https://app.driftstack.io/signup/ using the invitee email address (must match exactly), then clicks the accept link from the invite email.
The link takes them to the dashboard’s /team/accept page, which
checks that the signed-in account’s email matches the invitation
and adds them to the team.
If the teammate already has an account (under the same email), they can sign in first and then click the accept link.
Step 3 — See the team you’re on (member)
The dashboard sidebar displays an Acting as picker that:
- Lists the member’s own account (default) + each owner team.
- Remembers your selection in this browser.
- Auto-injects
X-Driftstack-Account: acc_<owner-uuid>on every subsequent dashboard fetch.
The owner’s account id (acc_<owner-uuid>) is the value a member’s
program sends. The owner can read it with
GET /v1/whoami (account_id) and share it.
Step 4 — Act on the owner’s resources (member, admin role)
Once the member has a team, any /v1/* request can be scoped to
the owner by passing X-Driftstack-Account: acc_<owner-uuid>:
# start an agent session OWNED by the team owner, through one of the
# OWNER's saved proxies; it counts against the OWNER's concurrent cap.
# The request counts against your rate limit first, then the owner's.
curl -X POST https://api.driftstack.dev/v1/agent-sessions \
-H "Authorization: Bearer $MEMBER_KEY" \
-H "X-Driftstack-Account: acc_owner-uuid" \
-H "content-type: application/json" \
-d '{ "mode": "ai", "proxy_id": "<owner-proxy-id>" }'
On hosted Driftstack every session runs through a saved proxy: a create
with no proxy_id is refused with 422 proxy-required.
Role gating:
- Persisted metadata reads on
/v1/sessions(list and detail): bothmemberandadminallowed. - Agent-session reads are the exception.
GET /v1/agent-sessionsrequiresadmineven though it is a read: an agent session carries the model transcript and live control state, so the collection is not widened to read-only members. Amemberacting on an owner gets403there while the plain session list still works. - Live session state (
GET /v1/sessions/:id/state):adminonly. It returns the live session’s cookies and local storage;membergets403and the session is left untouched. - API key list:
adminonly. Amemberacting on an owner gets403instead of the owner’s key names and key prefixes. - Write endpoints (POST / PATCH / PUT / DELETE):
adminrole only.membergets403.
Session and agent-session requests made on behalf of an owner count
against two rate limits: yours first, then the owner’s. When you start or
reopen an agent session for an owner, the first is a limit kept for you
acting for that owner and sized by the owner’s plan, not your own plan’s
limit. If the owner’s limit is exhausted you get a generic 429 with only
Retry-After: the rate-limit headers are left out, and the request still
counts against the first limit.
Endpoints that honor the header:
| Resource | Which routes |
|---|---|
| Sessions | every /v1/sessions route you can call |
| Agent sessions | every /v1/agent-sessions route, including /message. Note the collection READ needs admin — see above |
| Profiles | every /v1/profiles route — not just CRUD, but trash, restore, purge, clone, export, import, trim |
| Profile snapshots | every /v1/profile-snapshots route, and /v1/profiles/{id}/snapshots |
| Webhooks | every /v1/webhooks route — GET / POST / PATCH / DELETE plus {id}/deliveries, {id}/rotate-secret, {id}/test, and delivery replay |
| Usage | GET, /series |
| Saved proxies | every saved-proxy route — an admin reads and writes the owner’s proxies, a member only lists them, as narrow rows |
| Recipes | every /v1/recipes route — the recipe is saved from, and filed under, the owner’s workspace. Every one needs admin, reads included: a recipe is a saved copy of an agent session’s steps |
A member reading the owner’s webhook deliveries gets
last_response_excerpt as null. It is the reply of the owner’s
endpoint, which can repeat an event that names an API key. An admin
gets the excerpt.
If you are unsure about a route not listed here, the safe assumption is
that it operates on your own account. GET /v1/whoami always describes
the key that made the request.
Step 5 — Audit the team’s actions (owner)
When an endpoint emits an audit entry for a member’s action on the owner’s resources, that entry is written to the OWNER’s audit log with:
account_id: the owner.actor_account_id: the member, when that endpoint records who acted.actor_key_id: the member’s API key id, when that is recorded.
Not every endpoint currently writes an audit entry or records who acted. Treat these actor fields as a per-endpoint detail, not as a complete record of every team action.
So the owner sees, in their audit log, “Member [email protected]
(acc_…) created session ses_… on this account at 2026-05-08
14:02 UTC”.
The owner reads the log, and downloads it as CSV, from the dashboard’s Audit log page.
A member reading the owner’s log (or its download) gets the API key
entries without the key’s name, as for the key list; an
admin and the owner see them.
A download holds at most the 10,000 newest entries. When the log is
longer, the download is cut there: a JSON download says
"truncated": true, and a program that fetches the export with an API
key also gets the response header x-driftstack-export-truncated: true
(false when nothing was cut). A CSV file has no field for this, so a
program fetching CSV must check that header before treating the file as
the whole log, and in the dashboard a JSON download is the one that
tells you.
Removing a member (owner)
When a teammate leaves, the owner removes them from the dashboard’s
Team page. The member is removed immediately; their
X-Driftstack-Account header stops working on the next request. Their
own account stays — only the team relationship is severed.
A team.member_removed audit entry lands on the owner’s log; the
member is NOT separately notified by Driftstack (the owner can do
that via their own channels).
Common patterns
Multiple admins on a team
A team can have any number of admin-role members, but every one
of them is invited by the owner, from the owner’s own dashboard.
The owner is always implicitly “admin” on their own team (no separate
membership record).
Each admin has their own rate limit, but all admins acting on the same owner share the owner’s limit; adding admins never increases the owner’s capacity.
Read-only collaborators
Use role: 'member' for read-only access. Useful for:
- Auditors / compliance reviewers (read the audit log + usage reports without write capability).
- Junior teammates being onboarded (read session list/detail metadata + profiles without risk of accidentally minting a key or destroying a session).
Graceful key rotation across the team
API keys are rotated in the dashboard’s API keys page. The new plaintext is shown ONCE. If multiple teammates use the same key (e.g. across CI machines), the rotation flow is:
- Owner (or admin member) rotates → new key, 24h grace on the old.
- Teammates have 24h to swap deployments to the new key.
- After 24h the old key expires automatically.
A teammate can’t rotate a key with account_owner, admin or an
admin:… scope — only the owner can. A key a teammate rotates counts as
theirs: it is revoked when they leave the team, so if your own systems
use it, rotate it again yourself before removing them.
Privacy note
A team member is a separate Data Subject from the owner. Their account email is processed under Privacy §3.1 on the same legal basis as any other Customer contact. Removing the member from the team does not delete their Driftstack account; only the membership relationship.
Next steps
- /reference/team-workspaces/ — the
X-Driftstack-Accountreference. - /webhooks/replay/ — replay individual deliveries; admin-only on team owners.