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:

ResponseBodyDescription
UnauthorizedErrorMissing or malformed Authorization Bot header (code 40001).
ForbiddenErrorMissing access (code 50001) or missing permissions (code 50013).
NotFoundErrorUnknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).
RateLimitedRateLimitErrorRate limited. Carries Retry-After and the X-RateLimit-* header set; X-RateLimit-Global: true only when the global bucket tripped.

Pagination

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:

HeaderTypeDescription
X-RateLimit-LimitintegerThe bucket's request budget for the window.
X-RateLimit-RemainingintegerRequests left in the current window (0 once denied).
X-RateLimit-ResetnumberUnix epoch seconds (3 decimals) at which the window resets.
X-RateLimit-Reset-AfternumberSeconds (3 decimals) until the window resets.
X-RateLimit-BucketstringBucket identity, e.g. messages.write, guild.read:{guild_id}, global, attachments.upload.
X-RateLimit-GlobalstringPresent (value true) only when the global bucket tripped.
Retry-AfternumberSeconds (3 decimals) to wait; on 429 responses only.

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:

IntentBitValueDescription
GUILDS1 << 01channels, threads, roles, guild metadata
GUILD_MEMBERS1 << 12member add/remove/update
GUILD_VOICE_STATES1 << 7128voice-state changes
GUILD_PRESENCES1 << 8256presence updates
GUILD_MESSAGES1 << 9512message create/update/delete
GUILD_MESSAGE_REACTIONS1 << 101024reaction add/remove
GUILD_MESSAGE_TYPING1 << 112048typing start
MESSAGE_CONTENT1 << 1532768privileged; does NOT gate events: without it MESSAGE_* dispatches still arrive but content, embeds, attachments and components are blanked.

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

StatusDescriptionBody
200The facade is up.

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

StatusDescriptionBody
200The bot user.User
401Missing or malformed Authorization Bot header (code 40001).Error
# 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

ParameterInTypeRequiredDescription
user_idpathstring (uuid)yes

Example request

curl https://api.speakspeak.net/api/v1/users/{user_id} \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
200The user.User
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error
# 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

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes

Example request

curl https://api.speakspeak.net/api/v1/guilds/{guild_id} \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
200The guild (SpeakSpeak server).Guild
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error
# 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

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes

Example request

curl https://api.speakspeak.net/api/v1/guilds/{guild_id}/channels \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
200All channels and categories.array of Channel
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error
# 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

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes

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

StatusDescriptionBody
201The created channel (or category, for type: 4).Channel
403Missing access (code 50001) or missing permissions (code 50013).Error
# 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

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes

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

StatusDescriptionBody
204Reordered.
403Missing access (code 50001) or missing permissions (code 50013).Error

Get a channel

GET/api/v1/channels/{channel_id}

Parameters

ParameterInTypeRequiredDescription
channel_idpathstring (uuid)yes

Example request

curl https://api.speakspeak.net/api/v1/channels/{channel_id} \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
200The channel.Channel
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error

Update a channel

PATCH/api/v1/channels/{channel_id}

Parameters

ParameterInTypeRequiredDescription
channel_idpathstring (uuid)yes

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

StatusDescriptionBody
200The updated channel.Channel
403Missing access (code 50001) or missing permissions (code 50013).Error
# 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

ParameterInTypeRequiredDescription
channel_idpathstring (uuid)yes

Example request

curl -X DELETE https://api.speakspeak.net/api/v1/channels/{channel_id} \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
204Deleted.
403Missing access (code 50001) or missing permissions (code 50013).Error

Messages

List messages

GET/api/v1/channels/{channel_id}/messages

Parameters

ParameterInTypeRequiredDescription
channel_idpathstring (uuid)yes
limitqueryintegerno
beforequerystring (uuid)noUUIDv7 cursor — return messages older than this id.
afterquerystringnoAccepted for library compatibility; ignored.
aroundquerystringnoAccepted for library compatibility; ignored.

Example request

curl https://api.speakspeak.net/api/v1/channels/{channel_id}/messages \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
200Messages, newest first.array of Message
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error
# 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

ParameterInTypeRequiredDescription
channel_idpathstring (uuid)yes

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

