Skip to content
FiveCord Docs

Messages

A message is one post in a channel, together with the text, attachments, embeds, stickers, and reactions stored with it. Channel metadata and membership are defined by the Channels resource.

A text-bearing channel is a channel type that stores messages, which is every type except a guild category and a guild link channel. The message history cutoff is a time stored on the guild, and a member without READ_MESSAGE_HISTORY reads nothing created before it.

FiveCord resolves the channel before it applies any per-route authorisation. A private channel returns 404 UNKNOWN_CHANNEL to a caller who is not a recipient. A guild channel returns 403 MISSING_PERMISSIONS to a non-member and to a member without VIEW_CHANNEL. A guild an operator has marked unavailable returns 403 MISSING_ACCESS before the route runs. A guild that no Gateway process serves returns 404 UNKNOWN_GUILD, or 403 ACCESS_DENIED when its stored record survives.

The message read, create, modify, and delete routes, Bulk delete messages, Bulk delete own messages, and List pinned messages also enforce age verification on an age restricted guild channel. A guild channel is age restricted when its own setting says so. A channel with no setting of its own uses its parent category’s setting, and then the guild’s. They return 403 NSFW_CONTENT_AGE_RESTRICTED until the account satisfies it. The attachment routes, the pin routes, the reaction routes, and Indicate typing resolve the channel without that check.

Clear channel read state and Acknowledge message resolve no channel. Acknowledge pins reads the channel without checking the caller’s access. None of the three checks a permission.

A message object is the full stored form of one post in a channel.

FieldTypeDescription
idsnowflakeThe ID of the message
channel_idsnowflakeThe ID of the channel
author1partial user objectThe user credited with the message
webhook_id?snowflakeOriginating webhook ID, present only for a webhook-authored message
typeintegerMessage type
flagsintegerMessage flags
contentstringThe text of the message, empty when the message has only media
timestampISO8601 timestampCreation time derived from the message snowflake
edited_timestamp?ISO8601 timestampMost recent edit time, or null when the message has never been edited
pinnedbooleanWhether the message is pinned
mention_everyonebooleanWhether the message mentions everyone
tts2booleanWhether the message requested text-to-speech
mentions3array[partial user object]The users the message actively mentions
mention_rolesarray[snowflake]The IDs of the roles the message actively mentions
mention_channels?array[channel mention object]The channels the message content links by ID
users?3array[partial user object]Users referenced by non-notifying content, embed, and snapshot text
embeds4array[embed object]The previews resolved or supplied for the message
attachments5array[message attachment object]The files attached to the message
stickersarray[sticker item object]The stickers sent with the message
nsfw_emojis?array[snowflake]IDs of the custom emojis in the message that are classified as explicit
reactions?array[reaction object]Reaction summaries
message_reference?message reference objectReply or forward reference
message_snapshots?array[message snapshot object]The immutable copies captured for a forward
nonce?6stringCaller-supplied message nonce, echoed to the sender as a string of 1 through 32 characters
call?message call objectCall state attached to a call message
referenced_message?7?message objectResolved referenced message without a nested referenced_message field

1 Built for a webhook-authored message and for a stored author ID that no longer resolves

2 Echoed from the create request, so a message read through any other operation always reports false

3 A user who became an active mention appears in mentions, and a user who is only referenced by rendered text appears in users instead, so the arrays never contain the same user

4 Empty while SUPPRESS_EMBEDS is set on the message

5 An attachment that a rich embed input names with an attachment:// URL in image or thumbnail is excluded from this array. The attachment’s URL is in that embed’s media object instead

6 Echoed only in the create message response and its originating Message Create Gateway Dispatch, and never stored on the message

7 The key is absent when the message has no reply reference, present and null when the reply target no longer resolves, and present with the message when it does. A client must tell absent apart from null by key presence

A webhook-authored message has no stored author user. FiveCord builds its author from the webhook, with the webhook ID as id, the stored webhook name as username, the discriminator 0000, the stored webhook avatar hash as avatar, and bot true. A message whose stored author ID no longer resolves to an account is served with a deleted-user placeholder. The placeholder has that ID, the discriminator 0000, and no avatar.

mentions, mention_roles, embeds, attachments, and stickers are always present and can be empty. A field marked ? above is omitted from the object when it has no value. referenced_message is the one optional field that can also be present and null. A message whose author user and webhook are both absent is omitted from every read.

{
"id": "1501320000000000000",
"channel_id": "1501314428688998182",
"author": {
"id": "1501314428688990000",
"username": "ada",
"discriminator": "0001",
"avatar": "a1b2c3d4e5f60718293a4b5c6d7e8f90"
},
"type": 0,
"flags": 0,
"content": "shipping it",
"timestamp": "2026-08-31T09:14:02.000Z",
"edited_timestamp": null,
"pinned": false,
"mention_everyone": false,
"tts": false,
"mentions": [],
"mention_roles": [],
"embeds": [],
"attachments": [],
"stickers": []
}
ValueNameDescription
0DEFAULT1 2Ordinary message
1RECIPIENT_ADDGroup direct message recipient-add system message
2RECIPIENT_REMOVEGroup direct message recipient-remove system message
3CALLPrivate call system message
4CHANNEL_NAME_CHANGEGroup direct message name-change system message
5CHANNEL_ICON_CHANGEGroup direct message icon-change system message
6CHANNEL_PINNED_MESSAGE2Channel pin system message
7USER_JOIN2User-join system message
19REPLY1 2Message with a reply reference

1 Only DEFAULT and REPLY can be modified, pinned, unpinned, or used as the target of a reply reference. Any other type rejects modification, pinning, and unpinning with 400 CANNOT_MODIFY_SYSTEM_WEBHOOK, and rejects being replied to with the field code CANNOT_REPLY_TO_SYSTEM_MESSAGE

2 Only DEFAULT, CHANNEL_PINNED_MESSAGE, USER_JOIN, and REPLY can be deleted, and any other type rejects deletion with 403 MISSING_PERMISSIONS

ValueNameDescription
1 << 2SUPPRESS_EMBEDS1Suppress rendering of embeds
1 << 12SUPPRESS_NOTIFICATIONSDo not generate ordinary mention notifications
1 << 13VOICE_MESSAGE2Message has one voice recording attachment

1 The only flag a guild moderator can change on a message they did not author

2 A message with this flag has the voice message restrictions listed under Create message

These bits are also the complete sendable set. A create masks the supplied value down to them, and a modify replaces only them and leaves every other stored bit untouched. A bit outside this table is discarded, and the request still succeeds.

An attachment is one file stored against a message and served from media delivery.

FieldTypeDescription
idsnowflakeThe ID of the attachment
filenamestringThe filename stored with the attachment
title?stringDisplay title, or null when none is stored
description?stringAlternative text or description, or null when none is stored
content_type1stringThe media type detected for the attachment
content_hash?stringThe hash of the attachment’s stored bytes, or null when they were never hashed
sizeintegerThe stored size of the attachment in bytes
url2?stringSigned public attachment URL, or null once the attachment has expired
proxy_url2?stringSigned Media Proxy URL, or null once the attachment has expired
width?3integerPixel width for visual media
height?3integerPixel height for visual media
placeholder?stringEncoded media placeholder
flagsintegerAttachment flags
nsfw?booleanWhether media inspection marked the attachment explicit
duration?integerAudio or video duration in seconds
waveform?stringBase64-encoded voice waveform
expires_at4?ISO8601 timestampAttachment URL expiry time, or null when the attachment has no recorded decay deadline
expired?5booleanWhether the attachment is expired

1 Always present. It falls back to a type guessed from the stored filename when the attachment has no recorded media type, and to application/octet-stream when the filename yields no guess

2 Both fields are the same media endpoint URL with the same signature, and both become null together once the attachment has expired

3 Omitted for an audio media type even when a stored dimension exists

4 The key is always present. Its value is the recorded decay deadline, or null when the attachment has none. It is unrelated to the ex time of a signed URL

5 Present and true only when the attachment has already expired, so an absent key must be read as false

A new attachment starts with the media type derived from the filename given when its upload was requested. For a pre-uploaded attachment that is the filename of its attachment upload request item, which can differ from the filename the message sends. When the starting type is not a supported image, video, or audio type, FiveCord checks the first 8 KiB of the file for one. For an image, video, or audio file the Media Proxy can read, content_type is then the type detected from its bytes. When the starting type is not a media type, SVG bytes keep it. So does a file FiveCord cannot check or the Media Proxy cannot read.

An instance configured with attachment URL secrets signs url and proxy_url. FiveCord adds the signature parameters to the attachment path, before any other parameter. An instance without those secrets returns the URL with none of them.

FieldTypeDescription
exstringThe Unix time in seconds at which the URL expires, as 8 lowercase hexadecimal digits, or 0 on a data package URL
isstringThe Unix time in seconds at which the URL was issued, as 8 lowercase hexadecimal digits
hmstringThe signature over ex, is, uc, and the attachment path, as 64 lowercase hexadecimal digits
ucstringPresent only on a data package URL, where the value is always dp

An ordinary URL has ex, is, and hm, and no uc. It is valid for 24 hours from is. is starts at the attachment’s creation time and moves forward in steps of 12 hours, so a URL FiveCord returns has more than 12 and at most 24 hours left. FiveCord signs the URL each time it returns the attachment, so a later read of the message returns a URL for the current step. Refresh attachment URLs signs a URL again without reading the message.

A client MUST keep ex, is, hm, and uc exactly as issued. It MAY add any other query parameter, such as a transformation, because the signature covers no other parameter. A client SHOULD refresh a URL whose ex is absent or less than one hour away. Once the operator enforces signatures, a read with a missing, altered, or expired signature returns 404 from the Media Proxy, as Signed attachment URLs defines.

FiveCord never rewrites message content or embed text. An attachment URL pasted into either keeps the parameters it was sent with, and it stops working when its own ex passes.

A data harvest archive records a data package URL for each attachment instead of an ordinary one. It has uc=dp, and its ex is the single character 0:

?ex=0&is={issued}&hm={signature}&uc=dp

A data package URL does not expire. The Media Proxy checks its signature against every configured secret and skips the expiry check, so it reads its object for as long as the secret that signed it stays in the instance’s secret list. Removing that secret is the only thing that ends it, and it ends every data package URL that secret signed at once.

is is set the way it is on an ordinary URL, and the signature covers uc, so an ordinary URL relabelled uc=dp does not verify, and neither does a data package URL with uc removed.

A client treats a data package URL as never expiring and does not refresh it before use. A refused read still triggers the same forced refresh as any other attachment URL, and that returns an ordinary URL.

