Turns JSON absentee ballot requests into PDFs and delivers them to registrars.
PHP Other
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


Horatio: Absentee Ballot Server

A server for an absentee ballot request form, with a corresponding client. Somebody who wants an absentee ballot can complete a web form, and Horatio will use those values to complete the state-sanctioned PDF and email it to the appropriate registrar. The creation of Horatio was funded by Aneesh Chopra’s Innovate Virginia Fund PAC. It is named for Horatio Seymour, the former governor of New York, who was an ardent opponent of President Abraham Lincoln’s creation of an absentee balloting system to allow deployed Union soldiers to vote in the 1864 election.

Absentee ballot request forms may be submitted electronically in Virginia under the authority of attorney general opinion #13-111 and approval of the State Board of Elections. (p. 7, lines 189–215).


Fundamentally, this is a system to:

  • validate provided JSON against a schema
  • use the JSON contents to populate a PDF
  • send the contents of that PDF to a geographically-appropriate email and, if the email bounces, send it to a fallback address

Which is to say that it's not very complicated.

First, somebody completes the HTML form produced by the Horatio client:


The contents of the form are converted to JSON and submitted to the Horatio server:


Horatio maps the JSON values to PDF form fields:


And, finally, it emails that PDF to the appropriate registrar.

System Requirements

  • PHP. Horatio has only been tested on Linux with Apache, but it may well work on Windows or with Nginx.
  • SSL (TLS). Horatio will function over HTTP when it’s in debug mode, but to actually put it to work, it requires that the connection be encrypted, via HTTPS.


  1. Download and install the api/ directory onto a web server. This may be at the root of a domain name, or within a subdirectory.
  2. Install Composer in the directory (e.g., curl -sS https://getcomposer.org/installer | php).
  3. Auto-install source libraries (JSON Schema, Mailgun, and Guzzle) with php composer.phar install.
  4. Set up an account with Mailgun (no credit card number required; <10,000 emails/month is free), following their instructions to get SPF records added to DNS for the domain.
  5. Choose "Webhooks" from the Mailgun dashboard, and for both "Hard bounces" and "Dropped messages," enter your site’s URL followed by /bounce/?key= and a 32-character random string, e.g. http://example.com/bounce/?key=qTugfIdCvB9SjymJW5yqQUofQu9iU119. Keep a copy of this 32-character string until the next step, where it’s the value of BOUNCE_API_KEY.
  6. Configure the site settings in includes/settings.inc.php.
  7. Ensure that the directory applications/ has write permissions for the web server, but not read permissions (i.e., drwx-wx-wx), because that is where the completed absentee ballot requests will be stored.


Horatio was created by Waldo Jaquith and funded by the Innovate Virginia Fund PAC.