# Polls API

The Polls feature provides a comprehensive API that enables seamless integration of polling capabilities within your application, enhancing engagement and interaction among users. Through this API, developers can effortlessly create, manage, and utilize polls as part of messages, gather user opinions, and make informed decisions based on real-time feedback.

### Key Features Include

- **Easy Poll Creation and Configuration** : Developers can create polls with customizable options, descriptions, and configurations, including setting voting visibility (public or anonymous), enforcing unique votes, and specifying the maximum votes a user can cast. Polls can also be designed to allow user-suggested options or open-ended answers, providing flexibility in how you engage with your audience.

- **Seamless Integration with Messages** : Once created, polls can be sent as part of messages, allowing for a seamless user experience. Users can view poll details and participate directly within the context of a conversation.

- **Dynamic Poll Management** : Polls are not static. You can update poll details, add or modify options, and even close polls to further responses. These actions can be performed through full or partial updates, giving you control over the poll's lifecycle.

- **Robust Voting System** : Users can cast votes on options or provide answers to open-ended questions, with the API supporting both single and multiple choice responses. Votes can be changed or removed, ensuring users' opinions are accurately captured.

- **Comprehensive Query Capabilities** : Retrieve detailed information about polls and votes based on various criteria, including poll status, creation time, and user responses. This enables developers to implement rich, data-driven features in their applications.

- **Customizability and Extensibility** : In addition to predefined poll properties, the API supports custom properties, allowing developers to tailor polls and options to their specific needs while maintaining performance and scalability.

## APIs

## Creating a poll and sending it as part of a message

Creating a poll is easy. You simply create a poll with your desired configuration, and once created, you send a message with the poll id.

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
const poll = await client.createPoll({
  name: "Where should we host our next company event?",
  options: [
    {
      text: "Amsterdam, The Netherlands",
    },
    {
      text: "Boulder, CO",
    },
  ],
});

// or if you're using PollManager

const pollInstance = await client.polls.createPoll({
  name: "Where should we host our next company event?",
  options: [
    {
      text: "Amsterdam, The Netherlands",
    },
    {
      text: "Boulder, CO",
    },
  ],
});

const { message } = await channel.sendMessage({
  text: "We want to know your opinion!",
  poll_id: poll.id,
});

// message.poll contains all relevant poll data
```

</codetabs-item>

<codetabs-item value="swift" label="Swift">

```swift
channelController.createPoll(
  name: "Where should we host our next company event?",
  allowAnswers: true,
  allowUserSuggestedOptions: true,
  description: "Pick best answers",
  enforceUniqueVote: true,
  votingVisibility: .public,
  options: [
    PollOption(text: "Amsterdam, The Netherlands"),
    PollOption(text: "Boulder, CO")
  ]) { result in
    // …
  }
```

</codetabs-item>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
val pollConfig = PollConfig(
    name = "Where should we host our next company event?",
    allowAnswers = true,
    allowUserSuggestedOptions = true,
    description = "Pick best answers",
    enforceUniqueVote = true,
    votingVisibility = VotingVisibility.PUBLIC,
    options = listOf(
        "Amsterdam, The Netherlands",
        "Boulder, CO",
    )
)

// Creates and sends the poll as part of a message
client.sendPoll(
    channelType = "<channel-type>",
    channelId = "<channel-id>",
    pollConfig = pollConfig,
).enqueue()
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
final poll = Poll(
  name: 'Where should we host our next company event?',
  allowAnswers: true,
  allowUserSuggestedOptions: true,
  description: 'Pick best answers',
  enforceUniqueVote: true,
  votingVisibility: VotingVisibility.public,
  options: const [
    PollOption(text: 'Amsterdam, The Netherlands'),
    PollOption(text: 'Boulder, CO'),
  ],
);

final pollResult = await client.createPoll(poll);
await client.sendMessage(
  Message(
    text: 'We want to know your opinion!',
    pollId: pollResult.poll.id,
  ),
  'channel-id',
  'channel-type',
);

// Or, if you are using channel instance
await channel.sendPoll(
  poll,
  messageText: 'We want to know your opinion!',
);
```

</codetabs-item>

</codetabs>

<admonition type="info">

Please take into account that the poll can be sent only by the user who created it in the first place.

</admonition>

When creating a poll, the following properties can be configured:

| name                         | type                         | description                                                                                                                               | default | optional |
| ---------------------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------- | -------- |
| name                         | string                       | The name of the poll                                                                                                                      | -       |          |
| description                  | string                       | The description of the poll                                                                                                               | -       | ✓        |
| voting_visibility            | enum                         | Designates whether the votes are casted anonymously                                                                                       | public  | ✓        |
| enforce_unique_vote          | boolean                      | Designates whether the poll is multiple choice or single choice                                                                           | false   | ✓        |
| max_votes_allowed            | number                       | Designates how many votes a single user is allowed to cast on a poll. Allowed value is in range from 1 to 10. If null, no limits applied. | null    | ✓        |
| allow_user_suggested_options | boolean                      | Designates weather user can add custom options to the poll                                                                                | false   | ✓        |
| allow_answers                | boolean                      | Designates whether user can add an answer to the poll. Max 1 answer is allowed. This is for open ended polls.                             | false   |          |
| is_closed                    | boolean                      | Whether the poll is closed for voting or not                                                                                              | false   | ✓        |
| options                      | array of poll option objects | One or more options users can vote on. See below for more information on poll options                                                     | -       | ✓        |

