Files
InfiniteAPI/INTERACTIVE_MESSAGES.md
T
Renato Alcara 90db2512d9 feat(experimental): add interactive messages support (buttons, lists, templates, carousel)
⚠️ EXPERIMENTAL FEATURE - Use only for testing with disposable accounts

Implements full support for WhatsApp interactive messages including:
- Simple text buttons (up to 3 buttons)
- Buttons with images/videos
- List messages (up to 10 items in sections)
- Template buttons (quick reply, URL, call actions)
- Carousel messages (up to 10 scrollable cards)

Features:
- Feature flag 'enableInteractiveMessages' (default: true for dev/testing)
- Prometheus metrics for tracking sends, successes, failures, and latency
- Comprehensive TypeScript types for all interactive message formats
- Extensive logging with warnings about potential account bans
- Automatic 'biz' node injection when feature is enabled

CRITICAL WARNINGS:
- These features may NOT work on non-business WhatsApp accounts
- Can cause temporary or permanent account BANS
- WhatsApp actively blocks this functionality since April 2022
- Messages may be rejected or fail silently
- Use ONLY in dev environment with test accounts

Architecture:
- Added ButtonInfo, Templatable, Listable, Carouselable types to Message.ts
- Extended AnyMediaMessageContent and AnyRegularMessageContent
- Implemented message generation in messages.ts
- Added getButtonType() and getButtonArgs() helpers in messages-send.ts
- Injected 'biz' node in stanza construction with metrics tracking
- Added 4 new Prometheus metrics: interactiveMessagesSent, Success, Failures, Latency

Documentation:
- Complete usage guide in INTERACTIVE_MESSAGES.md
- Examples for all interactive message types
- Metrics monitoring queries
- Troubleshooting guide
- Migration path to WhatsApp Business API

Related issues:
- https://github.com/WhiskeySockets/Baileys/issues/56
- https://github.com/WhiskeySockets/Baileys/issues/25
- https://github.com/WhiskeySockets/Baileys/pull/2291

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-23 00:51:33 -03:00

11 KiB

⚠️ EXPERIMENTAL: Interactive Messages Guide

🚨 CRITICAL WARNING

These features MAY NOT WORK and can cause ACCOUNT BANS.

  • WhatsApp actively blocks non-business accounts from sending interactive messages
  • Can result in temporary or permanent account bans
  • Messages may not be delivered
  • Use ONLY for testing with DISPOSABLE accounts in DEV environment

📋 Table of Contents

  1. Configuration
  2. Simple Text Buttons
  3. Buttons with Image
  4. Buttons with Video
  5. List Messages
  6. Template Buttons (Action Buttons)
  7. Carousel Messages
  8. Metrics and Monitoring

Configuration

Enable Interactive Messages

import makeWASocket from '@whiskeysockets/baileys'

const sock = makeWASocket({
  auth: state,
  // ⚠️ Enable experimental interactive messages
  // WARNING: Can cause account bans!
  enableInteractiveMessages: true, // default: true
  logger: pino({ level: 'warn' })
})

Disable in Production

const sock = makeWASocket({
  auth: state,
  // ✅ Disable for production safety
  enableInteractiveMessages: false,
  logger: pino({ level: 'info' })
})

1. Simple Text Buttons

Send a message with up to 3 clickable buttons.

Example

await sock.sendMessage(jid, {
  text: 'Choose an option:',
  buttons: [
    {
      buttonId: 'btn1',
      buttonText: { displayText: 'Option 1' }
    },
    {
      buttonId: 'btn2',
      buttonText: { displayText: 'Option 2' }
    },
    {
      buttonId: 'btn3',
      buttonText: { displayText: 'Option 3' }
    }
  ],
  footerText: 'Optional footer text'
})

Parameters

Parameter Type Required Description
text string Main message text
buttons ButtonInfo[] Array of buttons (max 3)
footerText string Footer text below buttons

2. Buttons with Image

Send an image with interactive buttons.

Example

