Open experience UI from chat buttons

Existing sendChoices buttons and sendKeyboard options can open installed native experience pages or bottom sheets. Use a build of the SDK containing ExperienceButtonAction and an Atlas client supporting these actions. Experiences are always available; no developer setting is required.

Choose the right action

Where the button appears Action shape Result
Chat message or reply keyboard { type: 'experience.page', experienceId, target } Open a full-screen page
Chat message or reply keyboard { type: 'experience.sheet', experienceId, target } Open a medium/large bottom sheet
Inside an experience openPage(target, options?) Navigate within the mounted experience
Inside an experience openSheet(target) Present a sheet over the current experience screen

The openPage and openSheet helpers return UIAction objects with tool and args; they are not chat-button actions. Chat buttons use ExperienceButtonAction with type, experienceId, and target.

Complete TypeScript example

This bot shares a mock food-ordering pack with /install-food. After reviewing and installing that pack, the user sends /food to see the existing chat buttons. The example collects local demo choices and never submits a real order.

import {
  createAtlasBot,
  type ChoiceRows,
  type ExperienceButtonAction,
} from 'atlas-bot-sdk';
import {
  page, screen, ui, scope, openSheet, closeSheet, closePage, sendExperience,
  type ExperienceDefinition,
} from 'atlas-bot-sdk/experiences';

const experience: ExperienceDefinition = {
  id: 'food-chat-demo',
  title: 'Food ordering demo',
  version: '1.0.0',
  icon: 'takeoutbag.and.cup.and.straw',
  tools: ['ui.open_sheet', 'ui.close_sheet', 'ui.close_page'],
  permissions: [],
  properties: {
    address: { type: 'string', title: 'Delivery address', default: '123 Market Street' },
  },
  ui: screen({ title: 'Food ordering demo' }, [
    ui('Text', { text: 'Mock food ordering. Open the menu from the chat.' }),
    page('menu', { title: 'Tonight’s menu', subtitle: 'Deliver to {{address}}' }, [
      ui('ItemRow', { title: 'Chicken bowl', subtitle: 'Roasted vegetables and rice', text: '$14' }),
      ui('SummaryRow', { title: 'Delivery address', text: '{{address}}' }),
    ], [
      { title: 'Edit address', action: openSheet('address') },
      { title: 'Back to chat', action: closePage() },
    ]),
    ui('Sheet', {
      id: 'address', title: 'Delivery address',
      elements: [ui('Control', { scope: scope('address') })],
      buttons: [{ title: 'Done', action: closeSheet(), primary: true }],
    }),
  ]),
};

const menuAction: ExperienceButtonAction = {
  type: 'experience.page', experienceId: experience.id, target: 'menu',
};
const buttons: ChoiceRows = [
  [{ label: 'Browse menu', value: '/food-help', action: menuAction }],
  [{
    label: 'Delivery address', value: '/food-help',
    action: { type: 'experience.sheet', experienceId: experience.id, target: 'address' },
  }],
];

const bot = createAtlasBot({ accessToken: process.env.ATLAS_BOT_TOKEN, llm: false });
bot.onCommand('install-food', ctx => sendExperience(ctx, experience));
bot.onCommand('food', ctx => ctx.sendChoices('What would you like to do?', buttons));
bot.onCommand('food-help', ctx => ctx.sendText(
  'Install the pack with /install-food, then use an Atlas client supporting experience chat buttons.'
));
bot.start();

Sharing a pack only sends its install card. The bot does not receive an installation acknowledgement from a navigation-button tap. Offer installation and navigation as separate steps; do not assume sending a pack installs it.

Field reference

Field Meaning
label Visible button text
action.type Exactly experience.page or experience.sheet
action.experienceId Installed manifest ID, such as food-chat-demo; not a workflow ID
action.target Declared Page or Sheet element ID, such as menu or address
value Optional fallback reply for older clients; defaults to the label
disabled Freeze one button; the message widget can also be disabled
row Optional row index; nested option arrays usually make grouping clearer
silent Applies to callback buttons; action takes precedence when both are supplied

ChoiceInput describes one string or button object. ChoiceRows accepts a flat list or nested rows. ExperienceButtonAction describes the destination. All three are type-only exports from atlas-bot-sdk; the corresponding wire types are also available from atlas-bot-sdk/wire.

Targets resolve in the installed pack's primary UI definition, the same one opened by its launcher. The SDK builder creates exp.<experienceId>.app. Hand-authored packs with several UI definitions use the lexicographically first namespaced definition as their primary definition; this payload does not select an alternate definition. Declare entry pages and sheets in that primary UI. There is no implicit home target: the requested element must exist.

Existing editMessage(chatId, eventId, text, { choices, disabled }) accepts the same options. Use it to change a target or freeze a button. Atlas checks that the tapped option still belongs to the current, enabled message before opening.

Troubleshooting and compatibility

Symptom Check
“Unable to open experience” Install/update the pack; verify the manifest ID and target in its primary UI
Page exists but will not open Match experience.page to a Page, or experience.sheet to a Sheet; native mobile_app UI is required
A reply appears instead Older clients may ignore action and send value; handle the fallback command in the bot
Bot handler never runs on tap Navigation is local; use ordinary replies or silent: true without action when a bot callback is required
Button does nothing Check per-button/widget disabling, an already-answered message, or an edited/removed option

Unknown or malformed non-null actions stay unavailable on supporting clients; they do not become outgoing replies. Canvas/web experiences and arbitrary URLs are not supported by these destination actions. A tap does not expand tool permissions; pack installation and existing grants still apply.

See the native UI library reference, ride-booking example, and native example packs.