Admin messages
Admin message routes read, search, and permanently delete the messages of one channel. Others report the progress of a shred job and submit one attachment to NCMEC, the National Center for Missing and Exploited Children.
Shredding and bulk deletion for a whole account live under Admin users.
Admin message object
Section titled “Admin message object”A flattened moderation view of one message. It has the author identity, the content and timestamp, the attachments with their NCMEC submission state, and the NSFW context of the channel and guild.
Every operation that returns Admin message objects also returns the matching public message objects in a parallel message_responses array, so a client that needs the full wire shape joins on id.
Structure
Section titled “Structure”| Field | Type | Description |
|---|---|---|
| id | snowflake | The ID of the message |
| channel_id | snowflake | The channel the message was sent in |
| channel_name1 | ?string | The channel name, or null for a channel that has no name |
| channel_nsfw1 | ?boolean | Whether the channel is marked NSFW |
| channel_content_warning_level?2 | ?integer | The channel content warning level |
| channel_content_warning_text?2 | ?string | The channel content warning text (0-200 characters) |
| guild_id1 | ?snowflake | The guild containing the channel, or null for a private channel |
| guild_name1 | ?string | The guild name, or null for a private channel |
| guild_nsfw_level1 | ?integer | The guild NSFW level, or null for a private channel |
| guild_nsfw?2 | ?boolean | Whether the guild is marked NSFW |
| guild_content_warning_level?2 | ?integer | The guild content warning level |
| guild_content_warning_text?2 | ?string | The guild content warning text (0-200 characters) |
| author_id | snowflake | The ID of the author |
| author_username | string | The author username (1-100 characters) |
| author_global_name | ?string | The author display name, or null when unset |
| author_discriminator | string | The author discriminator tag (1-10 characters) |
| author_avatar | ?string | The author avatar hash, or null when unset |
| content | string | The message content (0-4,000 characters) |
| timestamp | ISO8601 timestamp | When the message was sent |
| attachments | array[Admin message attachment object] | The attachments of the message (at most 10) |
| user_prior_ncmec_report_ids?3 | array[string] | The NCMEC report IDs previously filed against the author, ascending (at most 100) |
1 Every object in one response has the same values, taken from the channel named in the request path or query. All five are null when the channel no longer exists
2 Always absent from the objects these operations return. The same member is populated on the report message context object
3 Written on every object these operations return, and empty when no NCMEC report has been filed against the author
Example
Section titled “Example”{ "id": "1502771119476146176", "channel_id": "1471002884199612420", "channel_name": "general", "channel_nsfw": false, "guild_id": "1471002884199612416", "guild_name": "Tidepool", "guild_nsfw_level": 0, "author_id": "1490118377238528000", "author_username": "kestrel", "author_global_name": null, "author_discriminator": "0117", "author_avatar": "a1b2c3d4e5f60718293a4b5c6d7e8f90", "content": "read the pinned message", "timestamp": "2026-03-04T18:19:07.220Z", "attachments": [], "user_prior_ncmec_report_ids": []}Admin message attachment object
Section titled “Admin message attachment object”An attachment entry has the ordinary attachment metadata together with the NCMEC submission state Fluxer holds for it.
Structure
Section titled “Structure”| Field | Type | Description |
|---|---|---|
| id | snowflake | The ID of the attachment |
| filename | string | The attachment filename (1-256 characters) |
| url | string | The CDN URL of the attachment (1-2,048 characters) |
| nsfw | ?boolean | Whether the attachment is marked NSFW, or null when it has no marking |
| content_type | ?string | The recorded MIME type, or null when none was recorded |
| width | ?integer | The pixel width, or null when no dimensions were recorded |
| height | ?integer | The pixel height, or null when no dimensions were recorded |
| size?4 | ?integer | The attachment size in bytes, or null when no size was recorded |
| ncmec_status | string | The NCMEC submission status of this attachment |
| ncmec_report_id5 | ?string | The report ID NCMEC assigned (1-256 characters), or null when none has been assigned |
| ncmec_failure_reason6 | ?string | The recorded reason the last submission failed (1-4,000 characters), or null |
4 Always emitted, so it is null and never absent when no size was recorded
5 Assigned only once NCMEC accepts the report, so it is null while ncmec_status is not_submitted and after a failed submission
6 Populated only when ncmec_status is failed, and it stores the raw upstream error. The sanitised wording appears in the failing response body instead
NCMEC submission status
Section titled “NCMEC submission status”| Value | Description |
|---|---|
| not_submitted | The attachment has never been submitted to NCMEC |
| submitted | The attachment was submitted to NCMEC and NCMEC accepted the report |
| failed | The submission failed, the opened report was retracted, and the attachment can be submitted again |
A submission that is still running is stored as submitting and returned unchanged, so tolerate a status outside this table. An attachment in that state is refused a second submission by Create NCMEC report.
Message shred status object
Section titled “Message shred status object”The state of one message shred job. The state expires one hour after it was last written, and an expired job is reported by status alone.
Structure
Section titled “Structure”| Field | Type | Description |
|---|---|---|
| status | not_found | in_progress | completed | failed | The job state |
| requested1 | integer | The number of message references the job was queued with |
| total1 | integer | The number of those references the job resolved as shreddable |
| processed1 | integer | The number of messages the job has shredded so far |
| skipped1 2 | integer | The number of references the job did not shred |
| started_at?1 3 | ISO8601 timestamp | When the job started |
| completed_at?1 | ISO8601 timestamp | When the job completed |
| failed_at?1 | ISO8601 timestamp | When the job failed |
| error?1 4 | string | Why the job failed (1-4,000 characters) |
1 Absent when status is not_found, a shape that has status alone
2 Counts a reference that repeats an earlier entry, one whose IDs are not valid snowflakes, one whose message no longer exists, and one no longer authored by the target user
3 Present whenever the counters are
4 Present only when status is failed
List channel messages
Section titled “List channel messages”GET/v1/admin/channels/{channel_id}/messagesReturns one page of Admin message objects for one channel. Requires message:lookup.
The read ignores message history permissions, channel membership, and the age cutoff applied to ordinary members, so it returns every message the channel still holds.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| channel_id | snowflake | The ID of the channel |
Query parameters
Section titled “Query parameters”| Field | Type | Description |
|---|---|---|
| limit? | integer | Maximum messages to return (1-100, default 50) |
| before?1 | snowflake | Return messages with a lower ID than this cursor |
| after?1 2 | snowflake | Return messages with a higher ID than this cursor |
1 Supplying neither selects the newest messages in the channel. Supplying both selects the newest messages below before and discards any at or below after
2 Supplying after also flips the order of the returned array
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| messages1 | array[Admin message object] | Messages in this page (at most 100) |
| message_responses?2 | array[message object] | The same messages in the public wire shape, same order (at most 100) |
| has_more3 | boolean | Whether the page was filled to limit |
1 Ordered oldest first, except when after was supplied, in which case it is ordered newest first
2 Written by every response this operation produces
3 True when the page holds exactly limit entries, so a page that ends on the last message reports true and the next request returns an empty page
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | Message page was returned |
The response has no cursor member. Page further by sending the ID of the oldest returned message as before, or the ID of the newest returned message as after.
A channel that does not exist returns an empty messages array with has_more false, so this operation never answers 404.
Rate limit
Section titled “Rate limit”100 requests per minute for each authenticated user, on the shared admin:message:operation bucket.
Get message
Section titled “Get message”GET/v1/admin/channels/{channel_id}/messages/{message_id}Returns the requested message together with the messages surrounding it, as one array of Admin message objects, oldest first. Requires message:lookup.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| channel_id | snowflake | The ID of the channel |
| message_id | snowflake | The ID of the message |
Query parameters
Section titled “Query parameters”| Field | Type | Description |
|---|---|---|
| context_limit?1 | integer | Size of the whole returned window, counting the requested message (1-100, default 50) |
1 At most context_limit divided by two, rounded down, are newer than the requested message and the rest are older, so the default returns at most 25 newer and 24 older
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| messages1 | array[Admin message object] | The requested message and its surrounding window (at most 100) |
| message_responses? | array[message object] | The same messages in the public wire shape, same order (at most 100) |
| message_id2 | ?snowflake | The requested message ID |
1 The requested message is one entry of this array, and it is absent when it no longer exists
2 Echoes the path parameter and is never null here
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | Window was returned |
An unknown message returns 200 with the window surrounding the position the missing ID would have occupied, so this operation never returns UNKNOWN_MESSAGE. An unknown channel returns 200 with an empty messages array.
Rate limit
Section titled “Rate limit”100 requests per minute for each authenticated user, on the shared admin:message:operation bucket.
Delete message
Section titled “Delete message”DELETE/v1/admin/channels/{channel_id}/messages/{message_id}Permanently deletes one message and purges the attachments it owned. Requires message:delete.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| channel_id | snowflake | The ID of the channel |
| message_id | snowflake | The ID of the message |
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| success | boolean | Always true |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | Request completed and the audit entry was written |
An unknown channel or message returns 200 with success true and still records an audit entry.
Side effects
Section titled “Side effects”The attachments the message owned are purged from storage and from the CDN, the message row is deleted, and its search index document is removed.
Message Delete is sent to the guild when the channel belongs to one, and to each recipient when the channel is private. No other Dispatch is emitted, and deleting a pinned message here emits no Channel Pins Update.
One Admin audit entry is recorded with the action delete_message and the target type message, targeting the message ID. Its metadata is channel_id and message_id. The entry is written whether or not a message was found.
Rate limit
Section titled “Rate limit”100 requests per minute for each authenticated user, on the shared admin:message:operation bucket.
Search messages
Section titled “Search messages”GET/v1/admin/messagesSearches the message index of one channel, or resolves one message in that channel by message ID or by attachment identity. Requires message:lookup.
The three modes return two different response bodies, and the query decides which one arrives.
Query parameters
Section titled “Query parameters”| Field | Type | Description |
|---|---|---|
| channel_id | snowflake | The channel every returned message was sent in |
| q?1 | string | Free-text query matched against message content (1-200 characters) |
| message_id?2 | snowflake | Resolve the message with this ID together with its surrounding window |
| attachment_id?2 3 | snowflake | Resolve the message with this attachment together with its surrounding window |
| filename?3 | string | The exact filename of the attachment named by attachment_id (1-255 characters) |
| context_limit?2 | integer | Size of the window returned by a lookup, counting the resolved message (1-100, default 50) |
| limit?1 | integer | Maximum messages to return when searching (1-100, default 25) |
1 Applies to the search mode only. Omitting q searches for the empty string
2 message_id takes precedence over every other member, and attachment_id over q. A lookup ignores limit and a search ignores context_limit
3 Supply both together. Sending attachment_id without filename returns 400 INVALID_FORM_BODY against filename
Search response body
Section titled “Search response body”Returned when neither message_id nor attachment_id was supplied.
| Field | Type | Description |
|---|---|---|
| messages4 | array[Admin message object] | Matching messages (at most 100) |
| message_responses? | array[message object] | The same messages in the public wire shape, same order (at most 100) |
| total5 | integer | The number of matches that still exist |
4 Holds at most limit entries. There is no cursor and no offset member, so this operation returns the first page of matches only
5 Counts every surviving match, so it can exceed the length of messages
Lookup response body
Section titled “Lookup response body”Returned when message_id or attachment_id was supplied. It is the same body Get message returns.
| Field | Type | Description |
|---|---|---|
| messages | array[Admin message object] | The resolved message and its surrounding window, oldest first (at most 100) |
| message_responses? | array[message object] | The same messages in the public wire shape, same order (at most 100) |
| message_id6 | ?snowflake | The ID of the resolved message |
6 Null only for an attachment lookup that matched no attachment, in which case messages is empty too. A message_id lookup echoes the supplied ID even when no such message exists
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | search or lookup response body | Search or lookup completed |
An unknown channel, a message that does not exist, and an attachment whose filename does not match all return an empty result. This operation never answers 404.
With no message search service configured the search mode returns an empty messages array and total zero. The two lookup modes are unaffected.
Rate limit
Section titled “Rate limit”100 requests per minute for each authenticated user, on the shared admin:message:operation bucket.
Get message shred job
Section titled “Get message shred job”GET/v1/admin/messages/shreds/{job_id}Returns one message shred status object. Requires message:shred.
Shred jobs are created by Shred user messages and by the non-dry-run form of Delete all user messages. Both return the identifier this operation reads.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| job_id1 | snowflake | The message shred job ID, taken from the queueing response |
1 A snowflake issued when the job was queued, so a value that is not a decimal integer fails path validation rather than reporting not_found
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | message shred status object | Job state was returned, including not_found for an unknown or expired job |
Rate limit
Section titled “Rate limit”100 requests per minute for each authenticated user, on the shared admin:message:operation bucket.
Create NCMEC report
Section titled “Create NCMEC report”POST/v1/admin/messages/ncmec-reportsSubmits one image or video attachment to NCMEC and starts the account enforcement workflow for its author. Requires csam:submit_ncmec, message:delete, user:delete, and archive:trigger:user.
A caller missing more than one of the four is refused with 403 MISSING_ACL naming the first it does not hold, in the order listed. The wildcard satisfies all four.
This operation ignores X-Audit-Log-Reason. The reason on every audit entry the workflow produces is synthesised from the assigned NCMEC report ID and the channel and attachment IDs, and it is returned as audit_log_reason.
JSON body
Section titled “JSON body”| Field | Type | Description |
|---|---|---|
| channel_id | snowflake | The channel the attachment was sent in |
| message_id | snowflake | The message the attachment belongs to |
| attachment_id | snowflake | The attachment to submit |
| filename | string | The exact filename of the attachment (1-255 characters) |
| source_report_id?1 | snowflake | The report whose frozen evidence has the attachment |
| reporter_full_name | string | The full legal name of the submitting Admin (1-200 characters) |
| confirmed_viewed2 | boolean | Confirmation that the submitting Admin has viewed the attachment |
1 Required when the live message no longer exists or has no author, in which case the attachment is read from that report’s frozen evidence. Supplying it otherwise only records the report ID
2 The literal value true is the only accepted value. Any other value fails body validation
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| success | boolean | Always true |
| ncmec_report_id | string | The report ID NCMEC assigned to this submission (1-256 characters) |
| audit_log_reason3 | string | The synthesised reason recorded on every audit entry the workflow produced (1-4,000 characters) |
3 The NCMEC report ID followed by the channel ID and attachment ID, so one string identifies the submission in the audit log and at NCMEC
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | Attachment was submitted and the workflow advanced |
| 400 | error response | The resolved attachment is neither an image nor a video and the request returns INVALID_FORM_BODY against attachment_id |
| 4041 | error response | UNKNOWN_MESSAGE when nothing resolves the attachment, or UNKNOWN_REPORT when source_report_id names no report |
| 409 | error response | NCMEC_ALREADY_SUBMITTED when a submission for the attachment is already running or has already succeeded |
| 500 | error response | Submission fails and the request returns NCMEC_SUBMISSION_FAILED |
1 The attachment resolves from a live message with an author, or from frozen report evidence
Side effects
Section titled “Side effects”Fluxer resolves the attachment from the live message when that message still exists and has an author, and from the frozen evidence named by source_report_id otherwise. Fluxer accepts the attachment only when its resolved content type begins with image/ or video/.
Fluxer marks the attachment submitting, opens an NCMEC report, uploads the attachment bytes, submits its file details with the viewed flag set, and finishes the report. On success the attachment is marked submitted with the assigned report ID. On failure the opened report is retracted, the attachment is marked failed with the raw reason, and the request returns NCMEC_SUBMISSION_FAILED. A retraction that itself fails is logged and does not change the recorded state.
When the resolved attachment has an author who has not already been enforced against, Fluxer sets the account’s deleted and disabled flags, clears any temporary ban, and records the deletion reason for child sexual content. It then schedules deletion 60 days ahead, deletes every authentication session, propagates the resulting user update, and triggers a user archive for that account.
Message content is deleted only after that archive completes. The archive is re-checked every 15 seconds by default, for at most 240 attempts. Once the archive is complete the reported message is deleted, its attachments are purged, and Message Delete is sent.
A successful submission records one Admin audit entry with the action NCMEC Report. It targets the reported user, or targets the source report when the attachment resolved to no author. Triggering the archive records a second entry with the action trigger_user_archive targeting the same user, skipped when the account already has an archive taken after the enforcement. Both entries have the synthesised reason returned as audit_log_reason.
An attachment that resolved to no author is submitted to NCMEC and audited. No account is disabled, no archive is triggered, and no message content is deleted.
Rate limit
Section titled “Rate limit”100 requests per minute for each authenticated user, on the shared admin:message:operation bucket.