Message β
A bot reads and writes messages as itself, with the permissions of its role in the channel. All calls need a bot token.
Message object β
| Field | Type | Description |
|---|---|---|
id | UUID | |
seq | integer | The order number of the message across the whole service (what history pages by) |
channel_id | UUID | |
server_id | UUID | |
author | user | id, username, display_name, avatar, bot |
content | string | The text, up to 4000 characters. Empty when withheld |
timestamp | time | |
edited_timestamp | ?time | |
type | integer | 0 a message, 19 a reply, 20 the answer to a slash command, 21 the answer to a button or a menu |
kind | string | TEXT, SYSTEM (the server says something: someone joined), POLL or GIVEAWAY |
pinned | boolean | |
mention_everyone | boolean | |
mentions | array of strings | The usernames the text names |
mention_roles | array of UUIDs | |
reactions | array | { emoji, count, me } |
embeds | ?array | Embeds |
components | ?array | Rows of components |
attachments | array | { id, filename, content_type, size }. Downloading a file a member attached is not part of the App API yet |
flags | integer | Message flags |
application_id | ?UUID | The application whose bot wrote it, or answered with it |
referenced_message_id | ?UUID | The message this one replies to |
interaction | ?object | { type, name, user } when a member caused the message: type is command, component or modal |
content_withheld | boolean | true when the bot has no message content intent and this message is not one it may read |
A ? before a type marks a field that is left out when it has no value; the API never sends null.
Example messageJSON
json
{
"id": "9b2f6c1e-7d4a-4e83-a1b5-3c0d8e5f7a21",
"seq": 18342,
"channel_id": "2d7c4a90-1b3e-4f58-9a6c-8e0b5d3c7f14",
"server_id": "5f1d2b8e-7a64-4d07-8d3a-0b9f6c4e2a11",
"author": {
"id": "b2a40959-d70f-4cd2-892f-1c037515d8fa",
"username": "dice_roller",
"display_name": "Dice Roller",
"bot": true
},
"content": "Rolled **4**",
"timestamp": "2026-11-03T18:21:07.412Z",
"type": 0,
"kind": "TEXT",
"pinned": false,
"mention_everyone": false,
"mentions": [],
"mention_roles": [],
"reactions": [{ "emoji": "π²", "count": 2, "me": false }],
"embeds": [{ "title": "d6", "color": 3145582 }],
"attachments": [],
"flags": 0,
"application_id": "0f6c1a52-3b9e-4c1c-9d1b-6e7d2a8f4b10",
"content_withheld": false
}Mentions β
Mentions are written in the text the way members write them in Moymo for Windows:
| Write | To mention |
|---|---|
<@username> | A member (3 to 24 lowercase letters, digits and underscores) |
<@&role-id> | A role (it has to be mentionable, or the bot needs Mention Everyone) |
@everyone, @here | Everybody (needs Mention Everyone) |
<#channel-id> | A channel |
Message flags β
| Flag | Value | |
|---|---|---|
SUPPRESS_EMBEDS | 1 << 2 (4) | Hide the cards made for links |
EPHEMERAL | 1 << 6 (64) | Only for interaction responses: only the member who acted sees it. Never stored |
LOADING | 1 << 7 (128) | A deferred response that has not been written yet: "App is thinkingβ¦". Set by Moymo |
SUPPRESS_NOTIFICATIONS | 1 << 12 (4096) | Make no notification for the message |
A bot may set SUPPRESS_EMBEDS and SUPPRESS_NOTIFICATIONS when it writes a message; the others are set by Moymo. Flags are stored and returned; clients do not act on SUPPRESS_EMBEDS and SUPPRESS_NOTIFICATIONS yet.
Allowed mentions β
allowed_mentions says whom the text may really ping. Without it a bot's message may ping everybody its text names (as a person's would); an interaction response may ping members only.
| Field | Description |
|---|---|
parse | Any of "users", "roles", "everyone": the kinds that may be pinged |
users | Up to 100 member ids that may be pinged (do not also parse users) |
roles | Up to 100 role ids that may be pinged (do not also parse roles) |
An empty object, {}, pings nobody: the text still shows the names, and nobody is notified.
Ping nobody, but still show the namesJSON
json
{
"content": "Thanks <@ada> and <@&0f6c1a52-3b9e-4c1c-9d1b-6e7d2a8f4b10>!",
"allowed_mentions": {}
}Embed object β
An embed is a formatted card inside a message. The shape and the limits are Discord's.
| Field | Type | Limit and description |
|---|---|---|
title | string | 256 characters |
description | string | 4096 characters |
url | string | https address the title links to |
timestamp | string | ISO 8601 time, shown in the footer |
color | integer | 0 to 16777215 (0xFFFFFF): the stripe at the edge |
footer | object | { text, icon_url }: text up to 2048 characters |
image | object | { url } |
thumbnail | object | { url } |
author | object | { name, url, icon_url }: name up to 256 characters |
fields | array | Up to 25 of { name, value, inline }: name 256, value 1024 characters |
A message has up to 10 embeds, and all the text in them (titles, descriptions, field names and values, footers, author names) is at most 6000 characters together. An embed with nothing in it is refused. A key this list does not name (type, video, provider) is ignored.
Every address is https, without a user name in it, and on a host name: pictures are loaded by each member's own program from your server, which is why nothing else is accepted.
Example embedJSON
json
{
"title": "Hades",
"description": "A roguelike about escaping the Underworld.",
"url": "https://moymo.net/games/hades",
"color": 3145582,
"fields": [
{ "name": "Hours", "value": "42", "inline": true },
{ "name": "Rating", "value": "9.1", "inline": true }
],
"footer": { "text": "Moymo" }
}Get channel messages β
GET
/channels/{channel.id}/messages Returns a page of the channel's history, oldest first.
| Query | Description |
|---|---|
limit | 1 to 50, default 50 |
before | A message id: the messages before it |
after | A message id: the messages after it |
Give before or after, not both. Without READ_HISTORY the answer is empty. Messages in forum posts are not in this list.
RequestGET
/channels/{channel.id}/messages bash
curl "https://moymo.net/api/app/v1/channels/$CHANNEL_ID/messages?limit=2" \
-H "Authorization: Bot $MOYMO_BOT_TOKEN"js
const response = await fetch(`https://moymo.net/api/app/v1/channels/${channelId}/messages?limit=2`, {
headers: { Authorization: `Bot ${process.env.MOYMO_BOT_TOKEN}` }
})
const messages = await response.json()python
import os
import requests
response = requests.get(
f"https://moymo.net/api/app/v1/channels/{channel_id}/messages",
params={"limit": 2},
headers={"Authorization": f"Bot {os.environ['MOYMO_BOT_TOKEN']}"},
)
messages = response.json()Response200OKJSON
json
[
{
"id": "4c8e1a7b-2f90-4d36-b5e1-7a3c9d0f6e82",
"seq": 18340,
"channel_id": "2d7c4a90-1b3e-4f58-9a6c-8e0b5d3c7f14",
"server_id": "5f1d2b8e-7a64-4d07-8d3a-0b9f6c4e2a11",
"author": {
"id": "1f0c8d64-2a37-4f5b-9a0e-7c4d3b2e1a90",
"username": "ada",
"display_name": "Ada",
"bot": false
},
"content": "Anyone up for a game tonight?",
"timestamp": "2026-11-03T18:19:42.108Z",
"type": 0,
"kind": "TEXT",
"pinned": false,
"mention_everyone": false,
"mentions": [],
"mention_roles": [],
"reactions": [{ "emoji": "π", "count": 3, "me": false }],
"attachments": [],
"flags": 0,
"content_withheld": false
},
{
"id": "9b2f6c1e-7d4a-4e83-a1b5-3c0d8e5f7a21",
"seq": 18342,
"channel_id": "2d7c4a90-1b3e-4f58-9a6c-8e0b5d3c7f14",
"server_id": "5f1d2b8e-7a64-4d07-8d3a-0b9f6c4e2a11",
"author": {
"id": "b2a40959-d70f-4cd2-892f-1c037515d8fa",
"username": "dice_roller",
"display_name": "Dice Roller",
"bot": true
},
"content": "Rolled **4**",
"timestamp": "2026-11-03T18:21:07.412Z",
"type": 0,
"kind": "TEXT",
"pinned": false,
"mention_everyone": false,
"mentions": [],
"mention_roles": [],
"reactions": [],
"attachments": [],
"flags": 0,
"application_id": "0f6c1a52-3b9e-4c1c-9d1b-6e7d2a8f4b10",
"content_withheld": false
}
]Get channel message β
GET
/channels/{channel.id}/messages/{message.id} A message is only found through the channel it is in. Returns a message object.
Create message β
POST
/channels/{channel.id}/messages Needs Send Messages in the channel. At least one of content, embeds, components or game is required. Returns the message that was written.
| Field | Type | Description |
|---|---|---|
content | string | Up to 4000 characters |
embeds | array | Up to 10 embeds |
components | array | Up to 5 rows of components |
allowed_mentions | object | Whom the text may ping |
reply_to | UUID | The message this one answers. Discord's message_reference: { message_id } works too. Needs Read History |
flags | integer | SUPPRESS_EMBEDS, SUPPRESS_NOTIFICATIONS |
nonce | string or number | Up to 64 characters of letters, digits and _ - . :. A message with a nonce the bot used before is not written again: the earlier one is returned, so a send that was cut off can be tried again safely |
game | string | The slug of a game on Moymo, shown as a card (needs Embed Games) |
Fields that are not listed (Discord's tts, sticker_ids, poll) are ignored.
RequestPOST
/channels/{channel.id}/messages bash
curl -X POST "https://moymo.net/api/app/v1/channels/$CHANNEL_ID/messages" \
-H "Authorization: Bot $MOYMO_BOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"content": "Rolled **4**",
"embeds": [{ "title": "d6", "color": 3145582 }],
"components": [{
"type": 1,
"components": [{
"type": 2, "style": 1,
"label": "Roll again", "custom_id": "again"
}]
}]
}'js
const response = await fetch(`https://moymo.net/api/app/v1/channels/${channelId}/messages`, {
method: 'POST',
headers: {
Authorization: `Bot ${process.env.MOYMO_BOT_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: 'Rolled **4**',
embeds: [{ title: 'd6', color: 3145582 }],
components: [{
type: 1,
components: [{
type: 2, style: 1,
label: 'Roll again', custom_id: 'again'
}]
}]
})
})
const message = await response.json()python
import os
import requests
response = requests.post(
f"https://moymo.net/api/app/v1/channels/{channel_id}/messages",
headers={"Authorization": f"Bot {os.environ['MOYMO_BOT_TOKEN']}"},
json={
"content": "Rolled **4**",
"embeds": [{"title": "d6", "color": 3145582}],
"components": [{
"type": 1,
"components": [{
"type": 2, "style": 1,
"label": "Roll again", "custom_id": "again",
}],
}],
},
)
message = response.json()Response200OKJSON
json
{
"id": "9b2f6c1e-7d4a-4e83-a1b5-3c0d8e5f7a21",
"seq": 18342,
"channel_id": "2d7c4a90-1b3e-4f58-9a6c-8e0b5d3c7f14",
"server_id": "5f1d2b8e-7a64-4d07-8d3a-0b9f6c4e2a11",
"author": {
"id": "b2a40959-d70f-4cd2-892f-1c037515d8fa",
"username": "dice_roller",
"display_name": "Dice Roller",
"bot": true
},
"content": "Rolled **4**",
"timestamp": "2026-11-03T18:21:07.412Z",
"type": 0,
"kind": "TEXT",
"pinned": false,
"mention_everyone": false,
"mentions": [],
"mention_roles": [],
"reactions": [],
"embeds": [{ "title": "d6", "color": 3145582 }],
"components": [{
"type": 1,
"components": [{
"type": 2,
"style": 1,
"label": "Roll again",
"custom_id": "again"
}]
}],
"attachments": [],
"flags": 0,
"application_id": "0f6c1a52-3b9e-4c1c-9d1b-6e7d2a8f4b10",
"content_withheld": false
}Mentions ping people
A bot's message pings the members and roles its text names, as a person's would (@everyone and roles that are not mentionable need Mention Everyone). Send allowed_mentions whenever the text comes from somebody else (a member's input, a web page), so nobody can make the bot ping a whole server.
Edit message β
PATCH
/channels/{channel.id}/messages/{message.id} Changes a message the bot wrote. Fields you leave out stay as they were; embeds: [] or components: [] removes them. A message must keep some content, embeds, components, a file or a game. Returns the changed message.
RequestPATCH
/channels/{channel.id}/messages/ Shellbash
curl -X PATCH "https://moymo.net/api/app/v1/channels/$CHANNEL_ID/messages/$MESSAGE_ID" \
-H "Authorization: Bot $MOYMO_BOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "content": "Rolled **6**", "components": [] }'Delete message β
DELETE
/channels/{channel.id}/messages/{message.id} The bot's own message, or any message with Manage Messages.
Response204No Content
Create reaction β
PUT
/channels/{channel.id}/messages/{message.id}/reactions/{emoji}/@me Needs Add Reactions. {emoji} is a unicode emoji (URL-encoded) or one of the server's own, written as :name~code:. A message holds up to 20 different reactions.
RequestPUT
/channels/{channel.id}/messages/{message.id}/reactions/{emoji}/@me Shellbash
# π², URL-encoded
curl -X PUT \
"https://moymo.net/api/app/v1/channels/$CHANNEL_ID/messages/$MESSAGE_ID/reactions/%F0%9F%8E%B2/@me" \
-H "Authorization: Bot $MOYMO_BOT_TOKEN"Response204No Content
Delete own reaction β
DELETE
/channels/{channel.id}/messages/{message.id}/reactions/{emoji}/@me Takes the bot's reaction back. 204.
Pins β
GET
/channels/{channel.id}/pins PUT
/channels/{channel.id}/pins/{message.id} DELETE
/channels/{channel.id}/pins/{message.id} Pinning needs Manage Messages. A channel has up to 50 pinned messages. The list is an array of messages; pinning and unpinning answer 204.