await sock.sendMessage(jid, {
  image: { url: 'https://example.com/image.jpg' },
  caption: 'Image description',
  buttons: [
    {
      buttonId: 'view_more',
      buttonText: { displayText: 'View More' }
    },
    {
      buttonId: 'buy_now',
      buttonText: { displayText: 'Buy Now' }
    }
  ],
  footerText: 'Available now'
})

Parameters

Parameter Type Required Description
image WAMediaUpload Image URL or Buffer
caption string Image caption
buttons ButtonInfo[] Array of buttons (max 3)
footerText string Footer text

3. Buttons with Video

Send a video with interactive buttons.

Example

await sock.sendMessage(jid, {
  video: { url: 'https://example.com/video.mp4' },
  caption: 'Watch this!',
  buttons: [
    {
      buttonId: 'watch',
      buttonText: { displayText: 'Watch Now' }
    }
  ],
  footerText: 'New release'
})

4. List Messages

Send a message with a menu of up to 10 options organized in sections.

Example

await sock.sendMessage(jid, {
  text: 'Choose a product:',
  title: 'Product Catalog',
  buttonText: 'View Options',
  sections: [
    {
      title: 'Electronics',
      rows: [
        {
          rowId: 'laptop_1',
          title: 'Laptop Pro',
          description: '$999 - High performance'
        },
        {
          rowId: 'phone_1',
          title: 'Smartphone X',
          description: '$699 - Latest model'
        }
      ]
    },
    {
      title: 'Accessories',
      rows: [
        {
          rowId: 'case_1',
          title: 'Phone Case',
          description: '$29 - Protective'
        }
      ]
    }
  ],
  footerText: 'Free shipping'
})

Parameters

Parameter Type Required Description
text string Message description
title string List title
buttonText string Text on button that opens list
sections ListSection[] Array of sections (max 10 items total)
footerText string Footer text

Limits

  • Maximum 10 sections
  • Maximum 10 rows total across all sections
  • Row title: 24 characters max
  • Row description: 72 characters max

5. Template Buttons (Action Buttons)

Send buttons with actions: Quick Reply, URL, or Call.

Example

await sock.sendMessage(jid, {
  text: 'Contact us or visit our website',
  templateButtons: [
    // Quick Reply Button
    {
      index: 1,
      quickReplyButton: {
        displayText: 'Quick Reply',
        id: 'reply_1'
      }
    },
    // URL Button
    {
      index: 2,
      urlButton: {
        displayText: 'Visit Website',
        url: 'https://example.com'
      }
    },
    // Call Button
    {
      index: 3,
      callButton: {
        displayText: 'Call Us',
        phoneNumber: '+551199999999'
      }
    }
  ],
  footer: 'We are here to help'
})

Button Types

Type Description Parameters
quickReplyButton Quick response button displayText, id
urlButton Opens URL in browser displayText, url
callButton Initiates phone call displayText, phoneNumber

Send up to 10 scrollable cards with images/videos and buttons.

Example

await sock.sendMessage(jid, {
  text: 'Check out our products',
  carousel: {
    cards: [
      // Card 1
      {
        header: {
          title: 'Product 1',
          imageMessage: {
            url: 'https://example.com/product1.jpg',
            mimetype: 'image/jpeg'
          },
          hasMediaAttachment: true
        },
        body: { text: 'Amazing product - $99.90' },
        footer: { text: 'Limited stock' },
        nativeFlowMessage: {
          buttons: [
            {
              name: 'quick_reply',
              buttonParamsJson: JSON.stringify({
                display_text: 'Buy Now',
                id: 'buy_1'
              })
            },
            {
              name: 'cta_url',
              buttonParamsJson: JSON.stringify({
                display_text: 'More Info',
                url: 'https://example.com/product1'
              })
            }
          ]
        }
      },
      // Card 2
      {
        header: {
          title: 'Product 2',
          imageMessage: {
            url: 'https://example.com/product2.jpg',
            mimetype: 'image/jpeg'
          },
          hasMediaAttachment: true
        },
        body: { text: 'Great deal - $149.90' },
        footer: { text: 'Best seller' },
        nativeFlowMessage: {
          buttons: [
            {
              name: 'quick_reply',
              buttonParamsJson: JSON.stringify({
                display_text: 'Buy Now',
                id: 'buy_2'
              })
            }
          ]
        }
      }
    ],
    messageVersion: 1
  }
})

