Eternaltwin

Home | /api | v1 | users

/api/v1/users/:user_id/sanctions

Site-wide sanctions on an account — today, bans. Defined in crates/rest/src/users.rs.

A ban blocks authentication and nothing else: the account is not deleted, its links stay, its posts stay, and it remains as visible as any other account everywhere except the sign-in form. That is what makes it different from DELETE /api/v1/users/:user_id, which undoes the account.

Forum warnings and mutes are a different mechanism, scoped to the forum and served by /api/v1/forum/sanctions.

Administrators only, both verbs. There is no self-service: nobody bans or unbans themselves.

Sanctions are additive. Banning and unbanning write a new row each time rather than mutating one, so the history of past bans survives. A sanction carries an internal_note and nothing that is shown to the banned user.

There is no route to list them here. An account's current state is visible as is_banned on the short user returned by the moderation read shared-browser-clusters.

POST /:user_id/sanctions

Bans the account. Creating the sanction also closes every session the account has open, so the ban takes effect immediately rather than at the next sign-in.

Body

FieldTypeMeaning
internal_notestringWhy, for other moderators. Required.

Example

POST /api/v1/users/28dbb0bf-0fdc-40fe-ae5a-dde193f9fea8/sanctions
Content-Type: application/json
Cookie: sid=b8be19ef-2d61-44de-b7d2-9c34ccb8a763

{"internal_note": "ban evasion"}
{
  "id": "a3d6f0b1-2c9e-4f5a-8b7d-1e0c4a2b9f38",
  "user": {"type": "User", "id": "28dbb0bf-0fdc-40fe-ae5a-dde193f9fea8"},
  "created_at": "2026-09-14T08:21:03.114Z",
  "created_by": {"type": "User", "id": "9f310484-963b-446b-af69-797feec6813f"},
  "deleted_at": null,
  "deleted_by": null,
  "internal_note": "ban evasion"
}

Errors

StatusBodyWhen
403{"error": "forbidden"}Not an administrator.
404{"error": "not found"}No such account.
409{"error": "already sanctioned"}A live ban already covers this account.
500{"error": "internal error"}—

DELETE /:user_id/sanctions/:sanction_id

Lifts a ban. :sanction_id is the id of the sanction; the :user_id in the path is not used to find it, so it must be the right one for the URL to mean what it says.

Answers 200 with the JSON literal null. The row is not removed: the service closes it, recording deleted_at and deleted_by.

Example

DELETE /api/v1/users/28dbb0bf-0fdc-40fe-ae5a-dde193f9fea8/sanctions/a3d6f0b1-2c9e-4f5a-8b7d-1e0c4a2b9f38
Cookie: sid=b8be19ef-2d61-44de-b7d2-9c34ccb8a763
null

Errors

StatusBodyWhen
403{"error": "forbidden"}Not an administrator.
404{"error": "not found"}No such sanction.
500{"error": "internal error"}—