---
title: Polls | API Docs
description: Send a poll into a chat, add options, vote, and read the tally.
---

Polls let a chat vote on a set of options inline, without leaving the conversation. They are an iMessage feature — see [Protocol Selection](/guides/messaging/protocol-selection/index.md) for what each protocol supports.

Two things shape everything below:

- **The chat must already exist.** A poll cannot be the first message of a new chat — create the chat with [`POST /v3/chats`](/api/resources/chats/methods/create/index.md) first.
- **Options are add-only.** Once an option exists it can never be edited or removed. You can append new ones for the life of the poll.

## Create a poll

- [cURL](#tab-panel-134)
- [TypeScript](#tab-panel-135)
- [Python](#tab-panel-136)
- [Go](#tab-panel-137)

Terminal window

```
curl -X POST https://api.linqapp.com/api/partner/v3/chats/{chatId}/polls \
  -H "Authorization: Bearer $LINQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
      "poll": {
        "options": [
          {
            "text": "Tacos"
          },
          {
            "text": "Sushi"
          }
        ],
        "idempotency_key": "poll-abc123"
      }
    }'
```

```
await client.chats.polls.create({chatId}, {
  poll: {
    options: [
      {
        text: "Tacos",
      },
      {
        text: "Sushi",
      },
    ],
    idempotency_key: "poll-abc123",
  },
});
```

```
client.chats.polls.create(
    {chat_id},
    poll={
        "options": [
            {
                "text": "Tacos",
            },
            {
                "text": "Sushi",
            },
        ],
        "idempotency_key": "poll-abc123",
    },
)
```

```
client.Chats.Polls.Create(context.TODO(), {chatId}, linq.ChatPollNewParams{
  Poll: linq.F(map[string]any{
    Options: linq.F([]any{
      map[string]any{
        Text: linq.F("Tacos"),
      },
      map[string]any{
        Text: linq.F("Sushi"),
      },
    }),
    IdempotencyKey: linq.F("poll-abc123"),
  }),
})
```

| Field  | Required | Type     | Description |
| ------ | -------- | -------- | ----------- |
| `poll` | Yes      | `object` | —           |

A poll needs **at least two options**, and has no title or question field — send the question as a normal text message before the poll if you need one. Pass `idempotency_key` to make retries safe.

The response is a poll envelope. Its `message_id` is the poll-definition message, and it is how you reference this poll from every other endpoint — hold on to it. See the [Create Poll API reference](/api/resources/chats/subresources/polls/methods/create/index.md).

202, not 201

Every write here returns `202` — the poll is accepted for delivery, not confirmed delivered. The terminal outcome arrives on the [webhooks](#webhooks) below.

## Add options

- [cURL](#tab-panel-122)
- [TypeScript](#tab-panel-123)
- [Python](#tab-panel-124)
- [Go](#tab-panel-125)

Terminal window

```
curl -X POST https://api.linqapp.com/api/partner/v3/messages/{messageId}/poll/options \
  -H "Authorization: Bearer $LINQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
      "options": [
        {
          "text": "Pizza"
        }
      ]
    }'
```

```
await client.messages.poll.addOptions({messageId}, {
  options: [
    {
      text: "Pizza",
    },
  ],
});
```

```
client.messages.poll.add_options(
    {message_id},
    options=[
        {
            "text": "Pizza",
        },
    ],
)
```

```
client.Messages.Poll.AddOptions(context.TODO(), {messageId}, linq.MessagePollAddOptionsParams{
  Options: linq.F([]any{
    map[string]any{
      Text: linq.F("Pizza"),
    },
  }),
})
```

Appends options and returns the full poll. Anyone in the chat can add options, not just the creator — an inbound add arrives as a `poll.updated` webhook carrying only the new options. See the [Add Options API reference](/api/resources/messages/subresources/poll/methods/add_options/index.md).

## Vote

- [cURL](#tab-panel-126)
- [TypeScript](#tab-panel-127)
- [Python](#tab-panel-128)
- [Go](#tab-panel-129)

Terminal window

```
curl -X POST https://api.linqapp.com/api/partner/v3/messages/{messageId}/poll/votes \
  -H "Authorization: Bearer $LINQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
      "option_id": "97ce8c17-7ef6-4bbc-a89a-6b93d189712f",
      "operation": "add"
    }'
```

```
await client.messages.poll.vote({messageId}, {
  option_id: "97ce8c17-7ef6-4bbc-a89a-6b93d189712f",
  operation: "add",
});
```

```
client.messages.poll.vote(
    {message_id},
    option_id="97ce8c17-7ef6-4bbc-a89a-6b93d189712f",
    operation="add",
)
```

```
client.Messages.Poll.Vote(context.TODO(), {messageId}, linq.MessagePollVoteParams{
  OptionId: linq.F("97ce8c17-7ef6-4bbc-a89a-6b93d189712f"),
  Operation: linq.F("add"),
})
```

Votes are a **per-option toggle**: one call changes one option, with `operation` set to `add` or `remove`. A voter picking three options is three calls, and three separate `poll.vote.added` webhooks. See the [Vote API reference](/api/resources/messages/subresources/poll/methods/vote/index.md).

## Read the tally

- [cURL](#tab-panel-130)
- [TypeScript](#tab-panel-131)
- [Python](#tab-panel-132)
- [Go](#tab-panel-133)

Terminal window

```
curl https://api.linqapp.com/api/partner/v3/messages/{messageId}/poll \
  -H "Authorization: Bearer $LINQ_API_KEY"
```

```
await client.messages.poll.retrieve({messageId});
```

```
client.messages.poll.retrieve({message_id})
```

```
client.Messages.Poll.Retrieve(context.TODO(), {messageId})
```

Returns the poll’s current state: every option, the voters on each, and `total_voters`. Note that `total_voters` counts **distinct participants** — someone who voted for two options counts once — so it will not match the sum of the per-option voter counts. See the [Get Poll API reference](/api/resources/messages/subresources/poll/methods/retrieve/index.md).

You do not have to poll `GET /v3/messages/{messageId}/poll` to stay current; the webhooks below carry each change as it happens.

## Webhooks

Nine events cover the poll lifecycle. See [Webhook Events](/guides/webhooks/events/index.md) for payload schemas and [Webhooks](/guides/webhooks/index.md) for setup.

| Event                 | Fires when                                     |
| --------------------- | ---------------------------------------------- |
| `poll.received`       | A participant sends a poll to your line        |
| `poll.sent`           | A poll you created leaves the device           |
| `poll.delivered`      | That poll reaches the recipient                |
| `poll.read`           | The recipient reads it                         |
| `poll.updated`        | Someone adds options to an existing poll       |
| `poll.failed`         | An outbound poll or poll action failed to send |
| `poll.vote.added`     | A participant votes for an option              |
| `poll.vote.removed`   | A participant takes a vote back                |
| `poll.reaction.added` | Someone reacts to the poll message             |

Poll reactions are stickers, which cannot be removed, so `poll.reaction.added` has no removal counterpart.

## Important notes

- **iMessage only** — polls are not available on RCS or SMS.
- **The chat must exist first** — a poll cannot open a new chat.
- **No question field** — send the question as a separate text message.
- **Two options minimum**, and options are add-only and immutable once created.
- **One option per vote call** — votes toggle per option, not as a set.
- **Reference the poll by `message_id`**, the poll-definition message returned by create.

## Related

- [Reactions](/guides/messaging/reactions/index.md) — tapbacks, including on a poll message
- [Sending Messages](/guides/messaging/sending-messages/index.md) — the text message that carries your question
- [Webhook Events](/guides/webhooks/events/index.md) — every poll payload shape
