POST /messages/buttons, or picks a row on a list sent with POST /messages/list, WSAPI delivers a message event that says which one they chose.
Interactive messages are in beta. See the caveats on sending them.
What arrives
- List reply
How to use it
- Route on the
id, not on the text.buttonReply.idandlistReply.idare the values you set, and they stay the same whatever the label says. Labels get reworded, translated and A/B-tested — the example above is a Spanish “Confirmar” for a button whose id isconfirm. Matching ontextbreaks the first time someone edits the copy. textis there for everything else. It is filled with the label or row title so that integrations reading onlytext— a chatbot, a helpdesk inbox, a log — still show something sensible. Do not rely on it for logic.- Correlate with
replyTo.id. It is theidreturned byPOST /messages/buttonsorPOST /messages/list. Store that ID when you send, next to whatever the message is about — the appointment, order or ticket — and look it up when the reply arrives. A button id likeconfirmtells you what was chosen;replyTo.idtells you which appointment it was chosen for. - Only reply buttons and list rows produce events.
url,copyandcallbuttons act on the contact’s device — open a link, copy a code, start a call — and nothing comes back to you. - Replies arrive inside the
messageevent, somessagehas to be allowed in your event filters.
What can be absent
listReply.descriptionis omitted when the row was sent without a description.buttonReply.textandlistReply.titlecome from the contact’s WhatsApp client. Expect them, but treat them as optional.replyToshould always be present on a reply, since a reply is always to a message. Still, check for it rather than assume it: if it is missing, you know what was chosen but not for which message — fall back to the most recent open request in that chat, or ask again.
Make the handler idempotent
A contact can tap a button more than once, or scroll up and tap a button on an older message. Each tap is a separate event. Webhook deliveries can also be retried. Design for it:- Look up the state of the thing
replyTo.idpoints to before acting. An appointment that is already confirmed should not be confirmed twice; one that was cancelled should not be confirmed at all. - Decide what a second, different answer means — for example,
cancelafterconfirm— instead of letting the last event win by accident. - Deduplicate on
eventIdif your processing has side effects such as charges or outgoing notifications.
Example handler
Node.js
db, appointment and booking objects stand for your own data layer. The point is the shape: find the record through replyTo.id, switch on the id, and check the current state before changing it.