Skip to main content

dialogs

Use this skill when the user is building dialogs (task modules) in a Microsoft Teams bot on Teams SDK 2.1 — opening a dialog from a card with `OpenDialogData`, handling `dialog.open.<id>` and `dialog.submit.<action>`, returning an Adaptive Card or a web page, multi-step flows, dialogs opened from message extensions. Triggers on "Teams dialog", "task module", "modal in Teams", "dialog.open", "dialog.submit", "OpenDialogData".

Source facts

Repository
Agents-Store/opencode-plugins
Last source activity
October 3, 2026 at 23:16
Detected SKILL.md language
English
Stars
0
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
dialogs
description
Use this skill when the user is building dialogs (task modules) in a Microsoft Teams bot on Teams SDK 2.1 — opening a dialog from a card with `OpenDialogData`, handling `dialog.open.<id>` and `dialog.submit.<action>`, returning an Adaptive Card or a web page, multi-step flows, dialogs opened from message extensions. Triggers on "Teams dialog", "task module", "modal in Teams", "dialog.open", "dialog.submit", "OpenDialogData".
# Dialogs (task modules) A dialog is a modal the Teams client opens over a chat. Its body is an Adaptive Card or a web page; when the user submits, control returns to the bot. Two routes and two helpers cover it: `dialog.open.<id>` renders the dialog, `dialog.submit.<action>` receives the result, `OpenDialogData` names the dialog on the button that opens it, `SubmitData` names the action on the button inside it. Both helpers come from `@microsoft/teams.cards`. ## 1. Open a dialog from a card ```ts import { MessageActivityInput } from '@microsoft/teams.api'; import { AdaptiveCard, OpenDialogData, SubmitAction, TextBlock } from '@microsoft/teams.cards'; app.on('message', async ({ send }) => { const card = new AdaptiveCard( new TextBlock('Need details? Open the form.', { size: 'Large', weight: 'Bolder' }), ).withActions( new SubmitAction().withTitle('Simple form').withData(new OpenDialogData('simple_form')), new SubmitAction().withTitle('Web page').withData(new OpenDialogData('webpage_dialog')), ); await send(new MessageActivityInput('Pick one').addCard('adaptive', card)); }); ``` `OpenDialogData` marks the action as a `task/fetch` request and carries the dialog id used for routing. It is attached to a **`SubmitAction`**; that is what the Teams client requires. ## 2. `dialog.open.<id>` — return a card dialog ```ts import { cardAttachment } from '@microsoft/teams.api'; import { AdaptiveCard, SubmitAction, SubmitData, TextBlock, TextInput } from '@microsoft/teams.cards'; app.on('dialog.open.simple_form', async () => { const dialogCard = new AdaptiveCard( new TextBlock('This is a simple form', { size: 'Large', weight: 'Bolder' }), new TextInput().withId('name').withLabel('Name').withIsRequired().withPlaceholder('Enter your name'), ).withActions( new SubmitAction().withTitle('Submit').withData(new SubmitData('simple_form')), ); return { task: { type: 'continue', value: { title: 'Simple form', card: cardAttachment('adaptive', dialogCard), }, }, }; }); ``` The buttons inside a dialog are `Action.Submit` too. `SubmitData('simple_form')` makes the click arrive at `dialog.submit.simple_form`. `dialog.open` (without an id) is a catch-all; prefer one handler per dialog id. ## 3. `dialog.submit.<action>` — collect the result ```ts app.on('dialog.submit.simple_form', async ({ activity, send }) => { const name = activity.value.data.name; await send(`Hi ${name}, thanks for submitting the form!`); return { task: { type: 'message', value: 'Form was submitted' } }; // closes the dialog with a final message }); ``` Return values: - `{ task: { type: 'message', value: '…' } }` closes the dialog and shows the text. - `{ status: 200 }` closes it silently. - `{ task: { type: 'continue', value: { title, card } } }` swaps the body — a multi-step flow. ## 4. Multi-step dialogs Carry earlier answers forward through the extra data of `SubmitData`: ```ts import { cardAttachment } from '@microsoft/teams.api'; import { AdaptiveCard, SubmitAction, SubmitData, TextBlock, TextInput } from '@microsoft/teams.cards'; app.on('dialog.submit.step_one', async ({ activity }) => { const name = activity.value.data.name; const next = new AdaptiveCard( new TextBlock('Email', { size: 'Large', weight: 'Bolder' }), new TextInput().withId('email').withLabel('Email').withIsRequired(), ).withActions( new SubmitAction().withTitle('Submit').withData(new SubmitData('step_two', { name })), ); return { task: { type: 'continue', value: { title: `Thanks ${name}`, card: cardAttachment('adaptive', next) } } }; }); app.on('dialog.submit.step_two', async ({ activity, send }) => { const { name, email } = activity.value.data; await send(`Hi ${name}, we will write to ${email}.`); return { status: 200 }; }); ``` ## 5. Web-page dialogs Serve the page from the bot with `app.tab(...)` and return its URL. The page must be public, load `@microsoft/teams-js`, and its domain must be in the manifest's `validDomains`. ```ts import path from 'node:path'; app.tab('dialog-form', path.resolve('dist/dialog-form')); // served at /tabs/dialog-form app.on('dialog.open.webpage_dialog', async () => ({ task: { type: 'continue', value: { title: 'Web page dialog', url: `${process.env['BOT_ENDPOINT']}/tabs/dialog-form`, // your public https origin, set in .env width: 1000, height: 800, }, }, })); app.on('dialog.submit.webpage_dialog', async ({ activity, send }) => { await send(`Got ${activity.value.data.email}`); return { status: 200 }; // closes the dialog }); ``` Inside the page, submit with TeamsJS and include the `action` field so the router can find the handler: ```ts import * as microsoftTeams from '@microsoft/teams-js'; async function submitForm(email: string) { await microsoftTeams.app.initialize(); microsoftTeams.dialog.url.submit({ action: 'webpage_dialog', email }); } void submitForm; ``` Add the domain to the manifest through the CLI, for example `teams app manifest update <teamsAppId> --set-json validDomains='["bot.example.com"]'`. (`teams app update --endpoint` adds the bot's own domain on its own.) ## 6. Dialogs from message extensions An action command with `fetchTask: true` does not use `dialog.open.*`. The route is **`message.ext.open`**; the response has the same `task` envelope. See `message-extensions`. ## Common pitfalls - **The dialog never opens** — `OpenDialogData` sits on an `ExecuteAction` or on a card that was not sent through the SDK; use a `SubmitAction`. - **Submit reaches no handler** — the route is `dialog.submit.<action>` where `<action>` is the first argument of `SubmitData`; it is not the dialog id of `OpenDialogData` unless you reuse the same string. - **The web page is blank or 404** — its host is not in `validDomains`, or the page is not publicly reachable. - **`activity.value.data` is empty** — the inputs have no id, or a web page submitted without an `action` field. - **The dialog stays open** — the handler returned nothing; return `{ status: 200 }` or a `task` response.
View on GitHub