POST
/
api
/
v1
/
admin
/
users
/
:id
/
ban
Ban user
curl --request POST \
  --url https://api.example.com/api/v1/admin/users/:id/ban
import requests

url = "https://api.example.com/api/v1/admin/users/:id/ban"

response = requests.post(url)

print(response.text)
const options = {method: 'POST'};

fetch('https://api.example.com/api/v1/admin/users/:id/ban', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));
<?php

$curl = curl_init();

curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/api/v1/admin/users/:id/ban",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
package main

import (
"fmt"
"net/http"
"io"
)

func main() {

url := "https://api.example.com/api/v1/admin/users/:id/ban"

req, _ := http.NewRequest("POST", url, nil)

res, _ := http.DefaultClient.Do(req)

defer res.Body.Close()
body, _ := io.ReadAll(res.Body)

fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.post("https://api.example.com/api/v1/admin/users/:id/ban")
.asString();
require 'uri'
require 'net/http'

url = URI("https://api.example.com/api/v1/admin/users/:id/ban")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)

response = http.request(request)
puts response.read_body

Overview

Bans a user account. The operation:
  1. Sets users.status = BANNED, users.bannedAt = now, users.bannedReason = reason.
  2. Revokes every active refresh token the user holds — they are forced to re-login on all devices and will then hit the disabled-account gate.
It is idempotent: re-banning an already-banned user is a no-op (the original bannedAt/bannedReason are not overwritten).

Authentication

Bearer <accessToken> with scope: 'admin' required (requireAdmin).
Role-gated: requires SUPER or MODERATOR. A FINANCE admin is read-only on the user surface and receives 403 FORBIDDEN.

Path parameters

NameTypeRequiredNotesExample
idstringyesUser ObjectId, 1..64 chars.66400a8f1c2b4d5e6f7a8b01

Query parameters

None.

Request body

FieldTypeRequiredNotesExample
reasonstringyes1..500 chars. Recorded on the user row and surfaced in the audit log.Repeated harassment reports

Example payload

{
  "reason": "Repeated harassment reports"
}

Response — 200 OK

Returns the updated user summary object.
{
  "id": "66400a8f1c2b4d5e6f7a8b01",
  "email": "jane@example.com",
  "firstName": "Jane",
  "lastName": "Doe",
  "status": "BANNED",
  "tenancyStatus": "APPROVED",
  "subscriptionStatus": "ACTIVE",
  "bannedAt": "2026-05-23T11:02:00.000Z",
  "bannedReason": "Repeated harassment reports",
  "createdAt": "2026-04-12T09:00:00.000Z"
}

Error responses

StatusCodeMeaning
400VALIDATION_FAILEDreason missing, empty, or over 500 chars.
401UNAUTHENTICATEDMissing, malformed, expired, or non-admin-scope token.
403FORBIDDENCaller is a FINANCE admin (read-only on the user surface).
404NOT_FOUNDNo user with that id.

Example error — 403 FORBIDDEN

{
  "type": "https://api.swappr.co.uk/errors/forbidden",
  "title": "Forbidden",
  "status": 403,
  "code": "FORBIDDEN",
  "detail": "Admin role FINANCE is not permitted for this action",
  "instance": "/api/v1/admin/users/66400a8f1c2b4d5e6f7a8b01/ban",
  "requestId": "01HZQ7K3M4N5P6Q7R8S9T0V1W2"
}

Side effects

  • users row updated (status, bannedAt, bannedReason).
  • All of the user’s refresh_tokens rows revoked.
  • An audit-log row is written (admin id, target user id, action users.ban).

See also

curl

curl -X POST https://api.swappr.co.uk/api/v1/admin/users/66400a8f1c2b4d5e6f7a8b01/ban \
  -H "Authorization: Bearer $ADMIN_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Repeated harassment reports" }'

Postman

See docs/postman/swappr.postman_collection.jsonAdmin Users → Ban.