> ## Documentation Index
> Fetch the complete documentation index at: https://hntlabs-feature-bot-sdk-changelog-system.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Interactive Messages

Interactive messages let your bot create buttons, request transactions, and get signatures from users.

## Forms with Buttons

Create interactive UI elements like buttons for games, polls, and confirmations.

```ts theme={null}
import { hexToBytes } from 'viem'

bot.onSlashCommand('play', async (handler, event) => {
  await handler.sendInteractionRequest(
    event.channelId,
    {
      case: 'form',
      value: {
        id: 'game-menu',
        title: '🎮 Game Menu',
        subtitle: 'Choose your action:',
        components: [
          {
            id: 'start-button',
            component: {
              case: 'button',
              value: { label: '▶️ Start Game' }
            }
          },
          {
            id: 'help-button',
            component: {
              case: 'button',
              value: { label: '❓ Help' }
            }
          }
        ]
      }
    },
    hexToBytes(event.userId as `0x${string}`)  // recipient
  )
})
```

### Handling Button Clicks

```ts theme={null}
bot.onInteractionResponse(async (handler, event) => {
  if (event.response.payload.content?.case !== 'form') return
  
  const form = event.response.payload.content?.value
  
  for (const component of form.components) {
    if (component.component.case === 'button') {
      if (component.id === 'start-button') {
        await handler.sendMessage(event.channelId, '🎮 Starting game...')
      } else if (component.id === 'help-button') {
        await handler.sendMessage(event.channelId, '❓ Help...')
      }
    }
  }
})
```

## Transaction Requests

Prompt users to sign and execute blockchain transactions. Perfect for payments, NFT minting, token swaps, and contract interactions.

**Any Wallet:**

```ts theme={null}
bot.onSlashCommand('send-usdc', async (handler, event) => {
  const usdcAddress = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' // Base
  const recipient = '0x1234567890123456789012345678901234567890'
  const amount = '50000000' // 50 USDC (6 decimals)
  
  // Encode ERC20 transfer: transfer(address,uint256)
  const recipientPadded = recipient.slice(2).padStart(64, '0')
  const amountPadded = parseInt(amount).toString(16).padStart(64, '0')
  const data = `0xa9059cbb${recipientPadded}${amountPadded}`
  
  await handler.sendInteractionRequest(event.channelId, {
    case: 'transaction',
    value: {
      id: 'usdc-transfer',
      title: 'Send USDC',
      subtitle: 'Send 50 USDC to recipient',
      content: {
        case: 'evm',
        value: {
          chainId: '8453',
          to: usdcAddress,
          value: '0',
          data: data,
          signerWallet: undefined // User chooses wallet
        }
      }
    }
  })
})
```

**Restrict to Smart Account:**

```ts theme={null}
import { getSmartAccountFromUserId } from '@towns-protocol/bot'

bot.onSlashCommand('send-usdc-sm', async (handler, event) => {
  const smartAccount = await getSmartAccountFromUserId(bot, {
    userId: event.userId
  })
  
  if (!smartAccount) {
    await handler.sendMessage(event.channelId, "No smart account found")
    return
  }
  
  // ... same transaction setup ...
  
  await handler.sendInteractionRequest(event.channelId, {
    case: 'transaction',
    value: {
      // ...
      content: {
        case: 'evm',
        value: {
          // ...
          signerWallet: smartAccount // Only this wallet can sign
        }
      }
    }
  })
})
```

### Handle Transaction Response

```ts theme={null}
bot.onInteractionResponse(async (handler, event) => {
  if (event.response.payload.content?.case === 'transaction') {
    const txData = event.response.payload.content.value
    
    await handler.sendMessage(
      event.channelId,
      `✅ Transaction Confirmed!

Request ID: ${txData.requestId}
Transaction Hash: \`${txData.txHash}\`

View on explorer: https://basescan.org/tx/${txData.txHash}`
    )
  }
})
```

## Signature Requests

Request cryptographic signatures without executing transactions. Perfect for authentication, permissions, off-chain agreements, and gasless interactions.

```ts theme={null}
import { InteractionRequestPayload_Signature_SignatureType } from '@towns-protocol/proto'

bot.onSlashCommand('sign', async (handler, event) => {
  // EIP-712 Typed Data Structure
  const typedData = {
    domain: {
      name: 'My Towns Bot',
      version: '1',
      chainId: 8453,
      verifyingContract: '0x0000000000000000000000000000000000000000'
    },
    types: {
      Message: [
        { name: 'from', type: 'address' },
        { name: 'content', type: 'string' },
        { name: 'timestamp', type: 'uint256' }
      ]
    },
    primaryType: 'Message',
    message: {
      from: event.userId,
      content: 'I agree to the terms',
      timestamp: Math.floor(Date.now() / 1000)
    }
  }
  
  await handler.sendInteractionRequest(event.channelId, {
    case: 'signature',
    value: {
      id: 'message-signature',
      title: 'Sign Message',
      subtitle: `Sign: "${typedData.message.content}"`,
      chainId: '8453',
      data: JSON.stringify(typedData),
      type: InteractionRequestPayload_Signature_SignatureType.TYPED_DATA,
      signerWallet: undefined // User chooses wallet
    }
  })
})
```

### Handle Signature Response

```ts theme={null}
bot.onInteractionResponse(async (handler, event) => {
  if (event.response.payload.content?.case === 'signature') {
    const signatureData = event.response.payload.content.value
    
    await handler.sendMessage(
      event.channelId,
      `✅ Signature Received!

Request ID: ${signatureData.requestId}

Signature:
\`\`\`
${signatureData.signature}
\`\`\`

You can now verify this signature on-chain or use it for authentication.`
    )
  }
})
```

## Complete Response Handler

```ts theme={null}
bot.onInteractionResponse(async (handler, event) => {
  const { response } = event
  
  switch (response.payload.content?.case) {
    case 'form':
      const formData = response.payload.content.value
      // Handle button clicks
      for (const component of formData.components) {
        if (component.component.case === 'button') {
          // Route based on component.id
        }
      }
      break
      
    case 'transaction':
      const txData = response.payload.content.value
      await handler.sendMessage(
        event.channelId,
        `Transaction confirmed: ${txData.txHash}`
      )
      break
      
    case 'signature':
      const signatureData = response.payload.content.value
      await handler.sendMessage(
        event.channelId,
        `Signature received: ${signatureData.signature}`
      )
      break
  }
})
```
