Remove a Member
DELETE /v1/members/{member_id}
| Property | Value |
|---|---|
| Operation | remove_member |
| Group | Members |
| Scope | members:write |
| Route class | control |
| Idempotency | No Idempotency-Key |
| Audit | Audited identity.member.remove (mutation) |
| Released in | 1.51.0 |
| Phase 0 host | Served (the hosted api pins 1.65.0) |
Removes the Member at the revision If-Match names and answers a MemberRemoval (1.51.0, FR-identity-5). The removal ends the Member's sessions at once, in its transaction: a console session answers 401 unauthenticated from its next request, and a Member ID token is refused from its next request as an identity with no Member, so its next sign-in waits for approval as a sign-in attempt. The Member's id stays in the Audit events, its email may be invited again, and a second removal answers not_found. A Member may remove itself. The Audit event identity.member.remove records the Member id and its role and status, never the email. From 1.51.0 a write that would leave the Organization without an owner (removing the only owner, demoting it to another role or disabling it) is refused with last_owner and one detail whose reason is a LastOwnerChange, and changes nothing. That rule is checked first; then a caller that is not an owner and removes, demotes or disables an owner is refused with forbidden and the detail owner_required, because only an owner changes an owner. Phase 0 accepts a Member ID token for this route, so an operator signed in to the cockpit makes the write.
Request
Set VOCAL_API to the base URL of your API and VOCAL_API_KEY to an API key with the members:write scope (Authentication and keys). Replace each placeholder, such as agt_... or <name>, with your own value.
curl -X DELETE "$VOCAL_API/v1/members/mem_..." \
-H "Authorization: Bearer $VOCAL_API_KEY" \
-H 'If-Match: "1"'const response = await fetch(`${process.env.VOCAL_API}/v1/members/mem_...`, {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.VOCAL_API_KEY}`,
'If-Match': '"1"',
},
});
console.log(response.status, await response.text());import os
import requests
response = requests.delete(
f"{os.environ['VOCAL_API']}/v1/members/mem_...",
headers={
"Authorization": f"Bearer {os.environ['VOCAL_API_KEY']}",
"If-Match": '"1"',
},
)
print(response.status_code, response.text)Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
member_id | path | MemberId | yes | The id of a Member: mem_ and the row's UUIDv7 in 26 Crockford base32 characters. Opaque and safe to log. |
if-match | header | string | yes | The revision the change was made against, quoted as the ETag returns it, or * to overwrite. |
Responses
| Status | Content type | Shape | Error codes |
|---|---|---|---|
200 | application/json | MemberRemoval | |
400 | application/json | ErrorEnvelope | invalid_request |
401 | application/json | ErrorEnvelope | unauthenticated |
403 | application/json | ErrorEnvelope | forbidden, org_suspended |
404 | application/json | ErrorEnvelope | not_found |
409 | application/json | ErrorEnvelope | last_owner |
412 | application/json | ErrorEnvelope | revision_mismatch |
428 | application/json | ErrorEnvelope | precondition_required |
429 | application/json | ErrorEnvelope | rate_limited |
500 | application/json | ErrorEnvelope | internal_error |
Fields of the 200 answer
The shape is MemberRemoval.
| Field | Type | Required | Description |
|---|---|---|---|
id | MemberId | yes | |
object | "member_removal" | yes | |
removed_at | Timestamp | yes | |
role | MemberRole | yes | The role the Member held when it was removed. |
sessions_ended | true | yes | Always true: the removal ended the Member's sessions in its transaction. A console session of the Member answers 401 unauthenticated from its next request, and a Member ID token is refused from its next request as an identity with no Member. |
status | MemberStatus | yes | The status the Member had when it was removed. |