Skip to content
Micup API

Interactions

An interaction is one use of a bot by a person: a slash command, a click on a button or a select menu, a keystroke in an option with autocomplete, or a submitted modal. Micup sends it to the bot as an Interaction Create Gateway Dispatch with a token, and the bot answers with that token. Every bot can use interactions; no review is required.

Commands are registered through Application commands. Buttons and select menus are message components the bot puts on its own messages.

  1. A person runs a command, clicks a component, or submits a modal. The client sends the request, and Micup checks the caller, the command or component, and the channel.
  2. Micup sends one Interaction Create to exactly one session of the bot: a session of the guild’s shard, or in a direct message a session on shard 0 or without sharding. With several candidates the most recently connected one is chosen. It never goes to every session, to another bot, or to a user session.
  3. The bot sends its first answer to Create interaction response within 3 seconds. The token answers the first callback only once.
  4. For 15 minutes after the interaction was created, the bot may edit the original answer and send up to 20 followups with the same token, or with Create interaction followup. A started interaction stays answerable for those 15 minutes even if its command changes or is deleted, as on Discord; the bot’s membership and permissions are checked again on every answer.

Delivery is not retried. When no session of the bot is connected, nothing is delivered and the invoking user is told the bot did not receive it. A bot that misses the 3 second window cannot answer that interaction any more, and the user sees that the bot did not answer.

  • The bot must be a member of the guild. A public answer needs the bot’s SEND_MESSAGES in the channel, and a private answer needs its VIEW_CHANNEL.
  • In a direct message, interactions work only in a one-to-one direct message between the user and the bot, never in a group direct message. A command needs dm_permission true and no default_member_permissions.
  • The bot’s feature_bot_interactions limit key must be 1 and feature_bot_interactions_blocked must be 0. feature_bot_components, feature_bot_select_menus, feature_bot_modals, and feature_bot_components_v2 each gate one step. The invoking user’s feature_app_commands must be 1. A closed key returns 403 APPLICATION_COMMAND_FEATURE_DISABLED.

There is no HTTP interactions endpoint, no PING interaction, and no Ed25519 signature: every interaction arrives over the Gateway.

The d payload of Interaction Create.

FieldTypeDescription
idsnowflakeThe ID of the interaction
application_idsnowflakeThe ID of the bot’s application
typeintegerInteraction type
tokenstringThe interaction token
versionintegerAlways 1
datainteraction data objectWhat was used
guild_id?snowflakeThe guild, absent in a direct message
channel_idsnowflakeThe channel the interaction happened in
channelpartial channel objectThat channel
member?interaction member objectThe invoking member, in a guild
user?partial user objectThe invoking user, in a direct message
message?message objectThe message whose component was used
app_permissionsstringThe bot’s permissions in the channel
localestringThe invoking user’s locale
guild_locale?stringThe guild’s locale
entitlementsarrayAlways empty
contextinteger0 in a guild, 1 in a direct message with the bot
attachment_size_limitintegerThe largest upload in bytes

The object also has authorizing_integration_owners, a map with the guild ID under the key 0, kept for Discord libraries.

A guild member object with two more fields.

FieldTypeDescription
permissionsstringThe member’s permissions in the channel, with overwrites
flagsintegerAlways 0, kept for Discord libraries
ValueNameDescription
1PINGNot sent, because there is no HTTP endpoint
2APPLICATION_COMMANDA slash command was run
3MESSAGE_COMPONENTA button was clicked or a select menu was used
4APPLICATION_COMMAND_AUTOCOMPLETEThe user is typing in an option with autocomplete
5MODAL_SUBMITA modal the bot opened was submitted

Sent with types 2 and 4.

FieldTypeDescription
idsnowflakeThe ID of the command
namestringThe command name
typeintegerAlways 1
guild_id?snowflakePresent for a guild command
options?array[option value object]The filled options
resolved?resolved data objectOnly the users, members, roles and channels the options name

The options are plain values, or exactly one subcommand, or exactly one group holding one subcommand, each with its own options.

