Skip to content
Now live — sign in to your guild dashboard
Guild Butler
EN
Sign in Add to Discord
Killboard

Raids

Raids & signupsAttendance & stats

Economy

Loot & settlementRegearSiphoned energy

Management

Recruitment & applicationsRoles & nicknamesRoster upkeep

Extras

GiveawaysDashboard & white-labelFeatures Pricing Docs
Sign in Add to Discord

Features Dashboard & white-label

Your guild’s stats in a browser. Your guild’s name on the bot.

Sign in with Discord and read your guild — balances, attendance, regears — as tables and charts instead of scrolling embeds. And when your guild wants its own bot, we stand one up: same code, same data, your name and avatar on it.

Discord
🤖
Guild Butler ✓ APP Today at 04:12
🤖 Bot status
🟢 Online
Version: v2.6.0
Online since: 4 hours ago
Feedback
The pinned bot-status board, right after a deploy finishes. “Online since” is a live Discord timestamp — it counts up on its own with no further edits.
Read-only
It can’t touch your silver
The stats API opens the bot’s database with a read-only handle, and the container mounts the data volume read-only on top of that. Two independent layers.
3
Discord permissions asked for at login
identify, guilds, guilds.members.read — enough to know who you are and what rank you hold. No separate account, no password.
24h
Longest a sign-in lasts
Capped below Discord’s own multi-day token lifetime, so a stolen cookie is never valid for days.
39
Commands a dedicated bot registers
The full command set, pushed under your bot’s own application id as one bulk overwrite.

01 · Why it exists

Discord is a good place to run a raid and a bad place to read a table

Officers can’t compare twenty members inside an embed. And no setting anywhere puts your guild’s name on the shared bot — that wall is Discord’s, not ours.

What officers do today

An officer who wants to see who has earned what scrolls pages of balances in Discord, screenshots embeds into a spreadsheet, or asks the one person who understands the ledger. Discord embeds are excellent at showing one thing at a time and hopeless at showing forty rows side by side.

The bot has been recording the underlying events since day one — settlements, per-raid participants, the attendance ledger, the balance ledger. All the dashboard does is present them as a table and a chart, from the same file the bot writes.

And the other wall

A Discord application has exactly one global username and one global avatar, identical in every server it joins. There is no per-server avatar and no configuration flag that invents one. So a guild that wants "our bot" instead of "a bot" cannot get it from the shared application — which is why the answer is a second application, not a setting.

Discord

Only you can see this

🔒 Custom avatar needs a dedicated bot (white-label Enterprise). Ask the bot owner to set one up.

The refusal is specific on purpose: it names the reason and the fix, rather than telling you the command failed.

02 · Signing in

Four steps, and none of them is a password

There is no account to create. Discord already knows who you are and which servers you are in — the dashboard asks it, and asks for nothing else.

  1. Log in with Discord officer

    Open the dashboard and press Log in with Discord. It asks Discord for three things only: who you are, which servers you are in, and your roles in those servers (identify, guilds, guilds.members.read). There is no separate account and no password to remember. The code-for-token exchange happens on the server; your browser only ever holds an opaque, signed, httpOnly session cookie.

  2. Pick a server officer

    The left column is headed Your servers and lists the intersection of the servers you are in and the servers the bot actually has data for. Each entry shows the server icon, its name and its plan — Free, Pro or Enterprise. Nothing is cross-server: every query in the API takes one server id, and your membership of that server is re-checked on every request.

  3. Read the tabs your rank unlocks officer

    Everyone in the server gets Overview, My stats, Regear and Giveaways. Members is officer-only, because it lists other people’s earnings and balances. Officer is resolved exactly as the bot resolves it — Discord Administrator or server owner, or the officer role you configured — and the header shows a green Officer or grey Member pill so you always know which view you are in.

  4. Read here, act in Discord officer

    None of these tabs has a Save button, because there is nothing to save. Adjusting a balance, settling a raid, drawing a giveaway, approving a regear and every configuration change stay in Discord, where the permission checks and the audit trail already live. The dashboard is the reading surface, not a second way to move silver.