{
"id": "1501320000000000001",
"filename": "diagram.png",
"title": null,
"description": "Sequence diagram",
"content_type": "image/png",
"content_hash": "9f86d081884c7d659a2feaa0c55ad015",
"size": 18422,
"url": "https://media.example.com/attachments/1501314428688998182/1501320000000000001/diagram.png?ex=69fba4b5&is=69fa5335&hm=5586be3ae70411fe6582431649e2f28abc6c201e4f12a4f43a3256b72b90d3de",
"proxy_url": "https://media.example.com/attachments/1501314428688998182/1501320000000000001/diagram.png?ex=69fba4b5&is=69fa5335&hm=5586be3ae70411fe6582431649e2f28abc6c201e4f12a4f43a3256b72b90d3de",
"width": 1280,
"height": 720,
"flags": 0,
"expires_at": null
}
ValueNameDescription
1 << 3IS_SPOILERAttachment is presented as a spoiler
1 << 4CONTAINS_EXPLICIT_MEDIAAttachment contains explicit media
1 << 5IS_ANIMATEDAttachment is animated

An emoji is either a custom guild emoji identified by ID or a standard Unicode emoji.

FieldTypeDescription
id?snowflakeThe ID of the custom emoji, absent for a Unicode emoji
name1stringEmoji name or Unicode value
animated?booleanWhether a custom emoji is animated

1 Holds the guild emoji name for a custom emoji and the literal Unicode sequence for a standard emoji

A reaction object summarises one emoji group on a message. FiveCord has ordinary reactions only.

FieldTypeDescription
emojiemoji objectThe emoji this group counts
count1integerThe number of users who added this emoji
me?2booleanWhether the authenticated user has this reaction

1 Capped by the resolved max_users_per_message_reaction limit, which defaults to 1,000,000 users for each group

2 Present and true only when the authenticated user has this reaction, and omitted entirely otherwise, so an absent key must be read as false

Each message has at most the resolved max_reactions_per_message distinct groups, which defaults to 30. Groups are ordered by the time each group’s earliest reaction was added, then by emoji name, then by emoji ID.

{
"emoji": {
"id": "1501314428688991234",
"name": "party_parrot",
"animated": true
},
"count": 7,
"me": true
}

A message reference names the message that a reply points at or that a forward was taken from.

FieldTypeDescription
channel_idsnowflakeThe ID of the referenced channel
message_idsnowflakeThe ID of the referenced message
guild_id?snowflakeThe ID of the referenced guild, present only for a guild message
typeintegerMessage reference type
ValueNameDescription
0DEFAULT1Reply reference
1FORWARD2Forward reference represented by snapshots

1 The default when a create message request supplies message_reference without a type

2 Requires channel_id in the create message request, and the created message has message_snapshots and no referenced_message

A message reference input names the source message a create request replies to or forwards.

FieldTypeDescription
message_idsnowflakeThe ID of the referenced message
channel_id?1snowflakeThe ID of the referenced channel, required for a forward reference
guild_id?snowflakeThe ID of the referenced guild when applicable
type?integerMessage reference type, defaulting to DEFAULT
attachment_ids?2array[snowflake]At most 10 selected attachment IDs for a forward
embed_indices?2array[integer]At most 10 non-negative selected embed indices for a forward

1 A FORWARD reference without it fails validation, and a reply reference defaults to the channel the new message is being sent to

2 Selects a subset of the source message’s media to snapshot, and omitting both snapshots everything the source message has

Each snapshot is a flat immutable copy of one forwarded message. It has no message ID, channel ID, or author, so a client cannot trace a forward back to its source through this object.

FieldTypeDescription
content?stringSnapshotted text
timestampISO8601 timestampOriginal creation time
edited_timestamp?ISO8601 timestampOriginal edit time, omitted when the original message was never edited
mentions?1array[snowflake]Snapshotted mentioned user IDs
mention_roles?array[snowflake]Snapshotted mentioned role IDs
mention_channels?array[channel mention object]Snapshotted mentioned channels
embeds?array[embed object]Snapshotted embeds
attachments?2array[message attachment object]Snapshotted attachments
stickers?array[sticker item object]Snapshotted stickers
typeintegerOriginal message type
flagsintegerOriginal message flags

1 A client resolves each snapshotted ID against the parent message’s users array

2 Snapshot attachments are independent stored objects, so deleting the source message leaves them intact

Every optional field above is omitted entirely.

A message call has the participants and end time of the call that a CALL message records.

FieldTypeDescription
participantsarray[snowflake]Current participant user IDs
ended_timestamp?ISO8601 timestampCall end time, or null while the call is still active

A channel mention names one channel that the message content links by ID.

FieldTypeDescription
idsnowflakeThe ID of the mentioned channel
namestringMentioned channel name
typeintegerChannel type

Each sticker item is the trimmed sticker record on a message.

FieldTypeDescription
idsnowflakeThe ID of the sticker
namestringSticker name
animatedbooleanWhether the sticker is animated
nsfw?booleanWhether the sticker is classified as explicit

An embed is a rendered preview on a message, either resolved by the unfurler or supplied as rich input.

FieldTypeDescription
typestringEmbed type
title?stringEmbed title
description?stringEmbed description
url?stringEmbed destination URL
timestamp?ISO8601 timestampEmbed timestamp
color?integerEmbed colour
author?embed author objectEmbed author
provider?1embed provider objectExternal provider
thumbnail?embed media objectThumbnail image
image?embed media objectMain image
video?1embed media objectVideo media
audio?1embed media objectAudio media
footer?embed footer objectEmbed footer
fields?array[embed field object]Embed fields
nsfw?booleanWhether the embed contains explicit media
html?1stringSanitised oEmbed markup for a trusted specialised renderer
html_width?1integerPreferred pixel width of the sanitised oEmbed markup
html_height?1integerPreferred pixel height of the sanitised oEmbed markup
children?1array[embed object]At most one nested unfurler-generated embed, which itself has no children field

1 Produced only by the unfurler when it resolves a link, and never accepted from a rich embed input

Every optional field above is omitted entirely.

{
"type": "rich",
"title": "Release 2.4.0",
"description": "Voice reconnects are now instant.",
"url": "https://example.com/releases/2.4.0",
"color": 5793266,
"footer": {"text": "example.com"},
"fields": [{"name": "Platform", "value": "Desktop", "inline": true}]
}
ValueDescription
richAn embed the unfurler built itself, and the type of every accepted rich embed input
linkA generic link preview with page metadata and no resolved media
imageAn image preview resolved from a linked URL
videoA video preview resolved from a linked URL
audioAn audio preview resolved from a linked URL
gifvAn animated looping video preview
articleAn article or encyclopaedia page preview
blueskyA Bluesky post preview
FieldTypeDescription
namestringAuthor name
url?stringAuthor link
icon_url?stringAuthor icon URL
proxy_icon_url?stringProxied author icon URL
FieldTypeDescription
urlstringMedia URL
proxy_url1stringMedia Proxy URL
width?integerPixel width
height?integerPixel height
duration?integerDuration in seconds
description?stringMedia description
content_type?stringMedia type
content_hash?stringContent hash
placeholder?stringEncoded media placeholder
flags?integerMedia flags using CONTAINS_EXPLICIT_MEDIA and IS_ANIMATED from attachment flags

1 Always derived from url, so every serialised media object has it

When url is an attachment URL of this instance, FiveCord signs both fields, so url and proxy_url are the same URL with the same fresh signature. A rich embed can name such a URL with or without ex, is, hm, and uc. FiveCord removes them before it stores the URL, and signs both fields again each time it returns the embed.

FiveCord signs every other embed field that can hold an attachment URL of this instance the same way. That is url on the embed itself, url, icon_url, and proxy_icon_url on an embed author, icon_url and proxy_icon_url on an embed footer, and url on an embed provider. An icon URL and its proxy twin are then the same signed URL. A field whose URL is not an attachment URL of this instance is returned as it was stored, and a proxy field of an external URL is the signed external media path instead.

FieldTypeDescription
textstringFooter text
icon_url?stringFooter icon URL
proxy_icon_url?stringProxied footer icon URL
FieldTypeDescription
namestringField name
valuestringField value
inlinebooleanWhether the field can render inline

Only an embed the unfurler builds from a link has a provider. A rich embed input cannot set one.

FieldTypeDescription
namestringProvider name
url?stringProvider URL

An allowed mentions object selects which mentions written in the message text become active mentions, meaning the ones that notify a user or role.

FieldTypeDescription
parse?1 2array[string]Mention categories from allowed mention parse values
users?2array[snowflake]At most 100 user IDs permitted to become active mentions
roles?2array[snowflake]At most 100 role IDs permitted to become active mentions
replied_user?3booleanWhether a reply mentions the referenced message author (default true)

1 Defaults to every category when the object is absent, to the empty set when users or roles is supplied without it, and to the empty set when the object is present with none of its fields

2 A non-empty parse combined with a non-empty users or roles is rejected with the field code PARSE_AND_USERS_OR_ROLES_CANNOT_BE_USED_TOGETHER

3 Ignored in a one-to-one direct message and in personal notes, and ignored when the referenced message has the same author as the new message

mention_everyone is forced to false whenever the caller lacks MENTION_EVERYONE, without failing the request.

ValueDescription
usersParse user mentions
rolesParse role mentions
everyoneParse @everyone and @here mentions

A new message can refer to a previously uploaded attachment or attach a direct multipart file. An edited message can retain an existing attachment. Attachment IDs in a direct multipart request identify the matching zero-based files[N] field.

FieldTypeDescription
id1integer | integer stringAttachment position
filenamestringAttachment filename
upload_filename2stringOpaque upload key returned by Request attachment upload URLs
file_size1integerUploaded byte count
content_typestringMedia type of 1 through 255 characters
title??stringDisplay title (1-1,024 characters)
description??stringDescription (1-4,096 characters)
duration?3?integerVoice media duration in seconds (0-2,147,483,647)
waveform?3?stringBase64 waveform of 1 through 4,096 characters
flags?integerAttachment flags, defaulting to 0

1 Accepted either as a JSON number or as a decimal string

2 The presence of this key selects this variant over the direct multipart metadata variant