StatusDescriptionBody
201The created message.Message
400Validation failure or upload cap exceeded (code 40005).Error
403Missing access (code 50001) or missing permissions (code 50013).Error
# 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

ParameterInTypeRequiredDescription
channel_idpathstring (uuid)yes
message_idpathstring (uuid)yes

Example request

curl https://api.speakspeak.net/api/v1/channels/{channel_id}/messages/{message_id} \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
200The message.Message
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error

Edit a message

PATCH/api/v1/channels/{channel_id}/messages/{message_id}

Parameters

ParameterInTypeRequiredDescription
channel_idpathstring (uuid)yes
message_idpathstring (uuid)yes

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

StatusDescriptionBody
200The edited message.Message
403Missing access (code 50001) or missing permissions (code 50013).Error
# 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

ParameterInTypeRequiredDescription
channel_idpathstring (uuid)yes
message_idpathstring (uuid)yes

Example request

curl -X DELETE https://api.speakspeak.net/api/v1/channels/{channel_id}/messages/{message_id} \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
204Deleted.
403Missing access (code 50001) or missing permissions (code 50013).Error

Trigger a typing indicator

POST/api/v1/channels/{channel_id}/typing

Parameters

ParameterInTypeRequiredDescription
channel_idpathstring (uuid)yes

Example request

curl -X POST https://api.speakspeak.net/api/v1/channels/{channel_id}/typing \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
204Typing indicator triggered.
403Missing access (code 50001) or missing permissions (code 50013).Error

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

ParameterInTypeRequiredDescription
channel_idpathstring (uuid)yes
message_idpathstring (uuid)yes
emojipathstringyesUnicode emoji (URL-encoded) or custom emoji as name:uuid.

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

StatusDescriptionBody
204Reaction added.
403Missing access (code 50001) or missing permissions (code 50013).Error

Remove own reaction

DELETE/api/v1/channels/{channel_id}/messages/{message_id}/reactions/{emoji}/@me

Parameters

ParameterInTypeRequiredDescription
channel_idpathstring (uuid)yes
message_idpathstring (uuid)yes
emojipathstringyesUnicode emoji (URL-encoded) or custom emoji as name:uuid.

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

StatusDescriptionBody
204Reaction removed.
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error

Pins

List pinned messages

GET/api/v1/channels/{channel_id}/pins

Parameters

ParameterInTypeRequiredDescription
channel_idpathstring (uuid)yes

Example request

curl https://api.speakspeak.net/api/v1/channels/{channel_id}/pins \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
200The pinned messages.array of Message
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error

Pin a message

PUT/api/v1/channels/{channel_id}/pins/{message_id}

Parameters

ParameterInTypeRequiredDescription
channel_idpathstring (uuid)yes
message_idpathstring (uuid)yes

Example request

curl -X PUT https://api.speakspeak.net/api/v1/channels/{channel_id}/pins/{message_id} \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
204Pinned.
403Missing access (code 50001) or missing permissions (code 50013).Error

Unpin a message

DELETE/api/v1/channels/{channel_id}/pins/{message_id}

Parameters

ParameterInTypeRequiredDescription
channel_idpathstring (uuid)yes
message_idpathstring (uuid)yes

Example request

curl -X DELETE https://api.speakspeak.net/api/v1/channels/{channel_id}/pins/{message_id} \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
204Unpinned.
403Missing access (code 50001) or missing permissions (code 50013).Error

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

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes

Example request

curl https://api.speakspeak.net/api/v1/guilds/{guild_id}/members \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
200The members.array of GuildMember
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error
# 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

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes
user_idpathstring (uuid)yes

Example request

curl https://api.speakspeak.net/api/v1/guilds/{guild_id}/members/{user_id} \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
200The member.GuildMember
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error

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

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes
user_idpathstring (uuid)yes

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

StatusDescriptionBody
200The updated member.GuildMember
403Missing access (code 50001) or missing permissions (code 50013).Error
# 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

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes
user_idpathstring (uuid)yes

Example request

curl -X DELETE https://api.speakspeak.net/api/v1/guilds/{guild_id}/members/{user_id} \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
204Kicked.
403Missing access (code 50001) or missing permissions (code 50013).Error

