Discord
APX can run your agent as a Discord bot on a community server. It reads the rooms you list, answers where you allow it, and keeps a summary and a searchable memory of each room so a call in a busy channel does not mean reading a thousand messages.
Set it up
Section titled “Set it up”-
In the Discord Developer Portal, create an Application and open its Bot tab.
-
Under Privileged Gateway Intents, turn on Message Content Intent. Without it the bot connects but cannot read what people write, and APX stops with a clear error instead of retrying.
-
Invite the bot to your server from OAuth2 → URL Generator: scope
bot, permissions View Channels, Send Messages, Send Messages in Threads and Read Message History. -
Open Settings → Discord in the web panel, paste the token, your Discord user id and the names that call the bot, and save — the bot connects on the spot. Or from the terminal, with the token from stdin so it never lands in your shell history, and tell it who you are and what counts as calling the bot:
Terminal window pbpaste | apx discord set --token-stdinapx discord set --owner 1234567890123456789 --names robyapx restart -
List the rooms it may live in, in the same screen or from the terminal (right-click a channel → Copy Channel ID, with Developer Mode on):
Terminal window apx discord channel set 1234567890123456789 always --name helpapx discord channel set 1234567890123456790 mention --name general
Rooms and modes
Section titled “Rooms and modes”Deny by default. A room that is not listed does not exist for APX: its messages are not stored, not indexed and never answered. Somebody creating a new channel is not enough to put the agent in it.
| Mode | Stores and reads | Speaks |
|---|---|---|
always | yes | on every message — for a help room, where every message is a question |
useful | yes | when called — and, uncalled, when a quick model check finds it can really help (your When to answer uncalled criterion) |
mention | yes | only when called: an @mention, a reply to one of its messages, or one of its --names as a word |
read | yes | never |
A thread inherits the mode of the room it was opened in, unless you list the thread itself. An
@everyone is not a call. Messages from other bots are stored but never answered. Mode changes
apply to the next message — no restart.
apx discord channels # the listapx discord channel remove 1234567890123456790 # stop reading it entirelyuseful: answering only when it helps
Section titled “useful: answering only when it helps”In a useful room every uncalled message is a candidate. A short model call — the message, a few
lines of the room, your notes and your criterion — answers yes or no as JSON; only a yes runs the
real turn. A failed or unreadable check is a no. Each check is a paid call, so the room has its own
ceiling (limits.gate_checks_per_hour, 120), and the model is discord.gate_model, else your
compaction model, else the super-agent’s.
Busy rooms: storing is free, answering is not
Section titled “Busy rooms: storing is free, answering is not”Discord delivers every message whether you want it or not, so the cost of a busy room is never reading — it is calling the model. Code decides that before any model runs:
- Settle window (
limits.burst_window_ms, 8 s): “roby” / “a question” / the actual question from one person is one answer, not three. - Per-person cooldown (
limits.user_cooldown_ms, 20 s) between two answers to the same person. - Per-room ceiling (
limits.channel_replies_per_hour, 30). When a room hits it, the bot goes quiet there and tells you once, because it is usually flood or spam.
Your own account is exempt from the limits — they protect your budget from other people.
What a turn knows about the room
Section titled “What a turn knows about the room”When the bot is called it gets the room in layers, cheapest first:
- the message it is answering, and the one that message replies to;
- the room’s last messages, verbatim, capped in characters (
context.recent_messages,context.recent_chars); - a running summary of the older part, written in the background by your compaction model
once a room passes
context.summary_thresholdmessages; - older fragments recalled by similarity from that room’s own memory index (
context.rag_hits); - your notes on what the bot can do and answer — the notepad in Settings → Discord (or
apx discord set --knowledge-stdin), capped at 12k characters.
So “@roby what do you think?” is answered from what was just being discussed, and “what did you all decide about memory last week?” from the summary and the recall — without the whole room in the prompt.
What never reaches a public room
Section titled “What never reaches a public room”Every Discord turn is sealed, yours included: no tools, no private memory, no other channel, no projects. Whatever the bot writes is read by strangers, so a turn with your notebook in it would be one careless sentence from publishing it. Give the agent real work on Telegram or the panel.
- Each room’s messages are indexed under their own scope (
discord:<channel_id>): they never surface in your own recall, and your memory never surfaces in theirs. - Your rules (Rules for what it says) close every prompt, after everything else, so they win.
- A guardrail in code runs on every post, whatever the model wrote: registered secrets are masked,
paths on your machine become
[local path], and@everyone/@hereare defused. - Posts are sent with mentions switched off at the API level, so the bot cannot ping
@everyoneeven if the model writes it. - What the bot did reaches you on Telegram, coalesced per room and subject to the same interruption budget and quiet hours as any other push.
Answering only you
Section titled “Answering only you”Settings → Discord → Who it answers → Only me (or apx discord set --owner-only true)
makes the bot answer your Discord ID and nobody else, in every room and every mode. It still
reads every included room — that history is what lets it answer you well — but a message from
anyone else is stored and never answered, and in a useful room it is not even sent to the
check that decides whether to join in, so it costs nothing. The room’s mode still applies to
you: in a mention room you still have to call it.
With no Discord ID set, “only me” means it answers nobody. Turn it off to go back to whoever each room’s mode allows.
Reference
Section titled “Reference”apx discord [status]apx discord channelsapx discord channel set <channel_id> <always|useful|mention|read> [--name <name>]apx discord channel remove <channel_id>apx discord set [--token-stdin] [--owner <id,…>] [--owner-only <true|false>] [--names <a,b>] [--knowledge-stdin] [--enabled <true|false>]The panel screen shows the bot’s avatar large — click it to change it (the picture is cropped to a 512 px square; Discord rate-limits avatar changes to a couple an hour).
HTTP: GET /api/discord/status, GET /api/discord/rooms, PUT /api/discord/avatar, POST /api/discord/reconnect, PATCH /api/discord/settings, PUT|DELETE /api/discord/channels/:id,
GET /api/discord/channels/:id/history, POST /api/discord/send (only to a listed room).
The token goes in through settings and is never returned.