Bot API Reference
Public Bot API · version 1.0 · base URL https://api.speakspeak.net
Overview
SpeakSpeak offers a public, Discord-compatible bot API: REST endpoints and a real-time gateway. This page is the complete reference — generated from the same API description (OpenAPI 3.1) the server itself is checked against.
For a guided introduction see the developers page.
A Discord-compatible REST + gateway dialect for bots on SpeakSpeak. Object shapes, error bodies, rate-limit headers, opcodes and intents follow Discord's wire format closely enough that a ported client library works with a changed base URL — with documented divergences:
- IDs are UUIDv7 strings, not snowflakes. UUIDv7 is time-ordered, so an id remains a valid time cursor for
before pagination.
- Avatars/icons are relative proxy paths (e.g.
/api/v1/avatars/{id}), not Discord CDN hashes.
- Attachment
url/proxy_url are null — SpeakSpeak attachment bytes are only reachable through its own permission-checked presign flow, which the facade does not expose.
- Permission bit positions are SpeakSpeak's own (stringified u64, Discord's wire convention, but the bits mean SpeakSpeak permissions).
- Fields marked "accepted, ignored" below are consumed for library compatibility and have no effect.
Authentication
REST: Authorization: Bot <token> — exactly this scheme, case-sensitive, single space. Bearer, lowercase bot, or a bare token are rejected with 401 {"message":"401: Unauthorized","code":40001}. Gateway: authentication is in-band via the IDENTIFY payload, not a header.
Errors
Every error body is Discord-shaped: {"message": "...", "code": <int>} (the 429 body is {"message","retry_after","global"} instead, as on Discord). A resource that exists but is not visible to the bot returns the same 404 "Unknown X" as a truly absent one — existence never leaks.
Recurring error responses the endpoints below refer to:
List endpoints paginate with before=<UUIDv7 id> + limit. after / around are accepted for library compatibility but not implemented (ignored, logged server-side).
Rate limits
Every authenticated REST response carries X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (unix epoch seconds, 3 decimals), X-RateLimit-Reset-After (seconds, 3 decimals) and X-RateLimit-Bucket. A 429 additionally carries Retry-After and — only when the global bucket tripped — X-RateLimit-Global: true.
Buckets (fixed windows, budgets from the facade source):
- global, per bot: 50 requests / 1 s (bucket id
global)
- per bot + route bucket: 50 / 10 s — bucket id is the route class (
messages.write, messages.read, guild.read, reactions, typing, default), suffixed :{channel_id|guild_id} when the route carries that major parameter
- per bot + guild: 20 / 10 s
- destructive extra-throttle (message delete, kick): 5 / 10 s per bot + guild
- attachment upload: 10 / 60 s per bot (bucket id
attachments.upload)
- webhook execute: 30 / 60 s per webhook, plus 120 / 60 s per source IP
- interaction callback / follow-up: 120 / 60 s per source IP
The rate-limit headers in detail:
Gateway protocol
wss://api.speakspeak.net/api/v1/gateway. Discord opcode dialect: 0 DISPATCH, 1 HEARTBEAT, 2 IDENTIFY, 3 PRESENCE_UPDATE, 4 VOICE_STATE_UPDATE, 6 RESUME, 7 RECONNECT, 9 INVALID_SESSION, 10 HELLO, 11 HEARTBEAT_ACK. Close codes: 4004 authentication failed/token revoked, 4008 rate limited (identify rate / concurrent session cap — at most 2 concurrent sessions per application), 4009 session timeout (missed heartbeats).
RESUME (opcode 6 with session_id + seq) is implemented: a dropped session is parked for 90 seconds and a resume within that window replays buffered dispatches from seq. Outside the window (or on a bad token) the server sends opcode 9 INVALID_SESSION and the bot must re-IDENTIFY. READY includes resume_gateway_url.
Intents are declared in IDENTIFY and gate event classes — see the GatewayIntents schema. intents: 0 receives no dispatches at all; a typical message bot wants GUILDS | GUILD_MESSAGES | MESSAGE_CONTENT = 33281.
Intents
IDENTIFY intents bitfield. A dispatch is delivered only when the session's intents include its event class — intents: 0 receives nothing. Bits the facade honours:
A typical message bot: GUILDS | GUILD_MESSAGES | MESSAGE_CONTENT = 33281.
General
Liveness probe
GET/api/v1/healthno authentication
Example request
curl https://api.speakspeak.net/api/v1/health
Responses
Users
Get the bot's own user
GET/api/v1/users/@me
Example request
curl https://api.speakspeak.net/api/v1/users/@me \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
# Response 200
{
"id": "019e8232-90d4-70aa-8f3c-2d94bb1e6620",
"username": "diceroller#4102",
"discriminator": "0",
"global_name": "diceroller#4102",
"avatar": null,
"bot": true
}
Get a user
GET/api/v1/users/{user_id}
Parameters
Example request
curl https://api.speakspeak.net/api/v1/users/{user_id} \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
# Response 200
{
"id": "019a4a18-bfd9-73c1-9aa4-7b0d24e19af5",
"username": "alice#2044",
"discriminator": "0",
"global_name": "alice#2044",
"avatar": "/api/v1/avatars/019a4a18-bfd9-73c1-9aa4-7b0d24e19af5",
"bot": false
}
Guilds
Get a guild
GET/api/v1/guilds/{guild_id}
Parameters
Example request
curl https://api.speakspeak.net/api/v1/guilds/{guild_id} \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
# Response 200
{
"id": "019cebae-61c0-72d0-9be5-566cc17d0442",
"name": "Speedrun Lounge",
"icon": "/api/v1/server-icons/019cebae-61c0-72d0-9be5-566cc17d0442",
"owner_id": "019a4a18-bfd9-73c1-9aa4-7b0d24e19af5",
"description": "EU speedrunning community."
}
Channels
List guild channels
GET/api/v1/guilds/{guild_id}/channels
Includes categories as type-4 channels (Discord's model); SpeakSpeak stores them separately, the facade bridges the shape. Note the category object carries no topic/nsfw keys, as in the example.
Parameters
Example request
curl https://api.speakspeak.net/api/v1/guilds/{guild_id}/channels \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
# Response 200
[
{
"id": "019cebaf-7330-71f7-a8c3-344dd2e19a0b",
"guild_id": "019cebae-61c0-72d0-9be5-566cc17d0442",
"type": 4,
"name": "Text Channels",
"position": 0,
"parent_id": null
},
{
"id": "019cebb0-9828-74b0-a9d7-788aa3f24c5d",
"guild_id": "019cebae-61c0-72d0-9be5-566cc17d0442",
"type": 0,
"name": "general",
"topic": "Daily chatter",
"position": 0,
"parent_id": "019cebaf-7330-71f7-a8c3-344dd2e19a0b",
"nsfw": false
},
{
"id": "019cf0f0-40b8-76c1-b8ab-155ee4a35d6e",
"guild_id": "019cebae-61c0-72d0-9be5-566cc17d0442",
"type": 2,
"name": "Voice Lounge",
"topic": null,
"position": 1,
"parent_id": null,
"nsfw": false
}
]
Create a channel
POST/api/v1/guilds/{guild_id}/channels
Parameters
Request body
CreateChannelBody
Example request
curl -X POST https://api.speakspeak.net/api/v1/guilds/{guild_id}/channels \
-H "Authorization: Bot ssbot_<app>.<secret>" \
-H "Content-Type: application/json" \
-d '{
"name": "help-desk",
"type": 0,
"parent_id": "019cebaf-7330-71f7-a8c3-344dd2e19a0b"
}'
Responses
# Response 201
{
"id": "01a01952-a228-70d2-b9ac-266ff5b46e7f",
"guild_id": "019cebae-61c0-72d0-9be5-566cc17d0442",
"type": 0,
"name": "help-desk",
"topic": null,
"position": 2,
"parent_id": "019cebaf-7330-71f7-a8c3-344dd2e19a0b",
"nsfw": false
}
Bulk-reorder channels
PATCH/api/v1/guilds/{guild_id}/channels
Parameters
Request body
array of ChannelPositionEntry
Example request
curl -X PATCH https://api.speakspeak.net/api/v1/guilds/{guild_id}/channels \
-H "Authorization: Bot ssbot_<app>.<secret>" \
-H "Content-Type: application/json" \
-d '[
{
"id": "019cebb0-9828-74b0-a9d7-788aa3f24c5d",
"position": 0,
"parent_id": "019cebaf-7330-71f7-a8c3-344dd2e19a0b"
},
{
"id": "01a01952-a228-70d2-b9ac-266ff5b46e7f",
"position": 1,
"parent_id": "019cebaf-7330-71f7-a8c3-344dd2e19a0b"
}
]'
Responses
Get a channel
GET/api/v1/channels/{channel_id}
Parameters
Example request
curl https://api.speakspeak.net/api/v1/channels/{channel_id} \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Update a channel
PATCH/api/v1/channels/{channel_id}
Parameters
Request body
UpdateChannelBody
Example request
curl -X PATCH https://api.speakspeak.net/api/v1/channels/{channel_id} \
-H "Authorization: Bot ssbot_<app>.<secret>" \
-H "Content-Type: application/json" \
-d '{"name": "help-desk", "topic": "Questions and support"}'
Responses
# Response 200
{
"id": "01a01952-a228-70d2-b9ac-266ff5b46e7f",
"guild_id": "019cebae-61c0-72d0-9be5-566cc17d0442",
"type": 0,
"name": "help-desk",
"topic": "Questions and support",
"position": 2,
"parent_id": "019cebaf-7330-71f7-a8c3-344dd2e19a0b",
"nsfw": false
}
Delete a channel
DELETE/api/v1/channels/{channel_id}
Parameters
Example request
curl -X DELETE https://api.speakspeak.net/api/v1/channels/{channel_id} \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Messages
List messages
GET/api/v1/channels/{channel_id}/messages
Parameters
Example request
curl https://api.speakspeak.net/api/v1/channels/{channel_id}/messages \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
# Response 200
[
{
"id": "01a01949-a701-745b-83ef-599cd8e7910b",
"channel_id": "019cebb0-9828-74b0-a9d7-788aa3f24c5d",
"guild_id": "019cebae-61c0-72d0-9be5-566cc17d0442",
"author": {
"id": "019a4a18-bfd9-73c1-9aa4-7b0d24e19af5",
"username": "alice#2044",
"discriminator": "0",
"global_name": "alice#2044",
"avatar": "/api/v1/avatars/019a4a18-bfd9-73c1-9aa4-7b0d24e19af5",
"bot": false
},
"content": "Anyone else seeing lag on the EU voice nodes? Log attached.",
"timestamp": "2026-08-19T09:10:52.417306Z",
"edited_timestamp": null,
"attachments": [
{
"id": "01a01949-a178-767d-a501-7bbefa09b324",
"filename": "voice-debug.log",
"content_type": "text/plain",
"size": 48213,
"url": null,
"proxy_url": null
}
],
"embeds": [],
"mentions": [],
"mention_roles": [],
"mention_everyone": false,
"pinned": false,
"type": 0,
"flags": 0
},
{
"id": "01a01944-8c6d-71b2-9c3d-4e5f60718293",
"channel_id": "019cebb0-9828-74b0-a9d7-788aa3f24c5d",
"guild_id": "019cebae-61c0-72d0-9be5-566cc17d0442",
"author": {
"id": "019a4a18-bfd9-73c1-9aa4-7b0d24e19af5",
"username": "alice#2044",
"discriminator": "0",
"global_name": "alice#2044",
"avatar": "/api/v1/avatars/019a4a18-bfd9-73c1-9aa4-7b0d24e19af5",
"bot": false
},
"content": "gg everyone, that was a clean run",
"timestamp": "2026-08-19T09:05:17.933481Z",
"edited_timestamp": null,
"attachments": [],
"embeds": [],
"mentions": [],
"mention_roles": [],
"mention_everyone": false,
"pinned": false,
"type": 0,
"flags": 0
}
]
Send a message
POST/api/v1/channels/{channel_id}/messages
Two request forms:
application/json — content and/or embeds.
multipart/form-data — Discord's one-request file send: a payload_json part (the same JSON message object; optional — files only is a valid message) plus files[0]…files[n] parts. Caps are server-configured (defaults: 8 MiB per file, 10 files per message); exceeding them is a 400 code 40005. Uploads count against the attachments.upload bucket (10 / 60 s per bot).
Embed limits: at most 10 embeds; title ≤ 256, description ≤ 4096, field name ≤ 256, field value ≤ 1024, footer text ≤ 2048, author name ≤ 256 characters.
Parameters
Request body
CreateMessageBody
object (multipart/form-data)
Example request
curl -X POST https://api.speakspeak.net/api/v1/channels/{channel_id}/messages \
-H "Authorization: Bot ssbot_<app>.<secret>" \
-H "Content-Type: application/json" \
-d '{
"content": "Build 2103 is live. Changelog below.",
"message_reference": {
"message_id": "01a01949-a701-745b-83ef-599cd8e7910b"
},
"embeds": [
{
"title": "Build 2103",
"description": "Desktop build 2103 is rolling out now.",
"color": 3066993,
"footer": {
"text": "CI run 88"
},
"fields": [
{
"name": "Platform",
"value": "Linux x64",
"inline": true
},
{
"name": "Duration",
"value": "4m12s",
"inline": true
}
]
}
]
}'
Responses
# Response 201
{
"id": "01a0194d-0fa4-756c-94f0-6aade9f8a213",
"channel_id": "019cebb0-9828-74b0-a9d7-788aa3f24c5d",
"guild_id": "019cebae-61c0-72d0-9be5-566cc17d0442",
"author": {
"id": "019e8232-90d4-70aa-8f3c-2d94bb1e6620",
"username": "diceroller#4102",
"discriminator": "0",
"global_name": "diceroller#4102",
"avatar": null,
"bot": true
},
"content": "Build 2103 is live. Changelog below.",
"timestamp": "2026-08-19T09:14:35.812416Z",
"edited_timestamp": null,
"attachments": [],
"embeds": [
{
"type": "rich",
"title": "Build 2103",
"description": "Desktop build 2103 is rolling out now.",
"color": 3066993,
"footer": {
"text": "CI run 88"
},
"fields": [
{
"name": "Platform",
"value": "Linux x64",
"inline": true
},
{
"name": "Duration",
"value": "4m12s",
"inline": true
}
]
}
],
"mentions": [],
"mention_roles": [],
"mention_everyone": false,
"pinned": false,
"type": 0,
"flags": 0
}
Get a message
GET/api/v1/channels/{channel_id}/messages/{message_id}
Parameters
Example request
curl https://api.speakspeak.net/api/v1/channels/{channel_id}/messages/{message_id} \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Edit a message
PATCH/api/v1/channels/{channel_id}/messages/{message_id}
Parameters
Request body
UpdateMessageBody
Example request
curl -X PATCH https://api.speakspeak.net/api/v1/channels/{channel_id}/messages/{message_id} \
-H "Authorization: Bot ssbot_<app>.<secret>" \
-H "Content-Type: application/json" \
-d '{"content": "Build 2103 is live. Changelog pinned."}'
Responses
# Response 200
{
"id": "01a0194d-0fa4-756c-94f0-6aade9f8a213",
"channel_id": "019cebb0-9828-74b0-a9d7-788aa3f24c5d",
"guild_id": "019cebae-61c0-72d0-9be5-566cc17d0442",
"author": {
"id": "019e8232-90d4-70aa-8f3c-2d94bb1e6620",
"username": "diceroller#4102",
"discriminator": "0",
"global_name": "diceroller#4102",
"avatar": null,
"bot": true
},
"content": "Build 2103 is live. Changelog pinned.",
"timestamp": "2026-08-19T09:14:35.812416Z",
"edited_timestamp": "2026-08-19T09:16:02.145087Z",
"attachments": [],
"embeds": [
{
"type": "rich",
"title": "Build 2103",
"description": "Desktop build 2103 is rolling out now.",
"color": 3066993,
"footer": {
"text": "CI run 88"
},
"fields": [
{
"name": "Platform",
"value": "Linux x64",
"inline": true
},
{
"name": "Duration",
"value": "4m12s",
"inline": true
}
]
}
],
"mentions": [],
"mention_roles": [],
"mention_everyone": false,
"pinned": false,
"type": 0,
"flags": 0
}
Delete a message
DELETE/api/v1/channels/{channel_id}/messages/{message_id}
Counts against the destructive throttle (5 / 10 s per guild).
Parameters
Example request
curl -X DELETE https://api.speakspeak.net/api/v1/channels/{channel_id}/messages/{message_id} \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Trigger a typing indicator
POST/api/v1/channels/{channel_id}/typing
Parameters
Example request
curl -X POST https://api.speakspeak.net/api/v1/channels/{channel_id}/typing \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Reactions
Add a reaction
PUT/api/v1/channels/{channel_id}/messages/{message_id}/reactions/{emoji}/@me
{emoji} is either a Unicode emoji (URL-encoded) or a custom emoji as name:emoji_id (Discord's convention; the id is a UUID).
Parameters
Example request
curl -X PUT https://api.speakspeak.net/api/v1/channels/{channel_id}/messages/{message_id}/reactions/{emoji}/@me \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Remove own reaction
DELETE/api/v1/channels/{channel_id}/messages/{message_id}/reactions/{emoji}/@me
Parameters
Example request
curl -X DELETE https://api.speakspeak.net/api/v1/channels/{channel_id}/messages/{message_id}/reactions/{emoji}/@me \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Pins
List pinned messages
GET/api/v1/channels/{channel_id}/pins
Parameters
Example request
curl https://api.speakspeak.net/api/v1/channels/{channel_id}/pins \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Pin a message
PUT/api/v1/channels/{channel_id}/pins/{message_id}
Parameters
Example request
curl -X PUT https://api.speakspeak.net/api/v1/channels/{channel_id}/pins/{message_id} \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Unpin a message
DELETE/api/v1/channels/{channel_id}/pins/{message_id}
Parameters
Example request
curl -X DELETE https://api.speakspeak.net/api/v1/channels/{channel_id}/pins/{message_id} \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Members
List guild members
GET/api/v1/guilds/{guild_id}/members
Returns the member list. No limit/after query parameters in the current phase — the full CS member list is returned.
Parameters
Example request
curl https://api.speakspeak.net/api/v1/guilds/{guild_id}/members \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
# Response 200
[
{
"user": {
"id": "019a4a18-bfd9-73c1-9aa4-7b0d24e19af5",
"username": "alice#2044",
"discriminator": "0",
"global_name": "alice#2044",
"avatar": "/api/v1/avatars/019a4a18-bfd9-73c1-9aa4-7b0d24e19af5",
"bot": false
},
"nick": "Ali",
"roles": [
"019d0c59-dc88-723f-a1cd-377ab6c57f80"
],
"joined_at": "2026-03-14T09:30:00.184920Z",
"deaf": false,
"mute": false
},
{
"user": {
"id": "019e8232-90d4-70aa-8f3c-2d94bb1e6620",
"username": "diceroller#4102",
"discriminator": "0",
"global_name": "diceroller#4102",
"avatar": null,
"bot": true
},
"nick": null,
"roles": [],
"joined_at": "2026-06-01T08:00:14.062771Z",
"deaf": false,
"mute": false
}
]
Get a guild member
GET/api/v1/guilds/{guild_id}/members/{user_id}
Parameters
Example request
curl https://api.speakspeak.net/api/v1/guilds/{guild_id}/members/{user_id} \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Update a member (nick, roles, timeout)
PATCH/api/v1/guilds/{guild_id}/members/{user_id}
communication_disabled_until is echoed back in this PATCH response only (as in the example) — CS's member read carries no timeout column, so the field never appears on GET responses.
Parameters
Request body
UpdateMemberBody
Example request
curl -X PATCH https://api.speakspeak.net/api/v1/guilds/{guild_id}/members/{user_id} \
-H "Authorization: Bot ssbot_<app>.<secret>" \
-H "Content-Type: application/json" \
-d '{
"nick": "Speedrun Sam",
"roles": [
"019d0c59-dc88-723f-a1cd-377ab6c57f80"
],
"communication_disabled_until": "2026-08-19T21:00:00Z"
}'
Responses
# Response 200
{
"user": {
"id": "019a4a18-bfd9-73c1-9aa4-7b0d24e19af5",
"username": "alice#2044",
"discriminator": "0",
"global_name": "alice#2044",
"avatar": "/api/v1/avatars/019a4a18-bfd9-73c1-9aa4-7b0d24e19af5",
"bot": false
},
"nick": "Speedrun Sam",
"roles": [
"019d0c59-dc88-723f-a1cd-377ab6c57f80"
],
"joined_at": "2026-03-14T09:30:00.184920Z",
"deaf": false,
"mute": false,
"communication_disabled_until": "2026-08-19T21:00:00Z"
}
Kick a member
DELETE/api/v1/guilds/{guild_id}/members/{user_id}
Counts against the destructive throttle (5 / 10 s per guild).
Parameters
Example request
curl -X DELETE https://api.speakspeak.net/api/v1/guilds/{guild_id}/members/{user_id} \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Add a role to a member
PUT/api/v1/guilds/{guild_id}/members/{user_id}/roles/{role_id}
Parameters
Example request
curl -X PUT https://api.speakspeak.net/api/v1/guilds/{guild_id}/members/{user_id}/roles/{role_id} \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Remove a role from a member
DELETE/api/v1/guilds/{guild_id}/members/{user_id}/roles/{role_id}
Parameters
Example request
curl -X DELETE https://api.speakspeak.net/api/v1/guilds/{guild_id}/members/{user_id}/roles/{role_id} \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Roles
List guild roles
GET/api/v1/guilds/{guild_id}/roles
Parameters
Example request
curl https://api.speakspeak.net/api/v1/guilds/{guild_id}/roles \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
# Response 200
[
{
"id": "019cebae-6208-7c4d-8e11-23ab45cd67ef",
"name": "Members",
"color": 0,
"hoist": false,
"position": 0,
"permissions": "3072",
"mentionable": false,
"managed": false
},
{
"id": "019d0c59-dc88-723f-a1cd-377ab6c57f80",
"name": "Moderators",
"color": 15105570,
"hoist": true,
"position": 1,
"permissions": "8522776",
"mentionable": true,
"managed": false
}
]
Create a role
POST/api/v1/guilds/{guild_id}/roles
Parameters
Request body
CreateRoleBody
Example request
curl -X POST https://api.speakspeak.net/api/v1/guilds/{guild_id}/roles \
-H "Authorization: Bot ssbot_<app>.<secret>" \
-H "Content-Type: application/json" \
-d '{
"name": "Helpers",
"permissions": "134144",
"color": 3447003,
"hoist": true,
"mentionable": false
}'
Responses
# Response 201
{
"id": "01a01956-a198-734a-92de-488bc7d6809a",
"name": "Helpers",
"color": 3447003,
"hoist": true,
"position": 2,
"permissions": "134144",
"mentionable": false,
"managed": false
}
Bulk-reorder roles
PATCH/api/v1/guilds/{guild_id}/roles
Parameters
Request body
array of RolePositionEntry
Example request
curl -X PATCH https://api.speakspeak.net/api/v1/guilds/{guild_id}/roles \
-H "Authorization: Bot ssbot_<app>.<secret>" \
-H "Content-Type: application/json" \
-d '[
{
"id": "01a01956-a198-734a-92de-488bc7d6809a",
"position": 1
},
{
"id": "019d0c59-dc88-723f-a1cd-377ab6c57f80",
"position": 2
}
]'
Responses
# Response 200
[
{
"id": "019cebae-6208-7c4d-8e11-23ab45cd67ef",
"name": "Members",
"color": 0,
"hoist": false,
"position": 0,
"permissions": "3072",
"mentionable": false,
"managed": false
},
{
"id": "01a01956-a198-734a-92de-488bc7d6809a",
"name": "Helpers",
"color": 3447003,
"hoist": true,
"position": 1,
"permissions": "134144",
"mentionable": false,
"managed": false
},
{
"id": "019d0c59-dc88-723f-a1cd-377ab6c57f80",
"name": "Moderators",
"color": 15105570,
"hoist": true,
"position": 2,
"permissions": "8522776",
"mentionable": true,
"managed": false
}
]
Update a role
PATCH/api/v1/guilds/{guild_id}/roles/{role_id}
Parameters
Request body
UpdateRoleBody
Example request
curl -X PATCH https://api.speakspeak.net/api/v1/guilds/{guild_id}/roles/{role_id} \
-H "Authorization: Bot ssbot_<app>.<secret>" \
-H "Content-Type: application/json" \
-d '{"permissions": "134152", "mentionable": true}'
Responses
# Response 200
{
"id": "01a01956-a198-734a-92de-488bc7d6809a",
"name": "Helpers",
"color": 3447003,
"hoist": true,
"position": 1,
"permissions": "134152",
"mentionable": true,
"managed": false
}
Delete a role
DELETE/api/v1/guilds/{guild_id}/roles/{role_id}
Parameters
Example request
curl -X DELETE https://api.speakspeak.net/api/v1/guilds/{guild_id}/roles/{role_id} \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Bans
List bans
GET/api/v1/guilds/{guild_id}/bans
Parameters
Example request
curl https://api.speakspeak.net/api/v1/guilds/{guild_id}/bans \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
# Response 200
[
{
"reason": "spamming invite links",
"user": {
"id": "019ba915-e91a-751b-8bd0-c44f1a9c2731",
"username": "spamlord#9313",
"discriminator": "0",
"global_name": "spamlord#9313",
"avatar": null,
"bot": false
}
}
]
Get a ban
GET/api/v1/guilds/{guild_id}/bans/{user_id}
Parameters
Example request
curl https://api.speakspeak.net/api/v1/guilds/{guild_id}/bans/{user_id} \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Ban a user
PUT/api/v1/guilds/{guild_id}/bans/{user_id}
The ban reason is taken from the X-Audit-Log-Reason request header (Discord's convention). delete_message_seconds is not supported.
Parameters
Example request
curl -X PUT https://api.speakspeak.net/api/v1/guilds/{guild_id}/bans/{user_id} \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Remove a ban
DELETE/api/v1/guilds/{guild_id}/bans/{user_id}
Parameters
Example request
curl -X DELETE https://api.speakspeak.net/api/v1/guilds/{guild_id}/bans/{user_id} \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Emojis
List guild emojis
GET/api/v1/guilds/{guild_id}/emojis
Parameters
Example request
curl https://api.speakspeak.net/api/v1/guilds/{guild_id}/emojis \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
# Response 200
[
{
"id": "019f22bc-5df0-778e-b612-8cc01b1ac435",
"name": "pog",
"roles": [],
"user": null,
"require_colons": true,
"managed": false,
"animated": false,
"available": true
}
]
Get an emoji
GET/api/v1/guilds/{guild_id}/emojis/{emoji_id}
Parameters
Example request
curl https://api.speakspeak.net/api/v1/guilds/{guild_id}/emojis/{emoji_id} \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Delete an emoji
DELETE/api/v1/guilds/{guild_id}/emojis/{emoji_id}
Emoji creation is not available over the bot API (upload flow).
Parameters
Example request
curl -X DELETE https://api.speakspeak.net/api/v1/guilds/{guild_id}/emojis/{emoji_id} \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Invites
Create an invite
POST/api/v1/channels/{channel_id}/invites
SpeakSpeak invites are server-scoped: the channel resolves to its guild and a guild-wide invite is created (channel_id on the result is always null). Note in the example that the requested max_age comes back as a resolved expires_at timestamp, and max_age on the result is always null.
Parameters
Request body
CreateInviteBody
Example request
curl -X POST https://api.speakspeak.net/api/v1/channels/{channel_id}/invites \
-H "Authorization: Bot ssbot_<app>.<secret>" \
-H "Content-Type: application/json" \
-d '{"max_uses": 25, "max_age": 86400}'
Responses
# Response 201
{
"code": "Xk3qTz9wLmPa",
"guild_id": "019cebae-61c0-72d0-9be5-566cc17d0442",
"channel_id": null,
"inviter": null,
"uses": 0,
"max_uses": 25,
"max_age": null,
"temporary": false,
"expires_at": "2026-08-20T09:22:10.317502Z",
"created_at": "2026-08-19T09:22:10.318204Z"
}
Delete an invite
DELETE/api/v1/invites/{code}
Parameters
Example request
curl -X DELETE https://api.speakspeak.net/api/v1/invites/{code} \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
Audit log
List audit-log entries
GET/api/v1/guilds/{guild_id}/audit-logs
Only actions with an exact Discord equivalent are returned (MEMBER_KICK=20, MEMBER_BAN_ADD=22, MEMBER_BAN_REMOVE=23); SpeakSpeak-only actions are omitted rather than mapped to a wrong Discord action_type. users contains the *acting* users only — targets are referenced by target_id and resolved separately.
Parameters
Example request
curl https://api.speakspeak.net/api/v1/guilds/{guild_id}/audit-logs \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
# Response 200
{
"audit_log_entries": [
{
"id": "019ff577-dc88-7bc2-ba56-2a045f5e0879",
"action_type": 22,
"user_id": "019a4a18-bfd9-73c1-9aa4-7b0d24e19af5",
"target_id": "019ba915-e91a-751b-8bd0-c44f1a9c2731",
"reason": "spamming invite links",
"changes": [],
"options": null
},
{
"id": "019ff26b-67a8-7ab1-a945-1ff34e4df768",
"action_type": 20,
"user_id": "019a4a18-bfd9-73c1-9aa4-7b0d24e19af5",
"target_id": "019ba915-e91a-751b-8bd0-c44f1a9c2731",
"reason": "repeated spam after warning",
"changes": [],
"options": null
}
],
"users": [
{
"id": "019a4a18-bfd9-73c1-9aa4-7b0d24e19af5",
"username": "alice#2044",
"discriminator": "0",
"global_name": "alice#2044",
"avatar": "/api/v1/avatars/019a4a18-bfd9-73c1-9aa4-7b0d24e19af5",
"bot": false
}
]
}
Webhooks
Get webhook info (token-authenticated)
GET/api/v1/webhooks/{webhook_id}/{token}no authentication
Public route — the credential is the token in the URL. Rate-limited per webhook (30 / 60 s) and per source IP (120 / 60 s).
Parameters
Example request
curl https://api.speakspeak.net/api/v1/webhooks/{webhook_id}/{token}
Responses
# Response 200
{
"id": "019fa962-42e8-789f-8723-9dd12c2bd546",
"type": 1,
"guild_id": "019cebae-61c0-72d0-9be5-566cc17d0442",
"channel_id": "019cebb0-9828-74b0-a9d7-788aa3f24c5d",
"name": "Deploys",
"avatar": null,
"application_id": null
}
Execute an incoming webhook
POST/api/v1/webhooks/{webhook_id}/{token}no authentication
Public route — the credential is the token in the URL. Body limit 8 KiB, JSON only (no multipart). content ≤ 2000 characters, username override ≤ 80. There are no *outgoing* webhooks in SpeakSpeak, so there is no signature scheme to verify.
Parameters
Request body
WebhookExecuteBody
Example request
curl -X POST https://api.speakspeak.net/api/v1/webhooks/{webhook_id}/{token} \
-H "Content-Type: application/json" \
-d '{
"content": "gip deployed: build 2103, all checks green",
"username": "CI Status"
}'
Responses
# Response 200
{
"id": "01a0195b-5584-70e1-8f20-31425364758a",
"channel_id": "019cebb0-9828-74b0-a9d7-788aa3f24c5d",
"guild_id": "019cebae-61c0-72d0-9be5-566cc17d0442",
"author": {
"id": "019fa962-42fc-7a1b-9c2d-3e4f50617283",
"username": "CI Status",
"discriminator": "0",
"global_name": "CI Status",
"avatar": null,
"bot": true
},
"content": "gip deployed: build 2103, all checks green",
"timestamp": "2026-08-19T09:30:11.204863Z",
"edited_timestamp": null,
"attachments": [],
"embeds": [],
"mentions": [],
"mention_roles": [],
"mention_everyone": false,
"pinned": false,
"type": 0,
"flags": 0
}
Interactions
Respond to an interaction
POST/api/v1/interactions/{interaction_id}/{token}/callbackno authentication
Public route — the credential is the single-use ssint_… token in the URL. Body limit 16 KiB. type 4 = immediate channel message (default), 5 = deferred, 9 = modal (data is then {custom_id,title,components}). For type 4, data.flags bit 6 (EPHEMERAL, 64) is honoured. Rate-limited per source IP (120 / 60 s). The interaction token expires after its TTL (at most 15 minutes).
Parameters
Request body
InteractionCallbackBody
Example request
curl -X POST https://api.speakspeak.net/api/v1/interactions/{interaction_id}/{token}/callback \
-H "Content-Type: application/json" \
-d '{
"type": 4,
"data": {
"content": "You rolled a 17.",
"flags": 64
}
}'
Responses
Create an interaction follow-up message
POST/api/v1/webhooks/{app_id}/{token}/messagesno authentication
Public route — same ssint_… interaction token. Note the explicit /messages tail: this deliberately does not reuse Discord's bare POST /webhooks/{app_id}/{token} shape (which would collide with webhook execute). Body limit 16 KiB. Rate-limited per source IP (120 / 60 s).
Parameters
Request body
FollowupBody
Example request
curl -X POST https://api.speakspeak.net/api/v1/webhooks/{app_id}/{token}/messages \
-H "Content-Type: application/json" \
-d '{
"content": "Roll breakdown: d20 result 14, modifier 3, total 17."
}'
Responses
Edit the original interaction response
PATCH/api/v1/webhooks/{app_id}/{token}/messages/@originalno authentication
Public route — same ssint_… interaction token. @original does not exist for an ephemeral or not-yet-sent response (404).
Parameters
Request body
FollowupBody
Example request
curl -X PATCH https://api.speakspeak.net/api/v1/webhooks/{app_id}/{token}/messages/@original \
-H "Content-Type: application/json" \
-d '{"content": "Updated result: natural 20."}'
Responses
Delete the original interaction response
DELETE/api/v1/webhooks/{app_id}/{token}/messages/@originalno authentication
Parameters
Example request
curl -X DELETE https://api.speakspeak.net/api/v1/webhooks/{app_id}/{token}/messages/@original
Responses
Gateway connection
Gateway WebSocket upgrade
GET/api/v1/gatewayno authentication
WebSocket endpoint (documented here as the HTTP upgrade request). No Authorization header — the bot authenticates in-band with IDENTIFY (opcode 2) carrying its token and intents. See the top-level description for opcodes, close codes, RESUME and the 2-concurrent-sessions-per-application cap.
Example request
curl https://api.speakspeak.net/api/v1/gateway
Responses
Gateway connection info (authenticated)
GET/api/v1/gateway/bot
Example request
curl https://api.speakspeak.net/api/v1/gateway/bot \
-H "Authorization: Bot ssbot_<app>.<secret>"
Responses
# Response 200
{
"url": "wss://api.speakspeak.net/api/v1/gateway",
"shards": 1,
"session_start_limit": {
"total": 1000,
"remaining": 1000,
"reset_after": 86400000,
"max_concurrency": 1
}
}
OAuth2 & install
Bot-install metadata lookup
GET/api/v1/oauth2/authorizeno authentication
Public (no auth) — client_id is a public identifier, as on Discord. Returns what the install UI needs to render the consent screen. The actual install is completed in the SpeakSpeak client via the returned install_endpoint.
Parameters
Example request
curl "https://api.speakspeak.net/api/v1/oauth2/authorize?client_id=<client_id>&scope=<scope>"
Responses
# Response 200
{
"application": {
"app_id": "019e8230-9374-777e-9d51-c20aa4b3f9c1",
"name": "Dice Roller",
"icon_url": null
},
"requested_permissions": "3072",
"guild_id": "019cebae-61c0-72d0-9be5-566cc17d0442",
"install_endpoint": "/api/v1/applications/019e8230-9374-777e-9d51-c20aa4b3f9c1/install"
}
Objects
Error
Discord-shaped error body.
RateLimitError
The 429 body (Discord's shape — no code field).
User
Guild
Channel
GuildMember
Role
Ban
Emoji
Message
Attachment
Embed
Discord-shaped embed. type is rich (bot/webhook-authored) or link (URL unfurl). Image/thumbnail url values are null (same presign gap as attachments).
Invite
AuditLog
AuditLogEntry
Webhook
GatewayBotInfo
ApplicationSummary
AuthorizeResponse
CreateMessageBody
UpdateMessageBody
CreateChannelBody
UpdateChannelBody
Absent fields (and explicit null) mean "unchanged".
ChannelPositionEntry
CreateRoleBody
UpdateRoleBody
RolePositionEntry
UpdateMemberBody
CreateInviteBody
WebhookExecuteBody
InteractionCallbackBody
FollowupBody
Questions & feedback
The Bot API is in public beta and growing. Questions, feedback or a library request? Write to contact@speakspeak.net.