### Poll options

| name | type   | description                 | default | optional |
| ---- | ------ | --------------------------- | ------- | -------- |
| text | string | The text of the poll option | -       | ✓        |

Besides the above mentioned properties, it is also possible to supply your own custom properties for both polls and options:

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
const poll = await client.createPoll({
	name: 'Where should we host our next company event?',
	options: [
		{
			text: 'Amsterdam, The Netherlands'
			foo: 'bar'
		},
		{
			text: 'Boulder, CO'
			foo: 'baz'
		}
	],
	foo: 'bar'
});
```

</codetabs-item>

<codetabs-item value="swift" label="Swift">

```swift
channelController.createPoll(
  name: "Where should we host our next company event?",
  options: [
    PollOption(
      text: "Amsterdam, The Netherlands",
      extraData: ["venue capacity": .number(300)]
    ),
    PollOption(
      text: "Boulder, CO",
      extraData: ["venue capacity": .number(1000)]
    )
  ]
) { result in
    // …
  }
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
final poll = Poll(
  name: 'Where should we host our next company event?',
  options: const [
    PollOption(
      text: 'Amsterdam, The Netherlands',
      extraData: {'venue capacity': 300},
    ),
    PollOption(
      text: 'Boulder, CO',
      extraData: {'venue capacity': 1000},
    ),
  ],
);

await client.createPoll(poll);
```

</codetabs-item>

</codetabs>

<admonition type="info">

The total size of all custom properties on a poll cannot exceed 5KB.

</admonition>

### Example poll response as part of a message

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
{
 id: 'edffef3e-6cdf-4915-b08f-0f560dfd5500',
 name: 'Where should we host our next company event?',
 description: '',
 voting_visibility: 'public',
 max_votes_allowed: 1,
 allow_user_suggested_options: false,
 vote_count: 1,
 answers_count: 2,
 options: [
		{
			id: 'a5ec8075-68e7-4a4a-b60b-48a6ea54653f',
			text: 'Amsterdam, The Netherlands'
		},
		{
			id: 'a5ec8075-68e7-4a4a-b60b-48a6ea54653f',
			text: 'Boulder, CO'
		}
	],
 vote_counts_by_option: { 'a5ec8075-68e7-4a4a-b60b-48a6ea54653f': 1 },
 latest_answers: [
   {
	   poll_id: 'edffef3e-6cdf-4915-b08f-0f560dfd5500',
	   id: 'cbd67e2a-0ee2-48d2-8bde-dfb95392b6012',
	   answer_text: 'I dont like pineapple',
	   user_id: 'a172931b-8c52-48e5-b153-64070fc94dc8',
	   user: {
		   ...
	   }
	   created_at: '2023-12-04T12:16:23.188434Z',
	   updated_at: '2023-12-04T12:16:23.188434Z',
	  }
 ],
 latest_votes_by_option: {
	 'a5ec8075-68e7-4a4a-b60b-48a6ea54653f': [
	  {
	   poll_id: 'edffef3e-6cdf-4915-b08f-0f560dfd5500',
	   id: 'cbd67e2a-0ee2-48d2-8bde-bb95392b60b6',
	   option_id: 'a5ec8075-68e7-4a4a-b60b-48a6ea54653f',
	   user_id: 'a172931b-8c52-48e5-b153-64070fc94dc8',
	   user: {
		   ...
	   }
	   created_at: '2023-12-04T12:16:23.188434Z',
	   updated_at: '2023-12-04T12:16:23.188434Z',

	  }
	 ]
	},
 own_votes: [
  {
   poll_id: 'edffef3e-6cdf-4915-b08f-0f560dfd5500',
   id: '5af67ac1-eae8-4bbc-98dc-1e63a6209e88',
   option_id: 'a5ec8075-68e7-4a4a-b60b-48a6ea54653f',
   user_id: '6e9f35fa-fe8d-4c51-8832-c9c1eae3744f',
   created_at: '2023-12-04T12:14:28.047056Z',
   updated_at: '2023-12-04T12:14:28.047056Z'
  }
 ],
 created_by_id: '6e9f35fa-fe8d-4c51-8832-c9c1eae3744f',
 created_by: {
  id: '6e9f35fa-fe8d-4c51-8832-c9c1eae3744f',
  role: 'user',
  created_at: '2023-12-04T12:14:27.468935Z',
  updated_at: '2023-12-04T12:14:27.468935Z',
  last_active: '2023-12-04T12:14:27.482014Z',
  banned: false,
  online: true
 },
 created_at: '2023-12-04T12:14:27.4963Z',
 updated_at: '2023-12-04T12:14:27.4963Z'
}
```

</codetabs-item>

</codetabs>

<admonition type="info">

The  `latest_votes_by_option`  will contain at most the 10 latest votes for that particular option.

