// Create a poll
let poll = try await feed.createPoll(
request: CreatePollRequest(
name: "Where should we host our next company event?",
options: [
PollOptionInput(text: "Amsterdam, The Netherlands"),
PollOptionInput(text: "Boulder, CO")
]
),
activityType: "poll"
)
// The poll is automatically added as an activity to the feed
print("Poll created with ID: \(poll.id)")
Polls
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 Activities: Once created, polls can be sent as part of activities, 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.
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.
const response = await client.createPoll({
name: "Where should we host our next company event?",
options: [{ text: "Amsterdam, The Netherlands" }, { text: "Boulder, CO" }],
});
// Attach poll to an activity
await feed.addActivity({
type: "post",
poll_id: response.poll.id,
});
const response = await client.createPoll({
name: "Where should we host our next company event?",
options: [{ text: "Amsterdam, The Netherlands" }, { text: "Boulder, CO" }],
user_id: "<user id>",
});
// Attach poll to an activity
await client.feeds.addActivity({
fids: [feed.fid],
type: "post",
poll_id: response.poll.id,
user_id: "<user id>",
});
Please take into account that the poll can be sent only by the user who created it in the first place.
When creating a poll, the following properties can be configured:
Property | Type | Description | Default | Optional |
---|---|---|---|---|
name | String | The name of the poll | - | ❌ |
description | String | The description of the poll | - | ✅ |
votingVisibility | Enum | Designates whether the votes are casted anonymously | public | ✅ |
enforceUniqueVote | Boolean | Designates whether the poll is multiple choice or single choice | false | ✅ |
maxVotesAllowed | 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 | ✅ |
allowUserSuggestedOptions | Boolean | Designates whether user can add custom options to the poll | false | ✅ |
allowAnswers | Boolean | Designates whether user can add an answer to the poll. Max 1 answer is allowed. This is for open ended polls. | false | ❌ |
isClosed | 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 | - | ✅ |
Poll Options
Property | 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:
let poll = try await feed.createPoll(
request: CreatePollRequest(
custom: ["category": "event_planning", "priority": "high"],
name: "Where should we host our next company event?",
options: [
PollOptionInput(
custom: ["country": "Netherlands", "timezone": "CET"],
text: "Amsterdam, The Netherlands"
),
PollOptionInput(
custom: ["country": "USA", "timezone": "MST"],
text: "Boulder, CO"
)
]
),
activityType: "poll"
)
await client.createPoll({
name: "Where should we host our next company event?",
options: [
{
text: "Amsterdam, The Netherlands",
custom: {
country: "Netherlands",
timezone: "CET",
},
},
{
text: "Boulder, CO",
custom: {
country: "United States",
timezone: "MST",
},
},
],
custom: {
category: "event_planning",
priority: "high",
},
});
await client.createPoll({
name: "Where should we host our next company event?",
options: [
{
text: "Amsterdam, The Netherlands",
custom: {
country: "Netherlands",
timezone: "CET",
},
},
{
text: "Boulder, CO",
custom: {
country: "United States",
timezone: "MST",
},
},
],
custom: {
category: "event_planning",
priority: "high",
},
user_id: "<user id>",
});
The total size of all custom properties on a poll cannot exceed 5KB.
Casting a Vote
Once a poll has been sent as part of an activity (and as long as the poll isn’t closed for voting), votes can be cast.
Send Vote on Option
let activity = client.activity(
for: "activity_123",
in: FeedId(group: "user", id: "test")
)
let votes = try await activity.castPollVote(
request: .init(
vote: .init(optionId: "option_789")
)
)
client.castPollVote({
activity_id: "activity_123",
poll_id: "poll_456",
vote: {
option_id: "option_789",
},
});
client.feeds.castPollVote({
activity_id: "activity_123",
poll_id: "poll_456",
user_id: "<user id>",
vote: {
option_id: "option_789",
},
});
Send an Answer (if answers are configured to be allowed)
let votes = try await activity.castPollVote(
request: .init(
vote: .init(answerText: "Let's go somewhere else")
)
)
client.castPollVote({
activity_id: "activity_123",
poll_id: "poll_456",
vote: {
answer_text: `Let's go somewhere else`,
},
});
client.feeds.castPollVote({
activity_id: "activity_123",
poll_id: "poll_456",
user_id: "<user id>",
vote: {
answer_text: `Let's go somewhere else`,
},
});
Important Notes
- If
enforceUniqueVotes
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
ifenforceUniqueVotes
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 an activity.
Removing a Vote
A vote can be removed as well:
try await activity.removePollVote(voteId: "vote_789")
client.removePollVote({
activity_id: "activity_123",
poll_id: "poll_456",
vote_id: "vote_789",
});
client.feeds.removePollVote({
activity_id: "activity_123",
poll_id: "poll_456",
vote_id: "vote_789",
user_id: "<user id>",
});
Closing a Poll
If you want to prevent any further votes on a poll, you can close a poll for voting:
try await activity.closePoll()
client.updatePollPartial({
poll_id: "poll_456",
set: {
is_closed: true,
},
});
client.updatePollPartial({
poll_id: "poll_456",
set: {
is_closed: true,
},
user_id: "<user id>",
});
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):
let poll = try await activity.getPoll(userId: "john")
// userId is optional and can be provided for serverside calls
// in case you want to include the votes for the user
const poll = client.pollFromState(activity.poll?.id);
poll.state.subscribe((state) => {
// Called every time the poll state changes
console.log(state);
});
const response = await feed.getOrCreate({ user_id: "<user id>" });
console.log(response.activities[0].poll);
// or use poll id
const response = await client.getPoll({
poll_id: "<poll id>",
user_id: "<user id>",
});
Updating a Poll
There are two ways to update a poll: a full poll update and a partial update.
Full Update
let updatedPoll = try await activity.updatePoll(
request: .init(
id: "poll_456",
name: "Where should we not go to?",
options: [
PollOptionRequest(
custom: ["reason": "too expensive"],
id: "option_789",
text: "Amsterdam, The Netherlands"
),
PollOptionRequest(
custom: ["reason": "too far"],
id: "option_790",
text: "Boulder, CO"
)
]
)
)
await client.updatePoll({
id: 'poll_456'
name: 'Where should we host our next company event?',
options: [
{
id: 'option_789',
text: 'Amsterdam, The Netherlands',
custom: {
reason: 'too expensive'
}
},
{
text: 'Boulder, CO',
custom: {
reason: 'too far'
}
},
],
});
await client.updatePoll({
id: 'poll_456'
name: 'Where should we host our next company event?',
options: [
{
id: 'option_789',
text: 'Amsterdam, The Netherlands',
custom: {
reason: 'too expensive'
}
},
{
text: 'Boulder, CO',
custom: {
reason: 'too far'
}
},
],
user_id: '<user id>'
});
All the poll properties that are omitted in the update request will either be removed or set to their default values.
Partial Update
let updatedPoll = try await activity.updatePollPartial(
request: .init(
set: ["name": "Updated poll name"],
unset: ["custom_property"]
)
)
client.updatePollPartial({
poll_id: "poll_456",
set: {
name: "Updated poll name",
unset: ["custom"],
},
});
client.updatePollPartial({
poll_id: "poll_456",
set: {
name: "Updated poll name",
unset: ["custom"],
},
user_id: "<user id>",
});
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.
try await activity.deletePoll()
await client.deletePoll({
poll_id: "poll_456",
});
await client.deletePoll({
poll_id: "poll_456",
user_id: "<user id>",
});
Adding, Updating and Deleting Poll Options
Poll options can be added, updated or deleted after a poll has been created:
Add Poll Option
let pollOption = try await activity.createPollOption(
request: .init(
custom: ["added_by": "user_123"],
text: "Another option"
)
)
await client.createPollOption({
poll_id: "poll_456",
text: "Another option",
custom: {
added_by: "user_123",
},
});
await client.createPollOption({
poll_id: 'poll_456',
text: 'Another option',
custom: {
added_by: 'user_123'
}
user_id: 'user_123'
})
If allowUserSuggestedOptions
is set to true
on poll, then user only needs CastVote
permission to access this endpoint. Otherwise user needs UpdatePoll
permission.
Update Poll Option
let updatedPollOption = try await activity.updatePollOption(
request: .init(
custom: ["my_custom_property": "my_custom_value"],
id: "option_789",
text: "Updated option"
)
)
client.updatePollOption({
poll_id: "poll_456",
id: "option_123",
text: "Updated option",
custom: {
my_custom_property: "my_custom_value",
},
});
client.updatePollOption({
poll_id: "poll_456",
id: "option_123",
text: "Updated option",
custom: {
my_custom_property: "my_custom_value",
},
user_id: "<user id>",
});
Delete Poll Option
try await activity.removePollOption(optionId: "option_789")
await client.deletePollOption({
poll_id: "poll_456",
option_id: "option_789",
});
await client.deletePollOption({
poll_id: "poll_456",
option_id: "option_789",
user_id: "<user id>",
});
Querying Votes
You are able to query the votes on a poll:
// Retrieve all votes on either option1Id or option2Id
let pollVoteList = client.pollVoteList(
for: .init(
pollId: "poll_456",
filter: .in(.optionId, ["option_789", "option_790"])
)
)
let votesPage1 = try await pollVoteList.get()
let votesPage2 = try await pollVoteList.queryMorePollVotes()
let votesPage1And2 = pollVoteList.state.votes
client.queryPollVotes({
filter: {
option_id: { $in: ["option_789", "option_790"] },
},
poll_id: "poll_456",
});
client.queryPollVotes({
filter: {
option_id: { $in: ["option_789", "option_790"] },
},
user_id: "<user id>",
poll_id: "poll_456",
});
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:
// Retrieve all polls that are closed for voting sorted by created_at
let pollList = client.pollList(
for: .init(
filter: .equal(.isClosed, true)
)
)
let pollsPage1 = try await pollList.get()
let pollsPage2 = try await pollList.queryMorePolls()
let pollsPage1And2 = pollList.state.polls
client.queryPolls({
filter: {
is_closed: true,
},
sort: [{ field: "created_at", direction: -1 }],
});
client.queryPolls({
filter: {
is_closed: true,
},
sort: [{ field: "created_at", direction: -1 }],
user_id: "<user id>",
});
Poll Queryable Built-In Fields
Name | Type | Description | Supported operations | Example |
---|---|---|---|---|
id | String or list of strings | The ID of the poll | $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 name of the poll | $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 | { "created_by_id": { "$in": ["abcd", "defg"] } } |
Events
The following websocket events will be emitted:
feeds.poll.updated
whenever a poll (or its options) gets updated.feeds.poll.closed
whenever a poll is closed for voting.feeds.poll.deleted
whenever a poll gets deleted.feeds.poll.vote_casted
whenever a vote is casted.feeds.poll.vote_removed
whenever a vote is removed.feeds.poll.vote_changed
whenever a vote is changed (case of enforce_unique_vote as true)