Poll Management¶
The voting API records votes, computes statistics, applies per-user weights, and optionally stores poll configuration. The base URL is https://api.muxy.io/v1/e, and every route requires a valid Muxy extension JWT.
Generated operation pages are linked from each method/path section. The raw specification is available as rest-v1.yaml; the guidance below describes behavior that spans the current rest-api handlers.
| Route | Allowed JWT roles |
|---|---|
GET /vote |
Any authenticated role |
POST /vote |
Any authenticated role |
DELETE /vote |
broadcaster, admin, or backend |
GET /vote_logs |
backend, or an admin JWT for the extension owner or a listed extension admin |
POST /vote_modifier |
broadcaster, admin, or backend |
GET /vote_config |
Any authenticated role |
POST /vote_config |
broadcaster, admin, or backend |
For routes with an id query parameter, the default is default. IDs beginning with global use extension-wide storage; other IDs are scoped to the JWT's extension and channel.
Vote values, counts, and statistics¶
Vote values are integers from -128 through 128, inclusive. Statistics are returned as top-level fields, not under a stats object.
specificalways has 32 elements. Indexes0through31count those exact values.- Values outside
0..31still contribute tostddev,mean,sum, andcount. - In the default single-vote mode, a later vote from the same identity replaces the stored vote, so
countis not a request count or a unique-request count. - In configured multi-vote mode,
countis the number of stored vote entries after modifiers. A positive modifier can make it larger than the number of users; a modifier of-1or less removes that user's votes from statistics.
GET /vote¶
See the generated GET /vote reference.
Returns the poll statistics and, when present, the caller's latest known vote.
GET /v1/e/vote?id=round-1
{
"stddev": 0,
"mean": 2,
"sum": 2,
"specific": [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"count": 1,
"decay": {
"last_nudged": 0,
"val": 0,
"current_scale": 0,
"last_altered": 0
},
"vote": 2
}
vote is omitted when the caller has no known vote. An unknown or empty poll returns zero statistics rather than 404.
POST /vote¶
See the generated POST /vote reference.
Casts or replaces a vote. value must be in -128..128; if omitted, the current handler uses the integer zero value. count is optional: missing or 0 becomes 1.
POST /v1/e/vote?id=round-1
{
"value": 2,
"count": 1
}
count submits that many copies of value in one request. It must be positive after defaulting, no greater than 30, and no greater than the poll's configured totalVotesPerUser. The default poll configuration allows one total vote, so counts above 1 require a multi-vote configuration. The response has the same shape as GET /vote and includes the submitted vote.
Common 400 reasons are Invalid body, Value out of bounds, Count out of bounds, poll is closed, and unacceptable vote.
DELETE /vote¶
See the generated DELETE /vote reference.
Deletes the poll's current vote state, known-vote cache, and saved configuration, then broadcasts a vote deletion event. It is safe when the poll does not exist.
DELETE /v1/e/vote?id=round-1
The response is {}. This handler does not delete the poll's vote_logs; a later unconfigured POST /vote can create active vote state for the same ID again. Disallowed roles receive 403.
GET /vote_logs¶
See the generated GET /vote_logs reference.
Returns stored vote log records in ascending receipt order. Because the response can be large, reserve this route for trusted processing backends.
GET /v1/e/vote_logs?id=round-1
{
"result": [
{
"identifier": "67890",
"opaque": "U12345",
"value": 2,
"timestamp": 1720000000
}
]
}
identifier is the shared Twitch user_id recorded with the submission and may be empty; opaque is the recorded opaque_user_id. timestamp is Unix time in seconds. Unauthorized admin access returns 403; storage failures return 500.
POST /vote_modifier¶
See the generated POST /vote_modifier reference.
Adds a persistent weighting modifier for one poll identity. user must match the identifier used by that poll: normally opaque_user_id, or shared user_id when userIDVoting is enabled.
POST /v1/e/vote_modifier?id=round-1
{
"user": "U12345",
"add": 2
}
Modifiers accumulate. A user's effective contribution to statistics is max(0, 1 + total modifiers) times each stored vote. Success returns {}; malformed JSON returns 400 with reason Invalid body, and disallowed roles receive 403.
GET /vote_config¶
See the generated GET /vote_config reference.
Returns an explicitly saved configuration. Polls created only by POST /vote do not have one and return 404 with {}.
GET /v1/e/vote_config?id=round-1
{
"userIDVoting": false,
"distinctOptionsPerUser": 1,
"totalVotesPerUser": 1,
"votesPerOption": 1,
"global": false,
"disabled": false,
"prompt": "",
"options": ["Yes", "No"],
"userData": null,
"endsAt": 1720003600,
"startsAt": 1720000000,
"status": "active"
}
startsAt and endsAt are Unix seconds; status is pending, active, or expired. The current GET handler returns prompt as an empty string and userData as null, even if values were supplied when configuring the poll.
POST /vote_config¶
See the generated POST /vote_config reference.
Creates or updates an explicit configuration. Unlike the other routes, the poll id is in the JSON body.
POST /v1/e/vote_config
{
"id": "round-1",
"config": {
"userIDVoting": false,
"distinctOptionsPerUser": 1,
"totalVotesPerUser": 1,
"votesPerOption": 1,
"global": false,
"disabled": false,
"prompt": "Choose one",
"options": ["Yes", "No"],
"userData": {
"round": 1
},
"startsAt": 1720000000,
"endsAt": 1720003600
}
}
Configuration limits enforced by the handler are:
iddefaults todefault, is at most 64 characters, and cannot contain.. Ifglobalis true,idmust begin withglobal.distinctOptionsPerUseris1..258;totalVotesPerUseris1..1024;votesPerOptionmust be greater than0. All three default to1when omitted.optionshas at most 32 strings, each at most 128 bytes.promptis at most 256 bytes, and encodeduserDatais at most 1024 bytes.startsAtandendsAtare Unix seconds. When both are treated as set,startsAtcannot be afterendsAt.
Success returns {}. Invalid bounds, ID/global mismatches, closed-time ordering, and oversized fields return 400 with a reason; disallowed roles receive 403; configuration storage failures return 500.