OpenWA 5.5 adds a complete poll workflow around the native message: create it, listen for votes, read available totals, and submit or remove this account's choice. Message pins keep the result or instructions visible in the conversation.
Choose SDK or Easy API
Use the tabs below to choose an embedded Node.js SDK or an HTTP client. SDK examples assume a ready client. For Easy API, start the server, scan its QR, and use the printed address. Set these variables in the shell that runs your examples:
export OPENWA_API_URL='http://127.0.0.1:8080'export WA_API_KEY='REPLACE_WITH_YOUR_API_KEY'npx @open-wa/wa-automate@5.5.1 \ --host 127.0.0.1 --port 8080 --api-key "$WA_API_KEY"Python examples use requests: install it with python -m pip install requests. JavaScript examples use Node.js 22.21.1 or later and its built-in fetch. Keep the API key on your application server. Method responses contain { success: true, data }; the examples read data. The API explorer at /api-docs/ lists your server's routes and inputs.
const chatId = '447700900123@c.us';const pollId = await client.sendPoll(chatId, 'Which day?', [ 'Friday', 'Saturday', 'Sunday',], { selectableCount: 1 });if (typeof pollId === 'string') { const results = await client.getPollResults(pollId); await client.voteOnPoll(pollId, [results.options[0].id]); // To remove this account's vote: await client.voteOnPoll(pollId, []);}Options must be nonempty and unique. selectableCount defaults to one; zero allows all choices. Use option IDs from the returned results rather than deriving them from labels. Native WhatsApp limits still apply, and invalid, duplicate or excessive selections and closed polls reject explicitly.
Keep totals in sync
const listener = client.onPollVote(async vote => { const results = await client.getPollResults(vote.pollId); console.log(results.voterCount, results.options);});// Later:listener.off();Results have scope: 'synced': they describe the linked device's available vote store, not a guaranteed global census. Locally pending votes count; known failed sends don't. History hydration can emit listener callbacks, including this account's changes. Deduplicate ordinary votes by ID and timestamp when triggering side effects, and use a fresh results snapshot for current totals.
Closing times and hidden names
endTime uses Unix milliseconds, unlike event start times in seconds. Closing times and hideVoterNames require WhatsApp's sender feature flags for the account/chat, and recipient V6 rendering depends on its app and configuration. Unsupported sender features throw rather than dropping the option silently.
Hidden names describe native display behavior, not cryptographic anonymity. The SDK omits individual votes and identities from hidden-name results and notifications. These methods serve direct chats and groups; channel polls use another store and aren't exposed here.
Pin the useful message
Use a submitted message ID from your own conversation in place of REPLACE_WITH_MESSAGE_ID.
await client.pinMessage(messageId, 'SevenDays');Use OneDay, SevenDays or ThirtyDays for duration. Remove the pin with client.unpinMessage(messageId) or POST /api/messages/unpin and { "messageId": "..." }. HTTP clients can follow votes on /api/events?topics=poll.vote; each SSE JSON envelope contains payload.vote. The cURL workflow above needs jq.
Message pins affect the conversation and need native chat permissions. pinChat instead changes this account's chat list. Read the polls and pins guide for native feature requirements and listener behavior.