</admonition>

## Casting a vote

Once a poll has been send as part of a message (and as long as the poll isn’t closed for voting). Votes can be casted

### Send vote on option

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
const { votes } = await chatClient.castPollVote(messageId, pollId,
	option_id: 'some-option-id-328904342'
});

// or if you're using the reactive poll instance
const pollInstance = chatClient.polls.fromState(pollId);
const { votes } = await pollInstance.castVote(optionId, messageId);
```

</codetabs-item>

<codetabs-item value="swift" label="Swift">

```swift
let pollController = chatClient.pollController(
  messageId: "message-id",
  pollId: "poll-id"
)
pollController.castPollVote(
  answerText: nil,
  optionId: "option-id") { error in
    // æ
  }
```

</codetabs-item>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
client.castPollVote(
  messageId = "message-id",
  pollId = "poll-id",
  option = Option(id = "option-id", /* ... */),
).enqueue()
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
await client.castPollVote(
  messageId,
  pollId,
  optionId: 'some-option-id-328904342',
);

// Or, if you're using the channel instance
await channel.castPollVote(message, poll, option);
```

</codetabs-item>

</codetabs>

### Send an answer (if answers are configured to be allowed)

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
const { votes } = await chatClient.castPollVote(messageId, pollId, {
  answer_text: "some-option-id-328904342",
});

// or if you're using the reactive poll instance
const pollInstance = chatClient.polls.fromState(pollId);
const { votes } = await pollInstance.addAnswer(answerText, messageId);
```

</codetabs-item>

<codetabs-item value="swift" label="Swift">

```swift
let pollController = chatClient.pollController(
  messageId: "message-id",
  pollId: "poll-id"
)
pollController.castPollVote(
  answerText: "This is my answer text",
  optionId: "option-id") { error in
    // æ
  }
```

</codetabs-item>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
client.castPollAnswer(
  messageId = "message-id",
  pollId = "poll-id",
  answer = "This is my answer text",
).enqueue()
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
await client.addPollAnswer(
  messageId,
  pollId,
  answerText: 'This will be awesome!',
);

// Or, if you're using the channel instance
await channel.addPollAnswer(
  message,
  poll,
  answerText: 'This will be awesome!',
);
```

</codetabs-item>

</codetabs>

#### Few points to note here

- If  `enforce_unique_votes`  is set to **true** on poll, then any vote casted on option will replace the previous vote. Also this api will broadcast an event:

- `poll.vote_changed`  if  `enforce_unique_votes`  is **true**

- Otherwise  `poll.vote_casted`  event will be broadcasted

- Adding an answer will always replace the previous answer. This ensures that user can add maximum `1` answer (similar to what Polly app has)

- You need  `CastVote`  permission to be able to cast a vote

- API will return an error if poll is not attached to a message.

## Removing a vote

A vote can be removed as well:

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
const { vote } = await chatClient.removePollVote(messageId, pollId, voteId);

// or if you're using the reactive poll instance
const pollInstance = chatClient.polls.fromState(pollId);
const { vote } = await pollInstance.removeVote(voteId, messageId);
```

</codetabs-item>

<codetabs-item value="swift" label="Swift">

```swift
pollController.removePollVote(voteId: "vote-id") { error in
  // …
}
```

</codetabs-item>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
client.removePollVote(
  messageId = "message-id",
  pollId = "poll-id",
  vote = Vote(id = "vote-id", /* ... */),
).enqueue()
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
await client.removePollVote(messageId, pollId, voteId);

// Or, if you're using the channel instance
await channel.removePollVote(message, poll, vote);
```

</codetabs-item>

</codetabs>

## Closing a poll

If you want to prevent any further votes on a poll, you can close a poll for voting:

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
await chatClient.closePoll(pollId);

// or if you're using the reactive poll instance
const pollInstance = chatClient.polls.fromState(pollId);
await pollInstance.close();
```

</codetabs-item>

<codetabs-item value="swift" label="Swift">

```swift
pollController.closePoll { error in
  // …
}
```

</codetabs-item>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
client.closePoll(pollId = "poll-id").enqueue()
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
await client.closePoll(pollId);

// Or, if you are using channel instance
await channel.closePoll(poll);
```

</codetabs-item>

</codetabs>

## Retrieving a poll

If you know the id of a poll you can easily retrieve the poll by using the  `getPoll`  method. If you don’t know the id or if you want to retrieve multiple polls, use the query polls method (see below)

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
const poll = await chatClient.getPoll(pollId, userId);
// userId is optional and can be provided for serverside calls
// in case you want to include the votes for the user

