All integrations

Discord

Channel

Run your agent as a Discord bot across servers and threads.

At a glance

From Eve docs · synced Jul 22, 2026

Webhook route

POST /eve/v1/discord

Extension hooks

onCommand

Environment variables

DISCORD_PUBLIC_KEY
verifies X-Signature-Ed25519 + X-Signature-Timestamp
DISCORD_APPLICATION_ID
edits the deferred response and sends followups
DISCORD_BOT_TOKEN
proactive messages + fallback + typing indicators

Capabilities

  • Human-in-the-loopHITL renders as Discord components.
  • Proactive sessionsStart a session without an inbound interaction through receive(discord, { message, target, auth }) from a schedule run handler, or args.receive(discord, ...) from another channel.
  • AttachmentsInbound file attachments are not supported on this channel today.

Derived from Eve's Discord docs.

Install

Install the framework. The Discord channel ships with it:

npm install eve@latest

Quick start

Create agent/channels/discord.ts:

// agent/channels/discord.tsimport { discordChannel } from "eve/channels/discord";export default discordChannel({  botToken: () => process.env.DISCORD_BOT_TOKEN!,  publicKey: () => process.env.DISCORD_PUBLIC_KEY!,});

Configure

Create a Discord application, add a bot, and set the interactions endpoint URL to the route eve serves (/eve/v1/discord). Provide the bot token and public key through environment variables. See the Discord channel docs for intents and slash-command setup.

How it behaves

Excerpts from Eve's Discord docs. Prefer the source when something looks out of date.

Dispatch

onCommand(ctx, interaction) decides whether to dispatch and under what auth. Return { auth } to proceed or null to drop the interaction. By default, auth comes from the invoking user. Event handlers receive (eventData, channel, ctx), with Discord platform handles on channel.discord:

import { discordChannel } from "eve/channels/discord";

export default discordChannel({
  onCommand: (ctx, interaction) => ({
    auth: {
      principalId: interaction.user.id,
      principalType: "user",
      authenticator: "discord",
      attributes: { channel_id: interaction.channelId, guild_id: interaction.guildId ?? "" },
    },
  }),
  events: {
    "message.completed"(eventData, channel, ctx) {
      if (eventData.finishReason === "tool-calls") return;
      if (eventData.message) channel.discord.post(eventData.message);
    },
  },
});
Delivery

The default message.completed handler edits the deferred response for the first reply and sends followups after that. If the interaction token is rejected, it falls back to a bot-authenticated channel message. Long text is split to Discord's 2000-char limit, and generated messages default to allowed_mentions: { parse: [] }.

Typing fires on turn.started and actions.requested, but only when a bot token is present. In custom hooks, call channel.discord.startTyping() yourself.

Human-in-the-loop (HITL)

HITL renders as Discord components. Confirmations and options become buttons, display: "select" becomes a string select, and freeform input becomes a button that opens a modal. When the user responds, the parked session (paused awaiting input) resumes.

Proactive sessions

Start a session without an inbound interaction through receive(discord, { message, target, auth }) from a schedule run handler, or args.receive(discord, ...) from another channel. The proactive target shape is { channelId, conversationId?, initialMessage? }. Either path needs DISCORD_BOT_TOKEN.

Attachments

Inbound file attachments are not supported on this channel today.

Agents using Discord