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

Rendered preview of the event-schedule 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 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.

example.jsonjson
{
  "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

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": "event-schedule@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": "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 →

Try a free tool

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."
            ]
          }
        }
      }
    }
  }
}