3 Both are required when the message has the VOICE_MESSAGE flag. A waveform on an attachment whose resolved media type is not audio/* fails with the field code VOICE_MESSAGES_ATTACHMENT_MUST_BE_AUDIO

Only IS_SPOILER and CONTAINS_EXPLICIT_MEDIA survive from a caller-supplied flags value. IS_ANIMATED is set by media inspection alone.

Direct multipart attachment metadata object

Section titled “Direct multipart attachment metadata object”
FieldTypeDescription
id1integer | integer stringZero-based files[N] index
filenamestringDisplay filename
content_type?stringMedia type of 1 through 255 characters
title??stringDisplay title (1-1,024 characters)
description??stringDescription (1-4,096 characters)
flags?integerAttachment flags, defaulting to 0
duration?2?integerVoice media duration in seconds (0-2,147,483,647)
waveform?2?stringBase64 waveform of 1 through 4,096 characters

1 An entry whose id matches no supplied file index and that has a filename fails with the field code NO_FILE_FOR_ATTACHMENT_METADATA. An entry whose id matches no supplied file and that has no filename is read as an existing attachment reference instead

2 Both are required when the message has the VOICE_MESSAGE flag

FiveCord discards a key outside the table above and accepts the rest of the entry.

FieldTypeDescription
idsnowflake | integerExisting attachment ID or attachment position
title??stringReplacement display title (1-1,024 characters)
description??stringReplacement description (1-4,096 characters)
flags?1integerAttachment flags, accepted and discarded
filename?1 2stringDisplay filename, accepted and discarded
duration?1 2?integerDuration in seconds (0-2,147,483,647), accepted and discarded
waveform?1 2?stringBase64 waveform of 1 through 4,096 characters, accepted and discarded

1 A retained attachment is rebuilt from its stored record, and only title and description are written back onto it

2 Rejected with the field code CANNOT_EDIT_ATTACHMENT_METADATA when the caller is a guild moderator editing a message they did not author, because that caller can supply only id, title, description, and flags

A rich embed input is an embed a client supplies on a message, and FiveCord always stores it with the rich embed type. The number of embeds on one message is bounded by the resolved max_embeds_per_message limit, which defaults to 10. Exceeding it fails with the field code TOO_MANY_EMBEDS.

FieldTypeDescription
url??stringEmbed destination URL (1-2,048 characters)
title??stringTitle with at most 256 characters
color??integerEmbed colour
timestamp??ISO8601 timestampEmbed timestamp
description??stringDescription (1-4,096 characters, empty string treated as absent)
author?1?embed author input objectAuthor metadata
image?1?embed media input objectMain image metadata
thumbnail?1?embed media input objectThumbnail metadata
footer?1?embed footer input objectFooter metadata
fields??array[embed field input object]At most 25 embed fields

1 A nested object that omits its own required field is discarded. An author without name, a media object without url, and a footer without text are read as if the field had not been sent

FieldTypeDescription
namestringAuthor name (1-256 characters)
url??stringAuthor link (1-2,048 characters)
icon_url??stringAuthor icon URL (1-2,048 characters)
FieldTypeDescription
url1stringMedia URL or attachment reference
description??stringMedia description (1-4,096 characters)

1 Either an http or https URL, or an attachment://<filename> reference naming a png, jpg, jpeg, webp, or gif attachment on the same message, and from 1 through 2,048 characters in either form. Only image and thumbnail accept the attachment:// form

An attachment:// reference on a request that has no attachment fails with the field code CANNOT_REFERENCE_ATTACHMENTS_WITHOUT_ATTACHMENTS. A reference to a filename that no attachment on the request supplies fails with the field code REFERENCED_ATTACHMENT_NOT_FOUND, and a reference to an attachment whose extension is not a supported image extension fails with the field code ATTACHMENT_MUST_BE_IMAGE.

FieldTypeDescription
textstringFooter text (1-2,048 characters)
icon_url??stringFooter icon URL (1-2,048 characters)
FieldTypeDescription
namestringField name (1-256 characters)
valuestringField value with at most 1,024 characters
inline?booleanWhether the field can render inline, defaulting to false

FiveCord issues an upload URL for an attachment before any message can reference it. Request attachment upload URLs plans the upload. A plan whose declared size exceeds 10 MiB is a multipart plan, and Complete attachment upload finishes it.

An upload_url can point to object storage or the FiveCord upload relay. Send it exactly as issued without assuming its shape.

An upload key can be referenced only by a message that the same identity creates in the same channel.

One declared upload inside a Request attachment upload URLs body.

FieldTypeDescription
id1integerCaller-selected attachment position (0-2,147,483,647)
filenamestringFilename to upload
file_size1integerDeclared byte count (0-9,007,199,254,740,991)
content_type2stringMedia type of 1 through 255 characters the client uploads

1 A decimal string is converted to the integer

2 The issued upload URL uses the media type derived from filename. The message attachment object states how the stored attachment is typed

Returned when the declared file_size is at most 10 MiB.

FieldTypeDescription
idintegerCaller-selected attachment position echoed from the request
filenamestringFilename echoed from the request
upload_filename1stringOpaque upload key of 1 through 4,096 characters
file_sizeintegerDeclared byte count echoed from the request
content_typestringMedia type derived from filename
upload_modestringAlways singlepart
upload_url2stringPresigned URL that accepts the complete file in one PUT

1 This value is the upload_filename of a pre-uploaded attachment in a later Create message or Modify message request

2 The upload URL accepts only the derived media type and exactly file_size bytes, so a PUT of any other length is rejected

Returned when the declared file_size exceeds 10 MiB.

FieldTypeDescription
idintegerCaller-selected attachment position echoed from the request
filenamestringFilename echoed from the request
upload_filenamestringOpaque upload key of 1 through 4,096 characters
file_sizeintegerDeclared byte count echoed from the request
content_typestringMedia type derived from filename
upload_modestringAlways multipart
upload_idstringObject storage multipart upload identifier of 1 through 1,024 characters
part_size1integerByte size of every part except the last
partsarray[attachment upload part object]Presigned part upload URLs in ascending part number (1-10,000 entries)

1 The plan targets 20 parts, so part_size is one twentieth of file_size rounded up to a whole mebibyte, and it is never smaller than 10 MiB. The last part is the remainder

One presigned part of a multipart upload plan.

FieldTypeDescription
part_numberintegerOne-based part number
upload_url1stringPresigned URL that accepts exactly this part

1 Each part upload URL accepts only the exact byte count of its part, so a PUT of any other length is rejected

One planned upload named in a Complete attachment upload body.

FieldTypeDescription
upload_filenamestringOpaque upload key returned when the upload was planned
upload_idstringMultipart upload identifier returned when the upload was planned

One result of Refresh attachment URLs.

FieldTypeDescription
originalstringThe URL exactly as it was sent
refreshed1stringThe URL with a fresh signature, or original unchanged

1 Equal to original when the URL is not an attachment URL of this instance, or when the instance has no attachment URL secrets

A URL is an attachment URL of this instance when all of these hold:

  • It parses as an absolute URL whose scheme, host, and port equal those of the media endpoint. The host is compared without regard to case, and a default port equals no port.
  • Its path, as written in the string, starts with the media endpoint path followed by /attachments/.
  • The rest of the path, percent-decoded, is valid UTF-8 and a safe storage key with no empty, ., or .. segment.
  • That key is attachments/{channel_id}/{attachment_id}/{filename}, where both IDs are decimal digits and the filename is not empty.

To sign a URL, FiveCord removes every ex, is, hm, and uc parameter and every empty pair, then places the fresh ex, is, and hm first. Every other parameter follows in its original order and bytes, and a fragment is kept. A data package URL loses its uc this way, so the result is an ordinary URL valid for 24 hours.

A channel pin pairs one pinned message with the time it was pinned.

FieldTypeDescription
message1message objectThe message the pin points at
pinned_atISO8601 timestampTime at which the message was pinned

1 The pinned message omits referenced_message and reactions, so a client that needs either must read the message through Get message

FieldTypeDescription
items1array[channel pin object]Current page of pins
has_morebooleanWhether an older page exists

1 Ordered by descending pin time

The page has no cursor field. The next request repeats the operation with before set to the pinned_at of the last returned item. Continue while has_more is true. has_more is false only when the channel has no further visible pins.

FieldTypeDescription
itemsarray[partial user object]Users in ascending user ID order
has_morebooleanWhether another page exists
next_after1?snowflakeUser ID cursor for the next page

1 Null when no further page exists, and otherwise the ID of the last user in items

FieldTypeDescription
channel_idsnowflakeThe ID of the channel
limitintegerNumber of messages to return (1-50)
before?snowflakeReturn messages before this message ID
after?snowflakeReturn messages after this message ID
around?snowflakeReturn a window centred on this message ID
FieldTypeDescription
channel_idsnowflakeThe ID of the channel
messagesarray[message object]Reverse-chronological message window
GET/v1/channels/{channel_id}/messagesBot

Returns a reverse-chronological array of message objects. Requires VIEW_CHANNEL.

  • Reading history in a guild channel requires READ_MESSAGE_HISTORY.
  • A guild caller without READ_MESSAGE_HISTORY sees only messages created on or after the guild’s message history cutoff.
FieldTypeDescription
channel_idsnowflakeThe ID of the channel
FieldTypeDescription
limit?integerNumber of messages to return (1-100, default 50)
before?1snowflakeReturn messages before this message ID
after?1snowflakeReturn messages after this message ID
around?snowflakeReturn a window centred on this message ID, overriding before and after

1 Combining the two selects the page before before, then discards entries at or below after

StatusBodyCondition
200array[message object]Message window was returned
403error responseCaller lacks VIEW_CHANNEL and the request returns MISSING_PERMISSIONS, or the caller lacks age verification for the channel and the request returns NSFW_CONTENT_AGE_RESTRICTED
404error responseChannel does not exist or the caller is not a recipient of the private channel

100 requests per 10 seconds for each authenticated user and channel ID, on the channel:messages:read::channel_id bucket.

POST/v1/channels/messages/bulk

Returns a bulk message response object holding bounded message windows from several channels.

  • This is a user-only operation, and a bot credential is refused with 403 ACCESS_DENIED.
  • FiveCord resolves and authorises each requested channel independently, with exactly the same channel checks as List channel messages.
  • An inaccessible channel fails the whole request, so the response has no window for any channel.
FieldTypeDescription
requests1array[bulk message request object]From 1 through 25 channel requests

1 The limit values across all entries must sum to at most 250, and exceeding that sum fails the whole request

FieldTypeDescription
channels1array[bulk message channel object]Results in request order

1 Contains exactly one entry for each requested channel, in the order the entries were sent, and a duplicated channel ID therefore produces a duplicated result entry

StatusBodyCondition
200response bodyAll requested windows were returned
403error responseCaller is a bot and the request returns ACCESS_DENIED
403error responseThe caller lacks VIEW_CHANNEL for a requested channel and the request returns MISSING_PERMISSIONS
403error responseThe caller lacks age verification for a requested channel and the request returns NSFW_CONTENT_AGE_RESTRICTED
404error responseA requested channel does not exist or the caller is not a recipient of the private channel

20 requests per 10 seconds for each authenticated user, on the channel:messages:bulk_read bucket.

GET/v1/channels/{channel_id}/messages/{message_id}Bot

Returns one message object. Requires VIEW_CHANNEL.

  • In a guild channel without READ_MESSAGE_HISTORY, a message created before the guild’s message history cutoff is reported as 404 UNKNOWN_MESSAGE rather than 403.
FieldTypeDescription
channel_idsnowflakeThe ID of the channel
message_idsnowflakeThe ID of the message to return
StatusBodyCondition
200message objectMessage was returned
403error responseCaller lacks VIEW_CHANNEL and the request returns MISSING_PERMISSIONS, or the caller lacks age verification for the channel and the request returns NSFW_CONTENT_AGE_RESTRICTED
404error responseChannel or message does not exist, or the message is outside the caller’s message history cutoff

100 requests per 10 seconds for each authenticated user and channel ID, on the channel:message:read::channel_id bucket.

POST/v1/channels/{channel_id}/attachmentsBot

Plans from 1 through 10 attachment uploads. Returns a singlepart or multipart upload object for each declaration.

  • The channel must support messages, and one that does not fails with 400 CANNOT_SEND_MESSAGES_IN_NON_TEXT_CHANNEL.
  • A guild channel requires SEND_MESSAGES and ATTACH_FILES, and a timed-out member is refused with 403 COMMUNICATION_DISABLED.

FiveCord checks each declared file_size separately against the attachment size limit it resolves for the caller and the guild context. A declaration above that limit returns 400 FILE_SIZE_TOO_LARGE with the resolved ceiling. By default the resolved ceiling is 26214400 bytes (25 MiB) for a caller without premium and 524288000 bytes (500 MiB) for a caller with premium. A bot credential is clamped to 52428800 bytes, the 50 MiB bot ceiling, even when the resolved limit is higher.

FieldTypeDescription
channel_idsnowflakeThe ID of the target message channel
FieldTypeDescription
attachmentsarray[attachment upload request item object]Attachment upload declarations (1-10 entries)
FieldTypeDescription
attachments1array[singlepart or multipart attachment upload object]Upload plan for each declaration

1 Returned in the same order as the request, and each entry echoes the caller’s id so a client can pair a plan with its declaration. Read upload_mode to select the variant

StatusBodyCondition
200response bodyUpload plans were issued
400error responseBody, attachment declaration, or count is invalid and the request returns INVALID_FORM_BODY
400error responseThe channel does not support messages and the request returns CANNOT_SEND_MESSAGES_IN_NON_TEXT_CHANNEL
400error responseA declared size exceeds the resolved limit and the request returns FILE_SIZE_TOO_LARGE
403error responseCaller lacks SEND_MESSAGES or ATTACH_FILES and the request returns MISSING_PERMISSIONS
403error responseThe caller is timed out and the request returns COMMUNICATION_DISABLED
403error responsePresigned attachment uploads are switched off for the instance and the request returns FEATURE_TEMPORARILY_DISABLED
404error responseChannel does not exist or is unavailable and the request returns UNKNOWN_CHANNEL, or the authenticated account does not exist and the request returns UNKNOWN_USER

The operation reserves the uploads for the caller and channel. It creates no message.

An issued upload_filename becomes an attachment only when a later Create message or Modify message request references it from a pre-uploaded attachment entry. A singlepart plan needs no completion step, so a message can reference its key as soon as the PUT succeeds.

10 requests per 10 seconds for each authenticated user and channel ID, on the channel:attachment:upload::channel_id bucket.

POST/v1/channels/{channel_id}/attachments/completeBot

Completes from 1 through 10 multipart uploads after every part has been sent. Returns their upload keys.

  • Each upload must have been planned as multipart by the same authenticated identity for the same channel.

The request has no part list and no entity tags.

FieldTypeDescription
channel_idsnowflakeThe ID of the channel bound to the uploads
FieldTypeDescription
uploadsarray[multipart upload completion item object]Uploads to finalise (1-10 entries)
FieldTypeDescription
uploadsarray[object]One entry for each completed upload, each with upload_filename
StatusBodyCondition
200response bodyEvery multipart upload was completed
400error responseBody is invalid, or an upload is not a pending multipart upload of this caller in this channel and the request returns INVALID_FORM_BODY with the code UPLOADED_ATTACHMENT_NOT_FOUND on the path uploads.{index}.upload_filename
400error responseNo part was uploaded and the request returns INVALID_FORM_BODY with the code NO_UPLOADED_PARTS_TO_FINALIZE on the path parts
400error responseThe uploaded bytes exceed the resolved limit and the request returns FILE_SIZE_TOO_LARGE
403error responseCaller lacks SEND_MESSAGES or ATTACH_FILES and the request returns MISSING_PERMISSIONS
403error responseThe caller is timed out and the request returns COMMUNICATION_DISABLED
403error responsePresigned attachment uploads are switched off for the instance and the request returns FEATURE_TEMPORARILY_DISABLED
404error responseChannel does not exist or is unavailable and the request returns UNKNOWN_CHANNEL, or the authenticated account does not exist and the request returns UNKNOWN_USER

The operation makes each completed upload available for a message. It creates no message.

10 requests per 10 seconds for each authenticated user and channel ID, on the channel:attachment:upload::channel_id bucket, which is shared with Request attachment upload URLs.

POST/v1/attachments/refresh-urlsBot

Signs from 1 through 50 attachment URLs again. Returns one refreshed attachment URL object for each URL sent. A data package URL is accepted like any other, and its result is an ordinary URL valid for 24 hours.

  • An OAuth2 bearer token is refused with 403 ACCESS_DENIED.
  • FiveCord checks no channel membership, no permission, and no attachment existence, and reads no stored data. A URL is signed whether or not its object exists or the caller can see it.
FieldTypeDescription
attachment_urls1array[string]URLs to sign again (1-50 entries, each at most 2,048 characters)

1 Each entry is read exactly as sent, with no trimming or normalisation

FieldTypeDescription
refreshed_urls1array[refreshed attachment URL object]Results in request order

1 Contains exactly one entry for each requested URL, in the order the entries were sent, and a duplicated URL therefore produces a duplicated entry

StatusBodyCondition
200response bodyEvery URL was processed
400error responseBody, URL count, or a URL length is invalid and the request returns INVALID_FORM_BODY
401error responseThe credential is missing or invalid and the request returns UNAUTHORIZED
403error responseCaller presents a bearer credential and the request returns ACCESS_DENIED
403error responseThe account has an outstanding required action and the request returns ACCOUNT_SUSPICIOUS_ACTIVITY

20 requests per 10 seconds for each authenticated user, on the attachment:refresh_urls bucket.

POST/v1/channels/{channel_id}/messagesBot

Creates a message from a JSON body or from multipart form data. Returns the created message object. Emits a Message Create Gateway event.

  • The channel must be a text-bearing type.
  • A guild channel requires VIEW_CHANNEL and SEND_MESSAGES, an untimed-out membership, and a membership that meets the guild’s verification level.
  • A timed-out member is refused with 403 COMMUNICATION_DISABLED.
  • Embeds require EMBED_LINKS, attachments require ATTACH_FILES, a favourite meme requires both, and active everyone mentions require MENTION_EVERYONE.
  • Slowmode applies to a non-bot caller unless they hold BYPASS_SLOWMODE.
  • A one-to-one direct message also applies the recipient’s direct message policy and relationship state, and a denial returns 400 CANNOT_SEND_MESSAGES_TO_USER.
  • An unclaimed account can send only to its own personal notes channel, and a send to any other channel is refused with 400 UNCLAIMED_ACCOUNT_CANNOT_SEND_MESSAGES before the channel is resolved.
  • A non-bot caller must have started a session, and one that has not is refused with the field code MUST_START_SESSION_BEFORE_SENDING.

The request body is read as a multipart form when Content-Type contains multipart/form-data, and as JSON otherwise. An empty JSON body is read as {}. A JSON body that is neither valid JSON nor valid against the schema fails with the field code INVALID_MESSAGE_DATA on the path message_data, which has no per-field detail. A multipart body whose merged payload fails the same schema instead fails with a per-field 400 INVALID_FORM_BODY naming each offending path.

FieldTypeDescription
channel_idsnowflakeThe ID of the target channel
FieldTypeDescription
nonce?1string | integerCaller-generated identifier as a string of 1 through 32 characters or a non-negative safe integer
content?2?stringMessage text
embeds?6array[rich embed object]Rich embed inputs
attachments?7array[pre-uploaded attachment object]Attachment references
message_reference??message reference input objectReply or forward reference
allowed_mentions??allowed mentions objectActive mention policy
flags?3integerMessage flags, defaulting to 0
favorite_meme_id?4?snowflakeFavourite meme to attach
sticker_ids??array[snowflake]At most 3 sticker IDs
tts?5booleanWhether to request text-to-speech

1 Echoed back on the created message and on its Message Create Dispatch so a client can match the result to its optimistic entry. An integer is converted to its decimal string form

2 The schema sets no length bound. Exceeding the effective max_message_length described below returns 400 INVALID_FORM_BODY with CONTENT_EXCEEDS_MAX_LENGTH on the path content

3 FiveCord keeps only the bits in message flags and silently drops every other bit

4 The stored meme object is copied into a new attachment on the message, so the message has its own independent copy. A meme the caller does not own returns 400 INVALID_FORM_BODY with the code FAVORITE_MEME_NOT_FOUND

5 FiveCord forces the value to false when the caller lacks SEND_TTS_MESSAGES in a guild, and the request still succeeds

6 Bounded by the resolved max_embeds_per_message limit, which defaults to 10. Exceeding it fails with the field code TOO_MANY_EMBEDS on the path embeds

7 Bounded by the resolved max_attachments_per_message limit, which defaults to 10. Exceeding it fails with the field code TOO_MANY_FILES on the path attachments

The smallest body that works is one line of text:

{
"content": "hello"
}

FiveCord resolves the effective max_message_length for the author and guild context, and it defaults to 2,000 characters. A bot or webhook author takes whichever is larger, the resolved value or 4,000 characters.

A message must have at least one of content with visible characters, an embed, an attachment, a favourite meme, or a sticker. An otherwise empty request fails with 400 CANNOT_SEND_EMPTY_MESSAGE. SUPPRESS_NOTIFICATIONS changes notification generation, and the message still has its mention data.

Reusing a nonce in the same channel inside that window returns the message the first request created. Reusing it in a different channel fails with 404 UNKNOWN_MESSAGE. The replay check runs after authorisation and body validation, so a retry that is otherwise invalid still fails, and a retry after the window has passed creates a second message.

A reply from a guild caller without READ_MESSAGE_HISTORY fails with 404 UNKNOWN_MESSAGE when its target was created before the guild’s message history cutoff, or when the guild has no cutoff. A forward has no cutoff check on its source. A reply whose target is a system message fails with the field code CANNOT_REPLY_TO_SYSTEM_MESSAGE, and a reference that resolves to no message fails with 404 UNKNOWN_MESSAGE.

Every FORWARD reference has channel_id. One that does not is refused by body validation before the operation runs, with 400 INVALID_MESSAGE_DATA on a JSON body and a per-field 400 INVALID_FORM_BODY on a multipart body.

A forward request has no content, embeds, attachments, or sticker_ids, and one that does fails with the field code FORWARD_MESSAGES_CANNOT_CONTAIN_CONTENT. A guild_id that disagrees with the source channel’s guild fails with the field code GUILD_ID_MUST_MATCH_REFERENCED_MESSAGE. Reading the source channel requires VIEW_CHANNEL when that channel belongs to a guild.

Snapshots with embeds require EMBED_LINKS in the destination guild channel, and snapshots with attachments require ATTACH_FILES, each returning 403 MISSING_PERMISSIONS when absent.

FieldTypeDescription
payload_json?stringJSON encoding of the create message body, defaulting to {} when the field is absent
files[N]?1 2binaryDirect attachment file at zero-based index N
content?stringOverrides content in payload_json
nonce?stringOverrides nonce in payload_json
tts?stringOverrides tts in payload_json, parsed from true or false
flags?stringOverrides flags in payload_json, parsed as a decimal integer
favorite_meme_id?stringOverrides favorite_meme_id in payload_json
sticker_ids?stringOverrides sticker_ids in payload_json, repeated once per sticker

1 The legacy names file and file<N> are accepted and take the same indices. Any field name not listed above is ignored

2 The index runs from 0 through the resolved max_attachments_per_message limit minus one

A body that cannot be parsed as a multipart form fails with the field code FAILED_TO_PARSE_MULTIPART_FORM_DATA. A payload_json field that is not a JSON string fails with the field code INVALID_JSON_IN_PAYLOAD_JSON, and a field name beginning with files[ that does not match files[<N>] fails with the field code INVALID_FILE_FIELD_NAME.

File field conditionError
Index below 0 or above 10000FILE_INDEX_EXCEEDS_MAXIMUM
Resolved attachment limit is 0ATTACHMENTS_NOT_ALLOWED_FOR_MESSAGE
Index at or above a non-zero resolved limitFILE_INDEX_EXCEEDS_MAXIMUM
Index repeated across fieldsDUPLICATE_FILE_INDEX
More than one file under one indexMULTIPLE_FILES_FOR_INDEX_NOT_ALLOWED
More files than the resolved limitTOO_MANY_FILES

Each is a field code inside a 400 INVALID_FORM_BODY body. A FILE_INDEX_EXCEEDS_MAXIMUM element has a maxIndex member. It is 10000 when the index is below 0 or above 10000, and the resolved limit minus one when the index is at or above that limit.

The attachments array inside payload_json uses direct multipart attachment metadata, and an entry with upload_filename is instead read as a pre-uploaded attachment. An entry whose id matches a supplied file index supplies that file’s metadata, and two entries claiming the same file fail with the field code DUPLICATE_ATTACHMENT_IDS_NOT_ALLOWED.

FiveCord permits index gaps and attaches the files in ascending index order. A supplied file that no metadata entry claims is attached with its own multipart filename and no title or description, so omitting attachments entirely attaches every supplied file that way.

A message with VOICE_MESSAGE set has exactly one attachment and nothing else.

Voice message conditionError
Content presentVOICE_MESSAGES_CANNOT_HAVE_CONTENT
Embeds presentVOICE_MESSAGES_CANNOT_HAVE_EMBEDS
Favourite meme presentVOICE_MESSAGES_CANNOT_HAVE_FAVORITE_MEMES
Stickers presentVOICE_MESSAGES_CANNOT_HAVE_STICKERS
Any count other than one attachmentVOICE_MESSAGES_REQUIRE_SINGLE_ATTACHMENT
Missing waveformVOICE_MESSAGES_ATTACHMENT_WAVEFORM_REQUIRED
Missing durationVOICE_MESSAGES_ATTACHMENT_DURATION_REQUIRED
Duration above the resolved limitVOICE_MESSAGES_DURATION_EXCEEDS_LIMIT

The resolved max_voice_message_duration limit defaults to 1200 seconds.

StatusBodyCondition
200message objectMessage was created
400error responseBody, multipart form, content, reference, mention, attachment, embed, sticker, or voice message contract is invalid
400error responseThe recipient’s direct message policy denies the send and the request returns CANNOT_SEND_MESSAGES_TO_USER
400error responseSlowmode denies the send and the request returns SLOWMODE_RATE_LIMITED
403error responseCaller lacks a required permission and the request returns MISSING_PERMISSIONS
403error responseIs timed out and the request returns COMMUNICATION_DISABLED
403error responseIs age restricted from the channel and the request returns NSFW_CONTENT_AGE_RESTRICTED
403error responseMessage sending is temporarily disabled for the guild and the request returns FEATURE_TEMPORARILY_DISABLED
404error responseChannel, referenced message, sticker, or guild does not exist or is unavailable, or the supplied nonce was last used in a different channel and the request returns UNKNOWN_MESSAGE

retry_after is never more than one second below the Retry-After header. The response has no bucket headers and no X-RateLimit-Scope, so a client can tell it apart from a route or global limit denial.

The operation creates the message, attaches every referenced upload and direct multipart file, applies attachment decay deadlines, copies forwarded media, updates the channel’s last message, and makes the message searchable.

It advances the author’s read state unless the author is a bot, reopens a direct message for any recipient who had closed it, and resolves eligible uncached URLs into embeds.

It emits Message Create to every session that can see the channel. Mention and reply notifications follow the resolved allowed mentions policy.

It writes no audit log entry.

20 requests per 10 seconds for each authenticated user and channel ID, on the channel:message:create::channel_id bucket.

PATCH/v1/channels/{channel_id}/messages/{message_id}Bot

Modifies a message. Returns the updated message object. Emits a Message Update Gateway event.

  • The target must be a DEFAULT or REPLY message, and any other type fails with 400 CANNOT_MODIFY_SYSTEM_WEBHOOK.
  • A message that has message snapshots cannot be edited by its author and fails with the field code MESSAGES_WITH_SNAPSHOTS_CANNOT_BE_EDITED. A non-author moderator holding MANAGE_MESSAGES can still toggle SUPPRESS_EMBEDS and change existing attachment metadata on a forwarded message.
  • The author can modify every supported field, and a timed-out author is refused with 403 COMMUNICATION_DISABLED.
  • A caller who is not the author can act only in a guild channel, must hold MANAGE_MESSAGES, and can change only SUPPRESS_EMBEDS and the metadata of attachments that already exist. A non-author edit that does not satisfy all these conditions fails with 403 CANNOT_EDIT_OTHER_USER_MESSAGE.
  • MANAGE_MESSAGES is an elevated permission, so a caller who holds it with no enrolled authenticator receives 400 TWO_FACTOR_REQUIRED in a guild whose MFA level is elevated, unless they own the guild.
  • A caller who does not hold the bit at all is treated as any other non-author and receives 403 CANNOT_EDIT_OTHER_USER_MESSAGE.
  • Embeds require EMBED_LINKS and adding an upload requires ATTACH_FILES.
  • A concurrent edit can fail with 429 RESOURCE_LOCKED and Retry-After: 1.

The body is read exactly as it is for Create message, so a multipart/form-data request is accepted with the same payload_json and files[N] fields. A file that is not uploaded directly must first be planned through Request attachment upload URLs and then referenced by its upload_filename.

FieldTypeDescription
channel_idsnowflakeThe ID of the channel
message_idsnowflakeThe ID of the message to modify
FieldTypeDescription
content??stringReplacement text under the same effective limits as message creation
embeds?1array[rich embed object]Replacement embed collection
allowed_mentions?2?allowed mentions objectReplacement active mention policy
flags?3integerReplacement message flags
attachments?1array[pre-uploaded attachment object | existing attachment reference object]Complete retained and added attachment collection

1 Replaces the stored collection outright, so an existing embed or attachment that the array omits is removed. Both collections are bounded by the same resolved limits as message creation

2 Defaults to the stored reply mention state when omitted, so an edit that does not name a policy neither adds nor removes the reply mention of the referenced author

3 Only the bits in message flags are retained, and the message keeps every other stored bit unchanged

A request that supplies message_snapshots is rejected with 403 MISSING_PERMISSIONS, so snapshot metadata cannot be edited through this route.

An omitted field preserves its stored value, and the message’s edited_timestamp advances only when content changes. The request itself must supply at least one of content with visible characters, an embed, an attachment, or flags, and one that supplies none of them fails with 400 CANNOT_SEND_EMPTY_MESSAGE.

An id that names no attachment on the message is skipped, and the edit still succeeds.

StatusBodyCondition
200message objectMessage was modified
400error responseBody, message type, attachment, embed, or resulting payload is invalid, or the author is editing a message that has snapshots and the request returns MESSAGES_WITH_SNAPSHOTS_CANNOT_BE_EDITED
403error responseCaller is not the author and cannot moderate the message and the request returns CANNOT_EDIT_OTHER_USER_MESSAGE
403error responseThe caller supplied message_snapshots or lacks EMBED_LINKS or ATTACH_FILES and the request returns MISSING_PERMISSIONS
403error responseThe caller is timed out and the request returns COMMUNICATION_DISABLED
403error responseThe caller is age restricted from the channel and the request returns NSFW_CONTENT_AGE_RESTRICTED
403error responseMessage sending is temporarily disabled for the guild and the request returns FEATURE_TEMPORARILY_DISABLED
404error responseChannel or message does not exist

The operation updates the message, advances its edit timestamp when content changed, attaches every newly referenced upload, and drops each attachment the request did not retain. It recalculates mentions when content, allowed_mentions, or embeds changed, updates search results when the channel is indexed, and resolves newly added eligible URLs into embeds.

It emits Message Update to every session that can see the channel. It writes no audit log entry, even for a moderator edit.

20 requests per 10 seconds for each authenticated user and channel ID, on the channel:message:update::channel_id bucket.

DELETE/v1/channels/{channel_id}/messages/ackBot

Deletes the authenticated identity’s read state entry for one channel. Returns 204 with an empty body on success.

  • FiveCord does not resolve the channel, so the operation changes only the caller’s own read state.
  • The operation is idempotent, and a channel with no stored entry still returns 204.
FieldTypeDescription
channel_idsnowflakeThe ID of the channel whose read state is deleted
StatusBodyCondition
204emptyRead state was absent or deleted
403error responseCredential type or account state denies the request and it returns ACCESS_DENIED or ACCOUNT_SUSPICIOUS_ACTIVITY

The operation deletes the caller’s read-through marker, mention count, and pin acknowledgement for the channel. It emits no Gateway Dispatch.

40 requests per 10 seconds for each authenticated user and channel ID, on the channel:read_state:delete::channel_id bucket.

DELETE/v1/channels/{channel_id}/messages/{message_id}BotAudit reason

Deletes one message. Returns 204 with an empty body on success. Emits a Message Delete Gateway event.

  • The target must be a deletable message type.
  • The author can delete their own message anywhere.
  • Deleting another user’s guild message requires both SEND_MESSAGES and MANAGE_MESSAGES, which is an elevated permission. No caller can delete another user’s private channel message.
  • A caller who holds MANAGE_MESSAGES with no enrolled authenticator receives 400 TWO_FACTOR_REQUIRED in a guild whose MFA level is elevated, unless they own the guild.
FieldTypeDescription
channel_idsnowflakeThe ID of the channel
message_idsnowflakeThe ID of the message to delete
StatusBodyCondition
204emptyMessage was deleted
403error responseCaller lacks authorship or the required guild permissions, or the message type is not deletable, each returning MISSING_PERMISSIONS
403error responseThe caller lacks age verification for the channel and the request returns NSFW_CONTENT_AGE_RESTRICTED
403error responseMessage sending is temporarily disabled for the guild and the request returns FEATURE_TEMPORARILY_DISABLED
404error responseChannel or message does not exist

The operation permanently deletes the message, its reactions, and its attachments and removes it from search.

It emits Message Delete to every session that can see the channel. When the message was pinned, it also removes the channel pin entry and emits Channel Pins Update with the channel’s unchanged last-pin timestamp.

Every deletion in a guild channel writes one MESSAGE_DELETE guild audit log entry with the supplied reason, including deletion of the caller’s own message.

20 requests per 10 seconds for each authenticated user and channel ID, on the channel:message:delete::channel_id bucket.

DELETE/v1/channels/{channel_id}/messages/{message_id}/attachments/{attachment_id}Bot

Deletes one attachment from the caller’s own message. Returns 204 with an empty body on success. Emits a Message Update Gateway event.

  • Only the message author can use this operation, and a guild moderator must use Modify message instead.
  • This route cannot address an attachment that a resolved embed owns.
  • When the attachment is the message’s only attachment and the message has no content, embeds, or stickers, FiveCord deletes the whole message with the Delete message effects.
FieldTypeDescription
channel_idsnowflakeThe ID of the channel
message_idsnowflakeThe ID of the owning message
attachment_idsnowflakeThe ID of the attachment to delete
StatusBodyCondition
204emptyAttachment or resulting empty message was deleted
403error responseCaller is not the message author and the request returns MISSING_PERMISSIONS, or message sending is temporarily disabled for the guild and the request returns FEATURE_TEMPORARILY_DISABLED
404error responseChannel or message does not exist, or the message has no such addressable attachment

The operation permanently deletes the stored attachment object, purges it from media delivery, removes it from the message, and advances the message’s edit timestamp. It then emits Message Update to every session that can see the channel. When the removed attachment was the message’s only attachment and the message has no content, embeds, or stickers, FiveCord deletes the whole message instead, with the complete Delete message effect set, including its Dispatches and guild audit log entry.

20 requests per 10 seconds for each authenticated user and channel ID, on the channel:message:delete::channel_id bucket.

POST/v1/channels/{channel_id}/messages/bulk-deleteBotAudit reasonMFA

Deletes from 1 through 100 messages in one guild channel. Returns 204 with an empty body on success. Emits one Message Delete Bulk Gateway event.

  • The channel must belong to a guild, and a private channel fails with 400 CANNOT_EXECUTE_ON_DM.
  • The caller requires MANAGE_MESSAGES, which is an elevated permission.
  • A caller who holds MANAGE_MESSAGES with no enrolled authenticator receives 400 TWO_FACTOR_REQUIRED in a guild whose MFA level is elevated, unless they own the guild.
  • The operation applies no age boundary, so a message of any age can be selected.
FieldTypeDescription
channel_idsnowflakeThe ID of the guild channel

At least one of the fields below must be present. Supplying neither fails with 400 INVALID_FORM_BODY on the path message_ids, and supplying both uses message_ids and ignores messages.

FieldTypeDescription
message_ids?1array[snowflake]Message IDs to delete, at most 100 entries
messages?1array[snowflake]Alias for message_ids

1 An empty array fails with the field code MESSAGE_IDS_CANNOT_BE_EMPTY on the path message_ids. More than 100 entries is rejected by body validation with 400 INVALID_FORM_BODY before the operation runs

An ID that names no message in the channel is skipped, so a partially matching request still deletes every matching message. When no ID matches, the request returns 204 without deleting anything, emitting a Dispatch, or writing an audit log entry.

StatusBodyCondition
204emptyMessages were deleted
400error responseBody or entry count is invalid, the selection is empty and the request returns MESSAGE_IDS_CANNOT_BE_EMPTY, or the channel is a private channel and the request returns CANNOT_EXECUTE_ON_DM
403error responseCaller lacks MANAGE_MESSAGES and the request returns MISSING_PERMISSIONS, or the caller lacks age verification for the channel and the request returns NSFW_CONTENT_AGE_RESTRICTED
404error responseChannel does not exist

The operation permanently removes every selected message, attachment, and reaction and removes each message from search.

When at least one ID matched, it emits one Message Delete Bulk with every requested ID to sessions that can see the channel, including an ID that matched nothing. It emits no Channel Pins Update and no individual Message Delete.

It writes one MESSAGE_BULK_DELETE guild audit log entry with the matched count and the supplied reason.

10 requests per 10 seconds for each authenticated user and channel ID, on the channel:message:bulk_delete::channel_id bucket.

POST/v1/channels/{channel_id}/messages/purgeBot

Deletes every message in the authenticated user’s personal notes channel. Returns the number that were removed. Emits a Message Delete Bulk Gateway event.

  • The channel must be the caller’s own DM_PERSONAL_NOTES channel, and any other channel fails with 400 INVALID_CHANNEL_TYPE.

The response is returned only after deletion finishes, which can take longer for a large history.

FieldTypeDescription
channel_idsnowflakeThe ID of the personal notes channel
FieldTypeDescription
deleted_countintegerNumber of messages removed by this request
StatusBodyCondition
200response bodyEvery personal note was deleted
403error responseCredential type or account state denies the request and it returns ACCESS_DENIED or ACCOUNT_SUSPICIOUS_ACTIVITY
404error responseChannel does not exist or the caller cannot resolve it

The operation permanently deletes every personal note, its attachments, and its reactions and removes each from search. The caller’s sessions receive one Message Delete Bulk Dispatch for each page of at most 100 messages.

It emits no Channel Pins Update and no individual Message Delete, and it writes no audit log entry.

2 requests per minute for each authenticated user and channel ID, on the channel:message:purge::channel_id bucket.

POST/v1/channels/{channel_id}/messages/bulk-delete-mineBotMFA

Deletes every message the caller authored in one channel. Returns 202 with an empty body. Emits a Message Delete Bulk Gateway event.

  • User session and bot credentials are accepted, and a bot credential satisfies the sudo requirement without any proof.
  • The caller must be able to resolve the channel and be in sudo mode, established by the X-FiveCord-Sudo-Mode-JWT header or by supplying a proof in the body.
  • A caller who satisfies none of those receives 403 SUDO_MODE_REQUIRED with has_mfa and the available methods, which report totp, webauthn, and backup_codes.
  • An account that holds no verifiable credential at all, meaning it is not a bot, has no TOTP secret, no registered WebAuthn credential, and no stored password, satisfies the sudo requirement with an empty body.

The deletion runs inside the request despite the 202 status, so every matching message is gone by the time the response is returned.

FieldTypeDescription
channel_idsnowflakeThe ID of the channel whose caller-authored messages are deleted
FieldTypeDescription
X-FiveCord-Sudo-Mode-JWT?1stringExisting sudo mode proof

1 A valid header satisfies the sudo requirement on its own, and the body fields below are then unnecessary. The accepted proof is echoed back in the same response header

FieldTypeDescription
password?1stringAccount password proof
mfa_method?2stringMFA method, one of totp or webauthn
mfa_code?stringAuthenticator code (1-32 characters), supplied with the totp method
webauthn_response?WebAuthn assertion objectWebAuthn assertion for sudo verification
webauthn_challenge?stringChallenge bound to the sudo mode assertion

1 Accepted only when the account has no enrolled authenticator, and an incorrect password fails with the field code INVALID_PASSWORD. An account with neither a stored password nor an enrolled authenticator never reaches this check

2 Accepted only when the account has an enrolled authenticator. A failed verification returns the field code INVALID_MFA_CODE

StatusBodyCondition
202emptyEvery caller-authored message in the channel was deleted
403error responseCaller is not in sudo mode and the request returns SUDO_MODE_REQUIRED
403error responseThe caller cannot view the guild channel and the request returns MISSING_PERMISSIONS
403error responseThe caller is age restricted from the channel and the request returns NSFW_CONTENT_AGE_RESTRICTED
404error responseChannel does not exist or is unavailable

Each matching message, with its attachments and reactions, is permanently deleted and removed from search. FiveCord emits one Message Delete Bulk Dispatch for each batch of at most 100 deleted messages.

The operation emits no Channel Pins Update and no individual Message Delete, and it writes no guild audit log entry.

10 requests per 10 seconds for each authenticated user and channel ID, on the channel:message:bulk_delete::channel_id bucket.

GET/v1/channels/{channel_id}/messages/pinsBot

Returns a channel pins page object holding one page of pinned messages and their pin times.

  • The caller must be able to view the channel and must satisfy the channel’s age verification requirement.
  • The channel must be a text-bearing type.
  • A guild caller without READ_MESSAGE_HISTORY sees only pins whose message falls on or after the guild’s message history cutoff, and receives an empty page when the guild has no cutoff.
FieldTypeDescription
channel_idsnowflakeThe ID of the channel whose pins are returned
FieldTypeDescription
limit?integerNumber of pins to return (1-50, default 50)
before?ISO8601 timestampReturn pins pinned before this timestamp, defaulting to the current time
StatusBodyCondition
200channel pins page objectPin page was returned
400error responsePath or cursor is invalid, or the channel has no messages and the request returns CANNOT_SEND_MESSAGES_IN_NON_TEXT_CHANNEL
403error responseCaller lacks VIEW_CHANNEL and the request returns MISSING_PERMISSIONS, or the caller lacks age verification for the channel and the request returns NSFW_CONTENT_AGE_RESTRICTED
404error responseChannel does not exist or the caller is not a recipient of the private channel

Acknowledging pins is a separate operation, Acknowledge pins.

20 requests per 10 seconds for each authenticated user and channel ID, on the channel:pins::channel_id bucket.

PUT/v1/channels/{channel_id}/pins/{message_id}BotAudit reason

Pins a DEFAULT or REPLY message. Returns 204 with an empty body on success. Emits Message Update, Channel Pins Update, and Message Create Gateway events.

  • A guild caller requires PIN_MESSAGES and channel access.
  • A guild caller without READ_MESSAGE_HISTORY can pin only a message on or after the guild’s message history cutoff.
  • A one-to-one direct message caller must satisfy the recipient’s direct message policy, and one who may not send fails with 400 CANNOT_SEND_MESSAGES_TO_USER.
  • A group direct message and the personal notes channel apply no send policy.
  • The operation is idempotent.
FieldTypeDescription
channel_idsnowflakeThe ID of the channel
message_idsnowflakeThe ID of the message to pin
StatusBodyCondition
204emptyMessage was already pinned or was pinned
400error responsePath is invalid and the request returns INVALID_FORM_BODY
400error responseThe channel has no messages and the request returns CANNOT_SEND_MESSAGES_IN_NON_TEXT_CHANNEL
400error responseThe target is a system message and the request returns CANNOT_MODIFY_SYSTEM_WEBHOOK
400error responseThe private channel’s send policy denies the caller and the request returns CANNOT_SEND_MESSAGES_TO_USER
403error responseCaller lacks VIEW_CHANNEL or PIN_MESSAGES and the request returns MISSING_PERMISSIONS, or message sending is temporarily disabled for the guild and the request returns FEATURE_TEMPORARILY_DISABLED
404error responseChannel or message does not exist, or the message is outside the caller’s message history cutoff

Pinning a message that is not already pinned sets its pin timestamp, advances the channel’s last-pin timestamp, and stores a new CHANNEL_PINNED_MESSAGE system message authored by the caller and referencing the pinned message.

It emits Message Update for the target, Channel Pins Update for the channel, and Message Create for the system message, each to every session that can see the channel.

A guild pin also writes one MESSAGE_PIN guild audit log entry with the supplied reason. Pinning an already pinned message changes nothing and emits no Dispatch.

20 requests per 10 seconds for each authenticated user and channel ID, on the channel:pins::channel_id bucket.

DELETE/v1/channels/{channel_id}/pins/{message_id}BotAudit reason

Unpins a message. Returns 204 with an empty body on success. Emits Message Update and Channel Pins Update Gateway events.

  • Authorisation is exactly the same as Pin message.
  • The operation is idempotent.
FieldTypeDescription
channel_idsnowflakeThe ID of the channel
message_idsnowflakeThe ID of the message to unpin
StatusBodyCondition
204emptyMessage was already unpinned or was unpinned
400error responsePath is invalid and the request returns INVALID_FORM_BODY
400error responseThe channel has no messages and the request returns CANNOT_SEND_MESSAGES_IN_NON_TEXT_CHANNEL
400error responseThe target is a system message and the request returns CANNOT_MODIFY_SYSTEM_WEBHOOK
400error responseThe private channel’s send policy denies the caller and the request returns CANNOT_SEND_MESSAGES_TO_USER
403error responseCaller lacks VIEW_CHANNEL or PIN_MESSAGES and the request returns MISSING_PERMISSIONS, or message sending is temporarily disabled for the guild and the request returns FEATURE_TEMPORARILY_DISABLED
404error responseChannel or message does not exist, or the message is outside the caller’s message history cutoff

Unpinning a pinned message clears its pin timestamp and emits Message Update and Channel Pins Update to every session that can see the channel. A guild unpin also writes one MESSAGE_UNPIN guild audit log entry with the supplied reason.

It creates no system message. The CHANNEL_PINNED_MESSAGE system message the original pin created stays in the channel, and the channel’s last-pin timestamp is unchanged.

20 requests per 10 seconds for each authenticated user and channel ID, on the channel:pins::channel_id bucket.

POST/v1/channels/{channel_id}/pins/ackBot

Records the channel’s current pin timestamp in the authenticated identity’s read state. Returns 204 with an empty body. Emits a Channel Pins ACK Gateway event.

The request has no body. FiveCord acknowledges the channel’s own stored last-pin timestamp.

FieldTypeDescription
channel_idsnowflakeThe ID of the channel whose current pin state is acknowledged
StatusBodyCondition
204emptyPin state was absent or acknowledged
403error responseCredential type or account state denies the request and it returns ACCESS_DENIED or ACCOUNT_SUSPICIOUS_ACTIVITY

When the channel stores a last-pin timestamp, FiveCord writes that exact instant to the caller’s read state. It then emits one Channel Pins ACK with the channel ID and that timestamp to the caller’s own sessions. The write replaces the stored value. FiveCord writes nothing and emits nothing when the channel does not exist or has never held a pin.

When the Dispatch fails, the request returns 502, 503, or 504 after the read-state write has landed. A client treats those statuses as unknown and reconciles from a later read.

20 requests per 10 seconds for each authenticated user and channel ID, on the channel:pins::channel_id bucket, which is shared with List pinned messages, Pin message, and Unpin message.

The emoji path value is URI-encoded and is 1 through 64 characters. A custom emoji uses name:id, where id is the trailing decimal custom emoji snowflake and name is everything before the final colon. Any other decoded value is read as a Unicode emoji.

FiveCord validates the emoji in full only when a request would create a new reaction group on the message. At that point a Unicode value must be exactly one valid emoji, and one that is not fails with the field code NOT_A_VALID_UNICODE_EMOJI. A custom emoji must exist, and one that does not fails with the field code CUSTOM_EMOJI_NOT_FOUND. Every other reaction operation parses the value without validating it, so removing a reaction with an invalid emoji succeeds silently.

Every reaction operation is idempotent and writes no audit log entry. Every removal route emits its Dispatch whether or not the message held the reaction, group, or reactions the request names. Add own reaction, Remove own reaction, and Remove another user’s reaction each accept a session_id query parameter, which is echoed in that route’s Dispatch so the originating session can match its own optimistic update, and it never affects authorisation or the stored reaction.

GET/v1/channels/{channel_id}/messages/{message_id}/reactions/{emoji}Bot

Returns a bare array of partial user objects for one reaction.

  • Authorisation, query parameters, and ordering are exactly those of List reaction users, and the array is the same page that operation returns in its items field.

The pagination signal is in the response headers. X-Has-More is true or false. X-Next-After is the ID of the last user in the array, and it is absent when no further page exists. List reaction users returns the same users with both values in the body.

FieldTypeDescription
channel_idsnowflakeThe ID of the channel
message_idsnowflakeThe ID of the message
emojistringURI-encoded reaction emoji path value
FieldTypeDescription
limit?integerNumber of users to return (1-100, default 25)
after?snowflakeReturn users after this user ID cursor
StatusBodyCondition
200array[partial user object]Reaction users were returned
400error responsePath, emoji, or pagination input is invalid, or the channel has no messages and the request returns CANNOT_SEND_MESSAGES_IN_NON_TEXT_CHANNEL
403error responseCaller lacks VIEW_CHANNEL and the request returns MISSING_PERMISSIONS
404error responseChannel or message does not exist, or the message is outside the caller’s message history cutoff

An emoji with no reaction group on the message returns 200 with an empty array.

30 requests per 10 seconds for each authenticated user and channel ID, on the channel:reactions::channel_id bucket.

GET/v1/channels/{channel_id}/messages/{message_id}/reactions/{emoji}/usersBot

Returns a reaction users page object holding the users who added one reaction.

  • The caller must be able to view the channel, and the channel must be a text-bearing type.
  • A guild caller without READ_MESSAGE_HISTORY can read only a message on or after the guild’s message history cutoff.
FieldTypeDescription
channel_idsnowflakeThe ID of the channel
message_idsnowflakeThe ID of the message
emojistringURI-encoded reaction emoji path value
FieldTypeDescription
limit?integerNumber of users to return (1-100, default 25)
after?snowflakeReturn users after this user ID cursor
StatusBodyCondition
200reaction users page objectReaction users were returned
400error responsePath, emoji, or pagination input is invalid, or the channel has no messages and the request returns CANNOT_SEND_MESSAGES_IN_NON_TEXT_CHANNEL
403error responseCaller lacks VIEW_CHANNEL and the request returns MISSING_PERMISSIONS
404error responseChannel or message does not exist, or the message is outside the caller’s message history cutoff

An emoji with no reaction group on the message returns 200 with an empty items array, has_more false, and a null next_after.

30 requests per 10 seconds for each authenticated user and channel ID, on the channel:reactions::channel_id bucket.

PUT/v1/channels/{channel_id}/messages/{message_id}/reactions/{emoji}/@meBot

Adds the authenticated identity’s reaction. Returns 204 with an empty body. Emits a Message Reaction Add Gateway event.

  • The caller must be able to view the text-bearing channel, must not be timed out. A guild caller without READ_MESSAGE_HISTORY can react only to a message on or after the guild’s message history cutoff, and to no message when the guild has no cutoff.
  • A non-bot caller must have a verified email, and one that does not receives 403 REACTION_EMAIL_VERIFICATION_REQUIRED.
  • Creating a new emoji reaction group in a guild requires ADD_REACTIONS, while adding to an existing group does not.
  • A custom emoji whose source guild is not the channel’s guild requires the feature_global_expressions entitlement, and a caller without it receives the field code CUSTOM_EMOJIS_REQUIRE_PREMIUM_OUTSIDE_SOURCE.
  • In a guild channel a custom emoji also requires USE_EXTERNAL_EMOJIS.
  • A non-bot caller must have started a session, and one that has not receives the field code MUST_START_SESSION_BEFORE_SENDING.
  • An unclaimed account can react only in its personal notes channel, and elsewhere receives 400 UNCLAIMED_ACCOUNT_CANNOT_ADD_REACTIONS.
  • This route applies no direct message send policy.
FieldTypeDescription
channel_idsnowflakeThe ID of the channel
message_idsnowflakeThe ID of the message
emojistringURI-encoded reaction emoji path value
FieldTypeDescription
session_id?1stringOriginating Gateway session ID (1-64 characters)

1 See reaction session correlation

Reaction object states the group and user ceilings for a message, and reaching either returns 400 MAX_REACTIONS.

StatusBodyCondition
204emptyReaction already existed or was added
400error responsePath, emoji, or session is invalid, the channel has no messages and the request returns CANNOT_SEND_MESSAGES_IN_NON_TEXT_CHANNEL
400error responseThe account has not started a session and the request returns MUST_START_SESSION_BEFORE_SENDING
400error responseThe account is unclaimed and the request returns UNCLAIMED_ACCOUNT_CANNOT_ADD_REACTIONS
400error responseThe emoji is not a valid single Unicode emoji and the request returns NOT_A_VALID_UNICODE_EMOJI
400error responseThe custom emoji is unknown and the request returns CUSTOM_EMOJI_NOT_FOUND
400error responseThe external emoji requires the entitlement and the request returns CUSTOM_EMOJIS_REQUIRE_PREMIUM_OUTSIDE_SOURCE
400error responseThe message has reached a reaction ceiling and the request returns MAX_REACTIONS
403error responseCaller lacks VIEW_CHANNEL, ADD_REACTIONS, or USE_EXTERNAL_EMOJIS and the request returns MISSING_PERMISSIONS
403error responseThe caller is timed out and the request returns COMMUNICATION_DISABLED
403error responseThe caller has an unverified email and the request returns REACTION_EMAIL_VERIFICATION_REQUIRED
403error responseReactions are temporarily disabled for the guild and the request returns FEATURE_TEMPORARILY_DISABLED
404error responseChannel or message does not exist, or the message is outside the caller’s message history cutoff

Adding a reaction the caller does not already hold creates it and emits Message Reaction Add to every session that can see the channel, with session_id for originating session correlation. Adding a reaction the caller already has changes nothing and emits no Dispatch.

30 requests per 10 seconds for each authenticated user and channel ID, on the channel:reactions::channel_id bucket.

DELETE/v1/channels/{channel_id}/messages/{message_id}/reactions/{emoji}/@meBot

Removes the authenticated identity’s own reaction. Returns 204 with an empty body. Emits a Message Reaction Remove Gateway event.

  • The caller must be able to view the text-bearing channel and reach the message under the message history cutoff.
  • A removal re-runs none of the timeout, email verification, and emoji validity checks.
FieldTypeDescription
channel_idsnowflakeThe ID of the channel
message_idsnowflakeThe ID of the message
emojistringURI-encoded reaction emoji path value
FieldTypeDescription
session_id?1stringOriginating Gateway session ID (1-64 characters)

1 See reaction session correlation

StatusBodyCondition
204emptyReaction was absent or removed
400error responsePath, emoji, or session input is invalid, or the channel has no messages and the request returns CANNOT_SEND_MESSAGES_IN_NON_TEXT_CHANNEL
403error responseCaller lacks VIEW_CHANNEL and the request returns MISSING_PERMISSIONS, or reactions are temporarily disabled for the guild and the request returns FEATURE_TEMPORARILY_DISABLED
404error responseChannel does not exist, or the message is outside the caller’s message history cutoff

A message that does not exist returns 204 rather than 404.

FiveCord deletes the reaction and emits Message Reaction Remove to every session that can see the channel, with session_id for originating session correlation.

30 requests per 10 seconds for each authenticated user and channel ID, on the channel:reactions::channel_id bucket.

DELETE/v1/channels/{channel_id}/messages/{message_id}/reactions/{emoji}/{target_id}Bot

Removes one named user’s reaction. Returns 204 with an empty body. Emits a Message Reaction Remove Gateway event.

  • The caller must be able to view the text-bearing channel and reach the message under the message history cutoff.
  • The caller must be in a guild channel and must hold MANAGE_MESSAGES, so a caller in a private channel is refused with 403 MISSING_PERMISSIONS.
  • MANAGE_MESSAGES is an elevated permission, so a caller who holds it with no enrolled authenticator receives 400 TWO_FACTOR_REQUIRED in a guild whose MFA level is elevated, unless they own the guild.
FieldTypeDescription
channel_idsnowflakeThe ID of the channel
message_idsnowflakeThe ID of the message
emojistringURI-encoded reaction emoji path value
target_id1snowflakeThe ID of the user whose reaction is removed

1 Naming the authenticated user skips the moderation check entirely, so MANAGE_MESSAGES is not required in that case

FieldTypeDescription
session_id?1stringOriginating Gateway session ID (1-64 characters)

1 See reaction session correlation

StatusBodyCondition
204emptyTarget reaction was absent or removed
400error responsePath, emoji, or session input is invalid, or the channel has no messages and the request returns CANNOT_SEND_MESSAGES_IN_NON_TEXT_CHANNEL
403error responseCaller lacks VIEW_CHANNEL or MANAGE_MESSAGES, or the channel is private, each returning MISSING_PERMISSIONS, or reactions are temporarily disabled for the guild and the request returns FEATURE_TEMPORARILY_DISABLED
404error responseChannel does not exist, or the message is outside the caller’s message history cutoff

A message that does not exist returns 204 before the MANAGE_MESSAGES check. A request naming a target user with no such reaction also returns 204.

FiveCord deletes the target user’s reaction and emits Message Reaction Remove with the target user ID to every session that can see the channel, plus session_id when the request supplied one.

30 requests per 10 seconds for each authenticated user and channel ID, on the channel:reactions::channel_id bucket.

DELETE/v1/channels/{channel_id}/messages/{message_id}/reactions/{emoji}Bot

Removes every reaction for one emoji. Returns 204 with an empty body. Emits a Message Reaction Remove Emoji Gateway event.

  • The caller must be able to view the text-bearing channel and reach the message under the message history cutoff.
  • The caller must be in a guild channel and must hold MANAGE_MESSAGES, an elevated permission.
  • A caller who holds MANAGE_MESSAGES with no enrolled authenticator receives 400 TWO_FACTOR_REQUIRED in a guild whose MFA level is elevated, unless they own the guild.
FieldTypeDescription
channel_idsnowflakeThe ID of the channel
message_idsnowflakeThe ID of the message
emojistringURI-encoded reaction emoji path value
StatusBodyCondition
204emptyEmoji reactions were absent or removed
400error responsePath or emoji is invalid, or the channel has no messages and the request returns CANNOT_SEND_MESSAGES_IN_NON_TEXT_CHANNEL
403error responseCaller lacks VIEW_CHANNEL or MANAGE_MESSAGES, or the channel is private, each returning MISSING_PERMISSIONS, or reactions are temporarily disabled for the guild and the request returns FEATURE_TEMPORARILY_DISABLED
404error responseChannel does not exist, or the message is outside the caller’s message history cutoff

FiveCord deletes every reaction for the emoji and emits one Message Reaction Remove Emoji to every session that can see the channel. FiveCord emits no Message Reaction Remove for the individual users.

30 requests per 10 seconds for each authenticated user and channel ID, on the channel:reactions::channel_id bucket.

DELETE/v1/channels/{channel_id}/messages/{message_id}/reactionsBot

Removes every reaction from a message. Returns 204 with an empty body. Emits a Message Reaction Remove All Gateway event.

  • The caller must be able to view the text-bearing channel and reach the message under the message history cutoff.
  • The caller must be in a guild channel and must hold MANAGE_MESSAGES, an elevated permission.
  • A caller who holds MANAGE_MESSAGES with no enrolled authenticator receives 400 TWO_FACTOR_REQUIRED in a guild whose MFA level is elevated, unless they own the guild.
FieldTypeDescription
channel_idsnowflakeThe ID of the channel
message_idsnowflakeThe ID of the message
StatusBodyCondition
204emptyReactions were absent or removed
400error responsePath parameters are invalid, or the channel has no messages and the request returns CANNOT_SEND_MESSAGES_IN_NON_TEXT_CHANNEL
403error responseCaller lacks VIEW_CHANNEL or MANAGE_MESSAGES, or the channel is private, each returning MISSING_PERMISSIONS, or reactions are temporarily disabled for the guild and the request returns FEATURE_TEMPORARILY_DISABLED
404error responseChannel does not exist, or the message is outside the caller’s message history cutoff

FiveCord deletes every reaction on the message and emits one Message Reaction Remove All to every session that can see the channel. FiveCord emits no event for the individual groups or users.

30 requests per 10 seconds for each authenticated user and channel ID, on the channel:reactions::channel_id bucket.

POST/v1/channels/{channel_id}/typingBot

Publishes typing activity in a text-bearing channel. Returns 204 with an empty body. Emits a Typing Start Gateway event.

  • The caller must hold SEND_MESSAGES in a guild channel and must not be timed out.
  • A private channel caller needs only recipient access, because this route applies no direct message send policy.
FieldTypeDescription
channel_idsnowflakeThe ID of the channel where typing is shown
StatusBodyCondition
204emptyTyping operation was accepted
400error responsePath parameter is invalid, or the channel has no messages and the request returns CANNOT_SEND_MESSAGES_IN_NON_TEXT_CHANNEL
403error responseCaller lacks SEND_MESSAGES and the request returns MISSING_PERMISSIONS, or the caller is timed out and the request returns COMMUNICATION_DISABLED
404error responseChannel does not exist or the caller is not a recipient of the private channel

The operation emits Typing Start to every session that can see the channel, with the channel ID, the caller’s user ID, and a Unix timestamp in seconds. It creates no durable state, so no read operation reports a typing indicator, and the indicator expires on the client. When the guild has typing events disabled, the response is still 204 and no Dispatch is emitted.

20 requests per 10 seconds for each authenticated user and channel ID, on the channel:typing::channel_id bucket.

POST/v1/channels/{channel_id}/messages/{message_id}/ack

Advances the authenticated user’s read state through one message. Returns 204 with an empty body. Emits a Message ACK Gateway event.

  • This is a user-only operation, and a bot credential is refused with 403 ACCESS_DENIED.
  • FiveCord resolves neither the channel nor the message, so the operation writes the supplied identifiers straight into the caller’s own read state and changes nothing else.
FieldTypeDescription
channel_id1snowflakeThe ID of the channel
message_id1snowflakeMessage ID through which the channel is acknowledged

1 Neither identifier needs to name something that exists, because both are stored as read-state keys and watermarks

An empty body is read as {}, and {} is the minimal valid form.

FieldTypeDescription
mention_count?1integerMention count to store for the channel (0-2,147,483,647, default 0)
manual?2booleanWhether the acknowledgement was explicitly started by the user (default false)

1 FiveCord stores the value verbatim. A client that acknowledges partway through a channel must send the number of mentions that remain above the watermark, and one that omits the field resets the stored count to 0

2 Selects the watermark rule described below

StatusBodyCondition
204emptyMessage was acknowledged
403error responseCaller is a bot and the request returns ACCESS_DENIED

The operation updates the caller’s read-through marker and mention count, clears delivered notifications through the acknowledged watermark, and emits Message ACK to the caller’s own sessions only.

A non-manual acknowledgement whose message_id is below the stored watermark leaves the stored watermark and mention count unchanged. The Dispatch reports the stored values.

100 requests per 10 seconds for each authenticated user and channel ID, on the channel:message:ack::channel_id bucket.