Members
List organization members and change their role, project access, and state.
The organization directory and membership management, with the portal's guards. Invitations are on their own page: Invites.
| Endpoint | Scope | Token owner's role |
|---|---|---|
GET /members | members:read | Any member |
GET /members/{id} | members:read | Admin |
PATCH /members/{id} | members:write | Admin |
PUT /members/{id}/projects | members:write | Admin |
POST /members/{id}/disable | members:write | Admin |
POST /members/{id}/enable | members:write | Admin |
DELETE /members/{id} | members:write | Admin |
DELETE /members/{id}/sessions | members:write | Admin |
GET /members/removed | members:read | Admin |
POST /members/removed/{id}/restore | members:write | Admin |
members:write is a sensitive scope.
Every route except the list needs an owner who is currently an organization
Admin; a Member's token gets 403 insufficient_scope.
People join through invitations, which are sent from the portal; accepting one is the invitee's step.
Python examples share this setup:
import os
import uuid
import requests
BASE = "https://portal.<domain>/api/v1/mgmt"
s = requests.Session()
s.headers["Authorization"] = f"Bearer {os.environ['TF_PAT']}"
def idem():
return {"Idempotency-Key": str(uuid.uuid4())}The member object
| Field | Type | Notes |
|---|---|---|
id | string | The member's user id. |
email | string | |
name | string | null | |
role | string | admin or member. |
status | string | active or disabled. |
email_verified | boolean | |
created_at | string | When they joined. |
last_login_at | string | null | |
key_count | integer | API keys this member created. |
project_ids | string[] | Projects the member belongs to. Always present on single-member responses; list rows may omit it. Admins can access every project regardless. |
This is the full object an Admin token owner gets. A Member token owner listing members gets only each member's identity; see List members.
{
"id": "8e7d6c5b-4a39-4281-9f0e-1d2c3b4a5968",
"email": "[email protected]",
"name": "Dana Okafor",
"role": "admin",
"status": "active",
"email_verified": true,
"created_at": "2026-03-02T09:41:17Z",
"last_login_at": "2026-09-24T08:12:40Z",
"key_count": 3,
"project_ids": []
}List members
GET /api/v1/mgmt/membersNewest first. Paginated with limit and
cursor.
The rows depend on the token owner's current role:
- Admin: the full member object.
- Member: only each member's identity:
id,email,name,roleandcreated_at. Member state (status,email_verified,last_login_at,key_count) is admin-only.
{
"id": "8e7d6c5b-4a39-4281-9f0e-1d2c3b4a5968",
"email": "[email protected]",
"name": "Dana Okafor",
"role": "admin",
"created_at": "2026-03-02T09:41:17Z"
}| Query parameter | Notes |
|---|---|
role | admin or member. Any other value → 422 validation_failed. |
status | active or disabled. Any other value → 422 validation_failed. Admin only: a Member's token gets 403 insufficient_scope. |
curl -G https://portal.<domain>/api/v1/mgmt/members \
-H "Authorization: Bearer $TF_PAT" \
-d role=adminRetrieve a member
GET /api/v1/mgmt/members/{id}curl https://portal.<domain>/api/v1/mgmt/members/8e7d6c5b-4a39-4281-9f0e-1d2c3b4a5968 \
-H "Authorization: Bearer $TF_PAT"Change a member's role or name
PATCH /api/v1/mgmt/members/{id}| Body field | Type | Notes |
|---|---|---|
role | string | admin or member. |
name | string | Display name. |
Send at least one field. Returns 200 with the member object. The last
active admin cannot be demoted (409 last_admin), and the organization owner's
role cannot be changed (409 owner_protected). A demotion also narrows the
member's personal access tokens.
curl -X PATCH https://portal.<domain>/api/v1/mgmt/members/8e7d6c5b-4a39-4281-9f0e-1d2c3b4a5968 \
-H "Authorization: Bearer $TF_PAT" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 5f6a7b8c-9d0e-4f1a-8b2c-3d4e5f6a7b8c" \
-d '{"role": "member"}'Set a member's project access
PUT /api/v1/mgmt/members/{id}/projectsReplaces the set of projects the member belongs to. Returns 200 with the
member object.
| Body field | Type | Required | Notes |
|---|---|---|---|
project_ids | string[] | yes | The full set. [] removes all project access. A project outside the organization → 400 invalid_request, param: "project_ids". |
curl -X PUT https://portal.<domain>/api/v1/mgmt/members/8e7d6c5b-4a39-4281-9f0e-1d2c3b4a5968/projects \
-H "Authorization: Bearer $TF_PAT" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6a7b8c9d-0e1f-4a2b-9c3d-4e5f6a7b8c9d" \
-d '{"project_ids": ["5b3e9a41-0c7d-4e2f-8a16-9d4b7c2e1f08"]}'Disable a member
POST /api/v1/mgmt/members/{id}/disableReversible containment. The member can no longer sign in, is signed out of
every browser session, and every personal access token they created is
revoked. API keys they created keep working — they are project assets.
No body. Returns 200 with the member object (status: "disabled").
The last active admin cannot be disabled (409 last_admin), and you cannot
disable yourself (409 cannot_act_on_self).
curl -X POST https://portal.<domain>/api/v1/mgmt/members/8e7d6c5b-4a39-4281-9f0e-1d2c3b4a5968/disable \
-H "Authorization: Bearer $TF_PAT" \
-H "Idempotency-Key: incident-4711-disable-member"Warning
Revoked tokens stay revoked. Enabling the member again does not restore their personal access tokens — they mint new ones.
Enable a member
POST /api/v1/mgmt/members/{id}/enableRestores a disabled member's sign-in. No body. Returns 200 with the member
object (status: "active").
curl -X POST https://portal.<domain>/api/v1/mgmt/members/8e7d6c5b-4a39-4281-9f0e-1d2c3b4a5968/enable \
-H "Authorization: Bearer $TF_PAT" \
-H "Idempotency-Key: 7b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e"Remove a member
DELETE /api/v1/mgmt/members/{id}Permanent. The member's account in this organization is deleted, and their
personal access tokens are deleted with it. Past audit entries remain.
The person is listed under removed members and can't
rejoin through SSO automatic membership until you restore or invite them.
Returns 204 No Content.
| Body field | Type | Required | Notes |
|---|---|---|---|
delete_keys | boolean | no | true also revokes every API key the member created. Default false: their keys keep working. |
The last active admin cannot be removed (409 last_admin), you cannot
remove yourself (409 cannot_act_on_self), and the organization owner is
protected (409 owner_protected).
curl -X DELETE https://portal.<domain>/api/v1/mgmt/members/8e7d6c5b-4a39-4281-9f0e-1d2c3b4a5968 \
-H "Authorization: Bearer $TF_PAT" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 8c9d0e1f-2a3b-4c4d-9e5f-6a7b8c9d0e1f" \
-d '{"delete_keys": true}'Sign a member out everywhere
DELETE /api/v1/mgmt/members/{id}/sessionsEnds every sign-in session of the member, so each browser has to sign in
again. It does not revoke their personal access tokens (disable the
member for that). No body. Returns 204 No Content.
When the member has no active sign-in session to end, the call is
409 sessions_unavailable; if sign-out is temporarily
unavailable, 502 upstream_unavailable — retry with the same
Idempotency-Key.
curl -X DELETE https://portal.<domain>/api/v1/mgmt/members/8e7d6c5b-4a39-4281-9f0e-1d2c3b4a5968/sessions \
-H "Authorization: Bearer $TF_PAT" \
-H "Idempotency-Key: 9d0e1f2a-3b4c-4d5e-8f6a-7b8c9d0e1f2a"List removed members
GET /api/v1/mgmt/members/removedPeople an admin removed from the organization, newest removal first, in the
standard list envelope (limit, cursor). A removed person can't rejoin
through your SSO's automatic membership; restore
or invite them to give access again.
| Field | Type | Notes |
|---|---|---|
id | string | The removal's id, used to restore. |
email | string | |
name | string | null | |
last_role | admin | member | null | Their role when removed; null if not recorded. |
projects_recorded | boolean | false for removals made before project access was recorded. |
projects | array of {id, name} | Recorded projects that still exist. |
removed_at | string (date-time) | |
removed_by | {id, name, email} | null | The admin who removed them. |
invite_pending | boolean | A restore or invitation was sent and not accepted yet; they stay removed until they accept it. |
curl https://portal.<domain>/api/v1/mgmt/members/removed \
-H "Authorization: Bearer $TF_PAT"Restore a removed member
POST /api/v1/mgmt/members/removed/{id}/restoreGives the person access again with their previous role and the recorded
projects that still exist (Member with no projects when nothing was
recorded). No body. Removing someone deletes their sign-in, so they become a
member again the next time they sign in: your organization's SSO lets them
straight in, and any other sign-in method uses the link in the email they get
("You've been given access to <organization> again"). Until then they appear
under GET /invites as invite_id, and they
are still listed as removed (invite_pending: true): they stay removed until
they accept. If you revoke the invitation or it expires, they stay removed and
automatic membership never lets them back in.
{
"status": "restored",
"email": "[email protected]",
"role": "member",
"project_ids": ["3f2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9"],
"invite_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
"expires_at": "2026-10-14T10:00:00Z"
}409 already_a_member when the person is a member again; 409 conflict
when the invitation rules refuse the address (for example, your SSO only
allows invitations at your own email domain).
curl -X POST https://portal.<domain>/api/v1/mgmt/members/removed/6f1c2b0a-3d4e-4f50-8a9b-0c1d2e3f4a5b/restore \
-H "Authorization: Bearer $TF_PAT" \
-H "Idempotency-Key: 1e2f3a4b-5c6d-4e7f-8a9b-0c1d2e3f4a5b"Errors specific to members
| Status | Code | When |
|---|---|---|
| 400 | invalid_request (param set) | Empty PATCH body; unknown field; unknown role; project_ids missing or naming a project outside the organization. |
| 403 | insufficient_scope | Token lacks members:read/members:write, or the owner is not currently an Admin. |
| 404 | not_found | Member absent or in another organization. |
| 409 | last_admin | Demoting, disabling or removing the organization's last active admin. |
| 409 | owner_protected | Changing the role of, or removing, the organization owner. |
| 409 | cannot_act_on_self | Disabling or removing yourself. |
| 409 | sessions_unavailable | DELETE /members/{id}/sessions for a member with no active sign-in session. |
| 409 | already_a_member | Restoring someone who is a member again. |
| 422 | unsupported_filter · validation_failed | An unknown query parameter, or a role/status filter value outside the list above. |
{
"error": {
"message": "this change would leave the organization without an active admin",
"type": "conflict_error",
"code": "last_admin",
"param": null,
"request_id": "req_d5f16075d7984e1b99c537a18f7fc58a"
}
}