WhatsApp Buttons and Lists API for Any Account
A customer who has to type "yes" to confirm an appointment will sometimes type "Yes!", "ok" or nothing at all. A button removes the guessing. WSAPI now sends WhatsApp buttons and lists from a regular account, with no Business API setup, and tells your server exactly which one the customer tapped.
What you can send
| Message | Endpoint | What the customer sees | Event back |
|---|---|---|---|
| Buttons | POST /messages/buttons | 1 to 3 buttons under your text | For reply buttons |
| List | POST /messages/list | One button that opens up to 10 options in sections | Yes |
Buttons come in four types. A reply button sends the choice back to you. A url button opens a link, a copy button copies a code such as a coupon, and a call button starts a call. The last three act on the customer's phone and send nothing back, and you can mix types in one message.
Where it earns its place
Interactive messages shine wherever the answer is one of a few known choices. Confirm, reschedule or cancel an appointment. Let a customer pick a delivery slot from a list. Send an order update with a Track order link button and a Copy coupon button underneath. In each case the customer taps once, and your code receives a clean id instead of free text to parse.
Send buttons
Give every reply button an id. It is what you will receive, so make it something your code can route on.
const res = await fetch("https://api.wsapi.chat/messages/buttons", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": process.env.WSAPI_API_KEY,
"X-Instance-Id": process.env.WSAPI_INSTANCE_ID,
},
body: JSON.stringify({
to: "1234567890@s.whatsapp.net",
text: "Can we confirm your appointment for tomorrow at 10:00?",
footer: "Reply with a button",
buttons: [
{ id: "confirm", text: "Confirm" },
{ id: "reschedule", text: "Reschedule" },
{ id: "cancel", text: "Cancel" },
],
}),
});
const { id } = await res.json(); // store it next to the appointmentA 201 returns the message ID. Keep it: the reply event carries it back as replyTo.id, which is how you know which appointment the customer answered. The same shape works for lists at /messages/list, with a buttonText and sections of titled rows.
Read the reply
When the customer taps a reply button or picks a list row, you get a normal message event, over a webhook or SSE, with type set to button_reply or list_reply.
async function handleEvent(event) {
if (event.eventType !== "message") return;
const msg = event.eventData;
if (msg.type !== "button_reply") return;
const appointment = await db.appointments.findByMessageId(msg.replyTo?.id);
if (!appointment) return;
if (msg.buttonReply.id === "confirm" && appointment.status === "pending") {
await appointment.confirm();
}
}Route on buttonReply.id, not on the label. Labels get reworded and translated, ids do not. Use replyTo.id to find the record, and check its current state first: a customer can tap twice, or tap a button on an older message, and every tap is a separate event.
A list for more than three choices
Use a list when you have more than three options or when each one needs a short description: time slots, menu items, departments. One message can hold up to 10 rows across up to 10 sections, with a title of up to 24 characters and a description of up to 72 per row. A request that breaks a limit is rejected with a 400 that names the field, and nothing is sent.
Add the AI label
If a bot sends the message, set aiLabel: true and WhatsApp shows its AI badge in one-to-one chats. In a group the flag is ignored.
Before you build on it
Buttons and lists are in beta because they rely on WhatsApp behaviour that is not an official contract, so WhatsApp can change how they are delivered or shown without notice. Three things to plan for:
- A
201means WhatsApp accepted the message, not that it was rendered. Keep a fallback, such as also accepting a typed "confirm". - Buttons render on phones and on WhatsApp Web. Lists render on phones only, and WhatsApp Web shows a prompt to open the phone.
- These endpoints do not take
mentions,replyTo,isForwardedorephemeralExpiration.
Try it
Create an instance, scan the QR code, and send the snippet above (the quickstart walks through the setup). The full field reference is in the buttons and lists docs, the payloads are in button and list replies, and the free trial lasts 14 days with no credit card.
FAQ
Do I need a WhatsApp Business API account?
No. These messages go out from the account you already connect to an instance.
Can WhatsApp restrict my number?
WSAPI connects a regular WhatsApp account, so WhatsApp's usage policies apply and accounts that send unsolicited bulk messages can be limited.