> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usesink.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Access control

> Roles, team membership, invites and what each gate actually enforces.

Every authenticated request passes three checks in order, and a request that reaches a secret has cleared all three.

<Steps>
  <Step title="Workspace membership">
    The caller must be a member of the workspace named in the path (or, on routes without one, in the `X-Workspace-Id` header or the credential's own scope). A non-member gets `403`, and a workspace that does not exist gets `404`.
  </Step>

  <Step title="Team membership">
    The caller must be a member of the team that owns the project. This is the gate that actually keeps one team's secrets away from another's — being in the workspace is not enough.
  </Step>

  <Step title="Project ownership">
    The project must genuinely belong to that team and workspace. A project id borrowed from another scope reads as `404`, not as an authorisation error.
  </Step>
</Steps>

## Roles

Workspace membership carries one of four roles, ordered:

| Role       | Value | What it changes today                                                                                                                                                                         |
| ---------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Owner**  | 3     | Created by whoever created the workspace. Required to rename or delete the workspace, invite people, and wipe an environment's secrets. The owner's subscription governs the whole workspace. |
| **Admin**  | 2     | Can create teams, and sees every team in the workspace rather than only their own.                                                                                                            |
| **Member** | 1     | The default for someone who accepts an invite. Sees and works in the teams they belong to.                                                                                                    |
| **Viewer** | 0     | Recorded on the membership; the lowest rung.                                                                                                                                                  |

<Note>
  The boundaries Sink enforces today are workspace membership, team membership, and owner-only operations. The **Viewer / Member** distinction is stored on the membership but does not yet restrict writes at the secret layer — anyone in the team can read and write that team's secrets. Use team membership, not role, to scope who can change production values for now.
</Note>

### Owner-only operations

* Renaming or deleting a workspace
* Inviting someone to a workspace
* `DELETE` on an environment's entire secret set

### Admin-or-owner operations

* Creating a team
* Listing every team in the workspace (others see only the teams they are in)

## Inviting people

An owner sends an invite to an email address, optionally naming a team and a role:

```http theme={null}
POST /invite/{workspace_id}
```

```json theme={null}
{
  "workspace_id": "…",
  "invitee_email": "engineer@example.com",
  "team_id": "…",
  "role": 1
}
```

The invitee gets an email with a link. They can check it without committing to anything (`POST /invite/validate?token=…`), accept it (`POST /invite/accept`, which creates the account and signs them in if they are new), or decline it (`POST /invite/reject?token=…`). Invites carry a status of `pending`, `accepted`, `rejected` or `expired`, and `GET /invite/{workspace_id}` lists them.

Sending an invite counts against the workspace's seat limit, so a full workspace is refused with `402` before any mail goes out.

<Warning>
  An invite link is a bearer token. Anyone who reads the email can accept it. The three token routes share a rate-limit budget so they cannot be worked through in rotation, but the real protection is that the link goes to an address you chose.
</Warning>

## Credentials and scope

A **session JWT** carries a workspace claim from the login that minted it. An **API key** carries no workspace of its own — it falls back to the holder's current workspace, and a path parameter overrides that. In both cases the workspace in the URL wins, so a key cannot reach past its holder's memberships by omitting one.

An API key inherits the permissions of the user who created it. There is no way to mint a key that is weaker than its owner, so a key handed to a pipeline should belong to an account whose access you are comfortable with the pipeline having.

## Leaving and deleting

Deleting an account removes the workspaces it owns — along with their teams, projects, environments and secrets — and leaves the workspaces where it was only a member. The response says how many of each. It is irreversible and asks for the password to confirm.
