Skip to main content
Remove From Subtree
Removes the target user’s direct memberships on this workspace and every descendant you can reach, in one action. An owner (or ancestor owner, and any master/service key) reaches the whole subtree via the god-path; a non-owner actor only reaches descendants through an unbroken open inherit chain, so a sealed sub-workspace (inheritsFromParent = false) is left untouched for a non-owner. Reach-based access auto-revokes with the relevant ancestor membership. Each membership-row deletion fires workspace.member.removed. Rank rules apply per node — the sweep is refused (403 workspace/insufficient-rank) if you are outranked by the target at any node where you both hold a direct member row. Owned-descendant handling — block + report. Because owners have no member row, this sweep does not cover descendant workspaces the user owns. Instead it blocks and reports them (409 workspace/owns-descendants) rather than silently orphaning ownership. An admin or any ancestor owner then reassigns each via transfer-ownership or deletes it. No silent ownership change.
A non-owner’s sweep may be partial — check skipped before you call someone offboarded. If the target still holds a membership in a descendant you cannot reach, the 200 reports it in skippedCount / skipped. removedCount alone does not mean the user is gone from the subtree: treat any skippedCount > 0 as “still a member somewhere” and escalate to an owner or ancestor owner of that node. An owner / ancestor owner / master / service key always gets skippedCount: 0.

Path Parameters

string
required
The workspace UUID (subtree root).
string
required
The user to offboard.

Body Parameters

string
Service/master keys only — the acting user (must hold remove-member, rank-bounded per node).

Response

number
How many membership rows were torn down.
object[]
One { workspaceId, userId } entry per removed membership.
number
How many memberships the target retained because the sweep could not reach them. Always 0 for an owner, ancestor owner, master key or service key. Equals skipped.length.
object[]
One entry per retained membership — the descendants where the target is still a member after this call.
  • id / name — the workspace, mirroring the ownedWorkspaces shape on the 409. Both are null when you have no standing on that workspace: the sweep tells you a membership survived, but does not disclose the existence or name of a sealed sub-workspace you have no authority over. This is the same sealing fence the roster read applies to include=descendants. Get an owner or ancestor owner of that branch to finish the offboarding.
  • reason — currently always "out-of-reach": your authority does not extend to removing members there.
Only descendants where the target actually still holds a direct membership appear. Unreachable descendants the target was never a member of are never listed — the report is scoped to what actually survived the offboarding, not to the shape of the tree.
skipped covers surviving memberships only. Descendants the target owns are handled by the separate 409 workspace/owns-descendants block, and an owned descendant outside a non-owner’s reach is neither swept nor reported here — an owner re-running the sweep will surface it as the 409.

Error Responses