Limits

  • Maximum 10 cards per carousel
  • Each card requires an image or video
  • Up to 3 buttons per card
  • All cards must have same media type (all images or all videos)

Metrics and Monitoring

All interactive messages are tracked with Prometheus metrics:

Available Metrics

// Messages sent by type
metrics.interactiveMessagesSent.inc({ type: 'buttons' })

// Successful sends
metrics.interactiveMessagesSuccess.inc({ type: 'list' })

// Failures with reason
metrics.interactiveMessagesFailures.inc({
  type: 'template',
  reason: 'feature_disabled'
})

// Send latency
metrics.interactiveMessagesLatency.observe({ type: 'carousel' }, 1234)

Monitoring Dashboard

Query these metrics in Prometheus/Grafana:

# Total interactive messages sent
sum(interactive_messages_sent_total) by (type)

# Success rate
sum(interactive_messages_success_total) / sum(interactive_messages_sent_total)

# Failure breakdown
sum(interactive_messages_failures_total) by (type, reason)

# P95 latency
histogram_quantile(0.95, interactive_messages_latency_ms)

Handling Responses

Button Response

sock.ev.on('messages.upsert', async ({ messages }) => {
  const msg = messages[0]

  // Button response
  if (msg.message?.buttonsResponseMessage) {
    const buttonId = msg.message.buttonsResponseMessage.selectedButtonId
    const displayText = msg.message.buttonsResponseMessage.selectedDisplayText

    console.log(`User clicked: ${buttonId} - "${displayText}"`)
  }

  // Template button response
  if (msg.message?.templateButtonReplyMessage) {
    const selectedId = msg.message.templateButtonReplyMessage.selectedId
    const selectedText = msg.message.templateButtonReplyMessage.selectedDisplayText

    console.log(`User clicked template: ${selectedId} - "${selectedText}"`)
  }

  // List response
  if (msg.message?.listResponseMessage) {
    const rowId = msg.message.listResponseMessage.singleSelectReply.selectedRowId
    const title = msg.message.listResponseMessage.title

    console.log(`User selected from list: ${rowId} - "${title}"`)
  }
})

Troubleshooting

Message Not Showing

  • Verify enableInteractiveMessages is true
  • Check logs for [EXPERIMENTAL] warnings
  • Ensure you're using a test/disposable account
  • Interactive messages don't work on production accounts

Account Banned/Restricted

  • ⚠️ This is expected behavior
  • ⚠️ WhatsApp blocks non-business accounts
  • Create a new test account
  • Use official WhatsApp Business API for production

Metrics Not Showing

Check feature flag:

if (buttonType && !enableInteractiveMessages) {
  // Metrics will show reason: 'feature_disabled'
}

Migration to WhatsApp Business API

For production use, migrate to official API:

Evolution API (Cloud Mode)

// config.ts
export const evolutionConfig = {
  apiUrl: 'https://your-evolution-api.com',
  apiKey: 'your-api-key',
  instance: 'your-instance'
}

// Send button via Evolution API
await fetch(`${evolutionConfig.apiUrl}/message/sendButton`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'apikey': evolutionConfig.apiKey
  },
  body: JSON.stringify({
    number: '5511999999999',
    options: {
      delay: 1200,
      presence: 'composing'
    },
    buttonMessage: {
      text: 'Choose an option',
      buttons: [
        { id: '1', text: 'Option 1' },
        { id: '2', text: 'Option 2' }
      ],
      footer: 'Footer text'
    }
  })
})

References


⚠️ Final Reminder

THESE FEATURES ARE EXPERIMENTAL AND UNRELIABLE

  • Use only for testing
  • Expect failures and bans
  • Not suitable for production
  • Official WhatsApp Business API is the only supported way

Generated by rsalcara/InfiniteAPI