Reports, blocks and bans
Users block and report each other. You and your moderators ban users and IP ranges.
Before you start
Section titled “Before you start”- For blocks: the
policyrow from the user’s role to the peer’s role hasblockon.Conversation.can.blockshows it to a client. - For bans from your server: your server secret and app id, and the user’s
external_idor an IP range. See Sessions and tokens. - Set
links.support_emailif you want banned users to see how to reach you.
export MC=https://app.mondochat.io/v1export SESSION=<session token>auth=(-H "Authorization: Bearer $MONDOCHAT_SERVER_SECRET" -H "Mondochat-App-Id: $MONDOCHAT_APP_ID" -H 'content-type: application/json')-
Block and unblock a user.
user_idis the peer’sidinPeer, not the external id. Both calls answer 204.Terminal window curl -X PUT $MC/blocks/$USER_ID -H "Authorization: Bearer $SESSION"curl -X DELETE $MC/blocks/$USER_ID -H "Authorization: Bearer $SESSION"A block applies both ways for delivery: messages, calls, photos and presence stop in both directions.
call_endedandpurchasecards still arrive. A block also clears the blocker’s waiting state in that conversation, and sends your server theblock.createdorblock.removedwebhook with{blocker, blocked}. -
List what a user blocked with
GET /v1/blocks?cursor=...&limit=200. It answers{items: [{user, created_at}], next_cursor}, newest first. Deleted users are left out and thePeerhas no presence. Another user’s blocks are never listed. -
Decide what a blocked sender sees. By default the sender is not told:
- The send succeeds for the sender, the message is stored as
undelivered, and the peer receives nothing and gets no unread count. - A blocked reaction is stored and shown only to its author.
- The blocker who writes to someone they blocked gets 403
forbiddenwith reasonyou_blocked.
Set
chat.refuse_blocked_sendtotrueto refuse such sends instead, with 403forbiddenand reasonundelivered, and store nothing. It applies to text, photos, videos, voice notes and reactions. - The send succeeds for the sender, the message is stored as
-
Let users report. Send
POST /v1/reportswith{user_id, conversation_id?, reason}. It answers 202.Terminal window curl -X POST $MC/reports -H "Authorization: Bearer $SESSION" -H 'content-type: application/json' \-d "{\"user_id\": \"$USER_ID\", \"conversation_id\": \"$ID\", \"reason\": \"spam\"}"reasonis free text of 1 to 1000 characters and is stored as sent. The frame offersspam,harassment,inappropriate,scam,underageandother; your own client may send any text. The reported user must be one the reporter has a conversation with, andconversation_id, when given, must be that conversation. A report about a conversation peer opens a moderation case of kindreportand sends thereport.createdwebhook with{report_id, reporter, target, reason}. A second report from the same user about the same target while the first is open is a duplicate: there is still one open report. Withsafety.report_reasonsoff, the frame asks for no reason and reports with reasonother. -
Ban from your server with
POST /v1/server/bans. Ban a user byexternal_id, or an IP range bycidr(IPv4 or IPv6).reasonis required.expires_atis optional. Send anIdempotency-Key.Terminal window curl -X POST $MC/server/bans "${auth[@]}" -H 'Idempotency-Key: ban-carol-1' \-d '{"external_id": "carol", "reason": "spam", "expires_at": "2026-12-31T00:00:00Z"}'curl -X POST $MC/server/bans "${auth[@]}" -d '{"cidr": "203.0.113.0/24", "reason": "abuse"}'const res = await fetch('https://app.mondochat.io/v1/server/bans', {method: 'POST',headers: {authorization: `Bearer ${process.env.MONDOCHAT_SERVER_SECRET}`,'mondochat-app-id': process.env.MONDOCHAT_APP_ID,'idempotency-key': 'ban-carol-1','content-type': 'application/json',},body: JSON.stringify({ external_id: 'carol', reason: 'spam' }),})const ban = await res.json()The answer is a
Ban:{id, user_id, external_id, cidr, reason, expires_at, created_by, created_by_name, created_at, lifted_at}. For a ban made through the server API,created_byandcreated_by_nameare null. A ban sends theban.createdwebhook. -
Lift a ban from your server with
DELETE /v1/server/bans/{id}. It answers 204 and sendsban.lifted.Terminal window curl -X DELETE $MC/server/bans/$BAN_ID "${auth[@]}" -
Ban from the dashboard. Moderators list bans, create them with
{user_id?, cidr?, reason, expires_at?}, changeexpires_at(or clear it with null) and lift them. The bans list shows who made each ban, and “API” for those from your server. -
Know where bans bite. A ban is checked at session exchange, refresh, conversation open, message send, call start, call token and upload. A new ban on a user publishes
session.revokedto that user with{reason: "banned", temporary}, so a connected client learns at once.temporaryis true when the ban expires. Lookups are cached for 60 seconds per user and per IP, so a new ban can take up to a minute to reach every server.
Check it works
Section titled “Check it works”- Ban a user with
POST /v1/server/bans. The answer haslifted_at: null. - Send a message as that user. The answer is 403
banned. With anexpires_at, the problem carriesreason: "temporary". - Lift the ban. The same user can send again.
When it fails
Section titled “When it fails”| Answer | Cause |
|---|---|
403 banned |
The user or their IP is banned. detail is your links.support_email if you set one. reason is temporary for a ban with an expiry, and absent otherwise. It never carries the expiry or the kind of ban. |
403 policy_denied with reason block |
PUT /v1/blocks/{user_id} and the policy row from the user’s role to the peer’s has block off. |
404 not_found |
A block or report names a user that is not in your app, or a report names a user the reporter has no conversation with. A server ban names an external_id that is not a user of your app. |
| 422 | A block or report names the caller, a ban gives both or neither of external_id and cidr, or a body field is invalid. |
403 forbidden with reason you_blocked |
The user blocked the peer they are writing to. |
403 forbidden with reason undelivered |
The peer blocked the sender and chat.refuse_blocked_send is on. |
409 idempotency_conflict |
The same Idempotency-Key was sent with a different body. |
| 401 | The server secret or app id is wrong. |
Limits
Section titled “Limits”- An expired or lifted ban is not a ban.
- Blocked recipients are not an error unless you turn on
chat.refuse_blocked_send. - A repeat of a server ban call with the same
Idempotency-Keywithin 24 hours returns the first answer withIdempotent-Replayed: true. - There are no automatic bans. A moderator or your server makes each one.
- The client IP is the viewer’s address as the edge reports it.
X-Forwarded-Foris ignored.
Related topics
Section titled “Related topics”- Create a server ban, lift a server ban, block, unblock, list blocks, report
- ban.created webhook, ban.lifted webhook, block.created webhook, report.created webhook
- Moderation
- Settings reference for
chat.refuse_blocked_send,safetyandlinks.support_email - Errors