FieldTypeDescription
namestringThe option name
typeintegerOption type
value?string | number | booleanThe value, absent on a subcommand or group
options?array[option value object]The children of a subcommand or group
focused?booleanTrue on the one option being typed, type 4 only

In an autocomplete interaction the focused option’s value is the raw text typed so far, even for an integer or number option, and the other options can be incomplete.

Sent with type 3.

FieldTypeDescription
custom_idstringThe custom_id of the component
component_typeintegerThe component type
values?array[string]The chosen values of a select menu
resolved?resolved data objectThe chosen users, members, roles or channels of an automatic select

Sent with type 5.

FieldTypeDescription
custom_idstringThe custom_id of the modal
componentsarray[component] objectThe modal layout with each text input’s value filled in
FieldTypeDescription
users?map[snowflake, partial user object]Users by ID
members?map[snowflake, interaction member object]Members by ID, without user
roles?map[snowflake, guild role object]Roles by ID
channels?map[snowflake, resolved channel object]Channels by ID
FieldTypeDescription
idsnowflakeThe ID of the channel
name?stringThe channel name
typeintegerThe channel type
permissionsstringThe invoking member’s permissions in that channel
parent_id?snowflakeThe parent category

A token has the form base64url("interaction:" + id + ":" + base64url(32 random bytes)), so it starts with aW50ZXJhY3Rpb246. It is bound to its interaction and channel and expires 900 seconds after the interaction was created. The first callback must arrive within 3,000 milliseconds of creation and can be made once.

A route that receives an unknown, mismatched, or expired token answers 404 UNKNOWN_WEBHOOK or 410 INTERACTION_EXPIRED, and reveals nothing about other interactions.

ValueName
1PONG
4CHANNEL_MESSAGE_WITH_SOURCE
5DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE
6DEFERRED_UPDATE_MESSAGE
7UPDATE_MESSAGE
8APPLICATION_COMMAND_AUTOCOMPLETE_RESULT
9MODAL

Type 4 answers with a message. Type 5 shows that the bot is thinking, and the message follows as an edit of @original. Type 6 confirms a click and changes nothing yet. Type 7 edits the message the component is on. Type 8 sends autocomplete choices. Type 9 opens a modal. Type 1 is refused, because there is no HTTP endpoint.

CallbackValid for
4, 5Types 2, 3 and 5
6, 7Type 3
8Type 4 alone, which accepts no other callback
9Types 2 and 3, as the first answer

The body of Create interaction response.

FieldTypeDescription
typeintegerCallback type
data?objectThe response message, {flags} for type 5, {choices} for type 8, or the modal for type 9

Type 6 has no data. Type 5 takes only flags, 0 or 64, which fixes whether the later answer is private.

What Create interaction response returns with with_response, the shape of Discord’s InteractionCallbackResponse.

FieldTypeDescription
interactioncallback interaction objectThe answered interaction
resourcecallback resource objectWhat the answer produced
FieldTypeDescription
idsnowflakeThe ID of the interaction
typeintegerThe interaction type
response_message_id?snowflakeThe message of a type 4 or 7 answer
response_message_loading?booleantrue after type 5, false after type 4 or 7
response_message_ephemeral?booleanWhether the answer is private, after type 4, 5 or 7
FieldTypeDescription
typeintegerThe callback type
message?message objectThe message of a type 4 or 7 answer

A private message in resource goes only to the bot that answered and is stored nowhere else.

The message of callback types 4 and 7, of followups, and of edits.

FieldTypeDescription
content??stringMessage text
embeds??array[rich embed object]Up to 10 embeds, as a bot message takes
components??array[component object]Message components
allowed_mentions?allowed mentions objectWhich mentions notify, none by default
flags?integer0, 64 (EPHEMERAL), 32768 (IS_COMPONENTS_V2), or both
tts?booleanIgnored
attachments?arrayAccepted only when empty

A new message needs content, an embed, or components. With IS_COMPONENTS_V2 it carries neither content nor embeds. A whole response is at most 32,768 bytes of UTF-8 JSON. Embed media goes through the media proxy for public and private answers alike, so a client never loads a bot’s external URL directly.

