Interaction Responses And Data
Overview
bot.InteractionContext wraps an interactions.Interaction and provides the complete high-level response lifecycle. It can identify the interaction type, read command options, acknowledge a command or component, edit the original response, send follow-ups, display a modal, or answer autocomplete.
Every interaction gets one initial acknowledgement. A normal reply, defer, component update, modal, autocomplete result, or pong consumes that initial response. After a reply or defer, use the original-response and follow-up methods rather than replying again.
Architecture
The gateway produces an interactions.Interaction containing IDs, token, type, member/user, channel, guild, and raw Data. bot decodes command data and component data into InteractionContext helpers. Initial responses go through rest.Client.CreateInteractionResponse; follow-ups use the interaction webhook endpoints. The context applies short deadlines to initial acknowledgements and longer event-context deadlines to later REST operations.
Quick Start
This complete program registers a command, reads an option, and replies. It needs only the guilds intent for the command interaction path.
package main
import (
"log"
"os"
"github.com/discord-go/discord.go/bot"
"github.com/discord-go/discord.go/intents"
"github.com/discord-go/discord.go/interactions"
)
func main() {
token := os.Getenv("DISCORD_TOKEN")
if token == "" {
log.Fatal("DISCORD_TOKEN is required")
}
router := bot.NewRouter()
router.Command("echo", "Echo text", func(ctx *bot.InteractionContext) {
if err := ctx.Reply(ctx.GetStringOption("text")); err != nil {
log.Printf("reply: %v", err)
}
}, interactions.ApplicationCommandOption{
Type: interactions.ApplicationCommandOptionTypeString,
Name: "text",
Description: "Text to echo",
Required: true,
})
b := bot.New(token, bot.WithIntents(intents.Guilds), bot.WithRouter(router))
if err := b.Run(); err != nil {
log.Fatal(err)
}
}For a command that may take longer than the initial response window, replace the immediate reply with Defer and finish with EditReply or Followup.
Creating/Configuration
Handlers receive *bot.InteractionContext. The embedded *interactions.Interaction exposes ID, ApplicationID, Type, GuildID, ChannelID, Member, User, Token, Message, AppPermissions, Locale, and GuildLocale. These fields can be nil or empty for different interaction types, so use the predicate methods before reading them. The User() accessor handles the guild-vs-DM user lookup automatically; GuildID() and ChannelID() dereference the pointers and return zero for DMs.
Use interactions.InteractionCallbackData for embeds, components, flags, attachments, choices, modal fields, and polls. messages.FlagEphemeral makes an initial or follow-up response visible only to the invoking user.
Using
Basic: reply once
Use Reply, ReplyEphemeral, ReplyEmbed, or ReplyComplex. The complex form accepts *interactions.InteractionCallbackData, which is the most flexible way to include components.
Intermediate: defer and edit
Call Defer or DeferEphemeral immediately, perform work, then call EditReply, EditReplyComplex, or DeleteReply. GetReply retrieves the original message if the application needs its ID or final content.
Advanced: component and modal interactions
For a button or select, use Update or UpdateContent to edit the triggering message, or DeferUpdate when the edit will happen later. For modal submissions, use ModalValue or ModalValues. For autocomplete, inspect FocusedOption and return Autocomplete choices. FocusedOptionString() is a safe shortcut that returns the focused value as a string without type assertions.
Select menu values are always []string from Discord's API. For RoleSelect, UserSelect, and ChannelSelect, parse each value with snowflake.Parse to get a snowflake.ID. For context-menu commands, FetchTargetMember(ctx) fetches the target user's guild member via REST — useful for moderation commands that need to inspect the target's roles.
Common Patterns
- Check
IsChatInputCommand,IsContextMenuCommand,IsButton,IsSelectMenu,IsModalSubmit, andIsAutocompletebefore type-specific access. - Use
User()instead of manually checkingctx.Userandctx.Member.User; useGuildID()instead of dereferencingctx.GuildID. - Use
HasOptionbefore treating zero or an empty string as a supplied value. - Use
SubcommandandSubcommandGroupfor nested command definitions. - Use
FollowupEphemeralfor private progress or validation messages after a defer. - Use
HasResponded,Replied,Deferred, andEphemeralin shared helper code that may be called by multiple paths.
Best Practices
Acknowledge before doing network work
Why: Discord imposes a short initial interaction response window.
Pros: long-running commands can complete reliably and show a thinking state.
Cons: a defer creates an extra edit or follow-up request and may be less responsive for trivial handlers.
Prefer typed accessors
Why: option values are decoded through json.Number and may be nested under subcommands.
Pros: GetIntOption, GetFloatOption, and ID helpers avoid repeated decoding.
Cons: invalid or absent values return zero values, so use HasOption when that distinction matters.
Treat interaction tokens as short-lived credentials
Why: follow-up methods use ApplicationID and Token to call webhook routes.
Pros: the context keeps token handling out of most application code.
Cons: storing contexts or tokens for later use is fragile; persist a Discord message ID and use an authenticated REST path when appropriate.
Common Mistakes
Incorrect: reply twice as if the second call were another initial response.
_ = ctx.Reply("first")
_ = ctx.Reply("second")Correct: reply once, then send a follow-up.
if err := ctx.Reply("first"); err != nil {
return
}
_, _ = ctx.Followup("second")Incorrect: update a command interaction that has no triggering message.
_ = ctx.UpdateContent("changed")Correct: use Update only for component interactions; use Reply or EditReply for commands.
if ctx.IsMessageComponent() {
_ = ctx.UpdateContent("changed")
} else {
_ = ctx.Reply("This is a command response")
}API Walkthrough
InteractionContext.CommandName,CommandType,CustomID,Suffix,ComponentType,Values,ModalValue,ModalValues,FocusedOption,Options,Subcommand,SubcommandGroup,HasOption, andGetOptioninspect decoded interaction data.User() *users.Userreturns the invoking user, handling both guild (nested insideMember) and DM (top-levelUser) interactions.GuildID() snowflake.IDreturns the guild ID or zero for DMs.ChannelID() snowflake.IDreturns the channel ID or zero when absent.Suffix() stringreturns the portion of the custom ID after the matched prefix forButtonPrefix,SelectPrefix, andModalPrefixroutes.FocusedOptionString() stringreturns the focused autocomplete option's value as a string, or empty when no option is focused or the value is not a string.GetStringOption,GetIntOption,GetFloatOption,GetBoolOption,GetUserID,GetRoleID, andGetChannelIDreturn typed option values.IsChatInputCommand,IsCommand,IsContextMenuCommand,IsUserContextMenuCommand,IsMessageContextMenuCommand,IsRepliable,InGuild,IsAutocomplete,IsMessageComponent,IsButton,IsSelectMenu, andIsModalSubmitidentify valid response paths.MemberPermissions,BotPermissions,User,GuildID,ChannelID,FocusedOptionString,TargetID, andFetchTargetMemberexpose permission and context data.Reply,ReplyEphemeral,ReplyEmbed,ReplyComplex, andReplyComplexWithFilessend initial responses.Defer,DeferEphemeral,Update,UpdateContent,DeferUpdate,Pong,LaunchActivity,Autocomplete,ShowModal,ShowModalBuilder, andShowModalComplexsend specialized initial callbacks.GetReply,EditReply,EditReplyComplex,EditReplyWithFiles, andDeleteReplymanage the original response.Followup,FollowupEmbed,FollowupEphemeral,FollowupComplex,FollowupComplexWithFiles,GetFollowup,EditFollowup, andDeleteFollowupmanage follow-up messages.HasResponded,Replied,Deferred, andEphemeralreport response state.interactions.InteractionResponsecontainsTypeand callbackData;InteractionCallbackDatacontains content, embeds, flags, components, attachments, choices, modal fields, thread fields, and polls.- Response type constants include
Pong,ChannelMessageWithSource, both deferred forms,UpdateMessage, autocomplete,Modal, andLaunchActivity.interactions.VerifySignatureverifies an Ed25519 HTTP interaction request from a public key, timestamp, signature, and body.interactions.VerifyRequestadditionally checks timestamp freshness to prevent replay attacks.interactions.Serveris anhttp.Handlerthat verifies signatures and timestamps automatically, handles pings, and dispatches to a user-provided handler. InteractionContextresponse methods:Reply,ReplyEphemeral,ReplyComplex,ReplyEphemeralComplex(complex response with ephemeral flag),ReplyEmbed,Defer,DeferEphemeral,Update,ShowModal. Follow-up methods:Followup,FollowupEphemeral,FollowupEmbed,FollowupComplex.messages.Message.FirstEmbed()safely accesses the first embed without index-out-of-range panics.Routercomponent registration:Button/ButtonPrefix,Select/SelectPrefix,Modal/ModalPrefix. Prefix matching uses longest-match-first, so overlapping prefixes resolve to the most specific handler.bot.WithInteractionTimeoutmakes the 3-second initial response deadline configurable.snowflake.MustParse(s)panics on invalid input;snowflake.ID.IsZero()tests for an unset snowflake.permissions.Permission.String()returns the decimal string for channel permission overwrites.ApplicationCommandOptionhas anOptionsfield for nested subcommand options.
Examples
Related APIs
commands.mdfor routing and command options.components.mdfor callback components.embeds.mdfor rich responses.../low-level/interactions/README.mdfor raw interaction models.