# I got tired of running five Discord bots, so I built one

> Source: https://physicianforge.com/building/forge-grok-discord-bot/
> Author: Drew Albert
> Published: 2026-08-31
> Reading time: 8 min
> Tags: discord, grok, infrastructure, community
> Type: Ship log
> Site: Physician Forge — Where physician-builders find each other

Membership, moderation, and Grok in one Physician Forge bot — what broke with the bot pile, what’s live now, and how /ask actually works.

For a while the Physician Forge Discord looked like every other niche server on the internet.

I had [MEE6](https://mee6.xyz/) for levels. Something else for moderation (we tried Arcane and friends). Welcomes somewhere. Payments somewhere else. Each bot owned a slice of permissions, none of them knew what this room was for, and if I wanted a straight answer to a business question I still opened ChatGPT in another tab.

That got old.

The thing that finally annoyed me enough to write code: **Manage Roles** was enough power to hand out a paid membership by accident. That’s a bad failure mode when you’re charging $59/mo or $590/yr and the whole point of the room is that paying members actually get in.

So I built one bot instead: **Physician Forge#6492**. It runs as a single Node service on our box. Membership, moderation, and Grok live in the same process.

## Why Discord, and why a custom bot

I’m not trying to invent another course portal. Discord is where the conversation already is — channels, DMs, voice, low friction. Same reason a lot of builder communities ended up there instead of building a “member area” that nobody opens twice.

Discord’s own docs on [slash commands](https://discord.com/developers/docs/interactions/application-commands) and [privileged intents](https://discord.com/developers/docs/topics/gateway#privileged-intents) are pretty clear: if you want the bot to read @mentions and replies, you flip on Message Content Intent and you mean it. We did.

On the AI side I’m using [xAI’s Chat Completions API](https://docs.x.ai/docs/api-reference) (`grok-4.6`), streaming tokens back into Discord so the reply doesn’t feel like a loading spinner forever. Same pattern OpenAI and others use with SSE — Discord just has a ~3 second acknowledge window on interactions, so the bot defers, posts a placeholder, then edits as chunks arrive (~every 450ms). Messages still have to respect Discord’s 2,000 character cap, so long answers get split.

## What’s live right now

Slash commands currently registered to the guild:

![Live Discord slash commands registered to Physician Forge](/images/journal/bot-commands-live.png)

Public:

- `/ask` — Forge Grok (private reply by default; `public: true` if you want it in-channel)
- `/join` — membership / Discord OAuth path
- `/expense` — employer reimbursement packet ([invoice, vendor, W-9](/expense/))
- `/building` — ship logs
- `/help`

Admin only (`/grant`, `/revoke`, `/timeout`, `/kick`, `/say`, `/grok`):

Admin means Discord Administrator, the Forge Admin staff role, or an allowlisted owner ID. Manage Roles alone is not enough. That was the whole point.

You can also @Physician Forge, reply to the bot, or DM it. Same Grok path either way.

Production health for the service looks like this:

![Live health endpoint for physicianforge-discord-bot](/images/journal/bot-health-live.png)

## Membership is wired through the same bot

Join on the site still goes Discord OAuth → Stripe Checkout → webhook → Forge Member role. If someone pays before they’re in the server, we queue the grant and retry when they show up. Cancel removes the role.

I used to think of “AI bot” and “payments bot” as separate products. That’s how you end up with five apps again. Stripe’s [Checkout](https://docs.stripe.com/checkout/quickstart) + webhooks and Discord’s OAuth are boring when they work — the interesting part is that one process owns both so permissions don’t drift.

## A note on Grok vs another ChatGPT tab

I still use ChatGPT. A lot of people in medicine do. The difference I’m optimizing for is boring:

1. You’re already in the room talking shop — the answer lands next to the question.
2. Other members can see it (when it’s public), disagree, or steal the idea.
3. The system prompt knows this is Physician Forge: no medical advice, no inventing CME credit, keep it short unless someone asks for depth.

That last one matters. Discord is not CME. We don’t issue AMA PRA Category 1 Credit. If your hospital reimburses professional membership / development from a CME-budget pot, use the [expense packet](/expense/) and confirm with your coordinator — different thing.

## What still bugs me / what’s next

- Grok quality depends on the prompt and the model. I’ll keep tuning. xAI will keep shipping models; we just swap `XAI_MODEL`.
- I don’t want the bot to become a second “content product.” The Journal stays free writing. Discord stays the room. ([Start here](/start/) if you’re new.)
- Real checkout smoke tests (me, paying, watching the role land) still matter more than another blog paragraph.

## If you want to build something like this

I’m writing this section for the other physicians / builders who keep asking “how did you wire Grok into Discord?” You don’t need our exact stack. You need a clear job for the bot, an API key, and a prompt that sounds like *your* room — not a generic assistant.

### The shape that worked for me

1. **One Node process** (discord.js) — slash commands + optional message listener
2. **xAI Chat Completions** at `https://api.x.ai/v1/chat/completions` — same request shape as OpenAI-compatible APIs ([xAI API docs](https://docs.x.ai/docs/api-reference))
3. **Stream the answer** into Discord (`stream: true`, parse SSE `data:` lines, `editReply` / edit the message every ~450ms so it feels alive)
4. **Defer first** on slash commands — Discord kills interactions that don’t acknowledge fast ([interaction docs](https://discord.com/developers/docs/interactions/receiving-and-responding))
5. **Split long answers** under ~2,000 characters
6. **Strict admin gate** if the bot can grant paid roles — Administrator / allowlist / staff role, not “anyone with Manage Roles”

Optional but useful: Stripe Checkout + webhook in the *same* process so membership and AI don’t drift into two permission maps. Stripe’s [Checkout quickstart](https://docs.stripe.com/checkout/quickstart) is enough to start.

### Integrating the xAI (Grok) API

1. Create an API key at [console.x.ai](https://console.x.ai/)
2. Put it in env as `XAI_API_KEY` (never commit it)
3. Call Chat Completions with your model — we use `grok-4.6` via `XAI_MODEL` so we can swap without rewriting code
4. Send a **system** message (your room’s brain) + **user** messages (the question, maybe a few lines of channel context)
5. For Discord UX, prefer **streaming**:

```http
POST https://api.x.ai/v1/chat/completions
Authorization: Bearer $XAI_API_KEY
Content-Type: application/json
```

```json
{
  "model": "grok-4.6",
  "stream": true,
  "max_tokens": 1200,
  "temperature": 0.7,
  "messages": [
    { "role": "system", "content": "<your system prompt>" },
    { "role": "user", "content": "<their question>" }
  ]
}
```

Read the SSE body line by line. Lines look like `data: {...}` with `choices[0].delta.content`. When you see `data: [DONE]`, stop. Append deltas into one string and push that string into Discord as the message grows.

Non-streaming (`stream: false`) is fine for smoke tests. In a real server, streaming matters — people tolerate a typing answer more than a 10-second blank.

Discord-side checklist that burned me once:

- Developer Portal → Bot → enable **Message Content Intent** if you want @mentions / replies (then restart the bot with that intent on) — [privileged intents](https://discord.com/developers/docs/topics/gateway#privileged-intents)
- Register slash commands to your **guild** while iterating (global commands are slow to update)
- `/ask` → `deferReply` → stream → `editReply`; for channel messages, reply once then edit that message
- Add a small cooldown (we use ~2.5s) so nobody hammer-spams the API

### The system prompt we actually use (Forge Grok)

Steal the structure. Rewrite the domain lines for *your* community. The hard rules are the part I’d keep almost verbatim if you’re in medicine:

```text
You are **Forge Grok**, the AI in the Physician Forge Discord — a private community for physician-builders (clinicians who ship companies, products, investments, and leverage outside a pure W2).

Voice:
- Direct, sharp, practical. No corporate fluff, no CME theater, no medical advice.
- Short answers by default. Use bullets when useful. Go deep only when asked.
- Speak like a peer who's built things, not a chatbot or coach script.

Domain:
- Entrepreneurship, leadership, capital allocation, startups, side leverage while practicing medicine.
- Membership is one plan on Discord ($59/mo or $590/yr). Public writing lives at physicianforge.com/journal/.
- Founder: Drew Albert (@drewalbertmd).

Hard rules:
- Never give personalized medical, legal, or tax advice. Redirect clinical questions to appropriate licensed care.
- Never invent CME credit, accreditation, or that Discord chat earns CME hours.
- If unsure about private server details, say so and point to /join or physicianforge.com.
- Stay useful under Discord's 2000-character message limit unless the user asks for a long form answer (then structure in clear sections).
```

If your room is orthopedics recruiting, or a founders’ group, or a local mastermind — change Domain. Keep Voice + Hard rules honest. A vague “helpful assistant” prompt is how you get generic sludge in a paid server.

### Ideal prompt to hand an AI coding agent (Cursor, etc.)

If you want an agent to scaffold this for you, don’t say “build a Discord AI bot.” Be specific. Something like this has worked for me:

```text
Build a Node.js Discord bot (discord.js) for my private community.

Goals:
1. Slash command /ask that calls xAI Grok Chat Completions at https://api.x.ai/v1/chat/completions
2. Stream the response into Discord (deferReply, then editReply as SSE tokens arrive ~every 450ms)
3. Also answer @mentions, replies to the bot, and DMs (Message Content Intent)
4. Split replies over Discord’s 2000-character limit
5. System prompt is configurable; start from the Forge Grok prompt below (adapt names)
6. Env: DISCORD_TOKEN, XAI_API_KEY, XAI_MODEL=grok-4.6, GROK_ENABLED=1
7. Optional later: Stripe Checkout webhook that grants a Member role — admin grant/revoke must NOT be available to Manage Roles alone (Administrator / allowlisted user IDs / staff role only)

Constraints:
- No medical advice in the system prompt; never invent CME credit
- Cooldown ~2.5s between Grok calls per user
- Guild-register slash commands for fast iteration
- Include a /health HTTP endpoint showing grok ready + intents
- README with Discord Developer Portal steps (intents, invite URL, env)

Do not invent API keys. Use .env.example. Keep the code small and boring.
```

Paste your rewritten system prompt under that. Point the agent at [xAI’s API reference](https://docs.x.ai/docs/api-reference) and Discord’s [application commands](https://discord.com/developers/docs/interactions/application-commands) docs so it doesn’t hallucinate endpoints.

### What I would *not* copy blindly

- Don’t put your Stripe secret or Discord token in a blog post or a committed `.env`
- Don’t give the AI bot permission to grant paid roles without a second human-gated check
- Don’t promise CME hours from Discord chat — that’s a compliance mess; membership reimbursement is a different conversation ([our expense packet](/expense/) is for professional membership / PD, not Category 1 credit)
- Don’t start with five bots “and Grok later.” One process. Add payments when the AI path already works.

If you’re already in Physician Forge: try `/ask` or @ the bot and steal ideas from what it gets wrong — that’s how the prompt improves. If you’re not: [membership is here](/physician-forge-community/). Earlier rebuild notes: [Starting the Forge rebuild](/building/starting-the-forge-rebuild/).

---

Canonical HTML: https://physicianforge.com/building/forge-grok-discord-bot/
Journal: https://physicianforge.com/journal/
Membership: https://physicianforge.com/physician-forge-community/
