---
title: "Members"
description: "Membership and invitations, keyed by membership rather than by user."
url: "https://saved.sh/docs/api/members"
---

Membership is the join between a person and a workspace. It is what a role attaches to, and
it is what these endpoints address.

## List members [#list-members]

```bash
curl -fsS "https://api.saved.sh/v1/workspaces/$WID/members" \
  -H "Authorization: Bearer $TOKEN"
```

```json
{
  "data": [
    {
      "membership_id": "om_01H...",
      "user_id": "018f3c2a-...",
      "email": "you@example.com",
      "role": "admin",
      "status": "active"
    }
  ],
  "next_cursor": null
}
```

Permission: `members:read`.

<Callout type="warn">
  **`membership_id` is what the other endpoints take**, not `user_id` and not the email. A
  person may be a member of several workspaces, each with its own membership and its own role,
  so the user ID alone does not identify what you are changing.
</Callout>

## Change a role [#change-a-role]

```bash
curl -fsS -X PATCH "https://api.saved.sh/v1/workspaces/$WID/members/$MEMBERSHIP_ID" \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"role": "admin"}'
```

Permission: `members:write`. `role` is a role slug: `admin`, `member`, or a custom role's slug
from [roles](/docs/api/roles).

## Remove a member [#remove-a-member]

```bash
curl -fsS -X DELETE "https://api.saved.sh/v1/workspaces/$WID/members/$MEMBERSHIP_ID" \
  -H "Authorization: Bearer $TOKEN"
```

Permission: `members:write`. Returns `204`.

<Callout type="error">
  **Removing a member does not revoke API keys or workers they created.** Those belong to the
  workspace, not to the person, so automation keeps working when someone leaves. Offboarding
  therefore has a second step: list `/v1/api-keys` and `/v1/workers` and revoke anything you
  cannot account for.
</Callout>

## Invitations [#invitations]

### Invite [#invite]

```bash
curl -fsS -X POST "https://api.saved.sh/v1/workspaces/$WID/invitations" \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"email": "sam@example.com", "role": "admin"}'
```

```json
{
  "id": "invitation_01H...",
  "email": "sam@example.com",
  "role": "admin",
  "state": "pending",
  "created_at": "2026-08-08T09:14:02Z",
  "expires_at": "2026-08-15T09:14:02Z"
}
```

Permission: `members:invite`.

<Callout type="warn">
  **Omitting `role` means `member`, which carries no permissions at all.** The invitee joins,
  sees an empty workspace, and can do nothing. Send a role unless you mean exactly that.
</Callout>

### List, resend, revoke [#list-resend-revoke]

```bash
curl -fsS "https://api.saved.sh/v1/workspaces/$WID/invitations" \
  -H "Authorization: Bearer $TOKEN"

curl -fsS -X POST "https://api.saved.sh/v1/workspaces/$WID/invitations/$IID/resend" \
  -H "Authorization: Bearer $TOKEN"

curl -fsS -X DELETE "https://api.saved.sh/v1/workspaces/$WID/invitations/$IID" \
  -H "Authorization: Bearer $TOKEN"
```

| Action | Permission       |
| ------ | ---------------- |
| List   | `members:read`   |
| Resend | `members:invite` |
| Revoke | `members:invite` |

Invitations expire. `resend` issues a fresh one; `revoke` cancels it before it is accepted.

## Where membership actually lives [#where-membership-actually-lives]

Membership and roles are held by the identity provider, not in our database. Two consequences
worth knowing when building against this:

* **These endpoints are a facade over that system**, so a change made directly there is
  reflected here without any sync on our part.
* **Membership changes made outside our API do not appear in our audit trail.** If you manage
  members through both surfaces, ours is not a complete record.

## Quotas [#quotas]

| Plan  | Members per workspace |
| ----- | --------------------- |
| Trial | 1                     |
| Paid  | 25                    |

Inviting past the limit returns `409 quota_exceeded`.

## Next [#next]

<Cards>
  <Card href="/docs/api/roles" title="Roles" description="What a role slug refers to." />

  <Card href="/docs/security/permissions" title="Permissions" description="What each role can carry." />
</Cards>
