{"openapi":"3.1.0","info":{"title":"poll.trq.lol API","version":"1.0.0","description":"Open, read-only poll data plus an authenticated vote endpoint. Aggregates only.","license":{"name":"CC0-1.0","url":"https://creativecommons.org/publicdomain/zero/1.0/"}},"servers":[{"url":"https://poll.trq.lol/api/v1","description":"Production"}],"tags":[{"name":"Polls","description":"Community polls and their aggregate results."}],"paths":{"/polls":{"get":{"operationId":"listPolls","summary":"List polls","description":"A page of polls with their aggregate tallies. Filterable by status and vote type, sortable by creation time, and paginated. Returns `{ data, page }`.","tags":["Polls"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Page size, 1–100.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Items to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"status","in":"query","required":false,"description":"Filter by poll status.","schema":{"type":"string","enum":["open","closed"]}},{"name":"voteType","in":"query","required":false,"description":"Filter by ballot type.","schema":{"type":"string","enum":["single","multiple"]}},{"name":"sort","in":"query","required":false,"description":"Order by creation time.","schema":{"type":"string","enum":["newest","oldest"],"default":"newest"}}],"responses":{"200":{"description":"A page of polls."},"400":{"description":"Invalid query parameter (`INVALID_QUERY`)."},"429":{"description":"Rate limited (`RATE_LIMITED`)."},"503":{"description":"Data layer unavailable."}}}},"/polls/{slug}":{"get":{"operationId":"getPoll","summary":"Get a poll","description":"One poll with its aggregate tally.","tags":["Polls"],"parameters":[{"name":"slug","in":"path","required":true,"description":"The poll slug.","schema":{"type":"string"}}],"responses":{"200":{"description":"The poll."},"404":{"description":"No such poll (`POLL_NOT_FOUND`)."},"429":{"description":"Rate limited."},"503":{"description":"Data layer unavailable."}}}},"/polls/{slug}/export":{"get":{"operationId":"exportPoll","summary":"Export a poll (CSV)","description":"The poll’s aggregate tally as a CSV download. Aggregate rows only; no voter identity.","tags":["Polls"],"parameters":[{"name":"slug","in":"path","required":true,"description":"The poll slug.","schema":{"type":"string"}}],"responses":{"200":{"description":"CSV body (`text/csv`)."},"404":{"description":"No such poll."},"429":{"description":"Rate limited."}}}},"/polls/{slug}/timeline":{"get":{"operationId":"getTimeline","summary":"Get a poll timeline","description":"Turnout over time as a cumulative series. Only for polls opted into the chart; others 404.","tags":["Polls"],"parameters":[{"name":"slug","in":"path","required":true,"description":"The poll slug.","schema":{"type":"string"}}],"responses":{"200":{"description":"The timeline series."},"404":{"description":"No timeline for this poll (`NO_TIMELINE`)."},"429":{"description":"Rate limited."}}}},"/polls/{slug}/vote":{"post":{"operationId":"castVote","summary":"Cast or change a ballot","description":"Records the signed-in user’s ballot. Same-origin + Discord session required. Idempotent per account via a unique `(poll, user)` index; a re-vote overwrites, so no Idempotency-Key is needed.","tags":["Polls"],"parameters":[{"name":"slug","in":"path","required":true,"description":"The poll slug.","schema":{"type":"string"}}],"responses":{"200":{"description":"Ballot recorded."},"400":{"description":"Bad selection (`INVALID_SELECTION`)."},"401":{"description":"Not signed in (`UNAUTHENTICATED`)."},"403":{"description":"Not eligible to vote (`NOT_ELIGIBLE`)."},"404":{"description":"No such poll."},"409":{"description":"Poll is closed (`POLL_CLOSED`)."},"413":{"description":"Body too large (`PAYLOAD_TOO_LARGE`)."},"429":{"description":"Rate limited."}},"security":[{"sessionCookie":[]}]}}},"components":{"securitySchemes":{"sessionCookie":{"type":"apiKey","in":"cookie","name":"better-auth.session_token"}}}}