npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

gifted-btns

v1.0.2

Published

This is a plugin to make baileys more interactive(send buttons)

Readme

npm version npm downloads License

Installation

bun install gifted-btns
npm install gifted-btns
yarn add gifted-btns

Problem Statement

By default, Baileys cannot send interactive buttons. The root cause is that Baileys lacks the required binary node wrappers (biz, interactive, native_flow) that WhatsApp expects for interactive messages.

Solution

The enhanced functionality provided by this gifted-btns package provides the missing functionality by:

  1. Detecting button messages
  2. Converting Baileys interactiveButtons format to the proper protobuf structure
  3. Adding missing binary nodes (biz, interactive, native_flow, bot) via additionalNodes
  4. Automatically handling private vs group chat requirements

Key Features

  • No modifications to Baileys folders
  • Template functionality removed as requested
  • Automatic binary node injection for button messages
  • Image support (URLs, buffers, or local paths) for button messages
  • Private chat support with optional AI mode (bot node with biz_bot: '1') to enable advanced conversational AI flows
  • Group chat support (adds only biz node)
  • Backward compatibility (regular messages pass through unchanged)

Compatibility

  • Gifted-Baileys 2.0.0+
  • Baileys 7.0.0-rc.2+
  • ✅ Forks from Baileys 7.0.0-rc.2+
  • ✅ Node.js 20+
  • ✅ All button types supported by WhatsApp
  • ✅ Private and group chats

Quick Start (Most Common Case)

const { sendButtons } = require('gifted-btns');

await sendButtons(sock, jid, {
  title: 'Header Title',            // optional header
  text: 'Pick one option below',    // body
  footer: 'Footer text',            // optional footer
  image: { url: 'https://example.com/image.jpg' }, // optional image (can be url, buffer, or path)
  aimode: true,                     // optional boolean, defaults to false. Set true to add biz_bot: '1' node for AI features in private chats.
  buttons: [
    {
   id: 'quick_1', text: 'Quick Reply' },       // legacy simple shape auto‑converted
    {
      name: 'cta_url',
      buttonParamsJson: JSON.stringify({
        display_text: 'Open Site',
        url: 'https://example.com'
      })
    }
  ]
});

For full control (multiple advanced button kinds in one message) use sendInteractiveMessage with interactiveButtons directly.

const { sendInteractiveMessage } = require('gifted-btns');

await sendInteractiveMessage(sock, jid, {
  text: 'Advanced native flow demo',
  footer: 'All the things',
  interactiveButtons: [
    // Quick reply (explicit form)
    {
      name: 'quick_reply',
      buttonParamsJson: JSON.stringify({ display_text: 'Reply A', id: 'reply_a' })
    },
    // Single select picker (list inside a button)
    {
      name: 'single_select',
      buttonParamsJson: JSON.stringify({
        title: 'Pick One',
        sections: [{
          title: 'Choices',
          rows: [
            { header: 'H', title: 'Hello', description: 'Says hi', id: 'opt_hello' },
            { header: 'B', title: 'Bye', description: 'Says bye', id: 'opt_bye' }
          ]
        }]
      })
    }
  ]
});

Supported Button Types (Native Flow Names)

Below are the most common & observed name values for nativeFlowMessage.buttons[] along with their required JSON keys. You can mix several in one interactiveButtons array (WhatsApp will decide layout).

| Name | Purpose | buttonParamsJson (required keys) | |------|---------|----------------------------------| | quick_reply | Simple reply that sends its id back | { display_text, id } | | single_select | In‑button picker list | { title, sections:[{ title?, rows:[{ id, title, description?, header? }] }] } | | cta_url | Open URL | { display_text, url, merchant_url? } | | cta_copy | Copy text to clipboard | { display_text, copy_code } | | cta_call | Tap to dial | { display_text, phone_number } | | cta_catalog | Open business catalog | { display_text? } (WA may ignore extra keys) | | send_location | Request user location (special flow) | { display_text? } | | review_and_pay | Order / payment summary (special) | Payment structured payload (server‑validated) | | payment_info | Payment info flow | Payment structured payload | | mpm | Multi product message (catalog) | Vendor internal structure | | wa_payment_transaction_details | Show transaction | Transaction reference keys | | automated_greeting_message_view_catalog | Greeting -> catalog | (Minimal / internal) |

Not all special names are guaranteed to render outside official / business clients; unsupported ones are simply ignored by WhatsApp. Core stable ones for bots are: quick_reply, single_select, cta_url, cta_copy, cta_call.

Example: URL, Copy, Call & Email Together