Add a role to a member

PUT/api/v1/guilds/{guild_id}/members/{user_id}/roles/{role_id}

Parameters

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes
user_idpathstring (uuid)yes
role_idpathstring (uuid)yes

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

StatusDescriptionBody
204Role added.
403Missing access (code 50001) or missing permissions (code 50013).Error

Remove a role from a member

DELETE/api/v1/guilds/{guild_id}/members/{user_id}/roles/{role_id}

Parameters

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes
user_idpathstring (uuid)yes
role_idpathstring (uuid)yes

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

StatusDescriptionBody
204Role removed.
403Missing access (code 50001) or missing permissions (code 50013).Error

Roles

List guild roles

GET/api/v1/guilds/{guild_id}/roles

Parameters

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes

Example request

curl https://api.speakspeak.net/api/v1/guilds/{guild_id}/roles \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
200All roles.array of Role
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error
# 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

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes

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

StatusDescriptionBody
201The created role.Role
403Missing access (code 50001) or missing permissions (code 50013).Error
# 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

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes

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

StatusDescriptionBody
200The full role list after reordering.array of Role
403Missing access (code 50001) or missing permissions (code 50013).Error
# 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

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes
role_idpathstring (uuid)yes

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

StatusDescriptionBody
200The updated role.Role
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error
# 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

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes
role_idpathstring (uuid)yes

Example request

curl -X DELETE https://api.speakspeak.net/api/v1/guilds/{guild_id}/roles/{role_id} \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
204Deleted.
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error

Bans

List bans

GET/api/v1/guilds/{guild_id}/bans

Parameters

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes

Example request

curl https://api.speakspeak.net/api/v1/guilds/{guild_id}/bans \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
200The guild's bans.array of Ban
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error
# 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

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes
user_idpathstring (uuid)yes

Example request

curl https://api.speakspeak.net/api/v1/guilds/{guild_id}/bans/{user_id} \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
200The ban.Ban
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error

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

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes
user_idpathstring (uuid)yes
X-Audit-Log-Reasonheaderstringno

Example request

curl -X PUT https://api.speakspeak.net/api/v1/guilds/{guild_id}/bans/{user_id} \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
204Banned.
403Missing access (code 50001) or missing permissions (code 50013).Error

Remove a ban

DELETE/api/v1/guilds/{guild_id}/bans/{user_id}

Parameters

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes
user_idpathstring (uuid)yes

Example request

curl -X DELETE https://api.speakspeak.net/api/v1/guilds/{guild_id}/bans/{user_id} \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
204Unbanned.
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error

Emojis

List guild emojis

GET/api/v1/guilds/{guild_id}/emojis

Parameters

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes

Example request

curl https://api.speakspeak.net/api/v1/guilds/{guild_id}/emojis \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
200The guild's custom emojis.array of Emoji
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error
# 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

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes
emoji_idpathstring (uuid)yes

Example request

curl https://api.speakspeak.net/api/v1/guilds/{guild_id}/emojis/{emoji_id} \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
200The emoji.Emoji
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error

Delete an emoji

DELETE/api/v1/guilds/{guild_id}/emojis/{emoji_id}

Emoji creation is not available over the bot API (upload flow).

Parameters

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes
emoji_idpathstring (uuid)yes

Example request

curl -X DELETE https://api.speakspeak.net/api/v1/guilds/{guild_id}/emojis/{emoji_id} \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
204Deleted.
403Missing access (code 50001) or missing permissions (code 50013).Error

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

ParameterInTypeRequiredDescription
channel_idpathstring (uuid)yes

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

StatusDescriptionBody
201The created invite.Invite
403Missing access (code 50001) or missing permissions (code 50013).Error
# 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

ParameterInTypeRequiredDescription
codepathstringyes

Example request

curl -X DELETE https://api.speakspeak.net/api/v1/invites/{code} \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
204Deleted.
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error

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