A response with flags 64 is visible only to the user who triggered the interaction. It is never stored as a channel message: it reaches no search index, push notification, unread count, pin, reply, or data export. Micup sends it only to the session the user invoked from, as Interaction Response Create, and the client can recover it for 15 minutes through List private interaction responses.

A public answer is a normal message by the bot, with application_id and interaction_metadata set by the server. After a public type 5, no placeholder message is posted: only the invoking user sees that the bot is thinking, and the first edit of @original creates the message.

One private answer as List private interaction responses returns it, with the shape of Interaction Response Update.

FieldTypeDescription
interaction_idsnowflakeThe interaction this answers
kindstringAlways message
response_idsnowflakeThe private message ID
sequenceinteger0 for the original answer and 1 through 20 for followups
messagemessage objectThe private message
FieldTypeDescription
custom_idstringReturned in the modal submit, 1 through 100 characters
titlestring1 through 45 characters
componentsarray[component object]1 through 5 modal components

A modal opens only as the first answer to a command or component interaction. Its submit arrives as a type 5 interaction. A submit with no open modal returns 409 MODAL_NOT_OPEN, a field the modal did not have or a value over its limits returns 400, and a submit after 900 seconds returns 410 INTERACTION_EXPIRED.

A type 4 interaction is answered with callback type 8 within 3 seconds and cannot be deferred. choices has up to 25 entries, each a name of 1 through 100 characters and a value of the option’s type. The client sends a request only for options with autocomplete true, after a short pause in typing, and shows only the latest answer.

After the first answer, the token works on the webhook token routes with the application ID in place of the webhook ID. A token that starts with aW50ZXJhY3Rpb246 selects the interaction branch, and any other token keeps the ordinary webhook behaviour.

  • POST /v1/webhooks/{application_id}/{token} sends a followup, up to 20.
  • GET, PATCH, and DELETE on /v1/webhooks/{application_id}/{token}/messages/@original read, edit, and delete the original answer.
  • PATCH and DELETE on /v1/webhooks/{application_id}/{token}/messages/{message_id} edit and delete a followup.

The bodies are the response message. An edit keeps the visibility of the first answer and is checked against the stored IS_COMPONENTS_V2 flag. @original before the first public message exists returns 404. An application_id that does not match the interaction returns 404 UNKNOWN_WEBHOOK. Only the interaction’s own messages can be edited or deleted.

The interaction types, callback types, component types, button styles, flag values, event name, and routes match Discord. These do not:

TopicMicup
API versionREST /v1, Gateway v=1. In discord.js set rest: {api, version: '1'} and ws: {version: 1}
HTTP endpointNone. Every interaction arrives over the Gateway
Bot membershipThe bot must be in the guild. A public answer needs its SEND_MESSAGES
The dm_permission defaultFalse, or set by contexts. integration_types is discarded
Deferred public answerNo placeholder message. The first edit of @original creates it
Private answersSent to the invoking session as Interaction Response events, never as a stored message
Components v2 contentCarries a text fallback built by the server. A bot ignores it
Extra followup routeCreate interaction followup, with the bot token and Idempotency-Key
Command writesIf-Match is optional. Without it the newest set is changed
Command localisationname_localizations, description_localizations and nsfw are refused with 400
Not offeredContext menu commands, premium buttons, SKUs, activities, files in a response, selects and uploads in a modal
StatusCodeMeaning
403APPLICATION_COMMAND_FEATURE_DISABLEDA limit key is closed for the bot or the user
503APPLICATION_COMMAND_BOT_UNAVAILABLEThe bot cannot take the interaction
409APPLICATION_COMMAND_STALEThe command changed, or its bot was closed or left, since the client loaded it
410INTERACTION_EXPIREDThe 3 second or 15 minute window has passed
409INTERACTION_TOKEN_CONSUMEDThe interaction was already answered
409INTERACTION_NONCE_CONFLICTA nonce or idempotency key was reused with a different body
503INTERACTION_OUTCOME_UNKNOWNThe result could not be confirmed. Retry with the same nonce or key
400UNSUPPORTED_COMPONENTA component type that is not offered
400INVALID_COMPONENT_VALUEA select value that is not on the component
409MODAL_NOT_OPENA modal submit without an open modal
404UNKNOWN_WEBHOOKThe token or application ID does not match
  • A custom_id and every select value are untrusted input. Micup checks only that they are on the message and that values are among the stored options, so the bot validates them like any user input and never puts secrets or permissions in them.
  • Check permissions in the bot. default_member_permissions only hides a command, and an administrator can change who sees it.
  • A link button takes only an http or https URL of at most 512 characters without user information, and a blocklisted domain is refused. Clients open it after their external link warning.
  • Keep the token out of logs and error reports, and answer from the session that received the interaction.
