Discord Bot API
Create a service.discord client and read safe bot, giveaway, guild, and leaderboard data for community sites and widgets.
Local portal docs
The TCM Discord Bot API lets approved server-side apps read safe Discord bot data and manage Discord bot settings for servers the app owner is allowed to manage.
Read endpoints can power community sites, widgets, dashboards, and giveaway growth pages. Management endpoints use the same server-owner permission checks as Discord Manager, so a developer app can only change a guild when the app owner has a linked Discord account and can manage that Discord server.
Create A Service Client
Create a machine-to-machine service client in the Developers portal.
Use:
- Audience:
service.discord - Scopes:
discordbot:readdiscordbot:leaderboards:readdiscordbot:managediscordbot:giveaways:writediscordbot:leveling:writediscordbot:economy:write
The client secret is shown once. Store it only on your backend.
Mint A Token
Use the OAuth token endpoint with client_credentials.
curl -u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=client_credentials \
-d resource=service.discord \
-d scope="discordbot:read discordbot:leaderboards:read" \
https://www.thecrimsonmarket.com/mana/oauth/tokenThe response contains a short-lived bearer token:
{
"access_token": "<jwt>",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "discordbot:read discordbot:leaderboards:read",
"aud": "service.discord"
}Every API request must send:
Authorization: Bearer <access_token>Base URL
Production:
https://www.thecrimsonmarket.com/mana/discord/api/v1Development:
https://dev.portal.raum.au/mana/discord/api/v1Endpoints
Bot Status
Requires discordbot:read.
GET /statusReturns bot runtime health and the enabled public API capabilities.
Public Giveaways
Requires discordbot:read.
GET /giveaways?limit=25
GET /guilds/{guildId}/giveaways?limit=25Returns active public giveaways, Discord join/message URLs, entry methods, entry counts, end timestamps, and server display metadata.
Guild Summary
Requires discordbot:read.
GET /guilds/{guildId}Returns safe server metadata for a server where the bot is installed:
- server id
- server name
- icon URL
- member count when Discord provides it
- Discord guild features
- bot join date
- availability status
This endpoint does not return channel lists, role lists, private dashboard config, moderation cases, member lists, or server-owner data.
Server XP Leaderboard
Requires discordbot:leaderboards:read.
GET /guilds/{guildId}/levels/leaderboard?limit=10Returns public server leveling rows:
- rank
- Discord user id
- display name
- username
- XP
- level
- message count
- voice seconds
Server Credit Leaderboard
Requires discordbot:leaderboards:read.
GET /guilds/{guildId}/economy/leaderboard?period=season&limit=10Supported periods:
seasonweeklymonthlyallTime
Returns public server credit leaderboard rows. It does not expose spendable balances or allow purchases.
Manageable Guilds
Requires discordbot:manage.
GET /me/guildsReturns the bot servers the developer app owner can manage. The owner must have Discord linked to their TCM account.
Guild Management Overview
Requires discordbot:manage.
GET /guilds/{guildId}/managementReturns dashboard-equivalent setup data for one server the app owner can manage, including eligible channels, roles, giveaway setup data, command status, and feature configuration.
Create Giveaway
Requires discordbot:giveaways:write.
POST /guilds/{guildId}/giveawaysCreates a giveaway using the same payload fields and validation as Discord Manager.
Update Leveling Config
Requires discordbot:leveling:write.
PATCH /guilds/{guildId}/leveling/configUpdates server leveling settings, including message XP, voice XP, level roles, rank-card behavior, global level-up webhooks, and per-level-role reward webhooks.
Each levelRoleRewards row can include its own outbound webhook. The webhook is sent only after that specific Discord role is awarded to the member.
{
"levelRoleRewards": [
{
"level": 10,
"roleId": "123456789012345678",
"webhookEnabled": true,
"webhookUrl": "https://example.com/api/discord/level-role-reward",
"webhookHeaderName": "X-TCM-Webhook-Key",
"webhookApiKey": "replace-with-shared-key",
"webhookSigningSecret": "optional-hmac-secret"
}
]
}Leave webhookApiKey or webhookSigningSecret blank when updating a row to keep the saved secret.
Update Economy Config
Requires discordbot:economy:write.
PATCH /guilds/{guildId}/economy/configUpdates server credit economy settings, including reward shop behavior and purchase webhooks.
Command Status And Refresh
Requires discordbot:manage.
GET /guilds/{guildId}/commands
POST /guilds/{guildId}/commands/refreshChecks slash-command registration status or refreshes commands for one server the app owner can manage.
Error Responses
Common responses:
401 Missing bearer token401 Invalid token401 Invalid token audience401 Service token required403 Insufficient scope403 Developer API client is not active403 The developer app owner must link Discord before using Discord management APIs403 You do not have permission to manage that Discord server404 The bot is not in that Discord server or the server is unavailable
Example
const tokenResponse = await fetch("https://www.thecrimsonmarket.com/mana/oauth/token", {
method: "POST",
headers: {
Authorization: `Basic ${Buffer.from(`${process.env.TCM_CLIENT_ID}:${process.env.TCM_CLIENT_SECRET}`).toString("base64")}`,
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
grant_type: "client_credentials",
resource: "service.discord",
scope: "discordbot:read discordbot:leaderboards:read",
}),
});
const { access_token } = await tokenResponse.json();
const leaderboard = await fetch(
"https://www.thecrimsonmarket.com/mana/discord/api/v1/guilds/1117111237231267884/levels/leaderboard",
{
headers: {
Authorization: `Bearer ${access_token}`,
},
},
).then((response) => response.json());Permission Boundaries
Management requests are authorized in this order:
- The bearer token must be valid for the
service.discordaudience. - The token must contain the required Discord bot API scope.
- The token client ID must resolve to an active Portal developer app.
- The developer app owner must have a linked Discord ID.
- For every guild action, the bot checks that Discord user is still a member and has Administrator, Manage Server, or Manage Channels.
The API is designed for safe external consumption:
- Read public bot and server data.
- Read active public giveaways.
- Read public leaderboards.
- Manage settings only for servers the app owner can manage.
- Do not expose moderation history.
- Do not expose role/member/channel inventories.
- Do not allow arbitrary Discord messages or moderation actions.
Outbound Webhook Events
Server owners can configure webhook delivery from the Discord Manager dashboard or through management API settings.
discord.level_up: sent when a member reaches a new server level. Includes guild ID, Discord ID, username/display name, previous/current level, XP totals, source, role rewards, and credit rewards.discord.level_role_reward: sent when a configured level role reward is actually awarded. Includes guild ID, Discord ID, username/display name, previous/current level, reward level, XP totals, source, and the awarded role ID/name.discord.reward_purchase: sent when a member buys a reward shop item. Includes guild ID, Discord ID, item details, purchase ID, status, price, role reward details, and remaining balance.
Webhook requests support a configurable API key header and optional x-tcm-signature HMAC SHA-256 signature over {timestamp}.{jsonBody}.