Drıftstack docs
Docs

Team workspaces

A Driftstack team is one owner account plus the member accounts that joined it. Team members, invites and roles are managed in the Driftstack dashboard; the API does not manage membership. What the API does is let a member’s program work in the owner’s workspace.

Acting on behalf of an owner

Once a member is on a team, they can scope a request to the owner’s resources by passing the X-Driftstack-Account header:

GET /v1/sessions
Authorization: Bearer ds_live_<member-key>
X-Driftstack-Account: acc_<owner-uuid>

The server validates that the caller is on the team referenced by the header. The response shape is unchanged; resources are scoped to the owner instead of the caller. The owner can read their own account id with GET /v1/whoami (account_id) and share it with the team.

Role gating:

  • Read endpoints (GET) accept both member and admin roles.
  • Write endpoints (POST / PATCH / PUT / DELETE) require the admin role on the team. The member role gets 403.
  • Your key’s scopes still apply. The team role decides whose resources you reach; the scopes on your own key decide what it may do there, exactly as on your own account. For example, managing the owner’s webhooks needs a key with account_owner, so a read:sessions key gets 403 even for a team admin.
  • Agent-session exception. /v1/agent-sessions contains AI transcripts and live-control state, so its collection and :id surface require admin for both reads and writes.
  • API key list. In the owner’s workspace only an admin is shown the owner’s API keys; a member gets 403.

Endpoints that honor the header include:

  • /v1/sessions (GET / POST / DELETE) and /:id/{navigate,interact,wait,capture,state}
  • /v1/agent-sessions (GET collection / POST create / :id reads and controls) — an admin member can list and operate the owner’s runs or launch the owner’s profile; each run scopes to the owner (counts against the owner’s cap and uses the owner’s profile data). The member role gets 403 on this whole surface.
  • /v1/profiles (GET / POST / PATCH / DELETE) and the profile snapshots
  • /v1/recipes — in a team owner’s workspace every recipe route, reads included, needs the admin role
  • /v1/webhooks (GET / POST / PATCH / DELETE), /:id/deliveries, /:id/rotate-secret, /:id/test and /v1/webhook-deliveries/:id/replay — a member reads /:id/deliveries with last_response_excerpt set to null (the reply of the owner’s endpoint); an admin gets it
  • /v1/usage and /v1/usage/series (GET)
  • /v1/account/me/proxies (GET / POST / PUT / DELETE, /:id/test, /import and the /:id/change-ip… routes) — an admin reads and writes the owner’s saved proxies; a member may only list them, as narrow rows (member_view: true, no address, user name or password flag). These routes need a key with account_owner.
  • /v1/account/me/credentials (GET) — either role lists the owner’s saved credentials: handle, label, websites, include_subdomains and created_at, never a value. Saving and deleting stay with the owner; sent with the header, they are refused with 400.
  • /v1/team/members (GET) — an admin lists the owner’s team members: the same list, with the same fields, the owner sees. The member role gets 403. Without the header it lists your own team’s members. Changed 2026-10-19: this route used to ignore the header. A program that sends X-Driftstack-Account on every request now gets the owner’s list as an admin, and 403 as a member or for an id it is not a member of (or a malformed one), or 403 plan_ended when the owner’s plan has ended. GET /v1/team/invites still ignores the header.

GET /v1/whoami always describes the key that made the request, with or without the header.

Your memberships

GET /v1/account/me lists every owner who added you as a member in teams, and GET /v1/team/owners returns the same list as data. Neither reads the X-Driftstack-Account header. Each entry has these fields:

Field Meaning
owner_account_id The owner’s account id (acc_<uuid>): the value to send as X-Driftstack-Account.
owner_email The owner’s email address (acc_<uuid> if it could not be loaded).
owner_name The owner’s display name, or null if they never set one.
role Your role with this owner: member or admin.
membership_id Your membership (mem_<uuid>).
team_id The owner’s team record (team_<uuid>), or null when the owner has none yet.
team_name That record’s name, or null when there is no record or the name is blank.

team_id and team_name are left out of an entry, rather than set to null, when the server could not read them for that response. Treat a missing field as “unknown right now”, and null as “the owner has no team name”.