// or if you're using PollManager
await chatClient.polls.getPoll(pollId);
// note that in this case the poll will be returned by
// the cache first and fetched only if it isn't there
```

</codetabs-item>

<codetabs-item value="swift" label="Swift">

```swift
let pollController = chatClient.pollController(
  messageId: "message-id",
  pollId: "poll-id"
)
pollController.synchronize { error in
  let poll = pollController.poll
}
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
await client.getPoll(pollId);
```

</codetabs-item>

</codetabs>

## Updating a poll

There are two ways to update a poll: a **full** poll update and a **partial** update.

### Full update

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
const updatedPoll = await chatClient.updatePoll({
 id: pollId,
	name: 'Where should we not go to?'
	options: [
		{
			text: 'Amsterdam, The Netherlands'
			foo: 'bar
		},
		{
			text: 'Boulder, CO'
			foo: 'baz'
		}
	]
});
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
final updatedPoll = Poll(
  id: pollId,
  name: 'Where should we not go to?',
  options: const [
    PollOption(
      text: 'Amsterdam, The Netherlands',
      extraData: {'foo': 'bar'},
    ),
    PollOption(
      text: 'Boulder, CO',
      extraData: {'foo': 'baz'},
    ),
  ],
);

await client.updatePoll(updatedPoll);

// Or, if you are using the channel instance
await channel.updatePoll(updatedPoll);
```

</codetabs-item>

</codetabs>

<admonition type="warning">

All the poll properties that are omitted in the update request will either be removed or set to their default values.

</admonition>

### Partial update

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
const updatedPoll = await chatClient.partialUpdatePoll(pollId, {
  set: { name: "Where should we not go to?" },
  unset: ["custom_property"],
});
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
await client.partialUpdatePoll(
  pollId,
  set: {'name': 'Where should we not go to?'},
  unset: ['custom_property'],
);
```

</codetabs-item>

</codetabs>

## Deleting a poll

Deleting a poll removes the poll, its associated options as well as all the votes on that poll. Be aware that removing a poll can’t be undone.

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
await chatClient.deletePoll(pollId);

// or if you're using the reactive poll instance
const pollInstance = chatClient.polls.fromState(pollId);
await pollInstance.delete();
```

</codetabs-item>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
client.deletePoll(pollId = "poll-id").enqueue()
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
await client.deletePoll(pollId);

// Or, if you are using channel instance
await channel.deletePoll(poll);
```

</codetabs-item>

</codetabs>

## Adding, updating and deleting poll options

Poll options can be added, updated or deleted after a poll has been created:

### **Add poll option**

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
const pollOption = await chatClient.createPollOption(pollId, {
  text: "Another option",
});

// or if you're using the reactive poll instance
const pollInstance = chatClient.polls.fromState(pollId);
const pollOption = await pollInstance.createOption({ text: "Another option" });
```

</codetabs-item>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
client.suggestPollOption(
  pollId = "poll-id",
  option = "Another option",
).enqueue()
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
await client.createPollOption(
  pollId,
  PollOption(text: "Another option"),
);

// Or, if you're using the channel instance
await channel.createPollOption(
  poll,
  PollOption(text: "Another option"),
);
```

</codetabs-item>

</codetabs>

<admonition type="warning">

If  `allow_user_suggested_options`  is set to `true` on poll, then user only needs  `CastVote`  permission to access this endpoint. Otherwise user needs  `UpdatePoll`  permission.

</admonition>

### Update poll option

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
const updatedPollOption = await chatClient.updatePollOption(pollId, {
  id: optionId,
  text: "Updated option",
  my_custom_property: "my_custom_value",
});

// or if you're using the reactive poll instance
const pollInstance = chatClient.polls.fromState(pollId);
const pollOption = await pollInstance.updateOption({
  id: optionId,
  text: "Updated option",
});
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
await client.updatePollOption(
  pollId,
  PollOption(
    id: optionId,
    text: "Updated option",
    extraData: {'my_custom_property': 'my_custom_value'},
  ),
);
```

</codetabs-item>

</codetabs>

### Delete poll option

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
await chatClient.deletePollOption(pollId, optionId);

// or if you're using the reactive poll instance
const pollInstance = chatClient.polls.fromState(pollId);
await pollInstance.deleteOption(optionId);
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
await client.deletePollOption(pollId, optionId);
```

</codetabs-item>

</codetabs>

## Querying votes

You are able to query the votes on a poll:

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
// retrieve all votes on either option1Id or option2Id
const filter = {
  option_id: { $in: [option1Id, option2Id] },
};
const { votes } = await chatClient.queryPollVotes(poll.id, filter);
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
await client.queryPollVotes(
  pollId,
  filter: Filter.in_('option_id', [option1Id, option2Id]),
);
```

</codetabs-item>

</codetabs>

### Votes Queryable Built-In Fields

| Name       | Type                                              | Description                                      | Supported operators       | Example                                             |
| ---------- | ------------------------------------------------- | ------------------------------------------------ | ------------------------- | --------------------------------------------------- |
| id         | string or list of strings                         | the ID of the vote                               | $in, $eq                  | { id: { $in: [ 'abcd', 'defg' ] } }                 |
| user_id    | string or list of strings                         | the ID of the user who casted the vote           | $in, $eq                  | { $user_id: { $eq: 'abcd' } }                       |
| created_at | string, must be formatted as an RFC3339 timestamp | the time the vote was created                    | $eq, $gt, $lt, $gte, $lte | { created_at: { $gte: '2023-12-04T09:30:20.45Z' } } |
| is_answer  | boolean                                           | whether or not the vote is suggested by the user | $eq                       | { is_answer: { $eq: true } }                        |
| option_id  | string or list of strings                         | The ID of the option the vote was casted on      | $in, $eq, $exists         | { option_id: { $in: [ 'abcd', 'defg' ] } }          |

