POST
/
api
/
v1
/
admin
/
tenancy
/
:id
/
reject
Reject tenancy verification
curl --request POST \
  --url https://api.example.com/api/v1/admin/tenancy/:id/reject
import requests

url = "https://api.example.com/api/v1/admin/tenancy/:id/reject"

response = requests.post(url)

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

fetch('https://api.example.com/api/v1/admin/tenancy/:id/reject', 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/tenancy/:id/reject",
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/tenancy/:id/reject"

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/tenancy/:id/reject")
.asString();
require 'uri'
require 'net/http'

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

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

Rejects a tenancy verification. Updates the verification row (reviewStatus = REJECTED, reviewedBy, reviewedAt, reviewNotes = reason) and flips users.tenancyStatus → REJECTED. A reason is required so the user has actionable feedback. Idempotent: re-rejecting an already-REJECTED row is a no-op.

Authentication

Bearer <accessToken> with scope: 'admin' required (requireAdmin).
Role-gated: requires SUPER or MODERATOR. A FINANCE admin receives 403 FORBIDDEN.

Path parameters

NameTypeRequiredNotesExample
idstringyesVerification row ObjectId, 1..64 chars.66400a8f1c2b4d5e6f7ab000

Query parameters

None.

Request body

FieldTypeRequiredNotesExample
reasonstringyes1..500 chars. Stored as reviewNotes and surfaced to the user.Document is illegible — please re-upload a clear scan.

Example payload

{
  "reason": "Document is illegible — please re-upload a clear scan."
}

Response — 200 OK

Returns the updated tenancy verification row with reviewStatus: "REJECTED" and reviewNotes set.
{
  "id": "66400a8f1c2b4d5e6f7ab000",
  "userId": "66400a8f1c2b4d5e6f7a8b01",
  "landlordName": "Acme Lettings",
  "documentType": "TENANCY_AGREEMENT",
  "uploadId": "66400a8f1c2b4d5e6f7ac000",
  "reviewStatus": "REJECTED",
  "reviewedBy": "66400a8f1c2b4d5e6f7a0001",
  "reviewedAt": "2026-05-23T11:12:00.000Z",
  "reviewNotes": "Document is illegible — please re-upload a clear scan.",
  "createdAt": "2026-05-20T08:30:00.000Z",
  "user": { "id": "66400a8f1c2b4d5e6f7a8b01", "email": "jane@example.com", "firstName": "Jane", "lastName": "Doe" },
  "upload": null,
  "documentDownloadUrl": null
}

Error responses

StatusCodeMeaning
400VALIDATION_FAILEDreason missing, empty, or over 500 chars.
401UNAUTHENTICATEDMissing, malformed, expired, or non-admin-scope token.
403FORBIDDENCaller is a FINANCE admin.
404NOT_FOUNDNo verification row with that id.
409STATE_CONFLICTRow is already APPROVED — cannot be flipped to rejected through this endpoint.

Example error — 400 VALIDATION_FAILED

{
  "type": "https://api.swappr.co.uk/errors/validation-failed",
  "title": "Validation failed",
  "status": 400,
  "code": "VALIDATION_FAILED",
  "detail": "Request body failed validation",
  "instance": "/api/v1/admin/tenancy/66400a8f1c2b4d5e6f7ab000/reject",
  "requestId": "01HZQ7K3M4N5P6Q7R8S9T0V1W2",
  "errors": [
    { "path": "reason", "message": "Too small: expected string to have >=1 characters", "code": "too_small" }
  ]
}

Side effects

  • Verification row + users.tenancyStatus updated.
  • Audit-log row written (action tenancy.reject).

See also

curl

curl -X POST https://api.swappr.co.uk/api/v1/admin/tenancy/66400a8f1c2b4d5e6f7ab000/reject \
  -H "Authorization: Bearer $ADMIN_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Document is illegible — please re-upload a clear scan." }'

Postman

See docs/postman/swappr.postman_collection.jsonAdmin Tenancy → Reject.