yappy docs Portal

Permissions

yappy uses a 64-bit permission bitfield, the same one for people and for bots. This page is the reference: the bits, how they combine, and the one rule that governs every check.

The rule

Authorisation is always checked against the person who invoked the action, never against the bot.

A moderation bot may hold every permission in a group. That must not let an ordinary member borrow those powers by pressing one of its buttons. When a button carries requiredPermissions, the server checks the presser's own effective permissions in that conversation and refuses with 403 if they fall short, before your bot is ever told the press happened.

On the wire

Bitfields travel as decimal strings, never as numbers:

json
{ "userId": "usr_...", "permissions": "4398046511111", "isStaff": false }

The field runs to bit 62, and a JavaScript number loses precision past 2^53, so a numeric field would silently corrupt the high bits. Parse as a big integer and test with a bitwise AND:

js
const held = BigInt(response.permissions);
const KICK_MEMBERS = 1n << 32n;

if ((held & KICK_MEMBERS) === KICK_MEMBERS) {
  // allowed
}

The bits

BitNameWhat it allows
0VIEW_CONVERSATIONSee that the conversation exists
1READ_HISTORYRead messages sent before joining
2SEND_MESSAGESPost
3SEND_MEDIAAttach images, video, files
4SEND_VOICE_NOTESPost voice notes
5SEND_STICKERSPost stickers
6SEND_GIFSPost GIFs
7SEND_POLLSCreate polls
8ADD_REACTIONSReact
9MENTION_ALLMention everyone at once
10EMBED_LINKSHave links unfurl into previews
11EDIT_OWN_MESSAGESEdit your own
12DELETE_OWN_MESSAGESDelete your own
13DELETE_ANY_MESSAGEDelete anyone's
14PIN_MESSAGESPin and unpin
20START_CALLStart a call
21JOIN_CALLJoin an ongoing call
22END_CALL_FOR_ALLEnd a call for everyone
23SCREEN_SHAREShare a screen
30INVITE_MEMBERSAdd people and bots
31MANAGE_INVITESCreate and revoke invite links
32KICK_MEMBERSRemove a member
33BAN_MEMBERSRemove and bar from returning
34MUTE_MEMBERSSilence a member
35MANAGE_ROLESCreate roles and assign them
36MANAGE_CONVERSATIONRename, re-picture, change settings
37MANAGE_STICKERSManage custom emoji and stickers
62ADMINISTRATOREverything

The gaps in the numbering are deliberate. Bits are grouped by area with room to grow, so a new media permission does not have to be allocated next to the moderation ones and make the constant list unreadable.

How a member's permissions are computed

Three layers, combined in order:

  1. The conversation's base permissions, which is what an ordinary member gets.
  2. Every role the member holds, ORed together.
  3. The member's rank: owner, admin, moderator, or member.

ADMINISTRATOR implies every other bit. Nothing implies ADMINISTRATOR.

Rank is not a permission and does not live in the bitfield. It answers a different question: not "may this action happen" but "may this person be acted upon". A member can only act on members below them, so holding KICK_MEMBERS does not let you kick the owner, and two admins cannot kick each other.

Checking someone else's permissions

Before acting on a typed command such as /ban @someone, ask:

GET /v1/conversations/:id/members/:userId/permissions
json
{ "userId": "usr_...", "permissions": "4398046511111", "isStaff": false }

For a button press you do not need this call. The interaction.pressed webhook already carries the invoker's permissions and isStaff.

Gating your own commands

Declare a command with the bits it needs and the composer stops offering it to members who lack them:

json
{
  "commands": [
    { "name": "ban", "description": "Ban a member", "requiredPermissions": "4294967296" }
  ]
}

4294967296 is 1 << 32, KICK_MEMBERS.

This is filtering, not enforcement. It keeps /ban out of the autocomplete of someone who could not use it, which is a courtesy rather than a boundary. The boundary is your own check on the way in, and the server's check on a button press. Use all three.