Welcome!
What did you do?
Tried to retrieve poll results via GET /polls/{pollMessageId}/results for polls that I sent via POST /send/poll, after a real recipient voted on the poll in their WhatsApp app.
Reproduction
- Pair an instance with a sender WhatsApp number.
- Send a poll:
POST /send/poll with { "number": "<recipient>", "question": "...", "options": ["A","B","C"], "maxAnswer": 1 }. Capture the response's data.Info.ID (e.g. 3EB0DBF1C91EA77B149327) and data.Message.messageContextInfo.messageSecret.
- On the recipient phone, open the chat and tap one of the poll options.
- Confirm the vote arrived at evolution-go by checking the webhook payload — it shows a
pollUpdateMessage with the original poll's pollCreationMessageKey.ID, an encIV, and an encPayload (the encrypted vote).
- Call
GET /polls/{pollMessageId}/results with the poll's WhatsApp message ID from step 2, using the same instance's apikey.
Tested via every available auth surface:
fetch() from a tab on the Manager origin with the instance's apikey
fetch() with the global API key (returns 401 — endpoint requires instance scope, as expected)
- The Manager UI's own API Tester at
/manager/api-tester against the same endpoint, with the correct instance selected and the same pollMessageId
Across all five attempts, response is identical.
What did you expect?
I expected GET /polls/{pollMessageId}/results to return the votes for the poll — either as decrypted option counts (preferred) or at minimum the encrypted vote records that evolution-go has clearly already received (we can see them in our webhook payload sitting in our DB).
The endpoint's description in the API tester says "Retorna todos os votos de uma enquete específica" / "Returns all votes of a specific poll," which sets the expectation that it should return ALL received votes.
What did you observe instead of what you expected?
Endpoint always returns HTTP 404 with body:
{
"error": "No votes found for this poll",
"message": "This poll has no votes yet, or the pollMessageId is incorrect"
}
This is consistent across:
- Polls created seconds ago
- Polls created hours ago
- Polls voted on before AND after the webhook was registered
- Polls voted on by a recipient with
readMessages=true set on the sender instance
- Multiple distinct
pollMessageId values
Meanwhile the corresponding pollUpdateMessage webhook event arrives normally in the configured webhook URL within ~1 second of the vote, including the pollCreationMessageKey.ID matching the pollMessageId we're querying. So the server clearly knows the vote exists — /polls/{id}/results just doesn't return it.
Reading pkg/routes/routes.go confirms the route is registered:
routes.GET("/:pollMessageId/results", r.pollHandler.GetPollResults)
Without source access to the handler implementation it's not clear whether (a) the handler is stubbed and never populates results from webhook ingestion, (b) it queries a vote table that's only populated by some other code path, or (c) it tries to decrypt the votes server-side using messageSecret and fails silently. Any of these explanations would result in the same observed 404.
Screenshots/Videos
No response
Which version are you using?
evolution-go v0.7.1 (Docker)
What is your environment?
Docker
If applicable, paste the log output
No response
Additional Notes
Sender / recipient setup
- Sender: personal WhatsApp number connected via QR (not Business)
- Recipient: personal WhatsApp number on Android, current app version, not on Business / WABA / Cloud API
- Webhook URL is correctly registered (other inbound events flow normally;
pollUpdateMessage arrives within ~1s of voting)
readMessages advanced setting was ON during the final retests (made no difference)
Sample webhook payload (the vote IS arriving)
{
"event": "Message",
"data": {
"Info": { "ID": "<vote msg id>", "Type": "poll", "Sender": "<voter>@s.whatsapp.net", "PushName": "..." },
"Message": {
"pollUpdateMessage": {
"vote": { "encIV": "<base64>", "encPayload": "<base64>" },
"pollCreationMessageKey": { "ID": "<original poll msg id>", "fromMe": false, "remoteJID": "<creator-lid>@lid" }
}
}
}
}
The pollCreationMessageKey.ID is the same pollMessageId we pass to GET /polls/{pollMessageId}/results.
Questions for maintainers
- Is
GetPollResults intended to return only votes decrypted server-side, or also the raw encrypted records?
- Is there a separate ingestion path that populates the underlying vote storage that the handler reads from, and is that ingestion working in v0.7.1? (i.e. does the webhook handler write votes anywhere durable, or is it fire-and-forward only?)
- If self-decryption is required client-side instead, would maintainers consider documenting the algorithm? I attempted ~11 HKDF/AES-GCM variants based on Baileys forks and none decrypted the
encPayload against messageSecret + stanzaId + creatorJid + voterJid + "Poll Vote" — would help to know whether the algorithm uses the LID-form JIDs vs phone-form, includes the senderTimestampMS, or follows a different scheme.
Happy to provide raw payload bytes (encIV + encPayload + messageSecret + JIDs) for a test case if it helps maintainers reproduce.
Thanks for the project!
Welcome!
What did you do?
Tried to retrieve poll results via
GET /polls/{pollMessageId}/resultsfor polls that I sent viaPOST /send/poll, after a real recipient voted on the poll in their WhatsApp app.Reproduction
POST /send/pollwith{ "number": "<recipient>", "question": "...", "options": ["A","B","C"], "maxAnswer": 1 }. Capture the response'sdata.Info.ID(e.g.3EB0DBF1C91EA77B149327) anddata.Message.messageContextInfo.messageSecret.pollUpdateMessagewith the original poll'spollCreationMessageKey.ID, anencIV, and anencPayload(the encrypted vote).GET /polls/{pollMessageId}/resultswith the poll's WhatsApp message ID from step 2, using the same instance's apikey.Tested via every available auth surface:
fetch()from a tab on the Manager origin with the instance's apikeyfetch()with the global API key (returns 401 — endpoint requires instance scope, as expected)/manager/api-testeragainst the same endpoint, with the correct instance selected and the samepollMessageIdAcross all five attempts, response is identical.
What did you expect?
I expected
GET /polls/{pollMessageId}/resultsto return the votes for the poll — either as decrypted option counts (preferred) or at minimum the encrypted vote records that evolution-go has clearly already received (we can see them in our webhook payload sitting in our DB).The endpoint's description in the API tester says "Retorna todos os votos de uma enquete específica" / "Returns all votes of a specific poll," which sets the expectation that it should return ALL received votes.
What did you observe instead of what you expected?
Endpoint always returns HTTP 404 with body:
{ "error": "No votes found for this poll", "message": "This poll has no votes yet, or the pollMessageId is incorrect" }This is consistent across:
readMessages=trueset on the sender instancepollMessageIdvaluesMeanwhile the corresponding
pollUpdateMessagewebhook event arrives normally in the configured webhook URL within ~1 second of the vote, including thepollCreationMessageKey.IDmatching thepollMessageIdwe're querying. So the server clearly knows the vote exists —/polls/{id}/resultsjust doesn't return it.Reading
pkg/routes/routes.goconfirms the route is registered:Without source access to the handler implementation it's not clear whether (a) the handler is stubbed and never populates results from webhook ingestion, (b) it queries a vote table that's only populated by some other code path, or (c) it tries to decrypt the votes server-side using
messageSecretand fails silently. Any of these explanations would result in the same observed 404.Screenshots/Videos
No response
Which version are you using?
evolution-go v0.7.1 (Docker)
What is your environment?
Docker
If applicable, paste the log output
No response
Additional Notes
Sender / recipient setup
pollUpdateMessagearrives within ~1s of voting)readMessagesadvanced setting was ON during the final retests (made no difference)Sample webhook payload (the vote IS arriving)
{ "event": "Message", "data": { "Info": { "ID": "<vote msg id>", "Type": "poll", "Sender": "<voter>@s.whatsapp.net", "PushName": "..." }, "Message": { "pollUpdateMessage": { "vote": { "encIV": "<base64>", "encPayload": "<base64>" }, "pollCreationMessageKey": { "ID": "<original poll msg id>", "fromMe": false, "remoteJID": "<creator-lid>@lid" } } } } }The
pollCreationMessageKey.IDis the samepollMessageIdwe pass toGET /polls/{pollMessageId}/results.Questions for maintainers
GetPollResultsintended to return only votes decrypted server-side, or also the raw encrypted records?encPayloadagainstmessageSecret + stanzaId + creatorJid + voterJid + "Poll Vote"— would help to know whether the algorithm uses the LID-form JIDs vs phone-form, includes the senderTimestampMS, or follows a different scheme.Happy to provide raw payload bytes (encIV + encPayload + messageSecret + JIDs) for a test case if it helps maintainers reproduce.
Thanks for the project!