Template gallery / ballot-and-proxy

HOA ballot and proxy template

Official ballot with a detachable proxy, drawn for a ballpoint on a folding table: 20px vote boxes at a 1.5pt rule, numbered voting instructions in two columns, a candidate row per nominee with their own statement, full-width write-in lines, a questions table with the passing threshold printed under each question, an owner certification, and a proxy behind a tear line with quorum-only, procedural and general options.

chromium default PDF 16 top-level fields Billed per page

Rendered preview of the ballot-and-proxy template
Rendered by Galley from the example payload below, at 816px wide , scale 2.

Fields

The full JSON Schema is below. Required fields are marked; anything missing comes back as a 422 naming the path, the expected type and a value that would be accepted.

Field Type Required Description
accent string optional The association's navy. The ballot's rules and vote boxes stay black ink and the brick red is reserved for the return deadline, so on a plain ballot this colour never appears. A hex colour or a CSS colour keyword. It is interpolated into the stylesheet, so nothing else is accepted.
inspector object { name, title } optional Who opens and counts the ballots, named under the return instructions. A ballot that does not say who counts it is the one owners write to the Board about.
association object { name, logo_url } required
election object { kind, date, term, time } required
instructions array of string required Numbered voting instructions in the box at the top. Say what makes a ballot spoil.
seats integer optional How many directors may be voted for. Printed in brick red beside the election heading.
overvote_note string optional
candidates array of object { name, unit, incumbent, since, … } optional One checkbox row each, in the order you want them printed.
write_ins integer optional How many blank write-in lines to print under the candidates.
options array of string optional Column headings on the questions table.
measures array of object { number, title, text, threshold } optional Questions put to the membership.
certification string optional Replaces the default owner certification wording.
return_by string (date) required
return_to string required
secrecy string optional The double-envelope or secret-ballot note.
proxy object { instructions, allow_general, return_to, deadline } optional The detachable proxy on the second page.

Default render options

Applied unless you override them. Options are part of the cache key.

page_size "Letter"
margin {"top":"0.6in","right":"0.6in","bottom":"0.85in","left":"0.6in"}
print_background true

Example payload

This exact object renders the preview above. It is also what the API echoes back in a validation error, so an agent can repair a payload without a round trip to the docs.

example.jsonjson
{
  "association": {
    "name": "Heron Bay Homeowners Association, Inc."
  },
  "election": {
    "kind": "Annual Meeting",
    "date": "2026-12-01",
    "term": "three-year",
    "time": "polls close when the meeting is called to order at 7:00 p.m."
  },
  "inspector": {
    "name": "Harwood Election Services",
    "title": "inspector of elections, appointed by the Board on October 13, 2026"
  },
  "instructions": [
    "Print your name and your unit or lot exactly as they appear on the deed, at the top of this ballot.",
    "Mark your choices with an X or a filled box. Do not initial beside a choice and do not write on the ballot outside the boxes and write-in lines.",
    "Vote for no more than three candidates. A ballot marked for four or more is void as to the election; the questions below are still counted.",
    "Sign and date the certification at the bottom. An unsigned ballot cannot be counted.",
    "One ballot per unit. If a unit is owned by more than one person, any one owner may sign."
  ],
  "seats": 3,
  "overvote_note": "A ballot marked for more than three candidates is void as to the election and is still counted on the questions below.",
  "candidates": [
    {
      "name": "Daniel Voss",
      "unit": "1418 Marsh Wren Drive",
      "incumbent": true,
      "since": "2023",
      "statement": "I have chaired the landscape committee for two years. I want to finish the drainage work on the north end before we spend reserve money on paving over it."
    },
    {
      "name": "Ana Alvarez",
      "unit": "1502 Tern Court",
      "incumbent": true,
      "since": "2023",
      "statement": "As Treasurer I rebuilt the reserve schedule from the 2024 study and closed the year under budget twice. I would like to see the 2027 budget through adoption."
    },
    {
      "name": "Lucia Marchetti",
      "unit": "1377 Egret Landing",
      "statement": "Twelve years in commercial property management. I would push for an online owner portal so applications and work orders stop living in three inboxes."
    },
    {
      "name": "Greg Sandoval",
      "unit": "1290 Egret Landing",
      "statement": "I have served on the architectural committee since 2025. My priority is publishing plain-language design guidelines so owners know the answer before they apply."
    },
    {
      "name": "Yusuf Demir",
      "unit": "1461 Tern Court",
      "statement": "Retired civil engineer. I would like the Association to get a second opinion on the swale and stormwater easement before the next paving cycle."
    }
  ],
  "write_ins": 2,
  "options": [
    "For",
    "Against",
    "Abstain"
  ],
  "measures": [
    {
      "number": "1",
      "title": "Waive the reserve study update for fiscal year 2027",
      "text": "A waiver defers the professional reserve study by one year, to fiscal year 2028. The reserve funding in the adopted 2027 budget is $68,400 either way.",
      "threshold": "a majority of the voting interests present in person or by proxy"
    },
    {
      "number": "2",
      "title": "Approve the clubhouse kitchen refurbishment as a capital project not to exceed $46,000",
      "text": "Funded from the clubhouse reserve component, which held $71,200 at September 30, 2026. Scope is cabinets, countertops, one commercial-rated range and the associated electrical.",
      "threshold": "a majority of the voting interests present in person or by proxy"
    },
    {
      "number": "3",
      "title": "Amend Article VII of the Declaration to allow composite fencing in the approved colour palette",
      "text": "The Declaration currently permits wood fencing only. The amendment would add composite fencing in the same three approved colours, with the same six-foot height limit.",
      "threshold": "67 percent of all voting interests, whether or not present"
    }
  ],
  "return_by": "2026-11-30",
  "return_to": "Seal this ballot in the inner envelope, place that envelope inside the outer envelope, and sign the outer envelope. Mail… [262 characters, shortened for display]",
  "secrecy": "Ballots are opened only at the meeting, by the inspector of elections, after the outer envelopes have been checked against the roster of owners in good standing. The inner envelope is never marked and is never matched to a name.",
  "proxy": {
    "allow_general": true,
    "return_to": "Return the proxy to: Secretary, Heron Bay HOA, PO Box 4127, Wilmington, NC 28406, or hand it to the sign-in table before 7:00 p.m.",
    "deadline": "2026-11-30"
  }
}

