Polls are how WhatsApp groups decide things: which slot, which menu, which date. When the poll comes from your app, the answers can land in your app too, already counted, instead of someone screenshotting the results into a spreadsheet.
This guide shows the exact request, the rules WhatsApp puts on questions and options, the vote payload, how to handle changed votes, and the sibling feature people often want next: events with RSVPs. Polls are part of everything you can send on the $15 per line plan.
Can you send WhatsApp polls with an API?
Not with Meta’s official WhatsApp Business API. It has no poll message type, so a business on the Cloud API can’t send a poll or receive one. Its nearest tools are interactive buttons and lists, which are a different thing: one tap that becomes a reply message, with no shared tally anyone can see.
Mossmoon takes the other route. Your customer links their own WhatsApp number as a linked device, so your code can do what they can do on their phone, including real polls. It works on personal WhatsApp and the WhatsApp Business app, in one-to-one chats and in groups, with no templates or business verification.
A poll is one send. Every vote after that comes to you, already counted.
How do I send a WhatsApp poll with the API?
Call the same send endpoint you use for text, with a poll object instead of text. To post into a group, put the group id (it ends in @g.us) in to_wa_id. Authenticate with your API key as a Bearer token.
curl https://mossmoon.app/api/v1/wa/lines/wa_8f3c2e1a/send \
-H "Authorization: Bearer mm_live_..." \
-H "Content-Type: application/json" \
-d '{
"to_wa_id": "[email protected]",
"poll": {
"question": "Long run this week?",
"options": ["Sat 7am", "Sat 9am", "Sun 8am"],
"multiple_answers": true
}
}'const res = await fetch("https://mossmoon.app/api/v1/wa/lines/wa_8f3c2e1a/send", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.MOSSMOON_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
to_wa_id: "[email protected]",
poll: {
question: "Long run this week?",
options: ["Sat 7am", "Sat 9am", "Sun 8am"],
multiple_answers: true,
},
}),
});
const { message_id } = await res.json(); // keep it: votes point back to itres = requests.post(
"https://mossmoon.app/api/v1/wa/lines/wa_8f3c2e1a/send",
headers={"Authorization": f"Bearer {MOSSMOON_KEY}"},
json={
"to_wa_id": "[email protected]",
"poll": {
"question": "Long run this week?",
"options": ["Sat 7am", "Sat 9am", "Sun 8am"],
"multiple_answers": True,
},
},
)
message_id = res.json()["message_id"] # keep it: votes point back to it202 Accepted
X-Mossmoon-Daily-Limit: 1500
X-Mossmoon-Daily-Remaining: 1247
X-Mossmoon-Daily-Reset: 2026-10-17T00:00:00.000Z
{
"message_id": "[email protected]_3EB0C431D4B0FF2A_36554986758374@lid",
"status": "queued",
"line_id": "wa_8f3c2e1a",
"to": "[email protected]",
"created_at": "2026-10-16T18:02:11.000Z"
}202 means queued, not delivered. message.delivered follows like for any send, and the message_id is WhatsApp’s own id for the poll.A poll to one person
For a one-to-one chat, use to with a phone number in international format. Leave out multiple_answers and each person can pick one answer.
{
"to": "+15553182290",
"poll": {
"question": "Which slot suits you for the viewing?",
"options": ["Thu 5pm", "Fri 12pm", "Sat 10am"]
}
}What are the rules for questions and options?
Mossmoon checks the same limits WhatsApp’s own “Create poll” screen enforces, before the message is stored or counted. A request that breaks one gets a 400 with the reason, so it is safe to fix and retry.
In words: a question of up to 255 characters, a list of 2 to 12 options of up to 100 characters each with no two the same, and an optional multiple_answers flag. A poll also travels alone, so it can’t share a send with text, media or mentions; if it needs an explanation, send that as its own message first. The full contract, including the other message kinds that share this endpoint, is in the docs under polls, events, locations, contact cards and forwards.
How do I get WhatsApp poll results?
Through your webhook. Each time someone votes, message.poll_vote arrives with the voter, the options they picked, and the whole current tally in results. You never add votes up yourself: the numbers come from WhatsApp’s own vote data for that poll.
POST https://your-app.com/webhooks/whatsapp
X-Mossmoon-Event: message.poll_vote
X-Mossmoon-Signature: sha256=...
{
"event": "message.poll_vote",
"line_id": "wa_8f3c2e1a",
"data": {
"message_id": "[email protected]_3EB0C431D4B0FF2A_36554986758374@lid",
"chat_wa_id": "[email protected]",
"is_group": true,
"voter_wa_id": "103414994325753@lid",
"voter_e164": "+15553182290",
"from_me": false,
"selected_options": ["Sat 7am", "Sun 8am"],
"ts": "2026-10-16T18:06:40.000Z",
"results": [
{ "option": "Sat 7am", "votes": 10 },
{ "option": "Sat 9am", "votes": 5 },
{ "option": "Sun 8am", "votes": 4 }
],
"voters": [
{
"wa_id": "103414994325753@lid",
"e164": "+15553182290",
"options": ["Sat 7am", "Sun 8am"],
"voted_at": "2026-10-16T18:06:40.000Z"
}
// ...one entry per person who has voted
]
}
}- Match on
message_id. It is the poll’s id, the same one the send returned.chat_wa_idandis_grouptell you which chat it is in. votersis everyone, not just this voter. One entry per person who has voted, with their options andvoted_at. It is the “View votes” screen as data.voter_e164can be null. Some people reach a line only by WhatsApp’s internal id (ending in@lid), so key voters onvoter_wa_id.from_me: truemeans the vote came from the line’s own phone, for example your customer voting on their own poll.
Every webhook is signed, so check X-Mossmoon-Signature before trusting it (signature docs). If you have no endpoint yet, the webhook setup guide walks through one.
What happens when someone changes or removes a vote?
You get another message.poll_vote. A changed vote carries the new choice; a vote taken back carries selected_options: []. Either way results and voters are recalculated, so the rule for your code is simple: keep the newest one.
Can I read poll results without the webhook?
Yes. The latest tally is stored on the poll message itself. GET /lines/:id/messages returns the poll with its question, options, current results and voters, which makes it the reconciliation path if your endpoint was down while people voted (message history docs).
Polls other people send arrive too. A poll posted into a chat the line is in comes in as message.received with readable text such as “Poll: Which day works?” plus a poll object with question, options and selectable_count (0 means any number of answers). Details are under locations, contact cards and polls.
Step by step: from a poll to its results
The whole flow, from a freshly linked number to a tally your app keeps up to date on its own.
- 1
Link the WhatsApp number
Create a line withPOST /api/v1/wa/linesand show your customer theconnect_url. They scan the QR from WhatsApp’s Linked devices screen, and you getline.ready. - 2
Send the poll
POST /lines/:id/sendwithto(a phone number) orto_wa_id(a group) and apollobject:question, 2 to 12options, andmultiple_answersif people may pick more than one. - 3
Store the message_id
The202response returns WhatsApp’s id for the poll message. Every vote webhook carries the samemessage_id, so save it next to whatever the poll is about (a booking, a class, a deal). - 4
Handle message.poll_vote
Verify the signature, find the poll bymessage_id, and replace your stored tally withresultsandvoterswhen the webhook’stsis newer than the one you have.Keep the newest tally// Express-style handler. Verify the signature first (see the docs). app.post("/webhooks/whatsapp", async (req, res) => { res.sendStatus(200); // ack fast, work after const { event, data } = req.body; if (event !== "message.poll_vote") return; const poll = await db.polls.findByMessageId(data.message_id); if (!poll) return; // a poll you didn't send through the API // Webhooks can arrive out of order: keep only the newest tally. if (poll.tally_at && poll.tally_at >= data.ts) return; if (data.results) { await db.polls.update(poll.id, { results: data.results, voters: data.voters, tally_at: data.ts, }); } }); - 5
Read the results any time
GET /lines/:id/messagesreturns the poll message with its latestresultsandvoters, so a missed webhook never loses the count.
How do I send a WhatsApp event and collect RSVPs?
When the question is “who’s coming?”, an event often beats a poll. It renders as a WhatsApp event card with a date, and people answer Going, Maybe or Not going. Send it with an event object on the same endpoint.
POST /api/v1/wa/lines/wa_8f3c2e1a/send
Authorization: Bearer mm_live_...
{
"to_wa_id": "[email protected]",
"event": {
"name": "Long run + coffee",
"start_time": "2026-10-24T14:00:00Z",
"end_time": "2026-10-24T16:00:00Z",
"location": "Riverside Park, north gate",
"allow_extra_guests": true
}
}name (up to 100 characters) and start_time (ISO 8601) are required. end_time must come after the start, and the start can be at most 5 years ahead. description, location, allow_extra_guests and call are optional; call: "voice" or "video" adds a WhatsApp call link to the event.
Every answer arrives as message.event_response, shaped like a poll vote: who answered (responder_wa_id, responder_e164), their response (going, not_going or maybe), everyone’s latest answer in responses, and the totals in counts. A changed answer sends a new webhook with the new totals, and the event message in GET /messages keeps the latest responses and counts too.
Reactions follow the same pattern for lighter feedback: message.reaction fires for every emoji left on a message, and you can react from the API too (reactions docs, which also hold the vote and RSVP payloads).
What are the limits of the WhatsApp polls API?
- Your code can’t vote. There is no endpoint to vote on a poll or answer an event as the line. People vote on their phones; you receive the result.
- A poll can’t be edited. The edit endpoint covers text and captions only. To change a poll, delete it for everyone (the line’s own messages, within about 2 days) and send a new one. There is no endpoint to close a poll either.
- One kind per message. No text, media or mentions in the same send as a poll or an event.
- Polls count as sends. Each poll counts against the line’s daily cap (soft 750, hard 1,500 for warmed lines, reset at midnight UTC; see rate limits). Votes coming back don’t.
- Treat
resultsas optional. If the tally can’t be read at that moment it comes back null whileselected_optionsstill tells you what this person picked. Keep the last tally you had. - No buttons or lists. Mossmoon sends what a person can send from the WhatsApp app, so there are no interactive buttons or list menus. A poll is the closest thing.
- Groups follow group rules. In a group where only admins can send, the line needs to be an admin to post a poll; otherwise the send fails with
cannot_send.
Mossmoon vs goghl.ai for WhatsApp polls
goghl.ai links WhatsApp by QR inside GoHighLevel. Its public site and help center don’t mention polls or events anywhere, so every row below is marked “Not listed” rather than “No”. These are the poll and event features we offer that their docs don’t list.
$15 a line. First line free for 7 days.
| Feature | RecommendedMossmoon$15 a line. First line free for 7 days.Start free → | goghl.ai |
|---|---|---|
| Send WhatsApp polls from your own code | Yespoll on POST /send, single or multiple answer, to a person or a group | Not listed |
| A webhook for every vote, with the full tally | Yesmessage.poll_vote with results and every voter | Not listedTheir webhooks cover inbound, outbound and failed messages |
| Send WhatsApp events and collect RSVPs | Yesevent on /send, plus message.event_response with counts | Not listed |
| Latest poll results in the message history | YesGET /messages returns results and voters on the poll | Not listed |
Checked against goghl.ai’s public site and help center on October 16, 2026. “Not listed” means we couldn’t find it in their docs, not that it can’t exist. Only features goghl.ai doesn’t list are shown. Sources: goghl.ai webhooks, goghl.ai help center, goghl.ai.
Meta’s WhatsApp Business API can’t send or receive polls at all. Our Business API vs personal API explainer covers the other differences, and the full three-way table is on the features page.
Frequently asked questions
Yes. With Mossmoon you call POST /api/v1/wa/lines/:id/send with a poll object (a question and 2 to 12 options) on any WhatsApp number linked by QR, personal or Business app. Each vote then reaches your webhook as message.poll_vote with the full tally.
No. Meta's WhatsApp Business API has no poll message type, so it can't send polls or receive them. Its closest tools are interactive buttons and lists, which work differently and which Mossmoon doesn't offer.
Every message.poll_vote webhook carries voters: one entry per person with their wa_id, phone number when WhatsApp shares it, the options they picked and when. The same list sits on the poll in GET /messages.
Between 2 and 12. Each option can be up to 100 characters and no two can be the same, and the question can be up to 255 characters. Anything outside those rules gets a 400 with the reason, before anything is sent or counted.
Yes. Use to with their phone number in international format instead of to_wa_id. Polls work the same in one-to-one chats and in groups.
No. The API sends polls and reports votes, but has no endpoint to vote or to close a poll. To stop answers, you can delete the poll for everyone (the line's own messages, within about 2 days) and post the result.
Yes, a poll is one send, wherever it goes. Votes coming back don't count. Warmed lines get a soft cap of 750 sends a day and a hard cap of 1,500, reset at midnight UTC.
