API reference
Every route lives under /api on the site origin, and every response is JSON.
Public reads answer with an ok boolean first. Anything authenticated answers
with ok: false and an error string instead of a payload, so a client can tell
"the bot said no" from "the bot is unreachable".
{ "ok": true, "generatedAt": "2026-10-18T20:00:00.000Z" }
Public routes
No token, no cookies. These are cached for 30-300 seconds.
| Method | Path | What it returns |
|---|---|---|
| GET | /api/health |
ok, deployment state, command and category counts, last push time |
| GET | /api/brand |
Bot name, avatar, invite URL, support server |
| GET | /api/stats |
Servers, members, channels, uptime, latency |
| GET | /api/stats/commands?limit=25 |
Most used commands with use counts |
| GET | /api/stats/shortcuts?limit=24 |
Short aliases worth remembering |
| GET | /api/catalog |
Every command with its metadata |
| GET | /api/catalog?category=moderation |
The same, filtered by category |
| GET | /api/catalog?q=ban&limit=20 |
The same, filtered by name, alias or description |
| GET | /api/catalog/categories |
Categories with command and alias counts |
| GET | /api/catalog/:name |
One command, by name or alias |
| GET | /api/servers?invites=0 |
The public server wall (name, icon, member count, invite) |
One command
GET /api/catalog/role returns the same card the bot prints in Discord:
{
"ok": true,
"command": {
"name": "role",
"aliases": ["r"],
"category": "moderation",
"description": "Add or remove a role from a member.",
"usage": ",role <member> [role]",
"cooldown": 5,
"parameters": [
{ "name": "member", "required": true },
{ "name": "role", "required": false }
],
"pages": [
{ "cmd": "all", "description": "add a role to every member", "parameters": ["(role)"] }
],
"examples": [",role @User @Role", ",role all @Role"],
"tierLabel": null,
"premium": false,
"docs": "/docs/commands/moderation",
"page": "/commands/moderation/role"
}
}
parameters is the legend: (name) and <name> are required, [name] is
optional. pages is one entry per subcommand page - the same list ,role with
no argument draws.
Authentication
The dashboard signs in with Discord OAuth2. The site never sees the token: it is handed back in the URL fragment after the callback and kept in the browser.
| Method | Path | Notes |
|---|---|---|
| GET | /api/auth/login |
Redirects to Discord |
| GET | /api/auth/callback |
Returns to the site with the session |
| GET | /api/me |
The signed-in user, their servers, owner flags |
Authenticated requests send Authorization: Bearer <token> and are never
cached.
Per-server routes
Every route below takes a guild id and checks that the signed-in user can manage
that guild. A server the user is not in answers 403, not an empty list.
| Method | Path | What it does |
|---|---|---|
| GET | /api/guilds |
Servers the user and the bot are both in |
| GET | /api/guilds/:id/config |
Every setting, its value and its schema |
| PATCH | /api/guilds/:id/config |
{ values: { key: value } }, partial |
| PATCH | /api/guilds/:id/settings |
Rename the server or change its icon |
| GET | /api/guilds/:id/structure |
Categories and channels, in server order |
| GET | /api/guilds/:id/channels |
Text channels the dashboard can target |
| GET | /api/guilds/:id/roles |
Every role except @everyone, highest first |
| GET | /api/guilds/:id/logs |
The last 25 audit-log entries |
| GET | /api/guilds/:id/autorole |
Auto roles and whether they are enabled |
| PATCH | /api/guilds/:id/autorole |
{ roles: [ids], enabled } |
| GET | /api/guilds/:id/security |
Antinuke modules and their state |
| PATCH | /api/guilds/:id/security/modules |
Turn a protection on or off |
| POST | /api/guilds/:id/security/panic |
Lock the server down now |
| POST | /api/guilds/:id/moderation/:action |
ban, unban, kick, mute, unmute, warn |
| GET | /api/guilds/:id/moderation/warnings/:userId |
One member's warnings |
| GET | /api/guilds/:id/tickets |
Tickets and their counts; ?open=true for open ones |
| POST | /api/guilds/:id/tickets/panel |
Post (or refresh) the ticket panel |
| POST | /api/guilds/:id/tickets/:channelId/claim |
Claim a ticket |
| POST | /api/guilds/:id/tickets/:channelId/close |
Close and delete the ticket channel |
| GET | /api/guilds/:id/tickets/:channelId/transcript |
HTML transcript of one ticket |
| GET | /api/guilds/:id/dm |
The last mass DM, its history and opt-outs |
| GET | /api/guilds/:id/dm/preview |
Who a campaign would reach, with counts |
| POST | /api/guilds/:id/dm |
Start a campaign (confirm: true to send) |
| GET | /api/guilds/:id/dm/:jobId |
Progress for one campaign |
| POST | /api/guilds/:id/dm/:jobId/cancel |
Stop a running campaign |
| POST | /api/guilds/:id/dm/optout |
{ userId, on } — add or remove an opt-out |
Mass DM is owner-tier: only the server owner, an extra owner, the configured owner role or a bot owner can read or start one. Every other per-server route needs only membership to read, and Manage Server to change something.
Owner routes
Bot owners only. A non-owner gets 403.
| Method | Path | What it does |
|---|---|---|
| GET | /api/admin/overview |
Every server, live stats and the owner list |
| GET | /api/admin/shortcuts |
The most used aliases |
| GET | /api/admin/guilds/:id/invite |
Mint (or reuse) an invite for one server |
| POST | /api/admin/guilds/:id/leave |
Make the bot leave (confirm: true) |
| GET | /api/admin/broadcast/preview |
Count and sample a broadcast audience |
| POST | /api/admin/broadcast |
Start a DM broadcast |
| GET | /api/admin/broadcast/:jobId |
Broadcast progress |
| POST | /api/admin/broadcast/:jobId/cancel |
Stop a broadcast |
PATCH /api/guilds/:id/config answers with what it applied and what it refused,
so a stale dashboard tab fails one field instead of the whole form:
{
"ok": true,
"applied": { "prefix": "!" },
"failed": []
}
When the bot is unreachable
The API proxies to the bot host. If that host is down, public reads fall back to
the last snapshot the bot pushed and say so in source - push, capture or
build. Private routes never fall back: they answer 503 rather than inventing
a server state.