ParameterInTypeRequiredDescription
guild_idpathstring (uuid)yes
limitqueryintegerno
beforequerystring (uuid)noUUIDv7 cursor.
afterquerystringnoAccepted for library compatibility; ignored.
user_idquerystring (uuid)noFilter by acting user.
action_typequeryintegernoDiscord audit-log action type (20, 22 or 23).

Example request

curl https://api.speakspeak.net/api/v1/guilds/{guild_id}/audit-logs \
  -H "Authorization: Bot ssbot_<app>.<secret>"

Responses

StatusDescriptionBody
200The audit log.AuditLog
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error
# 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

ParameterInTypeRequiredDescription
webhook_idpathstring (uuid)yes
tokenpathstringyes

Example request

curl https://api.speakspeak.net/api/v1/webhooks/{webhook_id}/{token}

Responses

StatusDescriptionBody
200The webhook.Webhook
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error
# 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

ParameterInTypeRequiredDescription
webhook_idpathstring (uuid)yes
tokenpathstringyes
waitquerybooleannoWhen true, returns the created message instead of 204.

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

StatusDescriptionBody
200The created message (wait=true).Message
204Executed (wait absent or false).
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error
429Rate limited. Carries Retry-After and the X-RateLimit-* header set; X-RateLimit-Global: true only when the global bucket tripped.RateLimitError
# 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

ParameterInTypeRequiredDescription
interaction_idpathstring (uuid)yes
tokenpathstringyes

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

StatusDescriptionBody
204Response accepted.
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error
429Rate limited. Carries Retry-After and the X-RateLimit-* header set; X-RateLimit-Global: true only when the global bucket tripped.RateLimitError

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

ParameterInTypeRequiredDescription
app_idpathstring (uuid)yes
tokenpathstringyes

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

StatusDescriptionBody
200The created follow-up message.Message
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error

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

ParameterInTypeRequiredDescription
app_idpathstring (uuid)yes
tokenpathstringyes

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

StatusDescriptionBody
200The edited message.Message
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error

Delete the original interaction response

DELETE/api/v1/webhooks/{app_id}/{token}/messages/@originalno authentication

Parameters

ParameterInTypeRequiredDescription
app_idpathstring (uuid)yes
tokenpathstringyes

Example request

curl -X DELETE https://api.speakspeak.net/api/v1/webhooks/{app_id}/{token}/messages/@original

Responses

StatusDescriptionBody
204Deleted.
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error

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

StatusDescriptionBody
101Switching protocols to the gateway WebSocket.

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

StatusDescriptionBody
200Where the gateway lives and the identify budget.GatewayBotInfo
401Missing or malformed Authorization Bot header (code 40001).Error
# 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

ParameterInTypeRequiredDescription
client_idquerystring (uuid)yes
scopequerystringyesOAuth2 scope string; bot is the supported scope.
permissionsqueryinteger (int64; min 0)noRequested permission bitfield (SpeakSpeak bits).
guild_idquerystring (uuid)no

Example request

curl "https://api.speakspeak.net/api/v1/oauth2/authorize?client_id=<client_id>&scope=<scope>"

Responses

StatusDescriptionBody
200Install metadata.AuthorizeResponse
404Unknown resource — also returned for resources the bot may not see (codes 10003 channel, 10004 guild, 10006 invite, 10008 message, 10011 role, 10013 user, 10014 emoji, 10026 ban).Error
# 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.

FieldTypeRequiredDescription
messagestringyes
codeintegeryesDiscord numeric error code; 0 for generic errors.

RateLimitError

