Skip to content

Aero developer docs

Components: embeds, buttons and menus

Messages your Aero sends can carry rich cards, buttons and dropdown menus. You describe the shape; Ballastra validates and renders it.

The message shape

A message an Aero sends can carry an embed (a rich card), components (rows of buttons or a dropdown), and a layout (stacked blocks). A button press or menu choice comes back to your Aero as a component interaction. Every field, limit and style is checked by the same validators the API uses, so a shape that validates is accepted by i.reply(...).

Buttons
{
  "text": "Choose an action:",
  "components": [{ "components": [
    { "type": "button", "style": "primary", "label": "Confirm", "custom_id": "confirm" },
    { "type": "button", "style": "danger", "label": "Cancel", "custom_id": "cancel" },
    { "type": "button", "style": "link", "label": "Learn more", "url": "https://www.ballastra.app/aero/docs" }
  ] }]
}
Dropdown menu
{
  "embeds": [{ "title": "Pick a role", "color": "#A78BFA" }],
  "components": [{ "components": [{
    "type": "select", "kind": "string", "custom_id": "role", "placeholder": "Select a role",
    "options": [
      { "label": "Developer", "value": "dev", "description": "Ships code" },
      { "label": "Designer", "value": "design", "description": "Ships pixels" },
      { "label": "Community", "value": "community" }
    ]
  }] }]
}
Embed
{
  "embeds": [{
    "title": "Release 2.0 is live",
    "description": "A rich card can hold a title, description, fields, an image, and an accent color.",
    "color": "#22C55E",
    "fields": [
      { "name": "Version", "value": "2.0.0", "inline": true },
      { "name": "Status", "value": "Stable", "inline": true }
    ],
    "footer": { "text": "Posted by your Aero" }
  }]
}

If a shape breaks a rule — too many buttons, a missing label, a field over its limit — the API rejects it with a 400 and an error explaining what is wrong, so nothing malformed ever reaches members.

Replying to a component interaction

  • i.reply(...) posts a normal public message.
  • i.replyEphemeral(...) replies privately; only the person who pressed sees it.
  • i.update(...) edits the message the button is on, in place.
  • i.deferReply() then i.editReply(id, ...) shows a placeholder, then the real answer.