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
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.
{
"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
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 …"
}' # 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 →
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"
]
}
}
}
}
}