Skip to content

Message schema

A message object is one stored chat message. Message commands and events carry it in message, and history commands return lists of them. Only id, type, user, author_id, content, and timestamp are always present. Every other field appears only when it applies.

{
"id": "b2d4f6a8-1c3e-4a5b-8d7f-0e9c1b2a3d4e",
"type": "message",
"user": "bob",
"author_id": "USR:local_bob",
"content": "Agreed, @alice. Shipping it.",
"timestamp": 1788177600.412,
"reply_to": {
"id": "9f3c2a1e-6b1d-4f7a-9c2e-3d4b5a6c7e8f",
"user": "alice",
"preview": "Should we ship the new theme picker to"
},
"ping": true,
"pings": { "users": ["alice"], "replies": ["alice"] },
"reactions": { "👍": ["alice", "carol"] },
"edited": true
}
interface Message {
id: string;
type: "message";
user: string;
author_id: string;
content: string;
timestamp: number;
thread_id?: string;
ephemeral?: boolean;
edited?: boolean;
edited_at?: number;
edited_by?: string;
pinned?: boolean;
reply_to?: { id: string; user: string; preview: string };
ping?: boolean;
pings?: { users?: string[]; roles?: string[]; replies?: string[] };
reactions?: Record<string, string[]>;
embeds?: Embed[];
attachments?: Attachment[];
alias?: { name: string; avatar?: string; usercolor?: string };
webhook?: { id: string; name: string; avatar: string | null; usercolor?: string };
interaction?: Interaction;
signed_at?: number;
key_id?: string;
signature?: string;
}
interface Interaction {
username: string;
command: string;
args?: Record<string, unknown>;
author_id?: string;
key_id?: string;
signature?: string;
timestamp?: number;
nonce?: string;
}
Field Description
id The message’s ID, a UUID assigned by the server.
type Always "message".
user The author’s username. Messages from webhooks, system announcements, and the server itself use originChats.
author_id The author’s user ID. For a signed message, the Rotur ID in the proof. System and webhook messages use originChats.
content The message text. Can be "" when the message has embeds or attachments.
timestamp When the server saved the message, in Unix seconds with a fraction.
thread_id The thread the message belongs to, for messages in forum threads.
ephemeral true on a private slash command reply that only the invoker sees. Ephemeral messages aren’t stored.
Field Description
edited true once the message has been edited. Link previews added by the server don’t set it.
edited_at When a signed edit happened, in Unix seconds. Only signed edits set it.
edited_by User ID of the moderator who changed someone else’s signed content. The old signature is removed at the same time.
pinned true while the message is pinned. It can be false after an unpin.
Field Description
reply_to The message this one replies to: its id, its author’s username in user, and the first 40 characters of its text in preview.
ping Present with reply_to. false when the reply was sent with ping: false, so the replied-to author isn’t notified.
pings.users Usernames mentioned with @name.
pings.roles IDs of roles mentioned, when the author was allowed to mention them.
pings.replies The replied-to author’s username, when the reply pings them.

pings is left out when nothing is mentioned. Clients use it for mention highlights and notifications.

reactions maps each emoji to the usernames that reacted with it. Custom emoji keys use the form originChats:<emoji>//<server>/<id>. The field is left out when there are none. See reactions.

Field Description
webhook Set on messages posted through a webhook: the webhook id, and the name, avatar, and optional usercolor to show for this message.
alias A display override the sender chose for this message: name (up to 80 characters), and optional avatar URL (up to 2048) and usercolor (up to 64). Any sender can set it on message_new or message_edit. user still names the real author, so show both.
interaction Set on slash command replies: the command and the invoking user’s username. For signed calls it also has the invoker’s args and proof fields.

Signed messages carry author_id, key_id, signature, and signed_at. The server checks their format but never verifies the signature, so verify it yourself before showing a message as signed. author_id is present on every message, so its presence alone doesn’t mean the message is signed.

Embeds can carry their own author_id, key_id, and signature. They are signed separately from the message.

attachments holds attachment objects. For uploads the server knows, it replaces what the client sent with its stored record.

An embed is a rich block shown with a message, such as a link preview, a card, or a poll.

{
"type": "rich",
"title": "Release 2.4",
"url": "https://example.com/changelog/2.4",
"description": "Faster search and a new theme picker.",
"color": 6641909,
"author": { "name": "Release bot", "icon_url": "https://example.com/bot.png" },
"fields": [
{ "name": "Downloads", "value": "[Windows](https://example.com/win) · [macOS](https://example.com/mac)", "inline": true }
],
"image": { "url": "https://example.com/banner.png", "width": 1200, "height": 630 },
"footer": { "text": "Posted automatically" }
}
Field Type Limits and notes
type string rich (default), poll, link, image, video, audio, or article.
id string Up to 64 characters.
title string Up to 256 characters.
description string Up to 4096 characters.
url string Link for the title. Required for link embeds.
canonical_url, timestamp string Free text. timestamp is usually an ISO 8601 date.
site_name string Up to 2048 characters.
html string Up to 8192 characters.
color number A colour from 0 to 16777215 (0xFFFFFF).
markdown boolean Whether to render text as Markdown.
generated boolean true on link previews the server created.
metadata object Free-form data.
author object name (required, up to 256), url, icon_url, proxy_icon_url.
footer object text (required, up to 2048), icon_url, proxy_icon_url.
provider object name (up to 256), url.
image, thumbnail, video, audio media See below. An image, video, or audio embed requires the matching object.
images media[] Up to 10 items.
fields object[] Up to 25 items, each with name (up to 256), value (up to 1024), and optional inline.
poll object Required for poll embeds. See polls.
author_id, key_id, signature string Embed signature. Up to 128, 256, and 100 characters.

A media object needs url. It can also have proxy_url, placeholder, content_type (up to 128), alt (up to 1024), html (up to 8192), and the numbers width, height, placeholder_version, and flags.

Across a whole message:

  • At most 10 embeds, and at most one of them a poll.
  • Each embed’s visible text must total 6000 characters or fewer. That counts the title, description, field names and values, author name, footer text, provider name, and poll question and options.

Validation errors name the embed, for example Embed 2: fields cannot contain more than 25 items.

To create a poll, send message_new with an embed of type: "poll" and the poll input in poll. The polls concept page describes the input.

In the stored message, the server replaces that input with the live poll, personal to each viewer:

Field Description
id, message_id, channel, thread_id Where the poll lives.
question, options The question and its options. The embed’s title is set to the question.
allow_multiselect Whether voters can pick several options.
created_by Username of the poll’s creator.
created_at, expires_at, ended, ended_at Timing and state.
total_votes Number of votes cast across all options.
results One entry per option, with votes, the voters usernames, and voted, whether you picked it.
user_votes Option IDs you voted for.