POST/v1/interactions

Runs a bot’s interaction for the signed-in user and returns its delivery status, never the token, the options, or the bot’s answer. The answer arrives over the Gateway. The same nonce returns the same interaction and never delivers twice.

  • This is a user-only operation that accepts only a session token. A bot, OAuth2 bearer, or admin credential is refused with 403 ACCESS_DENIED.
  • An unclaimed account is refused with 400 UNCLAIMED_ACCOUNT_CANNOT_SEND_MESSAGES, and one that has not started a session receives the field code MUST_START_SESSION_BEFORE_SENDING.
  • The caller needs the command’s default_member_permissions and access to the channel.
FieldTypeDescription
typeinteger2 a command, 4 autocomplete, 5 a modal submit
application_idsnowflakeThe bot’s application, types 2 and 4
command_idsnowflakeThe command, types 2 and 4
command_versionsnowflakeThe command version the client shows, types 2 and 4
channel_idsnowflakeThe channel, types 2 and 4
optionsarray[option value object]The filled options, types 2 and 4
parent_interaction_idsnowflakeThe interaction that opened the modal, type 5
custom_idstringThe modal’s custom_id, type 5
componentsarray[modal field object]Up to 5 field values, type 5
noncestring1 through 32 characters, unique per attempt
session_idstringThe Gateway session that receives private answers

Type 4 needs exactly one option with focused true. A command_version that is not the stored one returns 409 APPLICATION_COMMAND_STALE.

FieldTypeDescription
custom_idstringThe text input’s custom_id
valuestringAt most 4000 characters
FieldTypeDescription
idsnowflakeThe ID of the interaction
statusstringDelivery status
deadline_atISO8601 timestampWhen the first answer is due
ValueDescription
RESERVEDThe interaction was accepted
PREPAREDThe payload was built
DISPATCHINGDelivery to the bot is in progress
ENQUEUEDOne bot session has it queued
NOT_DELIVEREDNo session of the bot was connected, and nothing was sent
DELIVERY_UNKNOWNThe Gateway did not confirm, and the interaction is not sent again
StatusBodyCondition
200response bodyThe interaction was accepted
400error responseThe body is invalid
403error responseCaller is not a user session and the request returns ACCESS_DENIED
403error responseA key is closed and the request returns APPLICATION_COMMAND_FEATURE_DISABLED
404error responseThe channel cannot be used and the request returns UNKNOWN_CHANNEL
409error responseThe command or its bot changed and the request returns APPLICATION_COMMAND_STALE
409error responseThe nonce was used for another body and the request returns INTERACTION_NONCE_CONFLICT
503error responseNo Gateway node took the interaction and the request returns APPLICATION_COMMAND_BOT_UNAVAILABLE

20 requests per 5 seconds for each authenticated user, on the interaction:route bucket. An invocation also counts 5 requests per 5 seconds for each authenticated user, on the interaction:invoke bucket, 30 requests per 1 minute for each authenticated user, on the interaction:invoke:minute bucket, and 10 requests per 1 minute for each user and application, on the interaction:invoke:application bucket.

GET/v1/interactions/{interaction_id}

Returns the delivery status of an interaction the signed-in user invoked. Another user receives 404.

  • This is a user-only operation that accepts only a session token.