The 429 body (Discord's shape — no code field).

FieldTypeRequiredDescription
messagestring (always You are being rate limited.)yes
retry_afternumberyesSeconds until the tripped bucket resets.
globalbooleanyes

User

FieldTypeRequiredDescription
idstring (uuid)yes
usernamestringyes
discriminatorstring (always 0)yesAlways "0" — SpeakSpeak has no discriminators.
global_namestringno
avatarstring | nullnoRelative proxy path (/api/v1/avatars/{id}), not a Discord CDN hash. Example: "/api/v1/avatars/019a4a18-bfd9-73c1-9aa4-7b0d24e19af5"
botbooleanyes

Guild

FieldTypeRequiredDescription
idstring (uuid)yes
namestringyes
iconstring | nullnoRelative icon-proxy path, not a CDN hash. Example: "/api/v1/server-icons/019cebae-61c0-72d0-9be5-566cc17d0442"
owner_idstring (uuid)yes
descriptionstring | nullno

Channel

FieldTypeRequiredDescription
idstring (uuid)yes
guild_idstring (uuid)no
typeinteger (one of: 0, 2, 4, 5, 15)yes0 text, 2 voice, 4 category, 5 announcement, 15 forum (SpeakSpeak's board channels also map to 15).
namestringyes
topicstring | nullno
positionintegerno
parent_idstring | nullnoCategory id; null for categories themselves.
nsfwbooleanno

GuildMember

FieldTypeRequiredDescription
userUseryes
nickstring | nullno
rolesarray of string (uuid)yes
joined_atstring (date-time)yes
deafbooleannoAlways false in this phase.
mutebooleannoAlways false in this phase.

Role

FieldTypeRequiredDescription
idstring (uuid)yes
namestringyes
colorintegerno
hoistbooleanno
positionintegeryes
permissionsstringyesStringified u64 bitfield (Discord's wire format). Bit positions are SpeakSpeak's own permission model, not Discord's. Example: "8522776"
mentionablebooleanno
managedbooleannoAlways false.

Ban

FieldTypeRequiredDescription
reasonstring | nullno
userUseryes

Emoji

FieldTypeRequiredDescription
idstring (uuid)yes
namestringyes
rolesarray of stringnoAlways empty.
usernullnoUploader not resolved; always null.
require_colonsboolean (always true)no
managedboolean (always false)no
animatedbooleanno
availablebooleanno

Message

FieldTypeRequiredDescription
idstring (uuid)yes
channel_idstring (uuid)yes
guild_idstring | null (uuid)no
authorUseryes
contentstringyesEmpty string when the gateway session lacks the MESSAGE_CONTENT intent (REST reads always include it).
timestampstring (date-time)yes
edited_timestampstring | null (date-time)no
attachmentsarray of Attachmentno
embedsarray of Embedno
mentionsarray of Userno
mention_rolesarray of stringnoAlways empty in this phase.
mention_everyoneboolean (always false)no
pinnedbooleanno
typeinteger (always 0)yes
flagsinteger (always 0)no

Attachment

FieldTypeRequiredDescription
idstring (uuid)yes
filenamestringyes
content_typestring | nullno
sizeintegeryesBytes.
urlnullnoAlways null — attachment bytes are reachable only through SpeakSpeak's own presign flow, which the bot API does not expose.
proxy_urlnullnoAlways null (see url).

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).

FieldTypeRequiredDescription
typestring (one of: rich, link)no
titlestring | nullno
descriptionstring | nullno
urlstring | nullno
colorinteger | nullno
timestampstring | null (date-time)no
footerobjectno
footer.textstringno
footer.icon_urlstring | nullno
authorobjectno
author.namestringno
author.urlstring | nullno
author.icon_urlstring | nullno
imageobjectno
image.urlnullno
thumbnailobjectno
thumbnail.urlnullno
providerobjectno
provider.namestring | nullno
fieldsarray of objectno
fields[].namestringyes
fields[].valuestringyes
fields[].inlinebooleanno

Invite

FieldTypeRequiredDescription
codestringyes
guild_idstring (uuid)yes
channel_idnullnoAlways null — SpeakSpeak invites are server-scoped.
inviternullnoNot resolved; always null.
usesintegerno
max_usesintegerno
max_agenullnoAlways null — the expiry is expires_at instead.
temporaryboolean (always false)no
expires_atstring | null (date-time)no
created_atstring (date-time)no

AuditLog

FieldTypeRequiredDescription
audit_log_entriesarray of AuditLogEntryyes
usersarray of Useryes

AuditLogEntry

FieldTypeRequiredDescription
idstring (uuid)yes
action_typeinteger (one of: 20, 22, 23)yes20 MEMBER_KICK, 22 MEMBER_BAN_ADD, 23 MEMBER_BAN_REMOVE.
user_idstring (uuid)yesThe acting user.
target_idstring | nullno
reasonstring | nullno
changesarray of anynoAlways empty.
optionsnullno

Webhook

FieldTypeRequiredDescription
idstring (uuid)yes
typeinteger (always 1)yes1 = Incoming.
guild_idstring (uuid)yes
channel_idstring (uuid)yes
namestring | nullno
avatarstring | nullno
application_idnullno

GatewayBotInfo

FieldTypeRequiredDescription
urlstringyesExample: "wss://api.speakspeak.net/api/v1/gateway"
shardsinteger (always 1)yes
session_start_limitobjectyes
session_start_limit.totalintegerno
session_start_limit.remainingintegerno
session_start_limit.reset_afterintegernoMilliseconds.
session_start_limit.max_concurrencyintegerno

ApplicationSummary

FieldTypeRequiredDescription
app_idstring (uuid)yes
namestringyes
icon_urlstringnoRelative media path (e.g. /api/v1/avatars/{id}), not an absolute URL — resolve it against the GIP base like every other avatar.

AuthorizeResponse

FieldTypeRequiredDescription
applicationApplicationSummaryyes
requested_permissionsstringyesPermission bitfield as a DECIMAL STRING, not a number — the same JS-precision-safe convention the GIP bot-token endpoints use for TokenMetadata.scopes. Parse it as a 64-bit integer. Example: "0"
guild_idstring (uuid)no
install_endpointstringyesWhere the install is completed.

CreateMessageBody

FieldTypeRequiredDescription
contentstringno
message_referenceobjectnoReply reference.
message_reference.message_idstring (uuid)yes
embedsarray of Embedno

UpdateMessageBody

FieldTypeRequiredDescription
contentstringno

CreateChannelBody

FieldTypeRequiredDescription
namestringyes
typeinteger (one of: 0, 2, 4, 5, 15)no4 creates a category.
parent_idstring (uuid)no
user_limitintegerno

UpdateChannelBody

Absent fields (and explicit null) mean "unchanged".

FieldTypeRequiredDescription
namestringno
topicstringno
user_limitintegerno
parent_idstring (uuid)no
positionintegernoAccepted for library compatibility; ignored (reorder in bulk via PATCH /guilds/{id}/channels instead).

ChannelPositionEntry

FieldTypeRequiredDescription
idstring (uuid)yes
positionintegeryes
parent_idstring (uuid)no

CreateRoleBody

FieldTypeRequiredDescription
namestringno
permissionsstringnoStringified u64 bitfield (SpeakSpeak bits).
colorintegerno
hoistbooleanno
mentionablebooleanno

UpdateRoleBody

FieldTypeRequiredDescription
namestringno
permissionsstringno
colorintegerno
hoistbooleanno
mentionablebooleanno

RolePositionEntry

FieldTypeRequiredDescription
idstring (uuid)yes
positionintegeryes

UpdateMemberBody

FieldTypeRequiredDescription
nickstringno
rolesarray of string (uuid)noFull replacement role-id list.
communication_disabled_untilstring | null (date-time)noTimeout until this ISO-8601 timestamp; explicit null clears the timeout; absent leaves it unchanged.

CreateInviteBody

FieldTypeRequiredDescription
max_usesintegerno
max_ageintegernoSeconds until expiry (Discord's field name).

WebhookExecuteBody

FieldTypeRequiredDescription
contentstringno
embedsarray of Embedno
usernamestringnoPer-message author display-name override.
avatar_urlstringnoPer-message avatar override.
ttsbooleannoAccepted, ignored.
allowed_mentionsobjectnoAccepted, ignored.

InteractionCallbackBody

FieldTypeRequiredDescription
typeinteger (one of: 4, 5, 9)no4 CHANNEL_MESSAGE_WITH_SOURCE, 5 DEFERRED, 9 MODAL.
dataobjectnoFor type 4: {content, embeds, flags} — flags bit 64 (EPHEMERAL) is honoured; allowed_mentions/tts are ignored. For type 9: {custom_id, title, components}.

FollowupBody

FieldTypeRequiredDescription
contentstringno
embedsarray of Embedno

Questions & feedback

The Bot API is in public beta and growing. Questions, feedback or a library request? Write to contact@speakspeak.net.