Skip to content

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.

  1. In the Discord Developer Portal, create an Application and open its Bot tab.

  2. 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.

  3. 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.

  4. 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-stdin
    apx discord set --owner 1234567890123456789 --names roby
    apx restart
  5. 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 help
    apx discord channel set 1234567890123456790 mention --name general

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.

ModeStores and readsSpeaks
alwaysyeson every message — for a help room, where every message is a question
usefulyeswhen called — and, uncalled, when a quick model check finds it can really help (your When to answer uncalled criterion)
mentionyesonly when called: an @mention, a reply to one of its messages, or one of its --names as a word
readyesnever

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.

Terminal window
apx discord channels # the list
apx discord channel remove 1234567890123456790 # stop reading it entirely

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.

When the bot is called it gets the room in layers, cheapest first:

  1. the message it is answering, and the one that message replies to;
  2. the room’s last messages, verbatim, capped in characters (context.recent_messages, context.recent_chars);
  3. a running summary of the older part, written in the background by your compaction model once a room passes context.summary_threshold messages;
  4. older fragments recalled by similarity from that room’s own memory index (context.rag_hits);
  5. 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.

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/@here are defused.
  • Posts are sent with mentions switched off at the API level, so the bot cannot ping @everyone even 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.

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.

Terminal window
apx discord [status]
apx discord channels
apx 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.