Skip to content
DELETE /member/{userId}

Irreversible: revokes the member's access to this organization immediately. Re-adding them means a fresh invite, and their roles and team memberships are not restored. Their connections are ALWAYS deleted (the vaulted credential with them); a `connection_actions` entry saying `transfer` is refused with 400 `connection_transfer_removed`. To keep what used a connection working, name a replacement in `connection_replacements`: before the old connection is deleted, every toolbox entry and other member's synthetic tool step that names it is rewritten onto the replacement (toolbox entries are re-stamped with you as delegator). A replacement must be the same connector, active, not the leaver's, and usable by you (400 `replacement_*` otherwise) — the preflight's `replacement_candidates` are exactly those. Everything else they own goes to a person (`target_user_id`) or a team (`target_team_id`: the team's first active admin owns it and the team gets an `edit` share; 400 `team_has_no_admin` when nobody can) per row, or to one bulk `defaults.transfer_to_user_id` / `transfer_to_team_id`. A shared owned resource with no decision refuses with 409 `<kind>_need_resolution`. Requires `member:manage`. A BROWSER SESSION must additionally carry a fresh step-up reauthentication (`X-Step-Up-Token`, obtained from `/auth/step-up`); without one the call answers `428 step_up_required` and `error.details` names the action and resource to prove. An organization API token is not challenged: step-up re-proves the person behind a session, and a token has no person behind it. The gate is demanded LAST, after every resolution refusal above, so an admin never re-authenticates only to be told the removal needs decisions first. Custom connectors the member owns are resolved by `connector_actions` (`transfer` only — never delete, and never released: an ownerless connector is a legacy state). EVERY owned connector, private or shared, needs a new owner: one with no entry is refused with 409 `connectors_need_resolution` unless `defaults.transfer_to_user_id` names a bulk recipient. A body that still sends `action: "release"` or `defaults.release_connectors` is refused with 400 `connector_release_removed` (the message says what to send instead). Should the member still own a connector when the removal reaches the org itself, that is 409 `member_owns_connectors`. The response `applied` counts include `connectors`.

Path Parameters

userIdstring
required·

User id (usr_…) of an org member.

Request Body

bundle_actionsobject[]

Per-resource decisions; a shared one with none (and no bulk target) refuses the removal.

actionstring
Possible values:
transferdelete
resource_idstring

Id from the offboarding preflight.

target_team_idstring

A team (team_…): its first active admin owns it, the team gets an edit share. Never with target_user_id.

target_user_idstring

A person (usr_…): an active member other than the leaver and the caller.

collection_actionsobject[]

Per-resource decisions; a shared one with none (and no bulk target) refuses the removal.

actionstring
Possible values:
transferdelete
resource_idstring

Id from the offboarding preflight.

target_team_idstring

A team (team_…): its first active admin owns it, the team gets an edit share. Never with target_user_id.

target_user_idstring

A person (usr_…): an active member other than the leaver and the caller.

connection_actionsobject[]

Optional: every connection the member owns is deleted whether named or not.

actionstring

The only action. transfer answers 400 connection_transfer_removed.

Possible values:
delete
connection_idstring

Connection id (conn_…) from the offboarding preflight.

connection_replacementsobject[]

Before each old connection is deleted, rewrite every toolbox entry and other member's synthetic tool step naming it onto the replacement. One entry per old connection.

connection_idstring

The leaver's connection (conn_…).

replacement_connection_idstring

Same connector, active, not the leaver's, usable by the caller. null keeps this one connection out of its app's replacement_defaults entry: the tools that used it stop.

connector_actionsobject[]

One decision per custom connector the member owns (see connectors on the offboarding preflight). Required for every connector unless defaults.transfer_to_user_id covers it.

actionstring

transfer hands ownership to target_user_id (grants untouched). There is no delete, and no release: release was removed and answers 400 connector_release_removed.

Possible values:
transfer
connector_slugstring

Connector slug from the offboarding preflight.

target_team_idstring

For transfer: a team (team_…) instead of a person.

target_user_idstring

For transfer (usr_…) — an active member other than the one being removed and other than the caller.

dashboard_actionsobject[]

Per-resource decisions; a shared one with none (and no bulk target) refuses the removal.

actionstring
Possible values:
transferdelete
resource_idstring

Id from the offboarding preflight.

target_team_idstring

A team (team_…): its first active admin owns it, the team gets an edit share. Never with target_user_id.

target_user_idstring

A person (usr_…): an active member other than the leaver and the caller.

defaultsobject

Bulk decisions for every resource that carries no explicit per-resource action.

delete_synthetic_toolsboolean

Acknowledge, once, that every synthetic tool is deleted with its author.

transfer_to_team_idstring

Or one team (team_…): its first active admin owns everything, the team gets an edit share. Never with transfer_to_user_id.

transfer_to_user_idstring

One active member (usr_…) who inherits everything owned that has no explicit action. Connections never follow it.

kind_defaultsobject

One decision per KIND for every item of it with no per-resource entry. Per item: its own entry wins, then its kind's default, then defaults. A transfer names exactly one of target_user_id / target_team_id; a delete names neither (400 otherwise). Unlike defaults, a kind default may delete — it names one kind deliberately — and a delete reaches only the items in the summary the caller read: one created since is left for the next round (202).

collectionobject
required
3 properties
actionstring
Possible values:
transferdelete
target_team_idstring

For transfer: a team (team_…) instead of a person.

target_user_idstring

For transfer: a person (usr_…).

connectorobject
required
3 properties
actionstring

Custom connectors are never deleted.

Possible values:
transfer
target_team_idstring

A team (team_…) instead of a person.

target_user_idstring

A person (usr_…).

dashboardobject
required
3 properties
actionstring
Possible values:
transferdelete
target_team_idstring

For transfer: a team (team_…) instead of a person.

target_user_idstring

For transfer: a person (usr_…).

knowledge_baseobject
required
3 properties
actionstring
Possible values:
transferdelete
target_team_idstring

For transfer: a team (team_…) instead of a person.

target_user_idstring

For transfer: a person (usr_…).

templateobject
required
3 properties
actionstring
Possible values:
transferdelete
target_team_idstring

For transfer: a team (team_…) instead of a person.

target_user_idstring

For transfer: a person (usr_…).

toolboxobject
required
3 properties
actionstring
Possible values:
transferdelete
target_team_idstring

For transfer: a team (team_…) instead of a person.

target_user_idstring

For transfer: a person (usr_…).

knowledge_base_actionsobject[]

Per-resource decisions; a shared one with none (and no bulk target) refuses the removal.

actionstring
Possible values:
transferdelete
resource_idstring

Id from the offboarding preflight.

target_team_idstring

A team (team_…): its first active admin owns it, the team gets an edit share. Never with target_user_id.

target_user_idstring

A person (usr_…): an active member other than the leaver and the caller.

replacement_defaultsobject[]

One replacement per app instead of one connection_replacements entry per connection: every connection of connector_slug with no connection_replacements entry of its own is replaced by replacement_connection_id, validated per connection exactly like an explicit entry. A replacement the rules refuse is a 400 for the whole request (replacement_*); an app named twice is 400 duplicate_replacement_default; an app the member has no connection of is 400 replacement_default_unknown_connector.

connector_slugstring

The app, from the preflight's connections.

replacement_connection_idstring

One of that app's replacement_candidates.

template_actionsobject[]

Per-resource decisions; a shared one with none (and no bulk target) refuses the removal.

actionstring
Possible values:
transferdelete
resource_idstring

Id from the offboarding preflight.

target_team_idstring

A team (team_…): its first active admin owns it, the team gets an edit share. Never with target_user_id.

target_user_idstring

A person (usr_…): an active member other than the leaver and the caller.

toolbox_actionsobject[]

Per-resource decisions; a shared one with none (and no bulk target) refuses the removal.

actionstring
Possible values:
transferdelete
resource_idstring

Id from the offboarding preflight.

target_team_idstring

A team (team_…): its first active admin owns it, the team gets an edit share. Never with target_user_id.

target_user_idstring

A person (usr_…): an active member other than the leaver and the caller.

Response Body

successboolean
curl -X DELETE 'https://api.elaichi.ai/member/<userId>' \
  -H 'Authorization: Bearer $ELAICHI_API_TOKEN' \
  -H 'Content-Type: application/json'
const response = await fetch('https://api.elaichi.ai/member/<userId>', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer ' + process.env.ELAICHI_API_TOKEN,
    'Content-Type': 'application/json',
  },
});

const data = await response.json();
console.log(data);
import os
import requests

url = "https://api.elaichi.ai/member/<userId>"
headers = {
    "Authorization": f"Bearer {os.environ['ELAICHI_API_TOKEN']}",
    "Content-Type": "application/json",
}

response = requests.delete(url, headers=headers)
print(response.json())