await sendInteractiveMessage(sock, jid, {
  text: 'Contact actions',
  interactiveButtons: [
    { name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: 'Docs', url: 'https://example.com' }) },
    { name: 'cta_copy', buttonParamsJson: JSON.stringify({ display_text: 'Copy Code', copy_code: 'ABC-123' }) },
    { name: 'cta_call', buttonParamsJson: JSON.stringify({ display_text: 'Call Support', phone_number: '+1234567890' }) },
    { name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: 'Email Us', url: 'mailto:[email protected]?subject=Need%20Help' }) } // Using cta_url for email
  ]
});

Example: Mixed Quick Replies + Catalog

await sendInteractiveMessage(sock, jid, {
  text: 'Explore products or reply',
  interactiveButtons: [
    { name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Hello', id: 'hi' }) },
    { name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Pricing', id: 'pricing' }) },
    { name: 'cta_catalog', buttonParamsJson: JSON.stringify({}) }
  ]
});

Example: Location Request (Experimental)

await sendInteractiveMessage(sock, jid, {
  text: 'Please share your location for delivery:',
  interactiveButtons: [
    { name: 'send_location', buttonParamsJson: JSON.stringify({ display_text: 'Share Location' }) }
  ]
});

Example: Single Select Menu

await sendInteractiveMessage(sock, jid, {
  text: 'Choose one item',
  interactiveButtons: [
    // Single select picker (list inside a button)
    {
      name: 'single_select',
      buttonParamsJson: JSON.stringify({
        title: 'Pick One',
        sections: [
          {
            title: 'Choices',
            rows: [
              { header: 'H', title: 'Hello', description: 'Says hi', id: 'opt_hello' },
              { header: 'B', title: 'Bye', description: 'Says bye', id: 'opt_bye' }
            ]
          },
          {
            title: 'Other Options',
            rows: [
              { header: 'T', title: 'Testing', description: 'Just a test', id: 'opt_test' },
              { header: 'C', title: 'Cancel', description: 'Nevermind', id: 'opt_cancel' }
            ]
          }
        ]
      })
    }
  ]
});

Tip: Legacy simple objects like { id: 'x', text: 'Label' } passed to sendButtons auto‑convert to quick_reply.

Technical Details

Binary Node Structure (What The Wrapper Injects)

Private chat: adds biz + interactive/native_flow. If aimode: true is passed, it additionally injects the bot node (biz_bot: '1') to enable conversational AI rendering.

Group chat: adds only biz + interactive/native_flow.

When special first button names (review_and_pay, payment_info, mpm, etc.) are detected, version/name attributes change to match official client traffic so WhatsApp enables those flows.

Button Type Detection

The wrapper detects button types using the same logic as itsukichan:

  • listMessage → 'list'
  • buttonsMessage → 'buttons'
  • interactiveMessage.nativeFlowMessage → 'native_flow'

Content Conversion Flow

Authoring (you write):

{ text, footer, interactiveButtons: [{ name, buttonParamsJson }, ...] }

Wrapper builds (sent to WA):

{ interactiveMessage: { nativeFlowMessage: { buttons: [...] }, body:{ text }, footer:{ text } } }

Files Modified

Detailed API Reference: sendInteractiveMessage

Low‑level power helper used by all higher level wrappers. Use this when you need to:

  • Mix several advanced button kinds in one message (e.g. quick_reply + single_select + cta_url).
  • Provide pre‑built interactiveMessage content (after internal transformation) while still benefiting from automatic binary node injection.
  • Attach custom relay options (statusJidList, additionalAttributes, experimental fields) or manually append extra additionalNodes.

Signature

async function sendInteractiveMessage(sock, jid, content, options = {})

Parameters

  • sock: Active Baileys socket (must expose relayMessage, logger, authState or user).
  • jid: Destination WhatsApp JID (user or group). Auto‑detects group via WABinary.isJidGroup.
  • content: High‑level authoring object. Accepts either a regular Baileys message shape or the enhanced authoring shape:
    • text (string) Body text (mapped to interactiveMessage.body.text).
    • footer (string) Footer (mapped to interactiveMessage.footer.text).
    • title / subtitle (string) Optional header title (mapped to interactiveMessage.header.title).
    • image (Object) Optional header image. Can be a URL ({ url: '...' }), a buffer ({ buffer: <Buffer> }), or a local path.
    • aimode (boolean) Optional flag. Defaults to false. Set to true to inject the bot node (biz_bot: '1') which is often required to trigger conversational AI features and rendering in private chats.
    • interactiveButtons (Array) Array of button descriptors. Each item should be either:
      • { name: '<native_flow_name>', buttonParamsJson: JSON.stringify({...}) } (already normalized), or
      • A legacy quick reply shape { id, text } / { buttonId, buttonText: { displayText } } which is auto‑normalized to a quick_reply.
    • Any other Baileys message keys (e.g. contextInfo) pass through unchanged.
  • options: (Optional) Extra relay + generation options:
    • All fields accepted by generateWAMessageFromContent (e.g. custom timestamp).
    • additionalNodes (Array) Prepend your own binary nodes (the function appends required interactive nodes after detection).
    • additionalAttributes (Object) Extra attributes for the root relay stanza.
    • statusJidList, useCachedGroupMetadata (advanced Baileys relay options).

What It Does Internally

  1. Calls convertToInteractiveMessage(content) if interactiveButtons exist, producing:
    { interactiveMessage: { nativeFlowMessage: { buttons: [...] }, header?, body?, footer? } }
  2. Imports Baileys internal helpers (generateWAMessageFromContent, normalizeMessageContent, isJidGroup, generateMessageIDV2). Throws if unavailable.
  3. Builds a raw WAMessage bypassing normal send validation (lets unsupported interactive types through).
  4. Normalizes and determines button type via getButtonType then derives binary node tree with getButtonArgs.
  5. Injects required binary nodes:
    • Always a biz node (with nested interactive/native_flow/... for buttons and lists) when interactive.
    • Adds { tag: 'bot', attrs: { biz_bot: '1' } } automatically for private (1:1) chats enabling rendering of interactive flows.
  6. Relays the message using relayMessage with additionalNodes.
  7. Optionally emits the message locally (sock.upsertMessage) for private chats if sock.config.emitOwnEvents is set (groups are skipped to avoid duplicates).

Return Value

Resolves with the full constructed WAMessage object ({ key, message, messageTimestamp, ... }) so you can log/store/await acks exactly like a standard sock.sendMessage call.

Error Handling

  • Throws Socket is required if sock is null/undefined.
  • Throws WhiskeySockets functions not available if internal modules cannot be loaded (e.g. path changes). In such a case you may fall back to plain sock.sendMessage for non‑interactive messages.

Choosing Between Helpers

  • Use sendButtons / sendInteractiveButtonsBasic for simple quick replies + common CTA cases.
  • Use sendInteractiveMessage for any combination including single_select, special native flow names, or when you need to attach custom nodes.

Advanced Example: Mixed Buttons + List + Custom Node

const { sendInteractiveMessage } = require('gifted-btns');

await sendInteractiveMessage(sock, jid, {
  text: 'Pick or explore',
  footer: 'Advanced demo',
  interactiveButtons: [
    { name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Hi', id: 'hi' }) },
    { name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: 'Docs', url: 'https://example.com' }) },
    { name: 'single_select', buttonParamsJson: JSON.stringify({
        title: 'Menu',
        sections: [{
          title: 'Options',
          rows: [
            { id: 'a', title: 'Alpha', description: 'First item' },
            { id: 'b', title: 'Beta', description: 'Second item' }
          ]
        }]
    }) }
  ]
}, {
  additionalNodes: [ { tag: 'biz', attrs: { experimental_flag: '1' } } ] // will be merged before auto interactive nodes
});

Special Native Flow Names & Effects

| First Button Name | Injected Node Variant | Notes | |-------------------|-----------------------|-------| | review_and_pay | biz with native_flow_name=order_details | Payment/order style flow | | payment_info | biz with native_flow_name=payment_info | Payment info flow | | mpm, cta_catalog, send_location, call_permission_request, wa_payment_transaction_details, automated_greeting_message_view_catalog | biz > interactive(native_flow v=1) > native_flow(v=2,name=<name>) | Specialized (may require official client) | | Anything else / mixed | biz > interactive(native_flow v=1) > native_flow(v=9,name=mixed) | Generic path covering standard quick replies, lists, CTAs |

Performance / Throughput

Cost is roughly equivalent to a standard sendMessage call; extra overhead is a small synchronous transformation + node injection. Suitable for high‑volume bots. Consider standard Baileys concurrency limits for large broadcast scenarios.

Debugging Tips

  • Temporary console log emitted: Interactive send: { type, nodes, private } – remove or redirect if noisy.
  • If buttons do not render: ensure first binary node injected is biz and private chats include the bot node.
  • Confirm each button's buttonParamsJson is valid JSON string (catch JSON.stringify mistakes early).

Common Mistakes

  • Forgetting to JSON.stringify buttonParamsJson payloads.
  • Using sendInteractiveMessage without a socket that includes relayMessage (e.g., passing a partially constructed object).
  • Adding your own bot node for private chats (not needed; auto added).
  • Expecting unsupported special flows (payments/catalog) to render in a non‑business account—WhatsApp may silently ignore them.

Minimal Raw Usage

If you already built a correct interactiveMessage object you can call:

await sendInteractiveMessage(sock, jid, {
  interactiveMessage: {
    nativeFlowMessage: {
      buttons: [ { name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Hi', id: 'hi' }) } ]
    },
    body: { text: 'Direct native flow' }
  }
});

Result

You can now send all mainstream interactive button variants (quick replies, URL / copy / call CTAs, single select lists) plus experimental special flows from Baileys exactly like the official client, with automatic handling for groups vs private chats and without editing fork source.