Template gallery / event-schedule
Event schedule template
A whole day on one sheet: tracks as columns with their own width, time down the left in monospace, every session placed on a CSS grid from its start and end in minutes past midnight. Registration, lunch and the reception run across every track as solid black bands, a legend explains the left edges, and `row_height` is the one dial that decides whether the day fills the sheet.
chromium default PDF 8 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 one spot colour, used on the middots in the day line. Column heads and bands are black; the sheet carries no other hue. A hex colour or a CSS colour keyword. It is interpolated into the stylesheet, so nothing else is accepted. |
| event | object { name, venue, website, hashtag, … } | required | |
| day | object { title, date } | required | |
| grid | object { start_minutes, end_minutes, slot_minutes, row_height, … } | required | The vertical axis, in minutes past midnight. Everything is placed against it, so a session outside the range does not print where you expect. |
| tracks | array of object { name, room, width, sessions } | required | One column each, left to right, each with its own sessions. |
| all_day | array of object { title, start_minutes, end_minutes, detail, … } | optional | Anything that spans every track, such as registration, lunch and the reception. Drawn as a solid band reversed out of black across the full width, which is the only filled shape on the sheet. |
| legend | array of object { label, color } | optional | Swatches under the header, matching the left-edge colours you used on sessions. Three entries is usually the whole story; a legend longer than the schedule is a sign the colours are doing too much. |
| notes | array of object { title, text } | optional | The anchored footer strip, split into equal columns. It is what stops the sheet ending in white. |
Default render options
Applied unless you override them. Options are part of the cache key.
| page_size | "Letter" |
| margin | "0.45in" |
| 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.
{
"accent": "#a4133c",
"event": {
"name": "Tidewater Type & Letterpress Weekend",
"venue": "The Cotton Exchange, Wilmington NC",
"website": "tidewatertype.example.test",
"hashtag": "#TidewaterType26",
"wifi": "ExchangeGuest / press2026"
},
"day": {
"title": "Day one — Friday",
"date": "2026-10-16"
},
"grid": {
"start_minutes": 480,
"end_minutes": 1140,
"slot_minutes": 15,
"row_height": 17,
"clock": "12h"
},
"legend": [
{
"label": "Hands-on workshop",
"color": "#a4133c"
},
{
"label": "Talk or demonstration",
"color": "#111111"
},
{
"label": "Open floor, no sign-up",
"color": "#8a8580"
}
],
"all_day": [
{
"title": "Registration and press check-in",
"start_minutes": 495,
"end_minutes": 540,
"detail": "Atrium · red lanyards are volunteers, interrupt them"
},
{
"title": "Lunch, included, in the Courtyard",
"start_minutes": 720,
"end_minutes": 780,
"detail": "Vegetarian and gluten-free trays at the far end"
},
{
"title": "Guild reception and the annual swap table",
"start_minutes": 1050,
"end_minutes": 1140,
"detail": "Courtyard · bring type you will never use"
}
],
"tracks": [
{
"name": "Main Hall",
"room": "Ground floor",
"width": 1.15,
"sessions": [
{
"title": "Welcome and Guild notices",
"start_minutes": 540,
"end_minutes": 555,
"speaker": "Harriet Okonjo-Bell",
"color": "#111111"
},
{
"title": "Keynote: Punchcutting is just slow",
"start_minutes": 555,
"end_minutes": 615,
"speaker": "Dr. Emine Yalcin",
"note": "CAPTIONED LIVE",
"color": "#a4133c"
},
{
"title": "Restoring a barn-find treadle press",
"start_minutes": 630,
"end_minutes": 705,
"speaker": "Ruben Castellanos",
"color": "#111111"
},
{
"title": "The specimen book, read properly",
"start_minutes": 795,
"end_minutes": 870,
"speaker": "Dr. Emine Yalcin",
"color": "#111111"
},
{
"title": "Open floor: bring a problem print",
"start_minutes": 885,
"end_minutes": 960,
"speaker": "Six people who did it wrong first",
"color": "#8a8580"
},
{
"title": "Guild annual general meeting",
"start_minutes": 975,
"end_minutes": 1035,
"speaker": "Members only",
"color": "#8a8580"
}
]
},
{
"name": "Floor",
"room": "Presses 1–3",
"sessions": [
{
"title": "Make-ready on a Vandercook, properly",
"start_minutes": 555,
"end_minutes": 660,
"speaker": "Ruben Castellanos",
"note": "PRESS 3 · 12 PLACES",
"color": "#a4133c"
},
{
"title": "Inking and washing up",
"start_minutes": 675,
"end_minutes": 720,
"speaker": "Priya Raman",
"note": "BENCH 2",
"color": "#a4133c"
},
{
"title": "Polymer plates and metal type in the same forme",
"start_minutes": 795,
"end_minutes": 885,
"speaker": "Castellanos and Mwangi",
"note": "PRESS 1",
"color": "#111111"
},
{
"title": "Wood type: rescue and repair",
"start_minutes": 900,
"end_minutes": 990,
"speaker": "Anders Lindqvist",
"note": "BENCH 4",
"color": "#111111"
},
{
"title": "Press wash-down and lock-up",
"start_minutes": 1005,
"end_minutes": 1050,
"speaker": "All press stewards",
"color": "#8a8580"
}
]
},
{
"name": "Long Room",
"room": "Type cabinets",
"sessions": [
{
"title": "Paper for letterpress: what damp does",
"start_minutes": 555,
"end_minutes": 645,
"speaker": "Ingrid Solheim",
"color": "#111111"
},
{
"title": "Setting a page of metal type",
"start_minutes": 660,
"end_minutes": 720,
"speaker": "Tobias Mwangi",
"note": "16 PLACES",
"color": "#a4133c"
},
{
"title": "Setting the 2026 keepsake",
"start_minutes": 795,
"end_minutes": 915,
"speaker": "Tobias Mwangi, and anyone passing",
"note": "OPEN · NO SIGN-UP",
"color": "#a4133c"
},
{
"title": "Distribution: putting the case back",
"start_minutes": 930,
"end_minutes": 1005,
"speaker": "Curtis Nakamura",
"color": "#8a8580"
}
]
},
{
"name": "Mezzanine",
"room": "Stairs or service lift",
"sessions": [
{
"title": "Binding a short run",
"start_minutes": 555,
"end_minutes": 660,
"speaker": "Marguerite Delacroix-Hunt",
"note": "18 PLACES",
"color": "#a4133c"
},
{
"title": "Running a press as a business",
"start_minutes": 675,
"end_minutes": 720,
"speaker": "Delphine Aubert",
"color": "#111111"
},
{
"title": "The edition of forty",
"start_minutes": 795,
"end_minutes": 870,
"speaker": "Delphine Aubert",
"color": "#111111"
},
{
"title": "Selling the printed result",
"start_minutes": 885,
"end_minutes": 945,
"speaker": "Simone Eberhardt",
"color": "#111111"
},
{
"title": "Committee surgery",
"start_minutes": 960,
"end_minutes": 1035,
"speaker": "Okonjo-Bell, chairing",
"color": "#8a8580"
}
]
}
],
"notes": [
{
"title": "On the floor.",
"text": "Closed-toe shoes at the presses, no exceptions. Aprons and the washing-up station are by the freight door."
},
{
"title": "Type cabinets.",
"text": "Open all weekend. Sorts go back in the case they came from; the Long Room is not a sorting problem for Sunday."
},
{
"title": "Changes.",
"text": "Anything that moves is posted at the registration desk and on the board by the Main Hall door."
},
{
"title": "Access.",
"text": "Atrium, Main Hall, Long Room and Courtyard are step-free. Ask at the desk for the Mezzanine lift."
}
]
} Render it
curl -sS https://api.galleyrender.com/v1/render \
-H "Authorization: Bearer $GALLEY_API_KEY" \
-H 'content-type: application/json' \
-d '{
"template": "event-schedule@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": "event-schedule",
"format": "pdf",
"data": { … see the example payload above … }
}
}
}'
Pin the version — event-schedule@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 (8 top-level properties)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Event schedule grid",
"type": "object",
"required": [
"event",
"day",
"grid",
"tracks"
],
"additionalProperties": true,
"properties": {
"accent": {
"type": "string",
"description": "The one spot colour, used on the middots in the day line. Column heads and bands are black; the sheet carries no other hue. A hex colour or a CSS colour keyword. It is interpolated into the stylesheet, so nothing else is accepted.",
"examples": [
"#a4133c"
],
"pattern": "^(#[0-9a-fA-F]{3,8}|[a-zA-Z]{3,20})$"
},
"event": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"examples": [
"Tidewater Type & Letterpress Weekend"
]
},
"venue": {
"type": "string",
"examples": [
"The Cotton Exchange, Wilmington NC"
]
},
"website": {
"type": "string",
"examples": [
"tidewatertype.example.test"
]
},
"hashtag": {
"type": "string",
"examples": [
"#TidewaterType26"
]
},
"wifi": {
"type": "string",
"examples": [
"ExchangeGuest / press2026"
]
},
"logo_url": {
"type": "string",
"format": "uri",
"examples": [
"https://example.com/guild.png"
]
}
}
},
"day": {
"type": "object",
"required": [
"title"
],
"properties": {
"title": {
"type": "string",
"examples": [
"Day one — Friday"
]
},
"date": {
"type": "string",
"format": "date",
"examples": [
"2026-10-16"
]
}
}
},
"grid": {
"type": "object",
"required": [
"start_minutes",
"end_minutes"
],
"description": "The vertical axis, in minutes past midnight. Everything is placed against it, so a session outside the range does not print where you expect.",
"properties": {
"start_minutes": {
"type": "integer",
"minimum": 0,
"maximum": 1439,
"description": "Top of the grid. 8:00 a.m. is 480.",
"examples": [
480
]
},
"end_minutes": {
"type": "integer",
"minimum": 1,
"maximum": 1560,
"description": "Bottom of the grid. 6:00 p.m. is 1080.",
"examples": [
1080
]
},
"slot_minutes": {
"type": "integer",
"minimum": 5,
"maximum": 60,
"default": 15,
"description": "Grid resolution. A session is rounded to the nearest slot.",
"examples": [
15
]
},
"row_height": {
"type": "integer",
"minimum": 6,
"maximum": 60,
"default": 17,
"description": "Pixels per slot, and the dial that decides whether the day fills the sheet: slots x row_height is the height of the grid, and a Letter sheet at 0.45in margins gives it about 750px once the header, legend and footer notes have taken theirs.",
"examples": [
17
]
},
"clock": {
"type": "string",
"enum": [
"12h",
"24h"
],
"default": "12h",
"description": "How the hour ticks are written. `12h` prints 1:00 with a small a or p after it, so an afternoon is never mistaken for a morning; `24h` prints 13:00.",
"examples": [
"12h"
]
}
}
},
"tracks": {
"type": "array",
"minItems": 1,
"description": "One column each, left to right, each with its own sessions.",
"items": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"examples": [
"Main Hall"
]
},
"room": {
"type": "string",
"description": "Second line in the column header, set in monospace.",
"examples": [
"Ground floor"
]
},
"width": {
"type": "number",
"minimum": 0.25,
"maximum": 4,
"default": 1,
"description": "Relative width of this column against the others. Give the room with the long session titles 1.25 and the rest will still share what is left evenly.",
"examples": [
1.15
]
},
"sessions": {
"type": "array",
"items": {
"type": "object",
"required": [
"title",
"start_minutes",
"end_minutes"
],
"properties": {
"title": {
"type": "string",
"examples": [
"Punchcutting is not lost, it is just slow"
]
},
"start_minutes": {
"type": "integer",
"minimum": 0,
"maximum": 1439,
"examples": [
555
]
},
"end_minutes": {
"type": "integer",
"minimum": 1,
"maximum": 1560,
"examples": [
615
]
},
"speaker": {
"type": "string",
"examples": [
"Dr. Emine Yalcin"
]
},
"note": {
"type": "string",
"examples": [
"Captioned live"
]
},
"color": {
"type": "string",
"description": "The 3pt left edge of the block, which is how a category is read at a glance. Pair it with a legend entry and keep the palette to the pack's black, crimson and one grey. A hex colour or a CSS colour keyword. It is interpolated into the stylesheet, so nothing else is accepted.",
"examples": [
"#a4133c"
],
"pattern": "^(#[0-9a-fA-F]{3,8}|[a-zA-Z]{3,20})$"
},
"tint": {
"type": "string",
"description": "Block background. Blocks are white by default and should usually stay that way — the left edge already carries the category, and a wall of tinted boxes is a spreadsheet. A hex colour or a CSS colour keyword. It is interpolated into the stylesheet, so nothing else is accepted.",
"examples": [
"#ffffff"
],
"pattern": "^(#[0-9a-fA-F]{3,8}|[a-zA-Z]{3,20})$"
}
}
}
}
}
}
},
"all_day": {
"type": "array",
"description": "Anything that spans every track, such as registration, lunch and the reception. Drawn as a solid band reversed out of black across the full width, which is the only filled shape on the sheet.",
"items": {
"type": "object",
"required": [
"title",
"start_minutes",
"end_minutes"
],
"properties": {
"title": {
"type": "string",
"examples": [
"Lunch, included, in the Courtyard"
]
},
"start_minutes": {
"type": "integer",
"minimum": 0,
"maximum": 1439,
"examples": [
720
]
},
"end_minutes": {
"type": "integer",
"minimum": 1,
"maximum": 1560,
"examples": [
780
]
},
"detail": {
"type": "string",
"examples": [
"Vegetarian and gluten-free trays at the far end"
]
},
"color": {
"type": "string",
"description": "Band background. Black unless you have a reason. A hex colour or a CSS colour keyword. It is interpolated into the stylesheet, so nothing else is accepted.",
"examples": [
"#111111"
],
"pattern": "^(#[0-9a-fA-F]{3,8}|[a-zA-Z]{3,20})$"
}
}
}
},
"legend": {
"type": "array",
"description": "Swatches under the header, matching the left-edge colours you used on sessions. Three entries is usually the whole story; a legend longer than the schedule is a sign the colours are doing too much.",
"items": {
"type": "object",
"required": [
"label",
"color"
],
"properties": {
"label": {
"type": "string",
"examples": [
"Hands-on workshop"
]
},
"color": {
"type": "string",
"examples": [
"#a4133c"
],
"pattern": "^(#[0-9a-fA-F]{3,8}|[a-zA-Z]{3,20})$",
"description": "A hex colour or a CSS colour keyword. It is interpolated into the stylesheet, so nothing else is accepted."
}
}
}
},
"notes": {
"type": "array",
"description": "The anchored footer strip, split into equal columns. It is what stops the sheet ending in white.",
"items": {
"type": "object",
"required": [
"title",
"text"
],
"properties": {
"title": {
"type": "string",
"examples": [
"On the floor."
]
},
"text": {
"type": "string",
"examples": [
"Closed-toe shoes at the presses, no exceptions."
]
}
}
}
}
}
}