A ballot records one voter's vote in one election, including the candidate they chose for each position and their yes-or-no answer to each measure. You cannot record or manage ballots in the Console or Portal. Ballots are available only through the Rhythm API, and members cannot cast ballots for themselves in Rhythm today. An integration or staff member is responsible for recording each ballot.
Prerequisites
- You are signed in as staff of your Rhythm account, or you are calling as an integration with that access. A Portal member sign-in is refused.
- The election and voter contact must already exist. Any position or candidate you name on the ballot must exist too.
Routes
| Method and Path | Purpose |
|---|---|
POST /ballots/{tenantId} | Record a ballot |
GET /ballots/{tenantId}/{id} | Read one ballot |
PUT /ballots/{tenantId}/{id} | Replace a ballot's choices and answers |
PATCH /ballots/{tenantId}/{id} | Change part of a ballot's choices and answers |
DELETE /ballots/{tenantId}/{id} | Delete a ballot |
POST /ballots/{tenantId}/batch | Record, read, change, or delete many ballots in one request |
Recording a Ballot
Send the election, the voter, and any candidate choices, measure answers, or notes you want to include.
| Field | Required | Notes |
|---|---|---|
election_id | Yes | The election the ballot belongs to. |
voter_id | Yes | The contact who voted. |
candidates[].position_id, candidates[].candidate_id | No | Add one entry for each position where the voter chose a candidate. |
measures[].measure_id, measures[].response | No | Add one entry for each measure. response must be yes or no. |
notes | No | Free text. |
Rhythm accepts a ballot even when it has no candidate choices or measure answers.
The Ballot's Identifier
A ballot's identifier is always <election_id>:<voter_id>, such as NEB-2026:CCEC2. If you send an identifier, it must match that exact combination, or Rhythm refuses the save. If you omit it, Rhythm builds it for you. The combined identifier may contain only letters, digits, underscores, colons, pipes, and hyphens, so Rhythm cannot record a ballot when the election or voter identifier uses any other character.
One Ballot per Voter per Election
A voter can have only one ballot in a given election. If you try to record a second ballot for the same election and voter, Rhythm refuses it and leaves the first ballot unchanged. This rule applies within one election, so the same voter can have ballots in several different elections. If a voter needs to change their vote, correct the existing ballot's choices instead of recording a second one, or delete the existing ballot first and record a new one.
Rhythm does not compare the ballot against its election when you record it. It does not check the election's status, dates, or the voter's eligibility. Rhythm accepts a ballot whether the election is open, closed, or already certified.
Reading a Ballot
GET /ballots/{tenantId}/{id} returns one ballot's full record. You cannot list ballots by election, by voter, or in any other way, so you must already know the combined <election_id>:<voter_id> identifier. If a voter has not voted in that election, there is no ballot at that identifier, and the read returns not-found.
Correcting or Removing a Ballot
PUT and PATCH change a ballot's candidate choices, measure answers, and notes. You cannot change the election or voter named on a ballot. The election's status and dates do not limit corrections, so you can correct a ballot after voting closes and after the election is certified.
DELETE removes a ballot outright, which lets that voter have a new ballot recorded in the same election. Deletion is permanent. As with any delete, a ballot that was just removed may briefly be locked against another delete attempt.
Batch Requests
POST /ballots/{tenantId}/batch accepts {"operations": [...]}. Each entry names its kind, such as create, read, update, patch, or delete, and includes the identifier, ballot, or patch instructions that operation needs. Every operation runs through the same checks as a single request. The response reports success_count, error_count, and a results array with one outcome per operation, so one failed ballot does not stop the rest.
Warning
The same identifier cannot appear twice in one batch. For ballots, that means the same voter cannot appear twice for the same election in a single request. A batch with a duplicate fails entirely, and none of its operations are applied. There is no enforced maximum size for a batch, but a very large batch can time out before it finishes.
Errors
| Response | Cause |
|---|---|
400 … The id field must be a combination of the election_id and voter_id | The identifier you sent does not match <election_id>:<voter_id>, or you tried to change the election or voter on an existing ballot. |
400 naming response | A measure answer is something other than yes or no. |
409 ForeignKeyViolation | The election, voter, named position, or named candidate does not exist. |
| A refusal because a ballot already exists | You tried to record a second ballot for the same voter in the same election. |
404 | The ballot identified in the path does not exist. |
500 … Duplicate IDs were found in the request | A batch request named the same identifier more than once. |
Related
- Create and Maintain Elections Through the API
- Look Up Elections Through the API
- How Elections and Ballots Work