---
title: "Remove member"
method: DELETE
path: "/member/{userId}"
tags: ["Members"]
---

# Remove member

`DELETE /member/{userId}`

## Request

- Base URL: `https://api.elaichi.ai`
- URL: `https://api.elaichi.ai/member/{userId}`
- Auth: HTTP bearer

Removes a member from the organization. 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 (with their stored credentials); 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 also pass a fresh step-up reauthentication in the app (without it the call answers `428 step_up_required`). An organization API token is not challenged. The step-up check runs 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

- `userId` string, required

## Headers

- `X-Organization-Id` string

## Request body

- object
  - `connection_actions` object[]: Optional: every connection the member owns is deleted whether named or not.
    - `connection_id` string, required: Connection id (`conn_…`) from the offboarding preflight.
    - `action` 'delete', required: The only action. `transfer` answers 400 `connection_transfer_removed`.
  - `connection_replacements` object[]: 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_id` string, required: The leaver's connection (`conn_…`).
    - `replacement_connection_id` string, nullable, required: 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.
  - `toolbox_actions` object[]: Per-resource decisions; a shared one with none (and no bulk target) refuses the removal.
    - `resource_id` string, required: Id from the offboarding preflight.
    - `action` 'transfer' | 'delete', required
    - `target_user_id` string: A person (`usr_…`): an active member other than the leaver and the caller.
    - `target_team_id` string: A team (`team_…`): its first active admin owns it, the team gets an `edit` share. Never with `target_user_id`.
  - `template_actions` object[]: Per-resource decisions; a shared one with none (and no bulk target) refuses the removal.
    - `resource_id` string, required: Id from the offboarding preflight.
    - `action` 'transfer' | 'delete', required
    - `target_user_id` string: A person (`usr_…`): an active member other than the leaver and the caller.
    - `target_team_id` string: A team (`team_…`): its first active admin owns it, the team gets an `edit` share. Never with `target_user_id`.
  - `collection_actions` object[]: Per-resource decisions; a shared one with none (and no bulk target) refuses the removal.
    - `resource_id` string, required: Id from the offboarding preflight.
    - `action` 'transfer' | 'delete', required
    - `target_user_id` string: A person (`usr_…`): an active member other than the leaver and the caller.
    - `target_team_id` string: A team (`team_…`): its first active admin owns it, the team gets an `edit` share. Never with `target_user_id`.
  - `dashboard_actions` object[]: Per-resource decisions; a shared one with none (and no bulk target) refuses the removal.
    - `resource_id` string, required: Id from the offboarding preflight.
    - `action` 'transfer' | 'delete', required
    - `target_user_id` string: A person (`usr_…`): an active member other than the leaver and the caller.
    - `target_team_id` string: A team (`team_…`): its first active admin owns it, the team gets an `edit` share. Never with `target_user_id`.
  - `knowledge_base_actions` object[]: Per-resource decisions; a shared one with none (and no bulk target) refuses the removal.
    - `resource_id` string, required: Id from the offboarding preflight.
    - `action` 'transfer' | 'delete', required
    - `target_user_id` string: A person (`usr_…`): an active member other than the leaver and the caller.
    - `target_team_id` string: A team (`team_…`): its first active admin owns it, the team gets an `edit` share. Never with `target_user_id`.
  - `bundle_actions` object[]: Per-resource decisions; a shared one with none (and no bulk target) refuses the removal.
    - `resource_id` string, required: Id from the offboarding preflight.
    - `action` 'transfer' | 'delete', required
    - `target_user_id` string: A person (`usr_…`): an active member other than the leaver and the caller.
    - `target_team_id` string: A team (`team_…`): its first active admin owns it, the team gets an `edit` share. Never with `target_user_id`.
  - `connector_actions` object[]: 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.
    - `connector_slug` string, required: Connector slug from the offboarding preflight.
    - `action` 'transfer', required: `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`.
    - `target_user_id` string: For `transfer` (`usr_…`) — an active member other than the one being removed and other than the caller.
    - `target_team_id` string: For `transfer`: a team (`team_…`) instead of a person.
  - `replacement_defaults` object[]: 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_slug` string, required: The app, from the preflight's connections.
    - `replacement_connection_id` string, required: One of that app's `replacement_candidates`.
  - `kind_defaults` object: 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).
    - `toolbox` object
      - `action` 'transfer' | 'delete', required
      - `target_user_id` string: For `transfer`: a person (`usr_…`).
      - `target_team_id` string: For `transfer`: a team (`team_…`) instead of a person.
    - `template` object
      - `action` 'transfer' | 'delete', required
      - `target_user_id` string: For `transfer`: a person (`usr_…`).
      - `target_team_id` string: For `transfer`: a team (`team_…`) instead of a person.
    - `collection` object
      - `action` 'transfer' | 'delete', required
      - `target_user_id` string: For `transfer`: a person (`usr_…`).
      - `target_team_id` string: For `transfer`: a team (`team_…`) instead of a person.
    - `dashboard` object
      - `action` 'transfer' | 'delete', required
      - `target_user_id` string: For `transfer`: a person (`usr_…`).
      - `target_team_id` string: For `transfer`: a team (`team_…`) instead of a person.
    - `knowledge_base` object
      - `action` 'transfer' | 'delete', required
      - `target_user_id` string: For `transfer`: a person (`usr_…`).
      - `target_team_id` string: For `transfer`: a team (`team_…`) instead of a person.
    - `connector` object
      - `action` 'transfer', required: Custom connectors are never deleted.
      - `target_user_id` string: A person (`usr_…`).
      - `target_team_id` string: A team (`team_…`) instead of a person.
  - `defaults` object: Bulk decisions for every resource that carries no explicit per-resource action.
    - `transfer_to_user_id` string: One active member (`usr_…`) who inherits everything owned that has no explicit action. Connections never follow it.
    - `transfer_to_team_id` string: Or one team (`team_…`): its first active admin owns everything, the team gets an `edit` share. Never with `transfer_to_user_id`.
    - `delete_synthetic_tools` boolean: Acknowledge, once, that every synthetic tool is deleted with its author.

## Response `200`

Success

- Success
  - `success` boolean, required

## Other responses

- `400`: Bad request
- `401`: Unauthorized
- `403`: Forbidden
- `404`: Not found
- `409`: `<kind>_need_resolution` (toolboxes, templates, collections, dashboards, knowledge_bases, bundles) — a shared owned resource has no decision; `error.details.<kind>` lists each. `synthetic_tools_need_resolution` — another member's toolbox pins a synthetic tool of theirs; acknowledge it. Also `connectors_need_resolution` — the member owns custom connectors with no transfer; `error.details.connectors` lists each, and the retry carries a `connector_actions` transfer per connector, or `defaults.transfer_to_user_id` for all of them. `member_owns_connectors` is the same refusal from the org itself, for a connector that arrived after the summary was read. `connectors_need_resolution` carries `error.details.no_eligible_successor`: true when nobody can take a connector — no active member besides the leaver and the acting admin, and the admin lacks `connector:manage` — so no `connector_actions` body can satisfy it: invite another member first, or have the leaver transfer their connectors before they leave. `error.details.sole_successor` is true when the acting admin is the only other member and holds `connector:manage`: they may then name themselves as the recipient of connectors (only connectors; every other resource type still refuses a self-transfer).
- `429`: Rate limited

---

[API](https://skmtc.dev/elaichi/apis/elaichi-api.md) · [All operations](https://skmtc.dev/elaichi/apis/elaichi-api/llms.txt) · [OpenAPI document](https://skmtc.dev/elaichi/apis/elaichi-api/revisions/b22cfb7b6d15?raw)
