|
| 1 | +# Send actions and handle intents |
| 2 | + |
| 3 | +Actions let your agent send a message with tappable buttons. When the user picks one, the agent gets an intent event with the selected button's ID. This works well for confirmations, payment flows, menu navigation, and polls. |
| 4 | + |
| 5 | +:::note |
| 6 | +Buttons only render in apps that support this content type, such as the [Base](https://base.org) app. Other clients show a numbered text fallback. |
| 7 | +::: |
| 8 | + |
| 9 | +## Send actions |
| 10 | + |
| 11 | +```ts [Node] |
| 12 | +await ctx.conversation.sendActions({ |
| 13 | + id: `actions-${Date.now()}`, |
| 14 | + description: 'Would you like to proceed?', |
| 15 | + actions: [ |
| 16 | + { id: 'action-yes', label: 'Yes', style: 'primary' }, |
| 17 | + { id: 'action-no', label: 'No', style: 'secondary' }, |
| 18 | + ], |
| 19 | +}); |
| 20 | +``` |
| 21 | + |
| 22 | +Each action button has the following fields: |
| 23 | + |
| 24 | +- `id`: Returned as `actionId` in the intent when tapped |
| 25 | +- `label`: The button text |
| 26 | +- `style` (optional): `"primary"`, `"secondary"`, or `"danger"` |
| 27 | +- `imageUrl` (optional): An image to show alongside the button |
| 28 | +- `expiresAt` (optional): ISO 8601 timestamp after which the button expires |
| 29 | + |
| 30 | +Set a top-level `expiresAt` to expire the entire prompt. |
| 31 | + |
| 32 | +### Payment example |
| 33 | + |
| 34 | +```ts [Node] |
| 35 | +await ctx.conversation.sendActions({ |
| 36 | + id: 'payment_alice_123', |
| 37 | + description: 'Choose amount to send', |
| 38 | + actions: [ |
| 39 | + { id: 'send_10', label: 'Send $10', style: 'primary' }, |
| 40 | + { id: 'send_20', label: 'Send $20', style: 'primary' }, |
| 41 | + ], |
| 42 | +}); |
| 43 | +``` |
| 44 | + |
| 45 | +## Receive an intent |
| 46 | + |
| 47 | +When a user taps a button, the SDK fires an `"intent"` event. Match on `actionId` to know which button was pressed. |
| 48 | + |
| 49 | +```ts [Node] |
| 50 | +agent.on('intent', async (ctx) => { |
| 51 | + const { actionId } = ctx.message.content; |
| 52 | + |
| 53 | + if (actionId === 'send_10') { |
| 54 | + await ctx.conversation.sendText('Sending $10...'); |
| 55 | + } else if (actionId === 'send_20') { |
| 56 | + await ctx.conversation.sendText('Sending $20...'); |
| 57 | + } |
| 58 | +}); |
| 59 | +``` |
| 60 | + |
| 61 | +The intent payload has three fields: |
| 62 | + |
| 63 | +- `id`: The ID of the original action prompt |
| 64 | +- `actionId`: The ID of the button that was tapped |
| 65 | +- `metadata` (optional): Key-value context sent by the client |
0 commit comments