POST
/
api
/
v1
/
admin
/
reports
/
:id
/
dismiss
Dismiss report
curl --request POST \
  --url https://api.example.com/api/v1/admin/reports/:id/dismiss
import requests

url = "https://api.example.com/api/v1/admin/reports/:id/dismiss"

response = requests.post(url)

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

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

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

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

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

Resolves an abuse report by dismissing it — the moderator has reviewed the case and decided no action is warranted. Transitions the report row from OPENDISMISSED and stamps resolvedBy, resolvedAt, and (optionally) resolutionReason. Idempotent: re-dismissing an already-DISMISSED report returns the existing row unchanged (no new audit row, no re-stamp). Calling this on a report whose status is WARNED or BANNED returns 409 STATE_CONFLICT — terminal states do not transition back. See Admin reports feed for the queue this is acting on.

Authentication

Bearer <accessToken> with scope: 'admin' required.
Role-gated: SUPER or MODERATOR. FINANCE admins are read-only on the reports surface.

Path parameters

FieldTypeRequiredNotesExample
idstringyesThe report id from the list endpoint. 1..64 chars.665a3f1e9c2b0a0001a4d201

Query parameters

None.

Request body

The body is optional — pass an empty body, or include a moderator note that is stored on the row as resolutionReason.
FieldTypeRequiredNotesExample
reasonstringnoInternal moderator note, 1..1000 chars. Stored on reports.resolutionReason. Not surfaced to the reporter or the accused.Transcript shows the message was a joke between friends.

Example payload

{
  "reason": "Transcript shows the message was a joke between friends."
}

Response — 200 OK

Returns the full updated report row (same shape as list).
FieldTypeNotes
idstringReport id.
statusstringAlways "DISMISSED" for a successful call.
resolvedBystringThe admin id (from the bearer token).
resolvedAtstring (ISO 8601)Server clock at the moment of resolution.
resolutionReasonstring | nullEcho of the request reason, or null if omitted.
reporter, accusedobject | nullEmbedded user summaries.
transcriptSnapshotarrayImmutable transcript captured at report time. Unchanged by this endpoint.
{
  "id": "665a3f1e9c2b0a0001a4d201",
  "reporterId": "664a01...",
  "accusedId":  "664a02...",
  "conversationId": "664c10...",
  "messageId":      "664d44...",
  "reason": "HARASSMENT",
  "freeText": "They keep pestering me after I asked them to stop.",
  "transcriptSnapshot": [
    { "senderId": "664a02...", "text": "still around?", "sentAt": "2026-05-21T19:22:00.000Z" }
  ],
  "status": "DISMISSED",
  "resolvedBy": "664f99...",
  "resolvedAt": "2026-05-23T09:14:11.412Z",
  "resolutionReason": "Transcript shows the message was a joke between friends.",
  "createdAt": "2026-05-22T18:00:00.000Z",
  "reporter": { "id": "664a01...", "email": "alice@example.com", "firstName": "Alice", "lastName": "T." },
  "accused":  { "id": "664a02...", "email": "bob@example.com",   "firstName": "Bob",   "lastName": "G." }
}

Error responses

StatusCodeMeaning
400VALIDATION_FAILEDreason longer than 1000 chars, or body is not JSON.
401UNAUTHENTICATEDMissing / malformed / expired admin token, or non-admin scope.
403FORBIDDENCaller is a FINANCE admin. SUPER or MODERATOR required.
404NOT_FOUNDNo report exists with that id.
409STATE_CONFLICTReport is in WARNED or BANNED — terminal states never transition back.

Example error — 409 STATE_CONFLICT

{
  "type": "https://api.swappr.co.uk/errors/state-conflict",
  "title": "State conflict",
  "status": 409,
  "code": "STATE_CONFLICT",
  "detail": "Report is already resolved with status BANNED",
  "instance": "/api/v1/admin/reports/665a3f1e9c2b0a0001a4d201/dismiss",
  "requestId": "01HZQ7K3M4N5P6Q7R8S9T0V1W2"
}

Side effects

  • Report row updated: status = DISMISSED, resolvedBy, resolvedAt, resolutionReason set.
  • One admin audit row written (action: report.dismiss, targetType: report, targetId: <reportId>).
  • No notification is sent to the reporter or the accused (dismissal is silent).

See also

curl

curl -X POST https://api.swappr.co.uk/api/v1/admin/reports/665a3f1e9c2b0a0001a4d201/dismiss \
  -H "Authorization: Bearer $ADMIN_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"reason":"Transcript shows the message was a joke."}'

Postman

See docs/postman/swappr.postman_collection.jsonAdmin Reports → Dismiss.