The dashboard lives at app.guild-butler.com, behind HTTPS with automatically issued certificates. It is a separate container from the Discord bot, sharing only the database file — so a fault in the web code can never take your bot offline, and the marketing site's own status strip is just a poll of the dashboard's public health endpoint.

03 · What is on it

Six tabs, and the one that replaces a spreadsheet

Most of the dashboard is the same data your members already see in Discord, laid out so you can compare rows. One tab is officer-only, and one exists for the operator.

TabWho sees itWhat is on it
Overview Everyone in the server Nine guild totals — raids settled, gross silver, both banks, repairs, payouts, both taxes, paid to outsiders — plus a cumulative trend chart.
Members Officers only Every member in one table: attended, benched, points, earned, paid out, balance. Ordered by total earned.
My stats Everyone, own row only Six cards: raids attended, benched, attendance points, total earned, paid out, current balance.
Regear Everyone in the server Players and Events views, filtered by all time / this week / season, with a death-versus-overcharge split and a paid-over-time chart. Officers additionally see an approval rate and a turnaround column.
Giveaways Everyone in the server The 50 most recent posted giveaways with host, entrants, winners and what each actually paid.
White-label Bot operator only Every dedicated bot with its live status, the token intake form, and a name / avatar editor. Guild officers never see this tab.

Why Earned minus Paid out always equals Balance

The members board could have computed "earned" from settlement rows and "paid out" from payout rows. It does not. It splits the balance ledger by sign: earned is the sum of every credit, paid out the sum of every debit.

That choice makes the columns reconcile by construction. A settlement undo, a regear credit, a season payout or a manual adjustment can never be double-counted or quietly missing, because there is no list of reasons to keep up to date — there are only two signs. My stats uses the identical arithmetic, so a member can never be told one balance on the dashboard and a different one in Discord.

Anyone who has ever touched the guild's economy appears on the board, including a member who was hand-adjusted and never raided. Pure outsiders — pugs and allies who take their full share and carry no balance — do not, because they are not tracked.

MemberAttendedBenchedPoints EarnedPaid outBalance
Ashvane4264418,400,00012,000,0006,400,000
Brannt31113313,050,00013,050,0000
Cordo9293,720,0001,000,0002,720,000

The columns and their arithmetic are exactly as rendered; the figures above are made up. Rows are ordered by earned, then by balance.

04 · Read-only, and private

Any one of these guards would be enough

A web page over your guild’s silver should not hang on one check. It doesn’t — these guards are independent, and each holds on its own.

