Members
Membership and invitations, keyed by membership rather than by user.
View as MarkdownMembership 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
curl -fsS "https://api.saved.sh/v1/workspaces/$WID/members" \
-H "Authorization: Bearer $TOKEN"{
"data": [
{
"membership_id": "om_01H...",
"user_id": "018f3c2a-...",
"email": "you@example.com",
"role": "admin",
"status": "active"
}
],
"next_cursor": null
}Permission: members:read.
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.
Change a role
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.
Remove a member
curl -fsS -X DELETE "https://api.saved.sh/v1/workspaces/$WID/members/$MEMBERSHIP_ID" \
-H "Authorization: Bearer $TOKEN"Permission: members:write. Returns 204.
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.
Invitations
Invite
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"}'{
"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.
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.
List, resend, revoke
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
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
| Plan | Members per workspace |
|---|---|
| Trial | 1 |
| Paid | 25 |
Inviting past the limit returns 409 quota_exceeded.