Messages
A message is a post in a conversation. A conversation is a text channel, the chat of a voice channel, or a thread inside a forum channel. This page explains a message’s lifecycle. The command pages hold the exact fields.
What a message looks like
Section titled “What a message looks like”{ "cmd": "message_new", "channel": "general", "message": { "id": "5b1f7c2e-8d0a-4f5e-9a51-3c2d7e8f9a10", "user": "mist", "timestamp": 1760000000.123, "content": "Release notes are up: https://example.com/notes", "type": "message" }}The server assigns the id (a UUID) and the timestamp (Unix seconds with a fractional part). user is the author’s username. A message must have at least one of text content, an embeds entry, or an attachments entry.
See the message schema for every field, including embeds, reactions, mentions, and signing fields.
Commands
Section titled “Commands”| Task | Command |
|---|---|
| Send text, files, embeds, or a poll | message_new |
| Change a message | message_edit |
| Remove a message | message_delete |
| Load older history | messages_get |
| Jump to a known message | messages_around |
| Load one message or a list of IDs | message_get, messages_batch |
| List replies to a message | message_replies |
| Search text or filter by author, mentions, and content type | messages_search |
| Pin, unpin, or list pins | message_pin, message_unpin, messages_pinned |
Every message command takes channel, thread_id, or both. For a thread message, send thread_id. If you also send channel, it must be the thread’s parent forum.
Lifecycle of a new message
Section titled “Lifecycle of a new message”When a client sends message_new, the server:
- Checks that the caller can view the channel and has its
sendpermission. In a thread, it also checks that the thread is not locked. - Validates embeds, attachments, and the optional
alias, and checks role mentions. - Applies slow mode. A slowed message gets a
rate_limitframe instead of an error. - Checks the length against
limits.post_content(2000 characters by default). - Runs automod.
- Saves the message, then sends it back to the caller and broadcasts it to everyone else who can view the channel.
- Reopens the thread if it was archived.
- Queues push notifications and link previews in the background.
The caller’s copy carries the request listener. Other connections receive the same message_new without it. Users severed from the author don’t receive it.
Replies and mentions
Section titled “Replies and mentions”Send reply_to with a message ID to reply. The stored message gets a reply_to object with the target’s ID, author, and the first 40 characters of its text. Send ping: false to reply without notifying the author.
Mentions are written in the message text:
@usernamementions a user.@&<role_id>mentions every member of a role. The sender needs the role to haveis_mentionable, or needsmention_rolesoradministrator. The server owner can mention any role. Otherwise the send fails withYou do not have permission to mention the '@&<role_id>' role.
The server records who was mentioned when it saves the message. pings_get returns that history, and unread counts include a per-conversation ping count. See read state and mentions.
Editing and deleting
Section titled “Editing and deleting”Channel permissions decide who can change a message. See roles and permissions for the defaults.
| Action | Your own message | Someone else’s message |
|---|---|---|
| Edit | edit_own |
edit |
| Delete | delete_own |
delete |
An edit sends message_edit with the complete updated message, so replace your copy. The message gets edited: true. When someone edits another user’s message without signing the edit, the server removes the author’s signature and adds edited_by.
A delete sends message_delete with only the id and the conversation. Deleting a message also deletes its attachments and its poll.
Link previews
Section titled “Link previews”When a message contains web links, the server can fetch a preview for each one and attach it as an embed after the message is saved. Clients don’t need to fetch pages themselves.
Link previews are on by default. Operators control them with embeds.enabled and embeds.proxy_url in the server configuration. When they are on, the handshake lists the server_side_embeds capability.
When the server fetches previews
Section titled “When the server fetches previews”The server queues a preview job after a message_new or message_edit sent over the WebSocket, including public slash command replies. Webhook posts and system messages don’t get previews.
For each job, the server:
- Takes up to five
httporhttpsURLs from the message text. It skips URLs inside code spans and code blocks, and URLs wrapped in angle brackets such as<https://example.com>. Trailing punctuation is trimmed. - Skips a URL if the author already supplied an embed with the same
url. - Treats URLs ending in an image, video, or audio file extension as media. These become
image,video, oraudioembeds without fetching anything. - Fetches other pages through the configured proxy, with an 8-second timeout and a 1 MiB limit. It never fetches
localhost, private network ranges, or hosts ending in.localor.internal. - Builds a
linkembed from the page’s Open Graph and Twitter tags, falling back to<title>and thedescriptionmeta tag. A page with no title, description, or image gets no preview. - Stops once the message has 10 embeds in total.
The job only runs if the message still has the same text. Jobs are dropped if the queue already holds 64 jobs.
What gets stored
Section titled “What gets stored”Generated embeds are saved into the message’s embeds array after any embeds the author supplied. Each one has generated: true:
{ "id": "3f9a1c0b7d2e4a5b6c", "type": "link", "url": "https://example.com/notes", "generated": true, "title": "Release notes", "description": "What changed in this release.", "image": { "url": "https://example.com/cover.png" }, "site_name": "Example", "footer": { "text": "Example" }, "provider": { "name": "Example" }}When a message is edited, the server rebuilds its generated embeds. Previews for links that are still present are kept without refetching. Previews for removed links are dropped.
The follow-up event
Section titled “The follow-up event”If the generated embeds change, the server sends a message_edit with the full updated message. It goes only to connections that can view the channel and have reported server_side_embeds with the capabilities command. Other clients see the previews the next time they load the message.
This message_edit is not a user edit. The message’s edited flag doesn’t change.
Sticky messages
Section titled “Sticky messages”A sticky message stays at the bottom of a text or voice channel. Moderators set it with the built-in /sticky command, and it’s stored as the channel’s sticky_message ID.
When someone posts in that channel with message_new, the server moves the sticky message to right after the new message. It marks the message edited: true and sends two events after the new message:
message_deletefor the sticky message’s ID.message_newwith the sticky message and its new timestamp.
Handle these like any other delete and create. The sticky message keeps its ID.
Pinning needs the channel’s pin permission. Each pin or unpin also posts a system message that replies to the pinned message and says who pinned it. messages_pinned lists the pins in a conversation.
Aliases
Section titled “Aliases”A message can carry an alias object that sets the name, avatar, and color shown for that one message. A bridge can use it to show who a relayed message came from. alias.name is required, up to 80 characters. The message’s user stays the real author.
Push notifications
Section titled “Push notifications”When a user sends a message with message_new, the server can send a Web Push notification through Rotur to people who aren’t looking at the app. Edits, webhook posts, slash replies, and system messages don’t trigger pushes.
The operator chooses who gets notified with notifications.mode:
mentions(default): people mentioned directly, members of mentioned roles, and the author of the message being replied to.all: every member who can view the channel.
The server never notifies the author, members who have left, banned users, users who can’t view the channel, or users severed from the author. The notification text turns mentions, channel links, custom emoji, and timestamps into readable text.
Delivery needs notifications.enabled and a Rotur token on the server. It happens in the background after the message is saved, so a successful message_new doesn’t mean a push was delivered. See notifications to register a client for pushes, and server configuration for the settings.
See also
Section titled “See also”- Message schema
- Signing for optional message proofs
- Threads and forums
