Skip to content

Reactions, polls & unreads

This page covers three features that sit on top of messages: reactions, polls, and tracking what each user has read.

A reaction is an emoji that a user attaches to a message. Add one with reaction_add and remove your own with reaction_remove. Both need the channel’s react permission.

A message stores its reactions as a map from emoji to the usernames who used it:

{
"reactions": {
"👍": ["mist", "alice"],
"originChats:<emoji>//chat.example.com/4": ["alice"]
}
}
  • The emoji is a Unicode emoji or a custom emoji reference.
  • A message can have up to limits.max_reactions_per_message different emoji, 20 by default. Adding a new one past that fails.
  • Each change is broadcast to everyone who can view the channel.

A poll is a special embed in a message. Create one by sending a poll embed with message_new:

{
"cmd": "message_new",
"channel": "general",
"embeds": [
{
"type": "poll",
"poll": {
"question": "When should we meet?",
"options": [{ "text": "Monday" }, { "text": "Tuesday", "emoji": "📅" }],
"allow_multiselect": false,
"duration_hours": 24
}
}
]
}

Poll rules:

  • question is required, up to 300 characters.
  • options has 2 to 10 entries. Each needs text of up to 100 characters and can have an emoji. The server gives them IDs "0", "1", and so on.
  • allow_multiselect lets a voter pick more than one option. It defaults to false.
  • To set an end time, send expires_at in Unix seconds or a positive duration_hours. If you send both, expires_at wins. Leave both out for a poll that runs until someone ends it.
  • A message can hold only one poll, and message_edit can’t add, change, or remove it.

The saved message’s embed holds the full poll, including its id. Use that ID with the poll commands:

Task Command
Vote poll_vote
Read the current results poll_get
End the poll early poll_end

A vote replaces the voter’s previous choice. In a multiselect poll, sending a single option_id toggles that option on or off, and sending option_ids replaces the whole selection.

Each vote broadcasts poll_vote with the updated results to everyone who can view the channel. Each result has the option’s votes count and its voters as usernames.

total_votes counts selected options, not people. In a multiselect poll, one person can add more than one vote. Broadcast results don’t say which options the receiving user picked. Use poll_get to read the caller’s own user_votes.

A poll ends when:

  • Its creator, or someone with the channel’s pin permission, sends poll_end. This broadcasts poll_end with the final results.
  • Its end time passes. The server marks it ended the next time the poll is read or voted on. No event is sent at the end time, so clients should compare expires_at with the current time themselves.

Nobody can vote once a poll has ended. Deleting the message deletes its poll.

The server remembers, for each user and each conversation, the time of the last message they’ve read. Unread counts come from that time.

unreads_get returns a summary for every text channel, voice channel, and forum thread the caller can view:

{
"general": {
"last_viewed": 1760000000.123,
"unread_count": 3,
"ping_count": 1,
"total_messages": 482
}
}
  • unread_count counts newer messages from other people.
  • ping_count counts messages that mentioned or replied to the caller since they last read the conversation.
  • Thread entries use the key thread/<thread_id> and include parent_channel.

unreads_ack marks a conversation as read, up to a given message or up to the newest one. It resets ping_count to zero. The server sends unreads_update to all of the user’s connections, so their other devices can clear the badge too.

pings_get returns the messages that mentioned or replied to the caller, newest first. The server works out who a message mentions when it saves the message, so later role changes don’t rewrite old mention history. Reading this list doesn’t mark anything as read.

See replies and mentions for how mentions are written.