poll.vote

Vote on a poll in a conversation, naming the poll and the choice by the ids its poll.created event carried. A vote adds a choice; it never takes one back.

POST /v1/commands/poll.vote

Votes for one choice of a poll in a conversation on an iMessage line — a poll the line sent or one a contact sent. Name the poll and the choice by the ids the poll's poll.created event carried, on a stream opened with ?events=polls: a choice's id arrives nowhere else. See the poll frame.

A vote adds its choice to the line's selection, because an iMessage poll lets each person pick several. Voting for another choice does not take the first one back: vote for Ramen, then for Pho, and the line holds both. Taking the line's vote back is not available yet.

Honours Idempotency-Key.

Target

Prop

Type

Payload

Prop

Type

What is refused before anything is sent

Each is 422 validation_failed, and nothing reaches the line:

FieldWhen
target.conversationIdThe conversation is not one the transport has named
payload.pollExternalIdMissing, blank, or not an id of the form a poll's id takes
payload.optionExternalIdMissing, blank, or not an id of the form a choice's id takes
payload.removePresent at all: taking the line's vote back is not available yet
payload.<key>Any other key — poll.vote takes the two ids above and no others

Request

curl https://api.mapier.ai/v1/commands/poll.vote \
  -H "Authorization: Bearer $MAPIER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: lunch-poll-vote-ramen" \
  -d '{
    "target": { "conversationId": "a0000000-0000-4000-8000-000000000001" },
    "payload": {
      "pollExternalId": "2F6B1C3D-8E4A-4B5C-9D7E-0A1B2C3D4E5F",
      "optionExternalId": "B2C7E9A1-0D3F-4E6A-8B5C-7D9E1F2A3B4C"
    }
  }'

Response

202 Accepted
{ "commandId": "cmd-21", "status": "queued", "disposition": "queued" }

Then a command frame, type: "poll.vote":

  • succeeded once the line voted. A poll.voted with actor: "line" follows on a stream opened with ?events=polls, carrying the line's whole selection in selectedOptionExternalIds.
  • failed with stale_target when the poll is not among the conversation's 30 newest messages, or with a capability errorCode, failureClass: capability, on a line whose device cannot vote.
  • ambiguous with errorCode: "poll_vote_unverified" when the line could not prove the vote.
poll.voted, after voting for Ramen and then for Pho
{
  "type": "poll.voted",
  "conversationId": "a0000000-0000-4000-8000-000000000001",
  "actor": "line",
  "contactId": null,
  "pollExternalId": "2F6B1C3D-8E4A-4B5C-9D7E-0A1B2C3D4E5F",
  "selectedOptionExternalIds": [
    "B2C7E9A1-0D3F-4E6A-8B5C-7D9E1F2A3B4C",
    "C3D8F0B2-1E4A-4F7B-9C6D-8E0F2A3B4C5D"
  ],
  "occurredAt": "2026-10-01T18:12:09.000Z",
  "simulated": false
}

poll_vote_unverified: read the poll before you vote again

The vote may or may not have been made. Wait for the poll's next poll.voted with actor: "line" — its selectedOptionExternalIds is what the line holds — before voting again.

In test

A test line rehearses a vote on the poll it sent: the sandbox makes the vote, and the poll.voted it produces carries "simulated": true. Nobody on the sandbox phone creates polls or votes, so there is no contact's poll to vote on in test. The refusals above are the same.

Errors

A command answers the one set of HTTP refusals POST /v1/commands/{type} publishes, so that table is the one to handle. What is particular to this type:

  • 422 validation_failed at target.conversationId, payload.pollExternalId, payload.optionExternalId, payload.remove or payload.<key>, above.
  • 404 unsupported_command with reason: not_on_channel on a line of any other channel.

Settlement codes you will see for this command: stale_target, poll_vote_unverified, and a capability code on a line that cannot vote. See Error codes.

On this page