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) reaches the whole subtree via the god-path; a non-owner reaches a descendant when they hold remove-member there — which they may hold either by reaching in through an unbroken open inherit chain, or by holding a direct member row on that node. So sealing (inheritsFromParent = false) stops authority reaching in from above; it does not stop authority held locally. A non-owner who is a direct member of a sealed child with remove-member there does sweep it. The credential does not widen thisactingUserId is required here, so a service/master key always sweeps as the user it names, with that user’s reach; act as the owner for a full sweep. 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. Only an owner or ancestor owner is guaranteed skippedCount: 0. A service/master key is not — it must name an actingUserId on this route, and inherits that user’s reach, so a key acting as a mid-level admin can absolutely come back with skippedCount > 0. See Acting on behalf of a user.

Path Parameters

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

Body Parameters

string
required
Service/master keys only — the acting user (must hold remove-member, rank-bounded per node). Required for a key: there is no unbounded path on this route, so the sweep’s reach is always that user’s — a key that names nobody is refused with 400 workspace/missing-user-id. Name the owner for a full-subtree sweep.A plain user token omits it — the actor is the token’s own user. The field is not ignored for such a token: sending your own user id is a harmless no-op, but sending anyone else’s is a 403 workspace/unauthorized. See Acting on behalf of a user.

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. 0 for an owner or ancestor owner. A service/master key gets whatever its acting user gets — the credential grants no extra reach here. 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

A service/master key called this route without naming an actingUserId. The sweep’s reach is defined by the acting user’s standing per node, so it must be performed as somebody.
A plain user token sent an actingUserId naming a different user. Only a service/master key may act as someone else.
Every path id on the workspaces bundle is checked for UUID shape before the route runs, so a malformed one is a plain 400 rather than a 500 from the database.