CrownVote Developer API v1

Build custom election experiences with CrownVote.

Use CrownVote API Voting to build your own frontend while CrownVote handles election scoping, voter authentication, ballot delivery, encrypted vote submission, duplicate prevention, and result integrity.

Want the fastest path?

Follow the start guide to build a minimal remote voting app with HTML, CSS, and JavaScript.

Quick start

All API examples in this documentation use the v1 base URL.

https://api.crownvote.com/api/v1

Public key

Public API requests are scoped to an election using the X-PUBLIC-KEY header.

code GET /election
GET /api/v1/election HTTP/1.1
Host: api.crownvote.com
X-PUBLIC-KEY: cv_pub_xxxxx

Voter bearer token

Voter login returns an access token. Use it on protected voter routes with the Authorization header.

code POST /voter/login
POST /api/v1/voter/login HTTP/1.1
Host: api.crownvote.com
Content-Type: application/json
X-PUBLIC-KEY: cv_pub_xxxxx

{
  "voterId": "236165",
  "password": "voter-password"
}

Admin session token

In-person elections can require an admin session. When enabled, send the returned admin token as X-ADMIN-TOKEN.

Get the public election configuration

Before showing login screens, resolve the election attached to the public key. The response tells your frontend the election status, voting mode, access mode, instructions, schedule, and in-person requirements.

code Remote election response
{
  "status": true,
  "data": {
    "election": {
      "title": "SRC General Elections 2026",
      "institution": "CrownVote Demo Institution",
      "description": "Official voting event for the SRC General Elections.",
      "instructions": "Login with your voter ID and password to cast your vote.",
      "status": "live",
      "startsAt": "2026-06-01T08:00:00.000Z",
      "endsAt": "2026-06-01T17:00:00.000Z",
      "votingMode": "remote",
      "accessMode": "api",
      "inPersonRequireAdminLogin": null,
      "inPersonRequireVoterPassword": null
    }
  }
}
code In-person election response
{
  "status": true,
  "data": {
    "election": {
      "title": "SRC General Elections 2026",
      "institution": "CrownVote Demo Institution",
      "description": "Official in-person voting session.",
      "instructions": "Present your voting token to the polling officer.",
      "status": "live",
      "startsAt": "2026-06-01T08:00:00.000Z",
      "endsAt": "2026-06-01T17:00:00.000Z",
      "votingMode": "in_person",
      "accessMode": "api",
      "inPersonRequireAdminLogin": true,
      "inPersonRequireVoterPassword": false
    }
  }
}
Field
Type
Description
titlestringPublic title of the election.
institutionstring | nullInstitution or organization running the election.
descriptionstring | nullPublic election description.
instructionsstring | nullInstructions shown to voters before or during voting.
statusstringCurrent election status.
startsAtstring | nullElection start date/time.
endsAtstring | nullElection end date/time.
votingModeremote | in_personDetermines whether the election is remote or in-person.
accessModeportal | apiDetermines whether the election is accessed through CrownVote portal or API.
inPersonRequireAdminLoginboolean | nullFor in-person elections only. Null for remote elections.
inPersonRequireVoterPasswordboolean | nullFor in-person elections only. Null for remote elections.

Login returns the ballot payload

After voter login, you do not need a second request to render the ballot. The response includes the voter profile, ballot categories, candidates, and encrypted vote values.

code Voter login response
{
  "status": true,
  "data": {
    "accessToken": {
      "type": "Bearer",
      "value": "oat_xxxxx",
      "expiresAt": null
    },
    "voter": {
      "voterId": "236165",
      "displayName": "Voter 1",
      "status": "active",
      "hasVoted": false
    },
    "ballots": [
      {
        "ballotId": "70f74ae6-fb73-41e9-87e0-ece419585ed5",
        "ballotPosition": 1,
        "title": "PRESIDENT",
        "description": null,
        "instructions": "Select one candidate.",
        "minChoices": 1,
        "maxChoices": 1,
        "candidates": [
          {
            "name": "Kofi Angel",
            "image": "https://cdn-f.crownvote.com/elections/...",
            "bio": null,
            "manifesto": null,
            "vote": "encrypted_vote_value"
          }
        ]
      }
    ]
  }
}