Values longer than 240 characters — data URIs, inline SVG — are shortened here so the shape of the payload stays readable, and the replacement says how much was left out. get_template and GET /v1/templates/:ref return them in full.

Render it

With an API keyshell
curl -sS https://api.galleyrender.com/v1/render \
  -H "Authorization: Bearer $GALLEY_API_KEY" \
  -H 'content-type: application/json' \
  -d '{
  "template": "ballot-and-proxy@1",
  "format": "pdf",
  "data": "… the example payload above …"
}'
With no API key, over MCPshell
# No API key. The first call mints a trial of 10 PDF pages or 10 images.
curl -sS https://mcp.galleyrender.com/mcp \
  -H 'content-type: application/json' \
  -H 'accept: application/json, text/event-stream' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "render",
      "arguments": {
        "template": "ballot-and-proxy",
        "format": "pdf",
        "data": { … see the example payload above … }
      }
    }
  }'

Pin the version — ballot-and-proxy@1 — in anything you ship. Publishing a new version never changes an old one. Render endpoint reference →

Try a free tool

Renders the example payload on the production service. No account, no key.

JSON Schema

schema.json (16 top-level properties)
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Official ballot and proxy",
  "type": "object",
  "required": [
    "association",
    "election",
    "instructions",
    "return_by",
    "return_to"
  ],
  "additionalProperties": true,
  "properties": {
    "accent": {
      "type": "string",
      "description": "The association's navy. The ballot's rules and vote boxes stay black ink and the brick red is reserved for the return deadline, so on a plain ballot this colour never appears. A hex colour or a CSS colour keyword. It is interpolated into the stylesheet, so nothing else is accepted.",
      "default": "#1c3f60",
      "examples": [
        "#1c3f60"
      ],
      "pattern": "^(#[0-9a-fA-F]{3,8}|[a-zA-Z]{3,20})$"
    },
    "inspector": {
      "type": "object",
      "description": "Who opens and counts the ballots, named under the return instructions. A ballot that does not say who counts it is the one owners write to the Board about.",
      "properties": {
        "name": {
          "type": "string",
          "examples": [
            "Harwood Election Services"
          ]
        },
        "title": {
          "type": "string",
          "examples": [
            "inspector of elections, appointed by the Board on October 13, 2026"
          ]
        }
      }
    },
    "association": {
      "type": "object",
      "required": [
        "name"
      ],
      "properties": {
        "name": {
          "type": "string",
          "examples": [
            "Heron Bay Homeowners Association, Inc."
          ]
        },
        "logo_url": {
          "type": "string",
          "format": "uri",
          "examples": [
            "https://example.com/heron-bay.png"
          ]
        }
      }
    },
    "election": {
      "type": "object",
      "required": [
        "date"
      ],
      "properties": {
        "kind": {
          "type": "string",
          "default": "Annual Meeting",
          "examples": [
            "Annual Meeting"
          ]
        },
        "date": {
          "type": "string",
          "format": "date",
          "examples": [
            "2026-12-01"
          ]
        },
        "term": {
          "type": "string",
          "description": "Length of the term each seat carries.",
          "examples": [
            "three-year"
          ]
        },
        "time": {
          "type": "string",
          "description": "Printed after the date in the sub-title, so an owner returning a ballot by hand knows when the polls close.",
          "examples": [
            "7:00 p.m."
          ]
        }
      }
    },
    "instructions": {
      "type": "array",
      "minItems": 1,
      "description": "Numbered voting instructions in the box at the top. Say what makes a ballot spoil.",
      "items": {
        "type": "string",
        "examples": [
          "Mark your choices with an X or a filled box. Do not initial beside a choice."
        ]
      },
      "examples": [
        [
          "Mark your choices with an X or a filled box."
        ]
      ]
    },
    "seats": {
      "type": "integer",
      "minimum": 1,
      "description": "How many directors may be voted for. Printed in brick red beside the election heading.",
      "examples": [
        3
      ]
    },
    "overvote_note": {
      "type": "string",
      "examples": [
        "A ballot marked for more than three candidates is void as to the election and is still counted on the questions below."
      ]
    },
    "candidates": {
      "type": "array",
      "description": "One checkbox row each, in the order you want them printed.",
      "items": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "examples": [
              "Daniel Voss"
            ]
          },
          "unit": {
            "type": "string",
            "examples": [
              "1418 Marsh Wren Drive"
            ]
          },
          "incumbent": {
            "type": "boolean",
            "default": false,
            "examples": [
              true
            ]
          },
          "since": {
            "type": "string",
            "description": "Year first seated, shown beside \"incumbent\".",
            "examples": [
              "2023"
            ]
          },
          "statement": {
            "type": "string",
            "description": "The candidate's own statement, printed as submitted.",
            "examples": [
              "I have chaired the landscape committee for two years and want to finish the drainage work on the north end before we pave."
            ]
          }
        }
      }
    },
    "write_ins": {
      "type": "integer",
      "minimum": 0,
      "maximum": 10,
      "description": "How many blank write-in lines to print under the candidates.",
      "examples": [
        2
      ]
    },
    "options": {
      "type": "array",
      "description": "Column headings on the questions table.",
      "default": [
        "For",
        "Against",
        "Abstain"
      ],
      "items": {
        "type": "string",
        "examples": [
          "For"
        ]
      },
      "examples": [
        [
          "For",
          "Against",
          "Abstain"
        ]
      ]
    },
    "measures": {
      "type": "array",
      "description": "Questions put to the membership.",
      "items": {
        "type": "object",
        "required": [
          "title"
        ],
        "properties": {
          "number": {
            "type": "string",
            "description": "Defaults to its position in the list.",
            "examples": [
              "1"
            ]
          },
          "title": {
            "type": "string",
            "examples": [
              "Waive the reserve study update for fiscal year 2027"
            ]
          },
          "text": {
            "type": "string",
            "examples": [
              "A waiver defers the professional reserve study by one year. The reserve funding in the adopted 2027 budget is unchanged either way."
            ]
          },
          "threshold": {
            "type": "string",
            "description": "What it takes to pass, printed under the question.",
            "examples": [
              "a majority of the voting interests present in person or by proxy"
            ]
          }
        }
      }
    },
    "certification": {
      "type": "string",
      "description": "Replaces the default owner certification wording.",
      "examples": [
        "I certify that I am an owner of record of the unit named above and am entitled to cast this ballot."
      ]
    },
    "return_by": {
      "type": "string",
      "format": "date",
      "examples": [
        "2026-11-30"
      ]
    },
    "return_to": {
      "type": "string",
      "examples": [
        "Seal this ballot in the inner envelope, put that envelope inside the outer envelope, and sign the outer envelope. Mail to: Inspector of Elections, Heron Bay HOA, PO Box 4127, Wilmington, NC 28406, or hand it to the sign-in table before 7:00 p.m."
      ]
    },
    "secrecy": {
      "type": "string",
      "description": "The double-envelope or secret-ballot note.",
      "examples": [
        "Ballots are opened only at the meeting, by the inspector of elections, after the outer envelopes have been checked against the roster. The inner envelope is never marked."
      ]
    },
    "proxy": {
      "type": "object",
      "description": "The detachable proxy on the second page.",
      "properties": {
        "instructions": {
          "type": "string",
          "examples": [
            "A proxy counts your unit toward the quorum when you cannot attend. It is not a ballot."
          ]
        },
        "allow_general": {
          "type": "boolean",
          "default": false,
          "description": "Adds a general-proxy option beside quorum-only and procedural.",
          "examples": [
            true
          ]
        },
        "return_to": {
          "type": "string",
          "description": "Defaults to the ballot's return instructions.",
          "examples": [
            "Return the proxy to the Secretary, or hand it to the sign-in table before 7:00 p.m."
          ]
        },
        "deadline": {
          "type": "string",
          "format": "date",
          "examples": [
            "2026-11-30"
          ]
        }
      }
    }
  }
}