Review items and violations
The APIs on this page list and decide your review items, get and clear users' violations, get statistics, and report on users' behalf. They match "Review items", "Platform actions", "User violations" and "Statistics" under "Moderation" in the console, and let you connect moderation to your own review or support system. Where review items come from and how they're handled: Manual review; doing it in the console: Review items.
Connecting your own review system usually looks like this:
- Subscribe to the
moderation.item_createdcallback event (a new review item), or periodically list review items withstatus=pending; - Read the item's content and give it to a reviewer;
- Decide, recalling the message, clearing the profile, or muting or banning the author at the same time if needed.
Notes:
- Only your queue: the APIs only return your review items (including the status of items you escalated to the platform); items matching platform rules and reviewed by the platform don't appear. See actions the platform took on your app with List platform actions.
- Content is separate from lists: lists and single items don't contain users' content, which you can only get through Read an item's content. Reading content through the API isn't written to the audit log, so record in your own system who viewed what.
- Audit log: decisions, escalations and clearing violations are written to the audit log, with the server key used as the operator; decisions in items show as "App credential".
- Call quota: batch decisions count toward the app's OpenAPI call quota per item, see Batch APIs.
List review items
Lists review items page by page, see Pagination. By default only open items (pending, auto_violation) are returned, by priority and then newest first; with order=oldest, oldest first within each priority, for working through a backlog. When only closed statuses are queried, items are sorted by creation time only, not priority.
/{org_name}/{app_name}/moderation/itemsPath parameters
For org_name and app_name, see Server overview.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
status | String | No | Status; repeat for several, such as status=violation&status=no_violation. Values in Review item object. pending and auto_violation by default |
scene | String | No | Scene, values in What's checked and when; report for items from reporting users, groups and chat rooms |
target_type | String | No | Target type, values in Review item object |
category | String | No | Items with this category |
source | String | No | Items with this source: word_list, provider (third-party moderation), report, url_rejected (address couldn't be reviewed), escalation |
author | String | No | Author's username. An empty list if the user doesn't exist |
priority | Number | No | Priority, 0 to 2 |
from, to | String | No | Creation time range, RFC 3339, such as 2026-10-04T19:50:00Z |
escalated | Boolean | No | When true, lists items you escalated to the platform instead |
order | String | No | oldest for oldest first |
limit | Number | No | Items per page, 20 by default, at most 100 |
cursor | String | No | The next_cursor from the previous page |
Example request
curl "$IM_API/$ORG/$APP/moderation/items?source=word_list&limit=20" \
-H "Authorization: Bearer $APP_TOKEN"Response
Returns 200 OK:
| Field | Type | Description |
|---|---|---|
items | Array<Object> | Array of review item objects, without actions |
next_cursor | String | Cursor for the next page, null when there's none |
{
"items": [
{
"item_id": "100319142342557696",
"queue": "tenant",
"status": "pending",
"scene": "message",
"target_type": "message",
"target_id": "100319140375429120",
"target_username": null,
"target_group_id": null,
"conversation_id": "100311226008993792",
"author": "lisi",
"sources": [
{
"type": "word_list",
"list_id": "100311111852621824",
"list_name": "疑似诈骗",
"word": "兼职刷单",
"category": "fraud"
}
],
"categories": [
"fraud"
],
"suggestion": "review",
"auto_actions": [],
"report_count": 0,
"priority": 2,
"decision": null,
"escalated_at": null,
"content_available": true,
"version": 1,
"created_at": "2026-10-04T19:52:27.376Z",
"closed_at": null,
"close_reason": null
}
],
"next_cursor": null
}Errors
| HTTP status | code | Description |
|---|---|---|
| 400 | invalid_argument | Invalid value; details.field is status, scene, category, source, priority, from or to |
Get a review item
Gets one review item, including the actions actions it caused.
/{org_name}/{app_name}/moderation/items/{item_id}Path parameters
For org_name and app_name, see Server overview.
| Parameter | Type | Description |
|---|---|---|
item_id | String | Review item ID |
Example request
curl "$IM_API/$ORG/$APP/moderation/items/100311227732852736" \
-H "Authorization: Bearer $APP_TOKEN"Response
Returns 200 OK with a review item object. This item was sent for review by a word list and then reported by a user; a reviewer confirmed a violation and recalled the message, then changed it to no violation:
{
"item_id": "100311227732852736",
"queue": "tenant",
"status": "no_violation",
"scene": "message",
"target_type": "message",
"target_id": "100311226352926720",
"target_username": null,
"target_group_id": null,
"conversation_id": "100311226008993792",
"author": "zhangsan",
"sources": [
{
"type": "word_list",
"list_id": "100311111852621824",
"list_name": "疑似诈骗",
"word": "兼职刷单",
"category": "fraud"
},
{
"type": "report",
"count": 1,
"reason": "fraud"
}
],
"categories": [
"fraud"
],
"suggestion": "review",
"auto_actions": [],
"report_count": 1,
"priority": 0,
"decision": {
"result": "no_violation",
"by_type": "account",
"by": "文档(d***@example.com)",
"note": "用户申诉,确认误判",
"decided_at": "2026-10-04T19:26:40.754Z"
},
"escalated_at": null,
"content_available": true,
"version": 4,
"created_at": "2026-10-04T19:21:00.386Z",
"closed_at": "2026-10-04T19:26:32.494Z",
"close_reason": "decided",
"actions": [
{
"action_id": "100312620694765568",
"kind": "recall_message",
"authority": "tenant",
"trigger": "review",
"status": "done",
"last_error": null,
"created_at": "2026-10-04T19:26:32.497Z",
"done_at": "2026-10-04T19:26:32.545Z"
}
]
}Errors
| HTTP status | code | Description |
|---|---|---|
| 404 | not_found | The item doesn't exist or isn't yours |
Read an item's content
Reads the item's content snapshot and its reports. The content is a snapshot from when it was first found: if the message was later recalled or the profile changed, you still read the content as it was. Content is cleared 30 days after the item closes: content becomes null, and report descriptions and evidence are cleared too.
/{org_name}/{app_name}/moderation/items/{item_id}/contentPath parameters
For org_name and app_name, see Server overview.
| Parameter | Type | Description |
|---|---|---|
item_id | String | Review item ID |
Example request
curl "$IM_API/$ORG/$APP/moderation/items/100312464440164352/content" \
-H "Authorization: Bearer $APP_TOKEN"Response
Returns 200 OK:
| Field | Type | Description |
|---|---|---|
item_id | String | Review item ID |
content | Object | Content snapshot, shaped by target type, see Content snapshots; null once cleared |
reports | Array<Object> | Reports in this item, see Report details; an empty array if there are none |
An item for a reported user: profile is the profile at report time, with later profile changes in versions:
{
"item_id": "100312464440164352",
"content": {
"profile": {
"avatar_url": "",
"nickname": "zhangsan"
},
"versions": [
{
"captured_at": "2026-10-04T19:27:35.838Z",
"profile": {
"avatar_url": "https://cdn.example.com/avatars/zhangsan.png",
"nickname": "zhangsan"
}
}
]
},
"reports": [
{
"report_id": "100312464440164353",
"reporter": "lisi",
"reason": "abuse",
"description": "多次辱骂",
"evidence": [
{
"message_id": "100311226034159616",
"conversation_id": "100311226008993792",
"sender": "zhangsan",
"type": "text",
"body": {
"text": "你这个**,周末一起吃饭"
},
"created_at": "2026-10-04T19:20:59.978Z"
}
],
"via": "openapi",
"created_at": "2026-10-04T19:25:55.240Z"
},
{
"report_id": "100312886391341057",
"reporter": "wangwu",
"reason": "ad",
"description": "",
"evidence": [],
"via": "client",
"created_at": "2026-10-04T19:27:35.841Z"
}
]
}An item for a message:
{
"item_id": "100311227732852736",
"content": {
"body": {
"text": "在家兼职刷单,日结300"
},
"created_at": "2026-10-04T19:21:00.055Z",
"edits": [],
"ext": null,
"review_urls": [],
"type": "text"
},
"reports": [
{
"report_id": "100312442805944321",
"reporter": "lisi",
"reason": "fraud",
"description": "冒充兼职,要求先垫付",
"evidence": [],
"via": "openapi",
"created_at": "2026-10-04T19:25:50.082Z"
}
]
}An item for a profile field sent for review (such as a nickname):
{
"item_id": "100312702043291648",
"content": {
"fields": [
{
"captured_at": "2026-10-04T19:26:51.887Z",
"name": "nickname",
"value": "兼职刷单达人"
}
]
},
"reports": []
}Errors
| HTTP status | code | Description |
|---|---|---|
| 404 | not_found | The item doesn't exist or isn't yours |
Decide
Decides a review item: confirm a violation (optionally taking actions), or no violation. Also used to revise: within 30 days of a decision, "violation" can be changed to "no violation".
/{org_name}/{app_name}/moderation/items/{item_id}/decideWhat a decision does:
| Current status | Decision | Effect |
|---|---|---|
pending | violation | Status becomes violation and the chosen actions run; with count_violation true, counts as the author's violation and may trigger automatic penalties |
pending | no_violation | Status becomes no_violation |
auto_violation | violation | Status becomes violation. The automatic actions and violation already exist and aren't repeated; only actions chosen now run |
auto_violation, violation | no_violation | Revised to no violation: violations this item caused under your rules are deleted and files it blocked are unblocked. Recalled messages can't be restored, and mutes and bans already applied aren't lifted; call Unmute or Unban a user if needed |
no_violation | violation | Not allowed: returns 403 permission_denied (not_revisable). Wait for a new match or report if new violations appear |
expired (unhandled for 30 days) | Either | Same as pending; same as auto_violation if it was acted on automatically |
Actions run after the decision is submitted, so actions in the response are usually still pending; get the item later to see the results. A retry after a timeout when the earlier request succeeded (same decision, no new actions) returns the current item without repeating actions.
Path parameters
For org_name and app_name, see Server overview.
| Parameter | Type | Description |
|---|---|---|
item_id | String | Review item ID |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
decision | String | Yes | violation or no_violation |
actions | Array<Object> | No | Actions to take at the same time, only with violation, see Actions |
count_violation | Boolean | No | Whether to count it as the author's violation, true by default. Pass false when, for example, the author's account was hijacked; ignored for items without an author |
note | String | No | Note, at most 256 characters, recorded in the decision and the audit log |
version | Number | No | The item's version you read. If given and different from the current version, returns 409 version_conflict with the current item in details.current, showing someone else's decision |
Example request
Confirm a violation, recall the message and mute the author for 1 hour:
curl -X POST "$IM_API/$ORG/$APP/moderation/items/100319140232822784/decide" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"decision": "violation",
"actions": [
{ "kind": "recall_message" },
{ "kind": "mute_user", "scopes": ["chat", "group"], "duration_seconds": 3600 }
],
"count_violation": true,
"note": "发布刷单兼职",
"version": 1
}'Revise to no violation:
curl -X POST "$IM_API/$ORG/$APP/moderation/items/100319140232822784/decide" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"decision": "no_violation",
"note": "用户申诉成功",
"version": 2
}'Response
Returns 200 OK with the review item object after the decision:
{
"item_id": "100319140232822784",
"queue": "tenant",
"status": "violation",
"scene": "message",
"target_type": "message",
"target_id": "100319139314270208",
"target_username": null,
"target_group_id": null,
"conversation_id": "100311226008993792",
"author": "lisi",
"sources": [
{
"type": "word_list",
"list_id": "100311111852621824",
"list_name": "疑似诈骗",
"word": "兼职刷单",
"category": "fraud"
}
],
"categories": [
"fraud"
],
"suggestion": "review",
"auto_actions": [],
"report_count": 0,
"priority": 2,
"decision": {
"result": "violation",
"by_type": "app",
"by": "App 凭据",
"note": "发布刷单兼职",
"decided_at": "2026-10-04T19:53:11.258Z"
},
"escalated_at": null,
"content_available": true,
"version": 2,
"created_at": "2026-10-04T19:52:26.872Z",
"closed_at": "2026-10-04T19:53:11.258Z",
"close_reason": "decided",
"actions": [
{
"action_id": "100319326401200128",
"kind": "recall_message",
"authority": "tenant",
"trigger": "review",
"status": "pending",
"last_error": null,
"created_at": "2026-10-04T19:53:11.263Z",
"done_at": null
},
{
"action_id": "100319326401200129",
"kind": "mute_user",
"authority": "tenant",
"trigger": "review",
"status": "pending",
"last_error": null,
"created_at": "2026-10-04T19:53:11.263Z",
"done_at": null
}
]
}Errors
| HTTP status | code | Description |
|---|---|---|
| 400 | invalid_argument | details.field is decision, note or actions: invalid value, such as a mute without scopes or a duration out of range |
| 400 | invalid_argument | details.reason is invalid_action: the action doesn't apply to this target, such as recalling a message on a user profile, blocking files on a message with no files of this app, or muting on an item with no author; or no_violation came with actions |
| 403 | permission_denied | details.reason is not_revisable: changing no violation to violation; revising after 30 days; the item was escalated, or closed by escalation or target deletion; decisions made by the platform can only be revised by the platform |
| 404 | not_found | The item doesn't exist |
| 409 | version_conflict | version differs from the current version; details.current is the current review item object |
{
"error": {
"code": "permission_denied",
"message": "不能把无违规改为违规",
"details": {
"reason": "not_revisable"
},
"request_id": "ZGPHLUYW77R3JNTDAUUSGNTOHP"
}
}Batch decide
Makes the same decision on 1 to 100 items at once, for example a flood of spam ads. Each item is handled separately, and one failing doesn't affect the others.
- Only undecided items are handled (
pending,auto_violation, and those closed after 30 days unhandled); already decided items returnversion_conflictand aren't revised in batches; - Actions can only be recalling messages, recalling chat room messages, blocking files, clearing profiles and deleting chat room attributes — not muting or banning users, or banning groups and chat rooms;
- No
versionis needed; each item is handled at its current version.
/{org_name}/{app_name}/moderation/items/batch-decidePath parameters
For org_name and app_name, see Server overview.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
item_ids | Array<String> | Yes | Review item IDs, 1 to 100 |
decision | String | Yes | violation or no_violation |
actions | Array<Object> | No | Actions for every item, only with violation, see Actions. Each item must support them, or that item fails |
count_violation | Boolean | No | Whether to count it as the author's violation, true by default |
note | String | No | Note, at most 256 characters |
Example request
curl -X POST "$IM_API/$ORG/$APP/moderation/items/batch-decide" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"item_ids": ["100319140253794304", "100319140253794311", "100319140232822784"],
"decision": "violation",
"actions": [{ "kind": "recall_message" }],
"count_violation": false,
"note": "批量刷单广告"
}'Response
Returns 200 OK; results gives each item's result in request order: item_id and the decided item item on success, item_id and error (code, message, sometimes details) on failure. Here the third item was already decided (items in between omitted):
{
"results": [
{
"item": {
"item_id": "100319140253794304",
"queue": "tenant",
"status": "violation",
"scene": "message",
"target_type": "message",
"target_id": "100319139679174656",
"target_username": null,
"target_group_id": null,
"conversation_id": "100311226008993792",
"author": "lisi",
"sources": [
{
"type": "word_list",
"list_id": "100311111852621824",
"list_name": "疑似诈骗",
"word": "兼职刷单",
"category": "fraud"
}
],
"categories": [
"fraud"
],
"suggestion": "review",
"auto_actions": [],
"report_count": 0,
"priority": 2,
"decision": {
"result": "violation",
"by_type": "app",
"by": "App 凭据",
"note": "批量刷单广告",
"decided_at": "2026-10-04T19:53:27.498Z"
},
"escalated_at": null,
"content_available": true,
"version": 2,
"created_at": "2026-10-04T19:52:26.878Z",
"closed_at": "2026-10-04T19:53:27.498Z",
"close_reason": "decided",
"actions": [
{
"action_id": "100319394516697088",
"kind": "recall_message",
"authority": "tenant",
"trigger": "review",
"status": "pending",
"last_error": null,
"created_at": "2026-10-04T19:53:27.498Z",
"done_at": null
}
]
},
"item_id": "100319140253794304"
},
{
"error": {
"code": "version_conflict",
"message": "数据已被其他人修改,请刷新后重试",
"details": {
"current": {
"item_id": "100319140232822784",
"queue": "tenant",
"status": "violation",
"scene": "message",
"target_type": "message",
"target_id": "100319139314270208",
"target_username": null,
"target_group_id": null,
"conversation_id": "100311226008993792",
"author": "lisi",
"sources": [
{
"type": "word_list",
"list_id": "100311111852621824",
"list_name": "疑似诈骗",
"word": "兼职刷单",
"category": "fraud"
}
],
"categories": [
"fraud"
],
"suggestion": "review",
"auto_actions": [],
"report_count": 0,
"priority": 2,
"decision": {
"result": "violation",
"by_type": "app",
"by": "App 凭据",
"note": "发布刷单兼职",
"decided_at": "2026-10-04T19:53:11.258Z"
},
"escalated_at": null,
"content_available": true,
"version": 2,
"created_at": "2026-10-04T19:52:26.872Z",
"closed_at": "2026-10-04T19:53:11.258Z",
"close_reason": "decided",
"actions": [
{
"action_id": "100319326401200128",
"kind": "recall_message",
"authority": "tenant",
"trigger": "review",
"status": "done",
"last_error": null,
"created_at": "2026-10-04T19:53:11.263Z",
"done_at": "2026-10-04T19:53:11.320Z"
},
{
"action_id": "100319326401200129",
"kind": "mute_user",
"authority": "tenant",
"trigger": "review",
"status": "done",
"last_error": null,
"created_at": "2026-10-04T19:53:11.263Z",
"done_at": "2026-10-04T19:53:11.347Z"
}
]
}
}
},
"item_id": "100319140232822784"
}
]
}Errors
These errors apply to the whole request, in which case no item is handled.
| HTTP status | code | Description |
|---|---|---|
| 400 | invalid_argument | details.field is item_ids: empty, more than 100, or containing invalid IDs |
| 400 | invalid_argument | details.reason is invalid_action: the actions include muting or banning |
Individual items may fail with not_found, version_conflict, invalid_argument (invalid_action) or permission_denied (not_revisable).
Escalate to the platform
Hands an open review item to the platform, for content you think may be illegal and need the platform. Afterwards the platform reviews it; you can still see its status (list review items with escalated=true) but can't decide it. If the platform already has an item for the same target, this item merges into it, with status expired and close_reason escalated.
/{org_name}/{app_name}/moderation/items/{item_id}/escalatePath parameters
For org_name and app_name, see Server overview.
| Parameter | Type | Description |
|---|---|---|
item_id | String | Review item ID |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
note | String | No | Escalation note, at most 256 characters. Without a body, Content-Type can be omitted |
Example request
curl -X POST "$IM_API/$ORG/$APP/moderation/items/100312522434805760/escalate" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"note": "群公告疑似违法,请平台处理"
}'Response
Returns 200 OK with the escalated review item object, with queue platform:
{
"item_id": "100312522434805760",
"queue": "platform",
"status": "pending",
"scene": "report",
"target_type": "group",
"target_id": "100311656273281024",
"target_username": null,
"target_group_id": null,
"conversation_id": null,
"author": null,
"sources": [
{
"type": "report",
"count": 1,
"reason": "other"
},
{
"type": "escalation",
"note": "群公告疑似违法,请平台处理"
}
],
"categories": [
"other"
],
"suggestion": null,
"auto_actions": [],
"report_count": 1,
"priority": 2,
"decision": null,
"escalated_at": "2026-10-04T19:53:39.067Z",
"content_available": true,
"version": 2,
"created_at": "2026-10-04T19:26:09.067Z",
"closed_at": null,
"close_reason": null
}Errors
| HTTP status | code | Description |
|---|---|---|
| 400 | invalid_argument | details.field is note: note longer than 256 characters |
| 403 | permission_denied | details.reason is not_revisable: the item is closed or already escalated |
| 404 | not_found | The item doesn't exist |
List platform actions
Lists actions the platform took on your app as the platform after platform rules matched, such as recalling messages, blocking files and bans, newest first, page by page. Only targets, categories and actions are returned, not content. The platform keeps evidence for messages it recalled, and you can't lift files it blocked or its bans.
/{org_name}/{app_name}/moderation/platform-actionsPath parameters
For org_name and app_name, see Server overview.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | Number | No | Items per page, 20 by default, at most 100 |
cursor | String | No | The next_cursor from the previous page |
Example request
curl "$IM_API/$ORG/$APP/moderation/platform-actions" \
-H "Authorization: Bearer $APP_TOKEN"Response
Returns 200 OK; each item of items:
| Field | Type | Description |
|---|---|---|
target_type, target_id, target_username, target_group_id | String | The target, as in Review item object |
category | String | Main category |
kind | String | Action kind, see Actions |
status | String | pending, done, skipped or failed |
created_at | String | When the action was taken |
done_at | String | When it finished, null if not finished |
next_cursor is the cursor for the next page, null when there's none.
{
"items": [
{
"category": "porn",
"created_at": "2026-10-04T19:25:27.939Z",
"done_at": "2026-10-04T19:25:27.995Z",
"kind": "recall_message",
"status": "done",
"target_group_id": null,
"target_id": "100312348417327104",
"target_type": "message",
"target_username": null
}
],
"next_cursor": null
}Get a user's violations
Gets a user's violations (newest first, page by page), current violation count and recent penalties. What counts as a violation: Violations and automatic penalties.
/{org_name}/{app_name}/moderation/users/{username}/violationsPath parameters
For org_name and app_name, see Server overview.
| Parameter | Type | Description |
|---|---|---|
username | String | Username |
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | Number | No | Violations per page, 20 by default, at most 100 |
cursor | String | No | The next_cursor from the previous page |
Example request
curl "$IM_API/$ORG/$APP/moderation/users/zhaoliu/violations" \
-H "Authorization: Bearer $APP_TOKEN"Response
Returns 200 OK:
| Field | Type | Description |
|---|---|---|
items | Array<Object> | Violations, see below |
next_cursor | String | Cursor for the next page, null when there's none |
violation_count | Number | Current violation count (within 180 days, not cleared), used by automatic penalty rules |
penalties | Array<Object> | The last 10 penalties (automatic penalties and mutes and bans from decisions), see below |
Each item of items:
| Field | Type | Description |
|---|---|---|
violation_id | String | Violation ID |
scene | String | Scene; report for those from reporting users, groups and chat rooms |
category | String | Category |
source | String | Source: sync rejected before sending or saving, async acted on automatically in post-review, review confirmed by a reviewer |
authority | String | Whose rules: tenant yours, platform the platform's |
item_id | String | Corresponding review item ID; null for rejections before sending or saving. Platform-rule violations correspond to platform items, and getting them returns 404 |
created_at | String | When the violation happened |
Each item of penalties:
| Field | Type | Description |
|---|---|---|
kind | String | mute_user (global mute) or disable_user (ban) |
params | Object | scopes (mute scopes), duration_seconds (null is permanent), reason |
status | String | pending, done, skipped or failed |
created_at | String | When the penalty was applied |
{
"items": [
{
"violation_id": "100312349927276546",
"scene": "message",
"category": "porn",
"source": "async",
"authority": "platform",
"item_id": "100312349927276544",
"created_at": "2026-10-04T19:25:27.941Z"
},
{
"violation_id": "100312269543440386",
"scene": "message",
"category": "ad",
"source": "async",
"authority": "tenant",
"item_id": "100312269543440384",
"created_at": "2026-10-04T19:25:08.777Z"
},
{
"violation_id": "100312045760544768",
"scene": "message",
"category": "ad",
"source": "sync",
"authority": "tenant",
"item_id": null,
"created_at": "2026-10-04T19:24:15.419Z"
}
],
"next_cursor": null,
"penalties": [
{
"kind": "mute_user",
"params": {
"scopes": [
"chat",
"group"
],
"duration_seconds": 3600,
"reason": "内容安全:自动处罚(24 小时内违规 3 次)"
},
"status": "done",
"created_at": "2026-10-04T19:25:27.943Z"
}
],
"violation_count": 3
}Errors
| HTTP status | code | Description |
|---|---|---|
| 404 | not_found | The user doesn't exist or was deleted |
Clear a user's violations
Deletes all of a user's violations recorded under your rules, lowering the violation count accordingly — for example after confirming their account was hijacked. Platform-rule violations are kept. Mutes and bans already applied aren't lifted, and their entries in penalties remain.
/{org_name}/{app_name}/moderation/users/{username}/violationsPath parameters
For org_name and app_name, see Server overview.
| Parameter | Type | Description |
|---|---|---|
username | String | Username |
Example request
curl -X DELETE "$IM_API/$ORG/$APP/moderation/users/lisi/violations" \
-H "Authorization: Bearer $APP_TOKEN"Response
Returns 200 OK; removed is the number of violations deleted:
{
"removed": 6
}Errors
| HTTP status | code | Description |
|---|---|---|
| 404 | not_found | The user doesn't exist or was deleted |
Get statistics
Gets moderation statistics per day and scene, plus the number of your pending review items. Days follow Beijing time, and numbers lag by up to 5 minutes.
/{org_name}/{app_name}/moderation/statsPath parameters
For org_name and app_name, see Server overview.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
from | String | Yes | Start date, such as 2026-10-01 |
to | String | Yes | End date, inclusive, not before from, at most 89 days after it (90 days in all) |
scene | String | No | Only this scene, values in What's checked and when |
Example request
curl "$IM_API/$ORG/$APP/moderation/stats?from=2026-10-05&to=2026-10-05" \
-H "Authorization: Bearer $APP_TOKEN"Response
Returns 200 OK:
| Field | Type | Description |
|---|---|---|
days | Array<Object> | One entry per day per scene: date, scene (report for reporting users, groups and chat rooms) and metrics. Days and scenes without data, and metrics equal to 0, are omitted |
pending | Object | Your pending review items (pending, auto_violation, excluding escalated ones): count, and oldest_wait_seconds for how long the oldest has waited, null if none |
Metrics in metrics:
| Metric | Description |
|---|---|
checked | Checks |
rejected, replaced, flagged | Results of reject, replace and review |
auto_actioned | Automatic actions in post-review |
provider_calls | Third-party moderation calls |
platform_calls_text, platform_calls_image, platform_calls_audio, platform_calls_video | Of those, text, image, voice and video reviews on the platform account |
cache_hits | Images, voice and video that hit an existing review result without calling the provider |
provider_errors | Failed third-party moderation calls |
timeouts | Sync review timeouts (deprecated; third-party review is no longer synchronous, always 0) |
quota_skipped | Checks done with word lists only because the per-minute post-review rate or the platform account's daily limit was exceeded |
reports | Reports |
{
"days": [
{
"date": "2026-10-05",
"metrics": {
"checked": 3,
"rejected": 1,
"replaced": 1,
"reports": 1
},
"scene": "chatroom_message"
},
{
"date": "2026-10-05",
"metrics": {
"checked": 1
},
"scene": "chatroom_profile"
},
{
"date": "2026-10-05",
"metrics": {
"checked": 3,
"rejected": 2,
"replaced": 1
},
"scene": "friend_request"
},
{
"date": "2026-10-05",
"metrics": {
"checked": 1,
"rejected": 1
},
"scene": "group_member"
},
{
"date": "2026-10-05",
"metrics": {
"checked": 3,
"rejected": 1,
"replaced": 1
},
"scene": "group_profile"
},
{
"date": "2026-10-05",
"metrics": {
"checked": 3,
"rejected": 2
},
"scene": "group_request"
},
{
"date": "2026-10-05",
"metrics": {
"auto_actioned": 2,
"checked": 15,
"flagged": 2,
"provider_calls": 8,
"provider_errors": 2,
"rejected": 8,
"replaced": 3,
"reports": 1,
"timeouts": 2
},
"scene": "message"
},
{
"date": "2026-10-05",
"metrics": {
"reports": 5
},
"scene": "report"
},
{
"date": "2026-10-05",
"metrics": {
"checked": 13,
"flagged": 1,
"provider_calls": 2,
"rejected": 4,
"replaced": 2
},
"scene": "user_profile"
}
],
"pending": {
"count": 7,
"oldest_wait_seconds": 1783
}
}Errors
| HTTP status | code | Description |
|---|---|---|
| 400 | invalid_argument | details.field is from or to: wrong date format, to before from, or more than 90 days; or scene: invalid scene |
Report on a user's behalf
Submits a report in a user's name, for example reports you collect in your own web pages or support system. Same rules as client reports, including visibility checks, but not subject to report_enabled or per-user rate limits; instead each app can make at most 600 a minute.
/{org_name}/{app_name}/moderation/reportsPath parameters
For org_name and app_name, see Server overview.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
reporter | String | Yes | Reporter's username. Whether they can report is judged by what they can see |
target_type | String | Yes | What's reported: message, user, group, chatroom or chatroom_message |
message_id | String | Depends on target | Reported message ID, required when target_type is message or chatroom_message |
username | String | Depends on target | Reported username, required when target_type is user |
group_id | String | Depends on target | Reported group ID, required when target_type is group |
room_id | String | Depends on target | Chat room ID, required when target_type is chatroom or chatroom_message |
reason | String | Yes | Report reason, see Report reasons |
description | String | No | Description, at most 500 characters, no control characters (line breaks allowed) |
evidence_message_ids | Array<String> | No | Evidence message IDs, at most 5, only for reporting users and groups: when reporting a user they must be sent by that user, when reporting a group they must be in that group, and the reporter must be able to see all of them |
Example request
Report a message:
curl -X POST "$IM_API/$ORG/$APP/moderation/reports" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"reporter": "lisi",
"target_type": "message",
"message_id": "100311226352926720",
"reason": "fraud",
"description": "冒充兼职,要求先垫付"
}'Report a user with evidence messages:
curl -X POST "$IM_API/$ORG/$APP/moderation/reports" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"reporter": "lisi",
"target_type": "user",
"username": "zhangsan",
"reason": "abuse",
"description": "多次辱骂",
"evidence_message_ids": ["100311226034159616"]
}'Response
Returns 201 Created with a report object. If this person already reported the same target, returns 200 OK with the original report, without recording it again or counting it toward rate limits.
{
"report_id": "100312442805944321",
"target_type": "message",
"username": null,
"message_id": "100311226352926720",
"group_id": null,
"room_id": null,
"reason": "fraud",
"status": "pending",
"created_at": "2026-10-04T19:25:50.082Z"
}Errors
| HTTP status | code | Description |
|---|---|---|
| 400 | invalid_argument | details.field is reporter: no reporter given, or the reporter doesn't exist |
| 400 | invalid_argument | details.field is target_type, reason, description or evidence_message_ids: invalid value, or the description or evidence exceeds limits |
| 400 | invalid_argument | details.reason is self_report: reporting oneself or one's own message |
| 400 | invalid_argument | details.reason is invalid_evidence: evidence attached when reporting a message, chat room or chat room message; or an evidence message doesn't exist, isn't visible to the reporter, was recalled, wasn't sent by the reported user, or isn't in the reported group |
| 404 | not_found | The target doesn't exist or the reporter can't see it: the message doesn't exist, was recalled or isn't visible to the reporter; the user doesn't exist or was deleted; the group doesn't exist, was dismissed, or is a private group the reporter isn't in; the chat room doesn't exist or was dismissed; the chat room message is no longer among recent messages |
| 429 | rate_limited | Over the 600-a-minute limit per app; details.reason is request_rate |
Reporting one's own message:
{
"error": {
"code": "invalid_argument",
"message": "不能举报自己的消息",
"details": {
"reason": "self_report"
},
"request_id": "N6YRITSEUZQRKK6376PMPGLSXB"
}
}Data structures
Review item object
| Field | Type | Description |
|---|---|---|
item_id | String | Review item ID |
queue | String | tenant your queue; platform escalated to the platform |
status | String | pending awaiting review; auto_violation acted on automatically, awaiting confirmation; violation; no_violation; expired closed, reason in close_reason |
scene | String | Scene, see What's checked and when; report for items from reporting users, groups and chat rooms |
target_type | String | Target type: message, chatroom_message, user_profile, group_profile, group_member (group nickname), group_request (join request), friend_request, chatroom_profile, chatroom_attribute, file, user (reported user), group (reported group), chatroom (reported chat room) |
target_id | String | Target ID: message ID; {room ID}:{message ID} for chat room messages; group ID for group profiles and reported groups; chat room ID for chat room profiles and reported chat rooms; {room ID}:{attribute key} for chat room attributes; file ID for files. null for targets involving users (user, user_profile, group_member, group_request, friend_request), which use the next two fields |
target_username | String | The user involved: the user for user and user_profile, the member whose group nickname changed for group_member, the applicant or invitee for group_request, the recipient for friend_request; null for other targets and deleted users |
target_group_id | String | Group ID for group_member and group_request, otherwise null |
conversation_id | String | Conversation ID of the message, null if not a message |
author | String | Author's username: the user who posted the content, or the reported user. Content submitted by your server, reported groups and chat rooms have no author; also null if the author was deleted |
sources | Array<Object> | Sources, see below |
categories | Array<String> | Matched categories |
suggestion | String | Machine suggestion: block or review; null for reports only |
auto_actions | Array<String> | Automatic actions taken, such as recall_message, block_file |
report_count | Number | Number of reporters |
priority | Number | Priority: 0 highest (includes categories the platform enforces, or reports for fraud, politics, terrorism or minors); 1 high (acted on automatically, or 3 or more reporters); 2 normal |
decision | Object | The decision, null if none. result is violation or no_violation; by_type is who decided: account (console member), app (your server), platform_admin (the platform); by is the decider — a console member's name and partly hidden email, App 凭据 for your server, null for the platform; note; decided_at |
escalated_at | String | When it was escalated, null if not |
content_available | Boolean | Whether the content snapshot still exists. Cleared 30 days after the item closes |
version | Number | Item version, incremented on every change |
created_at | String | Creation time |
closed_at | String | When it closed, null if open |
close_reason | String | Why it closed: decided; timeout unhandled for 30 days; escalated merged into a platform item; target_deleted the target was deleted with the user. null if open |
actions | Array<Object> | Only returned by Get a review item and decision responses: actions this item caused, each with action_id, kind, authority (tenant or platform), trigger (auto, review decision, penalty automatic penalty), status (pending, done, skipped, failed), last_error (why it was skipped or failed), created_at, done_at |
Each item of sources, with type as the source kind:
type | Other fields |
|---|---|
word_list | list_id, list_name, word (matched word), category. Platform word list matches only have platform (true) and category |
provider | provider, the moderation provider, such as aliyun, tencent, netease; labels, the provider's results, each with category, label (the provider's raw label) and score |
url_rejected | note: why the image address couldn't be handed to the moderation service |
report | count (number of reports), reason |
escalation | note: the escalation note |
Content snapshots
The shape of content depends on target_type:
| Target | Fields |
|---|---|
message, chatroom_message | type, body and ext (message content), created_at; edits for later edited versions, each with body, ext, edited_at; review_urls, viewing addresses for images, voice and video in the message |
file | file_id, kind, name, review_urls |
user, group, chatroom | profile, the profile at report time (nickname, avatar_url for users; name, avatar_url, description, announcement for groups; name, description, announcement for chat rooms); versions, later profiles, each with profile and captured_at |
| Others (profiles, group nicknames, requests, chat room attributes) | fields, the reviewed fields, each with name, value (the value at the time) and captured_at. Later values of the same field are listed too |
Messages and profiles keep at most 5 versions in total; beyond that, the first and the latest 4 are kept.
Each item of review_urls:
| Field | Type | Description |
|---|---|---|
source | String | The original address in the snapshot |
url | String | Viewing address. For files in this service, a download address valid for 10 minutes (blocked files can be viewed too); null for files blocked by platform rules; the original address for external ones |
external | Boolean | Whether it's external. Opening external addresses reveals the viewer's IP to that server, so be careful loading them directly in reviewers' browsers |
Report details
| Field | Type | Description |
|---|---|---|
report_id | String | Report ID |
reporter | String | Reporter's username, null if the user was deleted |
reason | String | Report reason |
description | String | Description, an empty string if none |
evidence | Array<Object> | Evidence messages, each with message_id, conversation_id, sender, type, body, created_at |
via | String | client client report, openapi reported by your server on a user's behalf |
created_at | String | When it was reported |
Actions
Each item of actions in a decision is a kind plus its parameters. Actions available per target:
| Target | Available kind |
|---|---|
message | recall_message; also block_file when the message has files of this app |
chatroom_message | recall_chatroom_message; also block_file when the message has files of this app |
file | block_file |
user_profile, group_member, user | clear_profile |
group_profile, group | clear_profile, disable_group |
chatroom_profile, chatroom | clear_profile, disable_chatroom |
chatroom_attribute | remove_chatroom_attribute |
Targets with an author, user | Also mute_user, disable_user |
Messages and reasons in join and friend requests can't be cleared; you can only mute or ban the author.
kind | Parameters | Description |
|---|---|---|
recall_message | — | Recalls the message, with recalled.role server |
recall_chatroom_message | — | Recalls the chat room message |
block_file | file_ids: file IDs to block; omitted means all of this app's files in the message | Blocks files; later downloads return file_blocked |
clear_profile | fields: field names to clear; omitted means every field in the snapshot. nickname, avatar_url for reported users; name, description, announcement, avatar_url for reported groups; name, description, announcement for reported chat rooms | Clears fields. Only cleared if they still hold the reviewed value, so later changes aren't affected |
remove_chatroom_attribute | — | Deletes the chat room attribute, only if its value hasn't changed |
mute_user | scopes: required, one or more of chat, group, room; duration_seconds: 1 to 315360000, omitted for permanent | Globally mutes the author |
disable_user | duration_seconds: as above | Bans the author |
disable_group | — | Bans the group |
disable_chatroom | — | Bans the chat room |
Actions run as your app, with the same effect as calling the matching APIs yourself, and can be lifted as usual. If the target no longer exists or is already in the target state (such as an already recalled message), the action's status is skipped.
Report object
| Field | Type | Description |
|---|---|---|
report_id | String | Report ID |
target_type | String | What was reported: message, user, group, chatroom or chatroom_message |
username | String | Reported username, only when target_type is user, otherwise null; also null if the user was deleted |
message_id | String | Reported message ID, only when target_type is message or chatroom_message, otherwise null |
group_id | String | Reported group ID, only when target_type is group, otherwise null |
room_id | String | Chat room ID, only when target_type is chatroom or chatroom_message, otherwise null |
reason | String | Report reason, see Report reasons |
status | String | pending in progress, closed handled |
created_at | String | When it was reported |
