# Users, teams & roles

Access for people in Bird is role-based: a user holds a role on your workspace, and each role is a fixed set of permissions. Nothing else needs configuring; pick the right role and the permissions follow.

Roles govern what _people_ can do in the dashboard. What _services_ can do is governed by [API key scopes](/docs/guides/authentication): the same permission vocabulary, granted per key instead of per role.

## Workspace roles

The roles you work with day to day live on the **workspace** (see [Workspaces](/docs/guides/workspaces)): `admin`, `developer`, and `analyst`. Manage them in the dashboard under [**Settings → Team**](https://bird.com/dashboard/w/settings/team).

Every permission is a `{scope, level}` pair, where `level` is `read` or `write` (`write` includes `read`). A role is a named, fixed set of these pairs:

| Scope                 | What `write` means                            | `admin` | `developer` | `analyst` |
| --------------------- | --------------------------------------------- | ------- | ----------- | --------- |
| `workspace`           | Edit workspace settings (name, notifications) | write   | read        | read      |
| `api_keys`            | Create and revoke API keys                    | write   | write       | none      |
| `emails`              | Send email                                    | write   | write       | read      |
| `email_management`    | Manage suppressions and email configuration   | write   | write       | read      |
| `email_marketing`     | Manage contacts, audiences, and broadcasts    | write   | write       | read      |
| `domains`             | Add, verify, and remove sending domains       | write   | write       | read      |
| `webhooks`            | Configure webhook endpoints                   | write   | write       | read      |
| `sms`                 | Send SMS                                      | write   | write       | read      |
| `verify`              | Send and check verification codes             | write   | write       | read      |
| `verify_management`   | Configure verification senders and countries  | write   | write       | read      |
| `whatsapp_messages`   | Send WhatsApp messages                        | write   | read        | read      |
| `whatsapp_management` | Manage WhatsApp templates and settings        | read    | read        | read      |
| `ip_pools`            | View the organization's IP pools (read-only)  | read    | read        | read      |
| `members`             | Manage the workspace team and invitations     | write   | read        | read      |
| `analytics`           | View reports and deliverability analytics     | read    | none        | read      |
| `audit`               | View the audit log                            | read    | none        | read      |
| `request_logs`        | View the request log (read-only)              | read    | read        | read      |

In practice: **admin** runs the workspace (team, settings, and everything a developer can do), **developer** builds the integration (send email with `emails:write`, manage domains with `domains:write`, configure webhooks with `webhooks:write`, mint API keys), and **analyst** is read-only everywhere. Two rows deserve a second look. `ip_pools` is read-only even for admins: purchasing dedicated IPs and managing pools is an organization-level operation (`org:ip_pools:write`), because pools are owned by the organization and shared across workspaces. And WhatsApp template management is currently an owner operation, so workspace roles hold read only. Other products add their own scopes as they ship; all follow the same `{scope, level}` model.

A `403` from any endpoint means the authenticated principal lacks the `{scope, level}` that endpoint requires. The fix is a role change (for a person) or a new key with the right scopes (for a service).

### Organization roles

Behind your workspace sits an organization that owns billing and the overall member list. Two roles manage that layer:

- `owner`: everything. Full read and write across all organization operations and every workspace, present and future, with no workspace role needed. An organization can (and should) have multiple owners. The person who created the account starts as owner.
- `billing_admin`: billing and organization settings (`org:billing:write`, `org:settings:write`) plus read on members and workspaces (`org:members:read`, `org:workspaces:read`). No access to workspace operations.

These rarely come up day to day: most teammates only need a workspace role.

## Members and the team

Membership is implicit: a user is "in" your organization if they hold any role, whether an organization role or a workspace role on any workspace. No separate membership record exists to manage.

You manage the people on your workspace in the dashboard under [**Settings → Team**](https://bird.com/dashboard/w/settings/team), gated by the workspace `members` scope; a workspace admin manages their own team here without any organization-level role. Behind it, the cross-workspace view of everyone in the organization (with their org role and all workspace roles) is served by `GET /v1/organization/members`, gated by `org:members`. Team management is something people do, so API keys cannot hold the `members` scope (see [Authentication](/docs/guides/authentication)).

![The workspace Team settings in the Bird dashboard, listing members with their role and the invite-members action](/images/docs/dashboard-settings-team.png)

Removing someone from a workspace removes only that workspace role; they remain an organization member if they have roles elsewhere. Removing someone from the organization (`DELETE /v1/organization/members/{member_id}`) revokes everything at once: their organization role and all their workspace roles. API keys they created keep working either way, because keys belong to the workspace, not the person.

## Invitations

Inviting is workspace-first: from [**Settings → Team**](https://bird.com/dashboard/w/settings/team) you invite an email address with a role, and Bird figures out the rest. Behind the button, `POST /v1/invitations` is a _smart invitation_: the same request handles both colleagues who are already in your organization and people who have never heard of Bird.

- **Already an organization member**: they are added to the workspace immediately with the given role. No email, no waiting; the response is a member object.
- **Not yet a member**: Bird creates an invitation, emails them a signup link, and the response is an invitation object with a `pending` status. The link is valid for **7 days**; after that the invitation expires and you invite them again.

The response's `type` field (`team_member` or `invitation`) tells you which happened. A second pending invitation for the same email returns `409` rather than creating a duplicate, and you can withdraw a pending invitation at any time from the same page.

Organization-level invitations (`POST /v1/organization/invitations`) are the multi-workspace variant: one invitation can carry an organization role (`owner` or `billing_admin`), workspace roles on several workspaces, or both. Creating them requires `org:members:write`, which only owners hold, so only an owner can make another owner.

## Guardrails

Two invariants are enforced on every role change, regardless of who asks:

- **The last owner is immovable.** Demoting or removing an organization's only owner returns `409`: an organization can never end up ownerless. Promote a second owner first.
- **You cannot change your own access.** Changing your own role or removing yourself returns `403`. This prevents both accidental self-lockout and quiet self-promotion; another admin or owner has to make the change.

## How context is selected

Member and team endpoints exist at two levels, and which organization or workspace a request targets depends on how it authenticates:

- **Session auth** (the dashboard, or tools acting as you) selects context explicitly per request: `X-Organization-Id` on organization-scoped endpoints, `X-Workspace-Id` on workspace-scoped ones.
- **API keys** carry their context implicitly. A key belongs to exactly one workspace, which also pins the organization; no headers are needed, and a context header that contradicts the key is rejected as malformed (`400`).

See [Workspaces](/docs/guides/workspaces) for the full context-resolution model.

## Next steps

- [Authentication & API keys](/docs/guides/authentication): scopes for services, and who can manage keys
- [Workspaces](/docs/guides/workspaces): the workspace these roles attach to
- [Billing & usage](/docs/guides/billing-and-usage): what the org-level billing roles manage