GuardWhat it isWhat it stops
Read-only database handle The API opens the bot's SQLite file with readonly: true, plus a query_only pragma on top. A bug in the web code physically cannot mutate guild data.
Read-only volume mount In production the container mounts the data volume :ro. The same thing again, one layer down, in case the first is ever wrong.
Separate container Same image, different command; the Discord gateway client runs in its own service. A dashboard fault can never take the bot offline — and vice versa.
No encryption key The dashboard container explicitly blanks the token-encryption key. The master key and the encrypted bot tokens never sit in the same process.
Control path fails closed The stats API rejects any /control/* request in its earliest hook, before a body is parsed. If a proxy is ever misconfigured, a misrouted bot token is dropped, not absorbed.

The check is on the server, not the tab

Hiding a tab is not a permission model. If a member asks the API for the members board, the route answers 403 — the check is on the server, and the hidden tab is only the polite version of it.

Cross-server access is impossible by construction rather than by policy: every query takes one server id, and your membership of that server is re-verified on each request from Discord's own answer. An adversarial security review of exactly these two dimensions — cross-guild isolation and SQL — found no leak, and the authorization boundary has its own unit tests.

Officer rank comes from the same two inputs the bot uses: the Administrator bit Discord returns for each of your servers, and the configured officer role read from your member roles. When a server has configured no tier roles at all, the role lookup is skipped rather than spending a Discord round-trip to learn that; when the lookup fails, you fall back to member tier, never up.

05 · Your name on the bot

A nickname, or a bot that is actually yours

Two levels, because Discord allows exactly two. One is a setting. The other is a second Discord application, standing behind the same code.

Lite: a per-server nickname

On the shared bot, /whitelabel name sets a per-server nickname — the one part of a bot's identity Discord lets a single application vary per server. It is stored on your server's row and re-applied on every restart, so a deploy does not quietly reset it. If the bot is missing Change Nickname, the command says so by name; the restart pass just skips that server rather than failing the whole boot.

/whitelabel avatar is refused here, with the reason and the fix. /whitelabel clear puts the default name back.

Full: a dedicated application

The only route to a fully branded bot is a separate Discord application whose global identity is your brand. Because that application serves exactly one customer, its global name and avatar are safe to set from inside your own server — the same two commands now change the real thing, not a nickname.

Discord

Only you can see this

🏷️ White-label is on. Current name: VITRYLA Butler.

/whitelabel status is officer-gated but runs on any plan, and tells you plainly whether white-label is unlocked.

Standing up a dedicated bot

  1. We create the application bot

    Discord has no API for creating bot applications, so this step is done by hand in the Discord Developer Portal: an app named after your guild, with your icon, and a Bot user added to it. That name and icon are what your members will see on the invite screen, and Discord offers no way to change an app’s name programmatically.

  2. You invite it with one link officer

    The White-label tab builds the invite link from the Application ID, with exactly the permissions the bot needs. That bitfield is maintained rather than guessed: an earlier one was missing Manage Roles, Manage Threads and Create Private Threads, so a freshly invited bot could not grant a recruit role, could not remove one on a departure sweep, and fell back to public threads where it should have used private ones.

  3. The token goes to the bot, not the dashboard bot

    The Application ID and Bot token are pasted into the owner-only White-label tab. The token is validated against Discord first — a bot token’s user id is its application id, so a single call proves the token is real *and* matches the id you typed — then encrypted with AES-256-GCM and stored. A typo can never persist as a dead row that fails later.

  4. The dedicated bot comes online bot

    The client logs in and registers every slash command under its own application id. Only on a confirmed start does the row flip to active and the server flip to Enterprise — a failed start says so and leaves the shared bot serving you meanwhile, rather than claiming a retry it will not perform.

  5. The shared bot stands down bot

    From that moment, one rule applies: each white-label server is served by exactly one of our bots. The shared bot ignores your server and its slash commands there are cleared, so nothing is ever posted twice and no command appears twice in the picker.

  6. Your data does not move member

    Raids, balances, regear claims, attendance, giveaways and settings are all keyed to your Discord server id, so the dedicated bot reads and writes the same rows the shared bot did. There is nothing to export and nothing to import. One practical note: buttons on messages the old bot posted stop responding, so re-run the command to post fresh panels.

The no-overlap rule

Everything above rests on one sentence: each server is served by exactly one of our bots. A dedicated client handles interactions from its own server and nothing else. The shared client handles everything except servers that already have a live dedicated bot. Direct messages go to the shared client. That is the whole decision, and it is a pure function with its own unit tests, so it can be reasoned about without Discord in the loop.

One detail matters more than it looks: the shared bot checks the live client registry, not the database status. A tenant row marked active whose client is not actually running must not silence the shared bot, or the server would be served by nobody at all — a brick that looks exactly like an outage.

This client Interaction from That server has a live dedicated bot Serve it?
Dedicated bot for server AServer Ayesyes
Dedicated bot for server AServer Bnono
Dedicated bot for server Aa direct messageno
Shared botServer Anoyes
Shared botServer Ayesno
Shared bota direct messageyes

06 · The token handover

The read-only API is never allowed to hold a bot token

Your bot’s token is the most valuable secret in the system, so it goes straight to the bot process — the stats API can’t take one, even by accident.

How the hand-off works

The dashboard API mints a short-lived, HMAC-signed capability ticket that proves only one thing: this operator authorized this server. It is valid for two minutes, single use, and bound to both the operator and the server. Along with the ticket, the API tells the browser where to post the token — the bot's own control endpoint, never the API's.

The bot verifies the ticket in constant time, re-checks operator status itself rather than trusting the ticket alone, and burns the single-use nonce into the database so a replay survives a restart. Only then does it validate the token against Discord, encrypt it with AES-256-GCM and start the client. The encryption key exists in the bot process and nowhere else — the ticket secret, which the API does hold, cannot decrypt anything.

The server id comes from the signed ticket, not from the request body, so an operator cannot retarget a server they were not authorized for. And both the Discord modal path and the dashboard path run the same audited intake core, so there is one validated route into a stored token, not two.

What the operator seesWhat it meansThe fix
“The token works, but the bot isn't in the server yet” The token is valid, but Discord refused command registration because the bot has not been invited. Use the invite link in the tab. It comes online by itself within a few seconds, or press Retry.
Online · capture off Message Content Intent is switched off in that application's portal, so regear screenshot capture cannot work. Enable it under Bot → Privileged Gateway Intents, then press Retry.
“Discord rejected the token on login” The token is wrong or has been revoked. Re-copy the Bot token (Reset Token if unsure) and onboard again.
“That looks like an Application ID, not a token” Caught in the browser before anything is sent. Paste the actual Bot token.
Offline Stored, but the client is not running. Invite it — no token needed — or rotate the token and onboard again.

Retry re-activates from the token that is already stored, so a token is pasted once and never again. Both the ticket route and the intake route carry their own strict per-IP rate limits, and the intake's limit rejects a flood before the request body is buffered, so it cannot pressure the bot's event loop. The control listener keeps request logging switched off entirely — precisely because one of those bodies carries a plaintext token.

07 · The status board

Members should not have to guess whether the bot is broken

Deploys restart the bot. For the minute or so that takes, buttons do nothing — and a member who presses one concludes the bot is broken. So the bot says so itself, in its own words: back in a minute.

One flip down, one flip back

On shutdown, every installed board flips to 🟠 Updating. That work is bounded to three seconds, so announcing a deploy can never overrun the shutdown window and delay the very restart it is announcing.

On boot, each client marks its own servers 🟢 Online with the running release version and a live "Online since" timestamp that ticks in the member's own client with no further edits from us.

It is opt-in per server and deliberately silent. The board edits itself and never pings, because a ping on every deploy would be spam.

Discord
🤖
Guild Butler ✓ APP Today at 04:08
🤖 Bot status
🟠 Updating — back in a minute
Version: v2.6.0
Feedback
The same pinned message during a deploy. There is no “since” line here — it only appears when the bot is actually online.
Discord
🤖
Guild Butler ✓ APP Today at 20:41
🤖 Bot status
🔧 Maintenance
Version: v2.6.0

> Moving the database to faster storage — back before 22:00 UTC.
Feedback
Planned work bigger than a deploy. One command pushes the same note to every installed board in the fleet; a second clears it.

Fleet-wide, from one command

/maintenance announce flips every installed board — the shared bot's servers and every dedicated bot's server — to 🔧 with your note. /maintenance clear puts them all back to 🟢. Both are bot-owner only, and both defer their reply first, because flipping a fleet of boards is network work that would otherwise miss Discord's three-second deadline.

The 💬 Feedback button is rebuilt with the board on every state change, so it survives all three flips and members always have somewhere to write from.

Installing it takes one press

Admins open /settings → 🤖 Status. The board can go in a channel you already have, or the bot can create a read-only #bot-status channel itself. A second picker chooses where member feedback is posted.

Changing your server's language re-renders the installed boards immediately, rather than leaving stale text pinned in a language nobody picked.

Discord

Only you can see this

🤖 Status

• Status channel: #bot-status

• Feedback channel: #officer-chat

A pinned board that flips to '🟠 Updating' during a deploy, then '🟢 Online' when the bot is back.

Move the status board to another channel…
Where member feedback posts…
Create #bot-status Remove board Back
The Status section of the settings hub. Create #bot-status makes the channel with @everyone denied Send Messages, so the board cannot be buried.

08 · Everything else

What else is in the box

Economy overview with a trend chart

Nine headline numbers, then the same silver charted settle by settle.

Raids settled, Gross silver, Merchant bank, Regear bank, Repairs, Payouts, Merchant tax, Regear tax and Paid to outsiders, aggregated straight off the settlement history and the guild banks. Below them a line chart plots gross, merchant tax and regear tax cumulatively, one point per settlement. Casual raids are excluded from every guild money figure and from the chart, because a casual settle credits nobody and fills no bank — yet its gross is a number typed by whoever ran the raid, with no officer review.

Who got regeared, who won what

The Regear tab by player or by event; the Giveaways tab with every recent draw and what it actually paid.

The Regear tab has a Players view and an Events view, filtered by All time, This week or the active season, with cards for Regeared, Claims, Drives and Biggest regear, a death-versus-overcharge split bar and a "Regear paid over time" chart. Names resolve in-game-name first, because a regear board is about the player, not the Discord account, and your own row is highlighted. Officers get two extra columns nobody else sees — an approval rate per player and a turnaround time per drive — because those measure the staff, not the members. Both tabs are open to members, because the same data was already public in Discord.

Ranks resolved exactly as the bot resolves them

Admin, officer, regear staff and recruiter — four separate bits, not one.

The dashboard mirrors the bot’s permission module field for field. Administrator is a separate bit from officer on purpose: the bot draws a hard line where a recruiter owns every action and an admin owns every setting, and settings that could escalate privilege gate on admin alone. A dashboard that only knew "officer" could not express that line at all. The role lookup is skipped entirely when a server has configured no tier roles, and a failed role fetch falls closed to member tier rather than guessing.

Plans, stored per server

Free, Pro and Enterprise exist as plans — today the paid feature is white-label.

Each server carries one plan, stored on its row and shown in /settings and on the dashboard’s server picker. The rule that turns a plan into features is a tiny pure function with its own tests, so gating a feature is one line, not a refactor — and an unknown or legacy plan string always reads as Free, never as more. What sits behind each paid tier is still being settled; today the paid feature is the white-label bot.

Your dedicated bot asks Discord for less Pro

It asks Discord only for what its own application has switched on, and if the gateway refuses, it steps down and stays online instead of dying.

Before login the runtime reads the tenant application’s advertised flags and picks the largest intent set it can plausibly use. If the gateway refuses that set because a privileged intent is switched off in the portal, the bot steps down a ladder and tries again, so it ends up online rather than becoming a dead shard. The ladder protects screenshot capture: server-member access is dropped first, message content last. The floor is two non-privileged intents — servers and voice states — which is lighter than the shared bot runs on.

One deploy updates the whole fleet Pro

Every dedicated bot is a separate Discord client in the same process, wired to the same handler.

There is no per-tenant code anywhere: brand and plan are rows in the database, and the interaction handler is client-agnostic. So a dedicated bot never falls a version behind the shared one — they update together. Command definitions can also be pushed to every tenant on demand, as a bulk overwrite rather than create-each, so an unchanged command set costs nothing against Discord’s daily budget. Tenant startup is isolated too: a bad or revoked token is logged and skipped, never taking down the shared bot or the other tenants.

The hardening two security reviews asked for

24-hour cap, constant-time state compare, 60-second Discord cache, errors that leak nothing.

Two adversarial security reviews were run against this surface — one on cross-guild isolation, sessions and SQL, one on the token-intake path. Neither found a leak, and the hardening they did ask for is in place: the OAuth state is compared in constant time, sessions expire at 24 hours regardless of Discord’s longer token life and a sweeper evicts dead ones, your server list and roles are cached on the session for 60 seconds so reads do not re-hit Discord, and Discord upstream failures map to a clean 502 or 503 instead of a 500 that leaks internal paths.

💬 Feedback and “what’s new” on the same board

A Feedback button rides on the status board, and each release posts its notes there once.

The Feedback button is rebuilt with the board on every state change, so it survives the 🟢 / 🟠 / 🔧 flips. Release notes are posted once per release into servers that installed the board, and the claim is made in the database before the message is sent — the shared client and the tenant clients boot in the same process and could otherwise both reach the same server, so exactly one wins and the loser skips. A send that fails releases the claim and retries on the next boot.

Six languages, one server-wide setting

English, Ukrainian, French, Russian, Portuguese (BR) and German, across 2,351 message keys.

An admin picks the server’s language in /settings → 👥 Server. That choice is authoritative for everything the bot says in that server, including private replies — a Ukrainian guild speaks Ukrainian to a member whose Discord client is set to English, which is what guilds actually expect. This was a deliberate reversal of an earlier version that followed each member’s own client language. Every key carries all six languages, and any gap falls back to English rather than rendering blank.

09 · Commands

The commands — most guilds use one of them

/whitelabel is officer-gated: name, avatar and clear need the Enterprise plan, while status works on any plan and tells you whether white-label is unlocked. /plan set, /tenant and /maintenance are bot-owner only; /plan show is open to anyone.

/plan show/plan set/whitelabel name/whitelabel avatar/whitelabel clear/whitelabel status/tenant add/tenant list/tenant remove/maintenance announce/maintenance clear/settings/feedback

/tenant list shows the application id and status of a server's dedicated bot and never the token. /tenant remove stops the client, deletes the row and restores the shared bot's commands in that server, so removing a dedicated bot is not a one-way door.

10 · What it cannot do

The limits, up front

Some of these are our choices. Some are Discord’s hard ceilings. Every one we know about is here.

11 · Questions

Questions officers actually ask

Can I change settings or pay people from the dashboard?
No, and that is the design. The stats API opens the database with a read-only handle and the container mounts the data volume read-only on top of it, so there is no second path to your silver that could drift out of step with the bot. Settling a raid, adjusting a balance, drawing a giveaway and every configuration change stay in Discord, where the permission checks and the audit trail already are.
Who can see what?
Anyone in the server can see the Overview (guild totals, no per-member rows), their own My stats, the Regear board and the Giveaways list — all of which are already public inside Discord. Only officers see the Members board, because it lists other people’s earnings and balances, and a member who asks for it is refused by the API with a 403, not just by a hidden tab. Officer means Discord Administrator or server owner, or the officer role you configured, exactly as the bot resolves it. Nobody ever sees another server’s data.
Do we need the dashboard to use the bot?
No. Everything the bot does is driven from Discord, and plenty of guilds never open the dashboard at all. It exists for the moments when a table beats an embed — comparing twenty members’ attendance side by side, or looking at the guild’s silver over months.
If we move from the shared bot to our own branded bot, do we lose our history?
No. Raids, balances, regear claims, attendance, giveaways and settings are all keyed to your Discord server, so the dedicated bot serves the exact same rows. There is nothing to export or import. Two practical notes: the shared bot goes silent in your server and its slash commands there are cleared, and buttons on messages the shared bot posted earlier stop responding — re-run the command to post fresh panels from the new bot.
Does a dedicated bot get updates, or does it fall behind?
It gets every update at the same moment. All the dedicated bots run as separate Discord clients inside the same process, wired to the same handler, with no per-tenant code — brand and plan are just database rows. One deploy updates the shared bot and every dedicated bot together. Only a change to a command’s definition needs re-registering, which happens on restart or on demand.
Can we set up a dedicated bot ourselves?
Not today. Discord has no API for creating bot applications, so someone has to do that step by hand in the Developer Portal, and the token intake is deliberately restricted to the bot operator. In practice we create the application with your name and icon, you invite it with a one-click link, and we bring it online. Everything after that is yours: /whitelabel name and /whitelabel avatar change the live bot from inside your own server.
Can the shared bot use our guild’s avatar?
No, and no amount of configuration changes that. A Discord application has exactly one global username and one global avatar. A per-server nickname is the only part of a bot’s identity Discord lets a single application vary, which is what the lite option uses. A custom avatar genuinely requires a dedicated application.
What language does the bot speak, and can each member pick their own?
An admin picks one language for the whole server, and today that choice is authoritative for everything the bot says there, including private replies. There is no per-member override yet. This was a deliberate reversal: an earlier version followed each member’s Discord client language, which meant a Ukrainian guild showed English to members whose client was English. The plumbing for an opt-in personal override is still in place for later.
What happens to the status board when you deploy?
It flips to 🟠 Updating — back in a minute as the bot shuts down, then back to 🟢 Online with the running version and a live "Online since" timestamp when it returns. The shutdown flip is bounded to three seconds so announcing a deploy can never delay the restart it is announcing. It never pings anyone.

See it running before you install

The community server runs the bot in the open — a live killboard, real boards to press, and humans who answer questions.

Watch it live

Your guild in a browser. Your name on the bot when you want it.

The dashboard is free and needs no setup — sign in with Discord and pick your server.