Components & Modals

discli serve mode supports Discord’s interactive message components — buttons, select menus, and modal forms. These are sent as part of the JSONL protocol via stdin.

Buttons

Send buttons by adding a components field to send or reply actions:

{"action": "send", "channel_id": "456", "content": "Click a button:", "components": [
[
{"type": "button", "label": "Approve", "style": "success", "custom_id": "approve", "emoji": "✅"},
{"type": "button", "label": "Reject", "style": "danger", "custom_id": "reject", "emoji": "❌"},
{"type": "button", "label": "Docs", "style": "link", "url": "https://example.com"}
]
]}

Button styles

StyleColorUse for
primaryBlueMain actions
secondaryGreyAlternative actions
successGreenConfirm/approve
dangerRedDestructive/reject
linkGreyExternal URL (requires url instead of custom_id)

Add "disabled": true to grey out a button.

Select Menus

String select

{"action": "send", "channel_id": "456", "content": "Pick:", "components": [
[{"type": "select", "custom_id": "color", "placeholder": "Choose a color...", "options": [
{"label": "Red", "value": "red", "emoji": "🔴", "description": "Warm color"},
{"label": "Blue", "value": "blue", "emoji": "🔵", "description": "Cool color"}
]}]
]}

User, Role, Channel selects

[{"type": "user_select", "custom_id": "pick_user", "placeholder": "Choose someone..."}]
[{"type": "role_select", "custom_id": "pick_role", "placeholder": "Choose a role..."}]
[{"type": "channel_select", "custom_id": "pick_ch", "placeholder": "Choose a channel..."}]

Handling Interactions

When a user clicks a button or selects from a menu, you receive a component_interaction event:

{"event": "component_interaction", "custom_id": "approve", "component_type": 2, "values": [], "user": "alice", "user_id": "123", "interaction_token": "ITK"}

Respond with one of:

ActionWhen to use
interaction_respondImmediate reply (supports ephemeral: true)
interaction_editUpdate the original message (e.g., disable buttons)
interaction_followupSend a new followup message
modal_sendOpen a modal form

Ephemeral response (only the clicker sees it)

{"action": "interaction_respond", "interaction_token": "ITK", "content": "Done!", "ephemeral": true}

Edit original message (disable button)

{"action": "interaction_edit", "interaction_token": "ITK", "content": "Approved! ✅", "components": [
[{"type": "button", "label": "Approved", "style": "secondary", "custom_id": "x", "disabled": true}]
]}

Modals are popup forms with text inputs. They can only be triggered from a component interaction (button click).

Open a modal

{"action": "modal_send", "interaction_token": "ITK", "title": "Feedback Form", "custom_id": "feedback", "fields": [
{"label": "Your Name", "custom_id": "name", "style": "short", "placeholder": "Enter name...", "required": true},
{"label": "Feedback", "custom_id": "feedback_text", "style": "long", "placeholder": "Share your thoughts...", "max_length": 1000}
]}

Field styles: short (single line) or long (multi-line textarea).

Handle submission

When the user submits, you receive a modal_submit event:

{"event": "modal_submit", "custom_id": "feedback", "fields": {"name": "Alice", "feedback_text": "Great bot!"}, "user": "alice", "interaction_token": "ITK2"}

Respond with interaction_followup:

{"action": "interaction_followup", "interaction_token": "ITK2", "content": "Thanks!", "embed": {"title": "Feedback Received", "color": "57F287"}}

Combining Embeds + Components

{"action": "send", "channel_id": "456", "content": "", "embed": {
"title": "Action Required", "description": "Approve this request?", "color": "FEE75C"
}, "components": [
[
{"type": "button", "label": "Approve", "style": "success", "custom_id": "approve"},
{"type": "button", "label": "Reject", "style": "danger", "custom_id": "reject"}
]
]}

Important Notes

  • Components are serve mode only — the CLI cannot send buttons or selects
  • Each row can have up to 5 buttons or 1 select menu
  • A message can have up to 5 rows of components
  • Component interactions must be responded to within 3 seconds (auto-deferred after 2.5s)
  • Modal text inputs are limited to 4000 characters each
  • custom_id max length is 100 characters