delete_users_by_id
Calls DELETE /api/users/{id} as the signed-in person · REST reference
Flag a user as deleted
A SOFT DELETE, and the wording of the response says so. DeleteUserCommand sets deleted/deletedAt/deletedBy on the :User node and removes nothing — no node, no edge, no property. NEVER DELETE DATA is a platform rule, not a preference of this route. WHAT THE FLAG DOES. Both of the reads that matter exclude it, and both are null-safe (coalesce(u.deleted, false) = false, so a node carrying no deleted property at all is still returned): ListUsersQuery, so the row leaves GET /api/users; and GetUserByEmailQuery, which is the lookup behind OTP verification — so POST /api/auth/login cannot resolve the account and answers 401. The same command SUSPENDS the user's DynamoDB auth row, so the password paths refuse them too; a failure there rolls the flag back. A user with no auth row is deleted all the same. REFUSED (409), writing nothing, when the user owns an organization (every org has exactly one owner; transfer ownership first) or is the last live holder of the PFF_SUPERADMIN role. The message names what to fix first. orgId is a REQUIRED QUERY PARAMETER for the same reason it is required on the create and the update: the DAL declares deleteUser(orgId: UUID!) and evaluates the write against that scope — the caller must hold execute:DeleteUserCommand IN that org, and the user being deleted must be a MEMBER of it unless it is the platform org (403 otherwise). A permission held in one org does not reach another org's users. No membership moves. deletedBy is the AUTHENTICATED CALLER and is never taken from the request. The DAL declares it UUID! and it is the audit actor; a client-supplied one would let the caller sign someone else's name to the deletion.
Arguments
| Argument | Type | Description | |
|---|---|---|---|
idpath | string (uuid) | required | User id. Not format-validated by this route. |
orgIdquery | string (uuid) | required | Non-empty string, the DAL's permission scope. Not validated as a UUID by this route. |
Example call
A client sends this over the MCP session; Claude and other agents build it for you. The values are placeholders — the shape is real.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "delete_users_by_id",
"arguments": {
"id": "11111111-1111-4111-8111-111111111111",
"orgId": "11111111-1111-4111-8111-111111111111"
}
}
}Result
On success (HTTP 200) the tool returns one text item holding the API's JSON, pretty-printed.
The user is flagged as deleted. The message names the flag rather than claiming a removal, because nothing was removed.
{
"message": "User flagged as deleted"
}Fields
| Field | Type | Description |
|---|---|---|
message | "User flagged as deleted" |
When it fails
A failed call returns isError: true with the API's message as text. A change the person declines, or lets time out, also comes back this way and nothing changes.
400orgIdmissing from the query string.403Authenticated, but the caller holds none of the required permissions.
404No such user, or the user is already flagged as deleted.
409Refused: the user owns an organization — every org has exactly one owner, so the owner is not deleted until ownership is transferred (or the organization is deleted with them) — or is the platform's last live PFF_SUPERADMIN. The DAL's own message, naming the organization or the role.
What the model is shown
This is the tool exactly as the server lists it in tools/list. The description starts with the REST call it makes.
{
"name": "delete_users_by_id",
"description": "DELETE /api/users/{id} — A SOFT DELETE, and the wording of the response says so. `DeleteUserCommand` sets `deleted`/`deletedAt`/`deletedBy` on the `:User` node and removes nothing — no node, no edge, no property. NEVER DELETE DATA is a platform rule, not a preference of this route. WHAT THE FLAG DOES. Both of the reads that matter exclude it, and both are null-safe (`coalesce(u.deleted, false) = false`, so a node carrying no `deleted` property at all is still returned): `ListUsersQuery`, so the row leaves `GET /api/users`; and `GetUserByEmailQuery`, which is the lookup behind OTP verification — so `POST /api/auth/login` cannot resolve the account and answers 401. The same command SUSPENDS the user's DynamoDB auth row, so the password paths refuse them too; a failure there rolls the flag back. A user with no auth row is deleted all the same. REFUSED (409), writing nothing, when the user owns an organization (every org has exactly one owner; transfer ownership first) or is the last live holder of the `PFF_SUPERADMIN` role. The message names what to fix first. `orgId` is a REQUIRED QUERY PARAMETER for the same reason it is required on the create and the update: the DAL declares `deleteUser(orgId: UUID!)` and evaluates the write against that scope — the caller must hold `execute:DeleteUserCommand` IN that org, and the user being deleted must be a MEMBER of it unless it is the platform org (403 otherwise). A permission held in one org does not reach another org's users. No membership moves. `deletedBy` is the AUTHENTICATED CALLER and is never taken from the request. The DAL declares it `UUID!` and it is the audit actor; a client-supplied one would let the caller sign someone else's name to the deletion.\n\nThis changes data. The person is asked to confirm it before it runs, and nothing happens if they decline.",
"inputSchema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "User id. Not format-validated by this route."
},
"orgId": {
"type": "string",
"description": "Non-empty string, the DAL's permission scope. Not validated as a UUID by this route."
}
},
"required": [
"id",
"orgId"
]
},
"annotations": {
"readOnlyHint": false,
"destructiveHint": true,
"idempotentHint": true,
"openWorldHint": false
}
}