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 can manage |
| 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, change prefix |
| GET | /api/guilds/:id/channels |
Channels, with the bot's own permissions |
| GET | /api/guilds/:id/roles |
Roles the bot can assign |
| GET | /api/guilds/:id/logs |
Recent moderation cases |
| 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/tickets |
Open tickets |
| GET | /api/guilds/:id/tickets/:channelId/transcript |
HTML transcript of one ticket |
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.