Developers
Developer docs
Everything you need to build a bot or app for Grumble, from your first token to every endpoint the server answers.
1. How it fits together
An application is your project. It can have a bot, which is a Grumble account your code logs in as with a token. Your bot talks to Grumble in two ways:
- the REST API, to do things like sending a message, creating a channel or registering a command;
- the gateway, a WebSocket that tells your bot what's happening as it happens, like new messages, people joining and commands being used.
Grumble's API is compatible with Discord's API version 10, with the same endpoints, request bodies, responses and gateway events. That's deliberate, so the libraries and bots people already have keep working. Where Grumble behaves differently, this page says so.
You manage your applications in the Developer Portal. It's in the Grumble app under Settings → Developers → Bots and apps, and on the web at developers.grumble.gg (another Grumble server has it at /developers/applications).
2. Your first bot in five minutes
- Open the Developer Portal and create an application. Give it a name.
- On Bot, choose Add a bot and copy the token. It's shown once.
- On Installation, pick one of your servers and add the bot to it.
- Point your library at Grumble and log in with the token. Examples for discord.js and discord.py are on the developers page.
Your bot then shows up in the server's member list and starts getting events.
3. Tokens
Every request carries the bot's token in the Authorization header, as Bot <token>. The gateway takes the same token when your bot identifies.
- Anyone with the token can act as your bot. Keep it out of code you share, and out of git.
- If it leaks, reset it on the Bot page. The old one stops working at once.
- Only bot accounts may be automated. Running scripts on a person's account isn't allowed (see the Developer Terms).
4. The REST API
- Base address
https://<your Grumble server>/api/v10. Versions 6 to 10 are all accepted and behave the same.- Format
- JSON in and out, UTF-8. File uploads are
multipart/form-datawith apayload_jsonpart. - IDs
- Snowflakes: 64-bit numbers sent as strings. The time something was made is in its ID.
- Errors
- A JSON body with a numeric
code, amessage, and for invalid fields anerrorsobject that says which field and why. - Rate limits
- Each response carries
X-RateLimit-Limit,-Remaining,-Reset,-Reset-Afterand-Bucket. Go over and you get a429withretry_afterin seconds. Wait that long, then try again. Libraries do this for you. - Closed places
- Grumble isn't offered in the UK, Brazil, Turkey, Mississippi or Tennessee. Requests from there get
451with code290006, and the gateway refuses to connect, so host your bot somewhere else. - Safety refusals
- An upload of a file on the list of known illegal material is refused with
400/290002, and a message search for child sexual abuse material with400/290008.
5. The gateway
Ask GET /gateway/bot where to connect, then open a WebSocket there with ?v=10&encoding=json.
- The server says hello (opcode 10) with a
heartbeat_intervalin milliseconds. - Send a heartbeat (opcode 1) every interval. The server answers each one (opcode 11).
- Identify (opcode 2) with your token and the intents you want, meaning which kinds of events to receive.
- You get
READY, then aGUILD_CREATEfor each server your bot is in, then events as they happen. - If the connection drops, resume (opcode 6) with your session ID and the last sequence number, and you get what you missed.
Intents
| Intent | Bit | For |
|---|---|---|
| GUILDS | 1 << 0 | servers, channels, roles, threads |
| GUILD_MEMBERS privileged | 1 << 1 | people joining, leaving and changing |
| GUILD_MODERATION | 1 << 2 | bans and the audit log |
| GUILD_EXPRESSIONS | 1 << 3 | emoji, stickers and soundboard sounds |
| GUILD_WEBHOOKS | 1 << 5 | webhook changes |
| GUILD_INVITES | 1 << 6 | invites made and deleted |
| GUILD_VOICE_STATES | 1 << 7 | who's in which voice channel |
| GUILD_PRESENCES privileged | 1 << 8 | online status and activities |
| GUILD_MESSAGES | 1 << 9 | messages in servers |
| GUILD_MESSAGE_REACTIONS | 1 << 10 | reactions in servers |
| GUILD_MESSAGE_TYPING | 1 << 11 | typing in servers |
| MESSAGE_CONTENT privileged | 1 << 15 | the text, files and embeds of messages that don't mention your bot |
| GUILD_SCHEDULED_EVENTS | 1 << 16 | events and who's interested |
| GUILD_MESSAGE_POLLS | 1 << 24 | poll votes |
A server can require its operator to switch privileged intents on for your app first. Asking for one you don't have closes the connection with code 4014.
Close codes
4004 bad token · 4008 too many requests · 4009 session timed out (identify again) · 4012 unknown API version · 4013 unknown intents · 4014 intents not allowed. For anything else, reconnect and resume.
6. Slash commands, buttons and forms
Register commands with PUT /applications/{application_id}/commands (everywhere your bot is) or PUT /applications/{application_id}/guilds/{guild_id}/commands (one server, available at once). Each has a name, a description and typed options.
When someone uses one, your bot gets an INTERACTION_CREATE event over the gateway. Answer it with POST /interactions/{id}/{token}/callback:
- 4: reply with a message straight away;
- 5: "thinking…", then send the real reply within 15 minutes with
PATCH /webhooks/{application_id}/{token}/messages/@original, plus follow-ups withPOST /webhooks/{application_id}/{token}; - 6 and 7: for a button or menu click, acknowledge it or update the message it's on;
- 8: suggestions while someone types an option (autocomplete);
- 9: open a pop-up form. What people fill in comes back as another interaction.
Answer within 3 seconds, or use type 5 to buy time.
Over HTTP instead: give your app an Interactions endpoint URL (Developer Portal, General) and its interactions are POSTed there instead of the gateway, signed: check X-Signature-Ed25519 over X-Signature-Timestamp followed by the body, with your app's public key, and answer 401 when it doesn't match. Answer each in the response with the callback's body ({"type": 4, "data": {...}}). Saving the address sends a PING (type 1) to answer with {"type": 1}. Libraries made for Discord's HTTP interactions work as they are.
7. Webhooks
A webhook posts into one channel from anywhere, with no bot and no gateway, just its URL. Create one in a channel's settings or with POST /channels/{channel_id}/webhooks, then POST /webhooks/{id}/{token} with your message. Add ?wait=true to get the message back. In a forum channel, give a thread_name and each post starts a new thread.
Other services' formats work too, as on Discord: add /slack to the URL for anything that posts to a Slack incoming webhook (text, attachments and blocks), or /github and give it to a GitHub repository as its webhook (content type application/json): pushes, pull requests, issues, comments, releases, stars and failed or passed checks each post a card.
8. Adding a bot to a server
Share an install link (the Developer Portal makes one for you):
https://<your Grumble server>/oauth2/authorize?client_id=<application id>&scope=bot+applications.commands&permissions=<number>
Whoever opens it picks one of their servers (they need Manage Server there) and sees exactly what your bot asks to do. permissions is the sum of the permission bits your bot needs. Ask for no more than it uses. Only you can add a bot that isn't marked public.
To let people find it, list it in the App Directory (its page in the Developer Portal). It then shows under Discover, Apps, where anyone can add it to a server they manage. It needs a public bot, a description and an icon. The directory is sorted by how many servers use each app, the same for everyone.
9. Log in with Grumble
Your website can let people sign in with their Grumble account. It works like Discord's OAuth2 (the authorization code grant), so libraries made for that work once you give them Grumble's addresses.
- On OAuth2 in the Developer Portal, add your redirect (https, or http on localhost while you try it out) and choose Reset secret to get a client secret. It's shown once.
- Send people to the sign-in link. The portal makes one for you:
Grumble shows them what you'll see and asks. Then it sends them to your redirect withhttps://<your Grumble server>/oauth2/authorize?client_id=<application id>&response_type=code&redirect_uri=<your redirect>&scope=identify+guilds&state=<random>?code=…&state=…, or?error=access_deniedif they say no. Checkstateis the one you sent. - From your server, swap the code for tokens within ten minutes. A code works once.
You getPOST https://<your Grumble server>/api/v10/oauth2/token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code&code=<code>&redirect_uri=<your redirect>&client_id=<application id>&client_secret=<secret>access_token(good for a week),refresh_token(30 days),expires_inandscope. HTTP Basic with the client ID and secret works too. - Call the API with
Authorization: Bearer <access_token>.
identifyGET /users/@me: their ID, username, display name, picture and banner. Never an email address or age.guildsGET /users/@me/guilds: the servers they're in.
Refresh with grant_type=refresh_token&refresh_token=…, which replaces both tokens. Revoke with POST /oauth2/token/revoke and token=…. GET /oauth2/@me tells you which application a token is for and what it may see.
A website's token only reads what its scopes allow. It can't send messages, read anything else or change anything. Other scopes Discord has, like email and guilds.join, aren't offered, and asking for one is an error. People can take access back any time in Settings, Authorised apps, and the token stops working at once.
10. Teams, testers, emojis, Rich Presence, apps on this computer, Activities and events
- Teams
- Look after apps with other people. Make a team on Teams in the Developer Portal, invite people by username, and hand an app to the team from its General page. Admins can do everything; developers change apps but can't delete them or reset their secrets; read only members just look. People join once they accept, from their Applications page.
- App testers
- People who may add your bot while it isn't public. Invite them on App Testers; they accept from their Applications page.
- App emojis
- Up to 2000 emojis that belong to your app, which its bot can use in any server or DM:
GETandPOST /applications/{id}/emojis, thenPATCHorDELETE /applications/{id}/emojis/{emoji_id}, as Discord's. Your bot's own token works for these. - Rich Presence
- Games show what's happening in them through the desktop app, which answers on the same local connection as Discord's (
discord-ipc-0to-9), so the Discord Game SDK and discord-rpc libraries work unchanged. Use your application ID as the client ID, and upload pictures on Rich Presence;large_imageandsmall_imagetake their keys. Pictures given as web addresses aren't shown, because Grumble doesn't fetch other sites' images. - Apps on this computer (RPC)
- Stream decks, overlays and tools of your own talk to the desktop app over the same local connection, with Discord's RPC commands, so libraries such as discord-rpc work unchanged. Send
AUTHORIZEwith your application ID and the scopes you need; Grumble asks the person in its window, and answers with a code you swap atPOST /oauth2/tokenwith your client secret (a redirect_uri is optional). ThenAUTHENTICATEwith the access token. The scopes:rpc(GET_GUILDS,GET_GUILD,GET_CHANNELS,GET_CHANNEL,SELECT_TEXT_CHANNEL,SELECT_VOICE_CHANNEL,GET_SELECTED_VOICE_CHANNEL, voice settings, and the voice events),messages.read(messages inGET_CHANNEL, andMESSAGE_CREATE,MESSAGE_UPDATEandMESSAGE_DELETE),rpc.voice.read,rpc.voice.writeandrpc.notifications.read(NOTIFICATION_CREATE). Messages come as the person sees them, encrypted ones opened, so they're told so before they agree. These tokens work only on the person's own computer, never on the REST API, and they can take one back in Settings, Authorised apps, which shuts the app out at once. - Event webhooks
- On Webhooks, give an https address and pick events:
APPLICATION_AUTHORIZED(your app was added to a server, or someone signed in to your website with it) andAPPLICATION_DEAUTHORIZED(someone took your website's access back). Saving sends a PING (type 0) that must be answered with 204. Every request is signed: checkX-Signature-Ed25519overX-Signature-Timestampplus the body with your app's public key, as with Discord's. - Activities
- Your app running inside a call, for everyone in it. On Activities, give the address it runs from and turn it on: you, your team and your testers can start it at once, everyone else once it's in the App Directory. Grumble opens your address in a frame (no proxy, so your site sees each player's internet address) and talks to it the way Discord's Embedded App SDK does, so activities made with that SDK work. Sign-in offers
identifyandguilds, and the code is swapped atPOST /oauth2/tokenwith no redirect_uri. Which activity a call is running is only said between the people in it, so there's no activity-instances endpoint; ask the SDK who's playing. Purchases, invites and image uploads answer with an error. - Embed tester
- Paste a message or embeds as JSON on Embed tester to see how they look in Grumble and whether they're within the limits, before your bot sends them.
11. What's different on Grumble
- Direct messages are end-to-end encrypted. The server only carries sealed messages it can't read, so bots can't send or read DMs. Talk to people in servers, or reply to their commands privately (ephemeral replies, flag 64).
- Servers are end-to-end encrypted too. Their messages, files, channel and role names, and the options people type into commands reach a bot still encrypted. The Grumble bridge, a small program that runs next to your bot, holds its keys and serves it this same API decrypted. Run it with
npx grumble-bridge. - Voice goes through the bridge. Calls are end-to-end encrypted and use their own media system. The bridge speaks Discord's voice to your bot (so voice libraries made for Discord work) and joins the call encrypted, like everyone's app.
- Accounts carry less. There's no email, phone number or locale on anyone, bots included, because Grumble doesn't collect them.
- Safety rules apply to bots too. Bots count as adults:
- they can't open age-restricted channels in ways that reach under-18s;
- they can't start conversations with people under 16, unless the under-16 has said a parent or carer agrees;
- they must follow the Community Rules.
- Nothing is paid. There are no premium tiers, entitlements or store items. Every limit is the same for everyone.
12. Libraries
Any library written for Discord's API version 10 should work. Change its API address to your Grumble server and, if it doesn't ask the server for the gateway address, its gateway address too.
- discord.js: set
rest: {api: "https://<server>/api"}. It asks for the gateway itself. - discord.py: set
discord.http.Route.BASEanddiscord.gateway.DiscordWebSocket.DEFAULT_GATEWAY. Set both, or it connects elsewhere. - serenity: give
HttpBuilderthe server as itsproxy, with its rate limiter off. - JDA: set the
RestConfigbase URL, and a session controller giving the gateway's address. Set both. - Discord.Net: give it a
RestClientProviderwith the server's API address. It asks for the gateway itself.
Our bot sweep tests each of them with the everyday things bots do, directly and through the bridge: how each one did. Library names belong to their authors. Grumble isn't affiliated with Discord Inc., and Discord is a trademark of Discord Inc.
13. Every endpoint
The 364 endpoints this version of the server answers, made from its own route table. Paths are relative to /api/v10. Ones marked Grumble are ours. The rest follow Discord's API version 10.
Other
| GET | / |
| GET | / |
| GET | / |
| GET | / |
| GET | / |
| GET | / |
| GET | / |
| POST | / |
| POST | / |
| GET | / |
| POST | / |
| POST | / |
| POST | / |
| POST | / |
| POST | / |
| POST | / |
| POST | / |
| POST | / |
| POST | / |
| GET | / |
| POST | / |
| GET | / |
| GET | / |
| POST | / |
| POST | / |
| GET PATCH DELETE | / |
| POST | / |
| POST | / |
| GET | / |
| GET | / |
| GET | / |
| GET | / |
| GET | / |
| GET | / |
| GET | / |
| GET | / |
| GET | / |
| POST | / |
| GET | / |
| GET | / |
| PUT | / |
| GET | / |
| GET | / |
| GET | / |
| GET | / |
| POST | / |
| POST | / |
| POST | / Grumble |
| POST | / Grumble |
| GET | / |
| GET | / |
| GET | / |
| GET | / |
| GET | / |
| POST | / |
| GET | / |
| POST | / |
| GET POST | / |
| GET PATCH | / |
| GET | / |
| POST | / |
| GET POST | / |
| PATCH DELETE | / |
| PUT DELETE | / |
| POST | / |
| POST | / |
| GET | / |
Applications, commands and installs
| GET POST | / |
| GET PATCH DELETE | / |
| GET PATCH | / |
| GET POST | / |
| DELETE | / |
| POST PATCH | / |
| POST | / |
| GET POST PUT | / |
| GET PATCH DELETE | / |
| POST | / |
| GET PATCH | / |
| GET POST | / |
| GET PATCH DELETE | / |
| GET PATCH | / |
| GET POST PUT | / |
| GET PATCH DELETE | / |
| GET PUT | / |
| GET | / |
| POST | / |
| GET | / |
| GET POST | / |
| DELETE | / |
| PUT DELETE | / |
| POST | / |
| GET PATCH | / |
| GET | / |
| GET | / |
| GET | / |
| GET | / |
| GET POST | / |
| POST | / |
| POST | / |
| GET | / |
| DELETE | / |
Channels and threads
| GET PATCH DELETE | / |
| POST | / |
| DELETE | / |
| POST | / |
| GET POST | / |
| PUT DELETE | / |
| PUT DELETE | / |
| GET | / |
| GET POST PUT DELETE | / |
| POST PUT DELETE | / |
| POST | / |
| GET | / |
| GET | / |
| GET | / |
| GET | / |
| PUT | / |
| GET POST | / |
Messages
| GET POST | / |
| GET PATCH DELETE | / |
| POST | / |
| PUT | / |
| POST | / |
| POST | / |
| GET | / |
| PUT DELETE | / |
| GET | / |
| GET | / |
| PUT DELETE | / |
| GET | / |
| PUT | / |
| POST | / |
| POST | / |
Emoji, stickers and sounds
| POST | / |
| GET POST | / |
| GET PATCH DELETE | / |
| GET POST | / |
| GET PATCH DELETE | / |
| GET POST | / |
| GET PATCH DELETE | / |
| GET | / |
Gateway, voice, invites and more
| GET | / |
| GET | / |
| GET | / |
| GET | / |
| GET | / |
| GET POST DELETE | / |
| GET | / |
| GET | / |
| GET | / |
Servers (guilds)
| POST | / |
| GET PATCH DELETE | / |
| GET | / |
| GET | / |
| GET POST | / |
| GET PATCH DELETE | / |
| GET | / |
| GET PUT DELETE | / |
| POST | / |
| GET POST PATCH | / |
| POST | / |
| GET PATCH | / |
| GET | / |
| GET POST | / |
| GET | / |
| POST | / |
| GET | / |
| GET | / |
| GET | / |
| GET PATCH DELETE | / |
| PUT DELETE | / |
| GET PUT PATCH DELETE | / |
| PATCH | / |
| GET | / |
| GET | / |
| GET | / |
| GET PATCH | / |
| GET | / |
| GET POST PATCH | / |
| GET PATCH DELETE | / |
| GET | / |
| GET POST | / |
| GET PATCH DELETE | / |
| GET | / |
| PUT DELETE | / |
| GET POST | / |
| PUT PATCH DELETE | / |
| GET | / |
| GET PATCH | / |
| GET PATCH | / |
| GET PATCH | / |
| GET | / |
| GET PATCH | / |
| GET | / |
| GET POST | / |
Interactions and webhooks
| POST | / |
| POST | / |
| GET PATCH DELETE | / |
| GET POST PATCH DELETE | / |
| GET PATCH DELETE | / |
Safety (Grumble)
| PUT | / Grumble |
| PUT | / Grumble |
| POST | / Grumble |
| GET | / Grumble |
| GET | / Grumble |
| GET | / Grumble |
| POST | / Grumble |
| GET POST | / Grumble |
| POST | / Grumble |
| POST | / Grumble |
Users and your account
| GET | / |
| GET | / Grumble |
| GET | / |
| GET PATCH | / |
| GET POST | / |
| PUT | / |
| POST | / |
| GET | / |
| POST | / |
| POST | / |
| GET PUT | / Grumble |
| GET POST | / Grumble |
| PATCH DELETE | / Grumble |
| GET | / |
| DELETE | / |
| GET PATCH | / |
| GET PATCH | / |
| GET PUT | / |
| GET | / |
| POST | / |
| POST | / |
| POST | / |
| GET PUT | / |
| GET | / |
| GET POST DELETE | / Grumble |
| PUT | / |
| POST | / |
| POST | / |
| POST | / |
| POST | / |
| GET PATCH | / |
| GET POST | / |
| PUT DELETE | / |
| GET PATCH | / |
| GET PATCH | / |
| GET | / Grumble |
| PUT DELETE | / Grumble |