FieldTypeDescription
interaction_idsnowflakeThe ID of the interaction
StatusBodyCondition
200invoke response bodyThe interaction is the caller’s
403error responseCaller is not a user session and the request returns ACCESS_DENIED
404error responseNo such interaction for the caller, and the request returns UNKNOWN_MESSAGE

30 requests per 10 seconds for each authenticated user, on the interaction:status bucket.

GET/v1/interactions/{interaction_id}/response

Returns the private answers to an interaction the signed-in user invoked, for a client that missed them while reconnecting. Each entry has the shape of Interaction Response Update. They exist for 15 minutes and only for the invoking user.

  • This is a user-only operation that accepts only a session token.
FieldTypeDescription
interaction_idsnowflakeThe ID of the interaction
StatusBodyCondition
200array[private response object]The interaction is the caller’s
403error responseCaller is not a user session and the request returns ACCESS_DENIED
404error responseNo such interaction for the caller, and the request returns UNKNOWN_MESSAGE

Shares the interaction:status bucket with Get interaction status.

POST/v1/interactions/{interaction_id}/{interaction_token}/callbackUnauthenticated

Sends the first answer to an interaction. Returns 204 with an empty body, or with with_response 200 and the callback response. discord.py asks for it on every first answer.

  • No Authorization header is needed, which is what discord.js sends. A header that is present must be the token of the bot the interaction went to, and any other answers 404 UNKNOWN_WEBHOOK.
  • The answer must arrive within 3 seconds of creation, and a later one returns 410 INTERACTION_EXPIRED. A second answer returns 409 INTERACTION_TOKEN_CONSUMED.
  • The callback type must be valid for the interaction type, and callback type 1 is refused with 400.
  • Callback type 9 needs the bot’s feature_bot_modals key.
FieldTypeDescription
interaction_idsnowflakeThe ID of the interaction
interaction_tokenstringThe token from Interaction Create
FieldTypeDescription
with_response?stringtrue or 1 returns the callback response

An interaction callback object.

StatusBodyCondition
200interaction callback response objectThe answer was accepted and with_response is true or 1
204emptyThe answer was accepted without with_response
400error responseThe body is invalid or the callback type does not fit
403error responseA key is closed and the request returns APPLICATION_COMMAND_FEATURE_DISABLED
404error responseThe token does not match and the request returns UNKNOWN_WEBHOOK
409error responseThe interaction was answered and the request returns INTERACTION_TOKEN_CONSUMED
410error responseThe 3 seconds passed and the request returns INTERACTION_EXPIRED

5 requests per 1 second for each interaction ID, on the interaction:callback::interaction_id bucket.

POST/v1/interactions/{interaction_id}/followupsBot

Sends one more message for an answered interaction with the bot token. The Idempotency-Key header makes a retry safe: the same key returns the same message. Returns the message, whose visibility follows the first answer.

  • Only the bot the interaction went to is accepted, and a user credential is refused with 403 ACCESS_DENIED.
  • Idempotency-Key is required, 1 through 64 printable ASCII characters. A missing or malformed key returns 400, and a key reused with another body returns 409 INTERACTION_NONCE_CONFLICT.
  • The interaction must have been answered, and at most 20 followups exist. After 15 minutes the route returns 410 INTERACTION_EXPIRED.
FieldTypeDescription
interaction_idsnowflakeThe ID of the interaction

An interaction response message object.

StatusBodyCondition
200message objectThe followup was sent, or the key was already used for this body
400error responseThe body or the key is invalid
403error responseCaller is not a bot and the request returns ACCESS_DENIED
404error responseNot this bot’s answered interaction, and the request returns UNKNOWN_WEBHOOK
409error responseThe key was used for another body and the request returns INTERACTION_NONCE_CONFLICT
410error responseThe 15 minutes passed and the request returns INTERACTION_EXPIRED
503error responseThe result is unknown and the request returns INTERACTION_OUTCOME_UNKNOWN

5 requests per 1 second for each interaction ID, on the interaction:followup::interaction_id bucket.