## Querying polls

It is also possible to query for polls based on certain filter criteria:

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
// retrieve all polls that are closed for voting sorted by created_at
const filter = { is_closed: true };
const sort = { created_at: -1 };
const { polls } = await chatClient.queryPolls(poll.id, filter, sort);

// or if you're using PollManager
const { polls: pollInstances } = await chatClient.polls.queryPolls({
  filter,
  sort,
});
// the returned polls will be ones with reactive state
```

</codetabs-item>

<codetabs-item value="swift" label="Swift">

```swift
let query = PollVoteListQuery(
  pollId: "poll-id",
  optionId: "option-id"
)
let voteListController = chatClient.pollVoteListController(query: query)
voteListController.synchronize { error in
  let votes = voteListController.votes
}
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
// retrieve all polls that are closed for voting sorted by created_at
await client.queryPolls(
  filter: Filter.equal('is_closed', true),
  sort: const [SortOption('created_at', direction: SortOption.DESC)],
);
```

</codetabs-item>

</codetabs>

### Poll Queryable Built-In Fields

| Name                         | type                                              | Description                                              | Supported operations           | Example                                          |
| ---------------------------- | ------------------------------------------------- | -------------------------------------------------------- | ------------------------------ | ------------------------------------------------ |
| id                           | string or list of strings                         | the ID of the vote                                       | $in, $eq                       | { id: { $in: [ 'abcd', 'defg' ] } }              |
| poll_id                      | string or list of strings                         | the ID of the poll                                       | $in, $eq                       | { poll_id: { $in: [ 'abcd', 'defg' ] } }         |
| name                         | string or list of strings                         | the ID of the user who casted the vote                   | $in, $eq                       | { name: { $eq: 'abcd' } }                        |
| voting_visibility            | string                                            | indicates whether the votes are casted anonymously       | $eq                            | { voting_visibility: { $eq: 'anonymous' } }      |
| max_votes_allowed            | number                                            | the maximum amount of votes per user                     | $eq, $ne, $gt, $lt, $gte, $lte | { max_votes_allowed: { $gte: 5 } }               |
| allow_user_suggested_options | boolean                                           | indicates whether the poll allows user suggested options | $eq                            | { allow_user_suggested_options: { $eq: false } } |
| allow_answers                | boolean                                           | indicates whether the poll allows user answers           | $eq                            | { allow_answers: { $eq: false } }                |
| is_closed                    | boolean                                           | indicates whether the poll is closed for voting          | $eq                            | { is_closed: { $eq: true } }                     |
| created_at                   | string, must be formatted as an RFC3339 timestamp | the time the poll was created                            | $eq, $gt, $lt, $gte, $lte      | { created_at: {$gte: ‘2023-12-04T09:30:20.45Z’ } |
| updated_at                   | string, must be formatted as an RFC3339 timestamp | the time the poll was updated                            | $eq, $gt, $lt, $gte, $lte      | { updated_at: {$gte: ‘2023-12-04T09:30:20.45Z’ } |
| created_by_id                | string or list of strings                         | the ID of the user who created the poll                  | $in, $eq                       | { id: { $in: [ 'abcd', 'defg' ] } }              |

## Events

The following websocket events will be emitted:

- `poll.updated`  whenever a poll (or its options) gets updated.

- `poll.closed`  whenever a poll is closed for voting.

- `poll.deleted`  whenever a poll gets deleted.

- `poll.vote_casted`  whenever a vote is casted.

- `poll.vote_removed` whenever a vote is removed.

- `poll.vote_changed`  whenever a vote is changed (case of enforce_unique_vote as true)

## Poll updated event

<codetabs>

<codetabs-item value="json" label="JSON">

```json
{
  "type": "poll.updated",
  "cid": "messaging-polls:a23de673-dcc4-413f-9923-4d1af0a6f596",
  "channel_id": "a23de673-dcc4-413f-9923-4d1af0a6f596",
  "channel_type": "messaging-polls",
  "message": {
    // ...
  },
  "poll": {
    "id": "3598617e-228b-480a-8004-f441ff195da2",
    "name": "Updated poll name",
    "description": "",
    "voting_visibility": "public",
    "enforce_unique_vote": false,
    "max_votes_allowed": null,
    "allow_user_suggested_options": false,
    "allow_answers": false,
    "vote_count": 0,
    "options": [],
    "vote_counts_by_option": {},
    "answers_count": 0,
    "latest_votes_by_option": {},
    "latest_answers": [],
    "own_votes": [],
    "created_by_id": "b3e6cf5b-d431-40f5-8022-27d246b3a890",
    "created_by": {
      "id": "b3e6cf5b-d431-40f5-8022-27d246b3a890",
      "role": "user",
      "created_at": "2024-04-09T20:43:39.192829Z",
      "updated_at": "2024-04-09T20:43:39.192829Z",
      "last_active": "2024-04-09T20:43:39.192829Z",
      "banned": false,
      "online": true
    },
    "created_at": "2024-04-09T20:43:39.360335Z",
    "updated_at": "2024-04-09T20:43:39.940022Z"
  },
  "created_at": "2024-04-09T20:43:39.96665Z",
  "received_at": "2024-04-09T20:43:39.971Z"
}
```

</codetabs-item>

</codetabs>

## Poll closed event

<codetabs>

<codetabs-item value="json" label="JSON">

```json
{
  "type": "poll.closed",
  "cid": "messaging-polls:2ac6751f-d1d9-41f2-a800-fe8efec27164",
  "channel_id": "2ac6751f-d1d9-41f2-a800-fe8efec27164",
  "channel_type": "messaging-polls",
  "message": {
    // ...
  },
  "poll": {
    "id": "d9e4bb1c-20b9-40fa-937d-616dff4268fc",
    "name": "Updated poll name",
    "description": "",
    "voting_visibility": "public",
    "enforce_unique_vote": false,
    "max_votes_allowed": null,
    "allow_user_suggested_options": false,
    "allow_answers": false,
    "is_closed": true,
    "vote_count": 0,
    "options": [],
    "vote_counts_by_option": {},
    "answers_count": 0,
    "latest_votes_by_option": {},
    "latest_answers": [],
    "own_votes": [],
    "created_by_id": "dcc77240-440c-436d-8016-ed666654d1ee",
    "created_by": {
      "id": "dcc77240-440c-436d-8016-ed666654d1ee",
      "role": "user",
      "created_at": "2024-04-09T20:54:42.894589Z",
      "updated_at": "2024-04-09T20:54:42.894589Z",
      "last_active": "2024-04-09T20:54:42.894589Z",
      "banned": false,
      "online": true
    },
    "created_at": "2024-04-09T20:54:43.060539Z",
    "updated_at": "2024-04-09T20:54:43.567798Z"
  },
  "created_at": "2024-04-09T20:54:43.592833Z",
  "received_at": "2024-04-09T20:54:43.597Z"
}
```

</codetabs-item>

</codetabs>

## Poll deleted event

<codetabs>

<codetabs-item value="json" label="JSON">

```json
{
  "type": "poll.deleted",
  "cid": "messaging-polls:5671fbb8-b02e-40e9-a93d-5c6da9db7ef0",
  "channel_id": "5671fbb8-b02e-40e9-a93d-5c6da9db7ef0",
  "channel_type": "messaging-polls",
  "message": {
    // ...
  },
  "poll": {
    "id": "3ed6694e-cb16-4190-b573-69fd9586ea74",
    "name": "poll-58612adb-cee6-4267-b47f-138aa21593a6",
    "description": "",
    "voting_visibility": "public",
    "enforce_unique_vote": false,
    "max_votes_allowed": null,
    "allow_user_suggested_options": false,
    "allow_answers": false,
    "vote_count": 0,
    "options": [
      {
        "id": "5f430775-45af-4112-87d9-fbe6a115229f",
        "text": "Option 1"
      },
      {
        "id": "93123a94-daf9-464e-a2fc-f00b20764dcd",
        "text": "Option 2"
      }
    ],
    "vote_counts_by_option": {},
    "answers_count": 0,
    "latest_votes_by_option": {},
    "latest_answers": [],
    "own_votes": [],
    "created_by_id": "17621af7-fab8-4daf-9812-42f3194526b8",
    "created_at": "2024-04-09T20:57:43.644546Z",
    "updated_at": "2024-04-09T20:57:43.644546Z"
  },
  "user": {
    "id": "17621af7-fab8-4daf-9812-42f3194526b8",
    "role": "user",
    "created_at": "2024-04-09T20:57:43.50881Z",
    "updated_at": "2024-04-09T20:57:43.50881Z",
    "last_active": "2024-04-09T20:57:43.50881Z",
    "banned": false,
    "online": true
  },
  "created_at": "2024-04-09T20:57:44.150552Z",
  "received_at": "2024-04-09T20:57:44.159Z"
}
```

</codetabs-item>

</codetabs>

## Vote casted event

<codetabs>

<codetabs-item value="json" label="JSON">

```json
{
  "type": "poll.vote_casted",
  "cid": "messaging-polls:629e4e73-1811-4e3d-bb0a-e9ba7a7a1502",
  "channel_id": "629e4e73-1811-4e3d-bb0a-e9ba7a7a1502",
  "channel_type": "messaging-polls",
  "message": {
    // ...
  },
  "poll": {
    "id": "8613282d-c492-4630-94fb-12537fa95e55",
    "name": "poll-10acb1ca-1140-45b7-bc12-61bd86403760",
    "description": "",
    "voting_visibility": "public",
    "enforce_unique_vote": false,
    "max_votes_allowed": null,
    "allow_user_suggested_options": false,
    "allow_answers": false,
    "vote_count": 1,
    "options": [
      {
        "id": "fff6357f-56e4-442b-b566-1312b48faf3a",
        "text": "Option 1"
      }
    ],
    "vote_counts_by_option": {
      "fff6357f-56e4-442b-b566-1312b48faf3a": 1
    },
    "answers_count": 0,
    "latest_votes_by_option": {
      "fff6357f-56e4-442b-b566-1312b48faf3a": [
        {
          "poll_id": "8613282d-c492-4630-94fb-12537fa95e55",
          "id": "7afe9ee6-1cf2-4ba2-a07e-4ee2dd51d4ba",
          "option_id": "fff6357f-56e4-442b-b566-1312b48faf3a",
          "user_id": "9e673f6f-e32b-46cf-9296-39bc84e86fd4",
          "user": {
            "id": "9e673f6f-e32b-46cf-9296-39bc84e86fd4",
            "role": "user",
            "created_at": "2024-04-09T20:58:52.154483Z",
            "updated_at": "2024-04-09T20:58:52.154483Z",
            "last_active": "2024-04-09T20:58:52.154483Z",
            "banned": false,
            "online": true
          },
          "created_at": "2024-04-09T20:58:52.799538Z",
          "updated_at": "2024-04-09T20:58:52.799538Z"
        }
      ]
    },
    "latest_answers": [],
    "own_votes": [],
    "created_by_id": "9e673f6f-e32b-46cf-9296-39bc84e86fd4",
    "created_by": {
      "id": "9e673f6f-e32b-46cf-9296-39bc84e86fd4",
      "role": "user",
      "created_at": "2024-04-09T20:58:52.154483Z",
      "updated_at": "2024-04-09T20:58:52.154483Z",
      "last_active": "2024-04-09T20:58:52.154483Z",
      "banned": false,
      "online": true
    },
    "created_at": "2024-04-09T20:58:52.324944Z",
    "updated_at": "2024-04-09T20:58:52.324944Z"
  },
  "poll_vote": {
    "poll_id": "8613282d-c492-4630-94fb-12537fa95e55",
    "id": "7afe9ee6-1cf2-4ba2-a07e-4ee2dd51d4ba",
    "option_id": "fff6357f-56e4-442b-b566-1312b48faf3a",
    "user_id": "9e673f6f-e32b-46cf-9296-39bc84e86fd4",
    "user": {
      "id": "9e673f6f-e32b-46cf-9296-39bc84e86fd4",
      "role": "user",
      "created_at": "2024-04-09T20:58:52.154483Z",
      "updated_at": "2024-04-09T20:58:52.154483Z",
      "last_active": "2024-04-09T20:58:52.154483Z",
      "banned": false,
      "online": true
    },
    "created_at": "2024-04-09T20:58:52.799538Z",
    "updated_at": "2024-04-09T20:58:52.799538Z"
  },
  "created_at": "2024-04-09T20:58:52.82498Z",
  "received_at": "2024-04-09T20:58:52.831Z"
}
```

</codetabs-item>

</codetabs>

## Vote removed event

<codetabs>

<codetabs-item value="json" label="JSON">

```json
{
  "type": "poll.vote_removed",
  "cid": "messaging-polls:!members-j8Uw02jyV30DUXTHql5eoq2EHQ8m3lDNMTidgJS833A",
  "channel_id": "!members-j8Uw02jyV30DUXTHql5eoq2EHQ8m3lDNMTidgJS833A",
  "channel_type": "messaging-polls",
  "message": {
    // ...
  },
  "poll": {
    "id": "987b7ffc-a4e5-4c02-b82c-f8d06d2b7f09",
    "name": "poll-dbbbe015-0a5b-4073-993c-bd40b83b8458",
    "description": "",
    "voting_visibility": "public",
    "enforce_unique_vote": false,
    "max_votes_allowed": null,
    "allow_user_suggested_options": false,
    "allow_answers": false,
    "vote_count": 0,
    "options": [
      {
        "id": "a89ef738-5443-406e-8f47-7f68ded8caab",
        "text": "Option 1"
      }
    ],
    "vote_counts_by_option": {},
    "answers_count": 0,
    "latest_votes_by_option": {},
    "latest_answers": [],
    "own_votes": [],
    "created_by_id": "50d6bf78-c529-45a8-9c69-6755e8a31ef3",
    "created_by": {
      "id": "50d6bf78-c529-45a8-9c69-6755e8a31ef3",
      "role": "user",
      "created_at": "2024-04-09T21:47:09.572264Z",
      "updated_at": "2024-04-09T21:47:09.572264Z",
      "last_active": "2024-04-09T21:47:09.572264Z",
      "banned": false,
      "online": true
    },
    "created_at": "2024-04-09T21:47:09.617585Z",
    "updated_at": "2024-04-09T21:47:09.617585Z"
  },
  "poll_vote": {
    "poll_id": "987b7ffc-a4e5-4c02-b82c-f8d06d2b7f09",
    "id": "dc7e9ea2-8df8-48e1-9268-1fcb574d0442",
    "option_id": "a89ef738-5443-406e-8f47-7f68ded8caab",
    "user_id": "50d6bf78-c529-45a8-9c69-6755e8a31ef3",
    "user": {
      "id": "50d6bf78-c529-45a8-9c69-6755e8a31ef3",
      "role": "user",
      "created_at": "2024-04-09T21:47:09.572264Z",
      "updated_at": "2024-04-09T21:47:09.572264Z",
      "last_active": "2024-04-09T21:47:09.572264Z",
      "banned": false,
      "online": true
    },
    "created_at": "2024-04-09T21:47:10.163473Z",
    "updated_at": "2024-04-09T21:47:10.163473Z"
  },
  "created_at": "2024-04-09T21:47:10.445489Z",
  "received_at": "2024-04-09T21:47:10.455Z"
}
```

</codetabs-item>

</codetabs>

## Vote changed event

<codetabs>

<codetabs-item value="json" label="JSON">

```json
{
  "type": "poll.vote_changed",
  "cid": "messaging-polls:629e4e73-1811-4e3d-bb0a-e9ba7a7a1502",
  "channel_id": "629e4e73-1811-4e3d-bb0a-e9ba7a7a1502",
  "channel_type": "messaging-polls",
  "message": {
    // ...
  },
  "poll": {
    "id": "8613282d-c492-4630-94fb-12537fa95e55",
    "name": "poll-10acb1ca-1140-45b7-bc12-61bd86403760",
    "description": "",
    "voting_visibility": "public",
    "enforce_unique_vote": false,
    "max_votes_allowed": null,
    "allow_user_suggested_options": false,
    "allow_answers": false,
    "vote_count": 1,
    "options": [
      {
        "id": "fff6357f-56e4-442b-b566-1312b48faf3a",
        "text": "Option 1"
      }
    ],
    "vote_counts_by_option": {
      "fff6357f-56e4-442b-b566-1312b48faf3a": 1
    },
    "answers_count": 0,
    "latest_votes_by_option": {
      "fff6357f-56e4-442b-b566-1312b48faf3a": [
        {
          "poll_id": "8613282d-c492-4630-94fb-12537fa95e55",
          "id": "7afe9ee6-1cf2-4ba2-a07e-4ee2dd51d4ba",
          "option_id": "fff6357f-56e4-442b-b566-1312b48faf3a",
          "user_id": "9e673f6f-e32b-46cf-9296-39bc84e86fd4",
          "user": {
            "id": "9e673f6f-e32b-46cf-9296-39bc84e86fd4",
            "role": "user",
            "created_at": "2024-04-09T20:58:52.154483Z",
            "updated_at": "2024-04-09T20:58:52.154483Z",
            "last_active": "2024-04-09T20:58:52.154483Z",
            "banned": false,
            "online": true
          },
          "created_at": "2024-04-09T20:58:52.799538Z",
          "updated_at": "2024-04-09T20:58:52.799538Z"
        }
      ]
    },
    "latest_answers": [],
    "own_votes": [],
    "created_by_id": "9e673f6f-e32b-46cf-9296-39bc84e86fd4",
    "created_by": {
      "id": "9e673f6f-e32b-46cf-9296-39bc84e86fd4",
      "role": "user",
      "created_at": "2024-04-09T20:58:52.154483Z",
      "updated_at": "2024-04-09T20:58:52.154483Z",
      "last_active": "2024-04-09T20:58:52.154483Z",
      "banned": false,
      "online": true
    },
    "created_at": "2024-04-09T20:58:52.324944Z",
    "updated_at": "2024-04-09T20:58:52.324944Z"
  },
  "poll_vote": {
    "poll_id": "8613282d-c492-4630-94fb-12537fa95e55",
    "id": "7afe9ee6-1cf2-4ba2-a07e-4ee2dd51d4ba",
    "option_id": "fff6357f-56e4-442b-b566-1312b48faf3a",
    "user_id": "9e673f6f-e32b-46cf-9296-39bc84e86fd4",
    "user": {
      "id": "9e673f6f-e32b-46cf-9296-39bc84e86fd4",
      "role": "user",
      "created_at": "2024-04-09T20:58:52.154483Z",
      "updated_at": "2024-04-09T20:58:52.154483Z",
      "last_active": "2024-04-09T20:58:52.154483Z",
      "banned": false,
      "online": true
    },
    "created_at": "2024-04-09T20:58:52.799538Z",
    "updated_at": "2024-04-09T20:58:52.799538Z"
  },
  "created_at": "2024-04-09T20:58:52.82498Z",
  "received_at": "2024-04-09T20:58:52.831Z"
}
```

</codetabs-item>

</codetabs>

## Permissions

The following permissions can be configured to allow or disallow certain actions on Polls:

## App permissions

- `CreatePoll`  allows or disallows a user to create polls.

- `UpdatePoll`  allows or disallows a user to update polls.

- `DeletePoll`  allows or disallows a user to delete polls.

- `ClosePoll`  allows or disallows a user to close a poll.

- `QueryPolls`  allows or disallows a user to query polls.

## Channel permissions

- `SendPoll`  allows or disallows a user to send a poll as part of a message.

- `CastVote`  allows or disallows a user to cast vote(s) on a poll.

- `QueryVotes`  allows or disallows a user to query the vote(s) on a poll.

---

This page was last updated at 2025-08-15T14:14:15.549Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/android/polls_api/](https://getstream.io/chat/docs/android/polls_api/).