Template gallery / slide-deck
slide-deck
16:9 presentation deck, one slide per PDF page at 13.333in x 7.5in. Each slide picks a layout — title, section, bullets, two-column, image, chart, quote, closing — and the deck carries a theme of one accent colour plus a font pairing. Speaker notes live in the schema and never render.
chromium default PDF 2 top-level fields 1 unit 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 |
|---|---|---|---|
| deck | object { title, subtitle, author, date, … } | required | Deck-level metadata: the cover credits, the running footer and the theme. |
| slides | array of object { layout, title, subtitle, eyebrow, … } | required | One slide per PDF page, in order. |
Default render options
Applied unless you override them. Options are part of the cache key.
| margin | "0" |
| 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.
{
"deck": {
"title": "Galley Render",
"subtitle": "Documents for agents. JSON in, PDF out.",
"author": "galleyrender.com",
"date": "September 2026",
"footer": "Galley Render — the short version",
"theme": {
"accent": "#0f766e",
"font_pairing": "sans"
}
},
"slides": [
{
"layout": "title",
"eyebrow": "Rendering API",
"notes": "Thirty seconds on the one-liner, then straight to the problem. Do not demo yet."
},
{
"layout": "section",
"eyebrow": "01",
"title": "Agents cannot hand you the file",
"lead": "They will draft the invoice, the certificate and the report, then stop one step short of the thing you asked for.",
"notes": "This is the whole wedge. Say it once, plainly, and move on."
},
{
"layout": "bullets",
"eyebrow": "What the job actually needs",
"title": "Five things a render has to be",
"lead": "Anything less and the agent is back to gluing a headless browser together.",
"bullets": [
{
"text": "Deterministic",
"detail": "The cache key hashes template version, data and options. Same request, same bytes."
},
{
"text": "Schema-first",
"detail": "A bad payload comes back as a field path, an expected type and an example value."
},
{
"text": "Versioned",
"detail": "Templates are code: create, diff, tag, roll back. Pin invoice@3 in anything you ship."
},
{
"text": "Delivered",
"detail": "Signed URLs with an expiry, push to R2, S3 or GCS, webhooks on completion."
},
{
"text": "Cheap enough to call in a loop",
"detail": "Priced per render, not per seat, under a spend cap the account owner sets."
}
],
"notes": "Land 'schema-first' — it is the one competitors have no answer to."
},
{
"layout": "two-column",
"eyebrow": "Architecture",
"title": "Two engines, one API",
"lead": "The template declares which path it takes. The call is identical either way.",
"columns": [
{
"heading": "Chromium",
"body": "A pooled headless browser for anything that has to paginate or print.",
"bullets": [
{
"text": "Full HTML and CSS",
"detail": "Print stylesheets, repeating table headers, controlled page breaks."
},
{
"text": "PDF, PNG, JPG, WebP"
},
{
"text": "Every subresource through an SSRF-safe fetcher"
}
]
},
{
"heading": "Satori fast path",
"body": "A flexbox subset compiled straight to SVG, then to PNG. No browser process.",
"bullets": [
{
"text": "Card-shaped templates",
"detail": "OG images, quote cards, social slides, tickets, badges."
},
{
"text": "Roughly two orders of magnitude cheaper"
},
{
"text": "Bundled fonts, no network at render time"
}
]
}
],
"notes": "If someone asks about PPTX: v2, gated on paying demand."
},
{
"layout": "image",
"eyebrow": "The request path",
"title": "From tool call to signed URL",
"image": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTA0MCA0MjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZm9udC1mYW… [4,282 characters, shortened for display]",
"image_fit": "contain",
"alt": "An agent calls render; the payload is validated against the template schema, routed to the Chromium pool or the Satori fast path, and returned as a signed URL",
"caption": "A cache hit skips the renderer entirely and is billed to nobody.",
"notes": "Point at the validate box. That is the part that makes agents succeed on the first try."
},
{
"layout": "chart",
"eyebrow": "Pricing",
"title": "The price floor is public now",
"svg": "<svg viewBox=\"0 0 1000 330\" xmlns=\"http://www.w3.org/2000/svg\" font-family=\"Inter, Helvetica, Arial, sans-serif\"><line x… [1,976 characters, shortened for display]",
"legend": [
{
"label": "Designer-first incumbents",
"color": "#cbd5e1"
},
{
"label": "Galley, Starter plan",
"color": "#0f766e"
},
{
"label": "Underlying compute",
"color": "#5eead4"
}
],
"caption": "Incumbent list prices, Galley's $19/mo Starter plan at its included volume, and the measured compute cost of a render.",
"notes": "Do not oversell the margin story. The point is that the floor is public now."
},
{
"layout": "quote",
"quote": "Documents for agents. JSON in, PDF out. Every render deterministic, cached, and signed.",
"attribution": "Galley Render",
"role": "The positioning line, and the whole product in one sentence",
"notes": "Pause here. Let it sit before the close."
},
{
"layout": "closing",
"eyebrow": "Try it",
"title": "50 renders. No key, no signup.",
"lead": "The first render with no API key mints a trial account and returns its token. Upgrade in place when you outgrow it.",
"links": [
{
"label": "Docs",
"value": "galleyrender.com/docs"
},
{
"label": "MCP",
"value": "mcp.galleyrender.com"
},
{
"label": "Claude skill",
"value": "galleyrender.com/skill.md"
}
],
"notes": "End on the keyless trial. It is the thing people repeat to someone else."
}
]
}
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": "slide-deck@1",
"format": "pdf",
"data": "… the example payload above …"
}' # No API key. The first call mints a 50-render trial.
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": "slide-deck",
"format": "pdf",
"data": { … see the example payload above … }
}
}
}'
Pin the version — slide-deck@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 (2 top-level properties)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Slide deck",
"description": "A 16:9 presentation. `deck` sets the running title, the cover credits and the theme; every entry in `slides` is one page of the PDF and picks its own layout.",
"type": "object",
"required": [
"deck",
"slides"
],
"additionalProperties": true,
"properties": {
"deck": {
"type": "object",
"description": "Deck-level metadata: the cover credits, the running footer and the theme.",
"required": [
"title"
],
"additionalProperties": true,
"properties": {
"title": {
"type": "string",
"minLength": 1,
"maxLength": 120,
"description": "Cover headline, and the running footer on every other slide unless `footer` overrides it.",
"examples": [
"Galley Render"
]
},
"subtitle": {
"type": "string",
"maxLength": 240,
"description": "Standfirst under the cover headline.",
"examples": [
"Documents for agents. JSON in, PDF out."
]
},
"author": {
"type": "string",
"maxLength": 120,
"description": "Credit line on the cover slide.",
"examples": [
"Galley Render"
]
},
"date": {
"type": "string",
"maxLength": 60,
"description": "Shown next to the author on the cover. Free text, so \"Q4 2026\" is as valid as a date.",
"examples": [
"September 2026"
]
},
"footer": {
"type": "string",
"maxLength": 120,
"description": "Running footer text. Defaults to `deck.title`.",
"examples": [
"Galley Render — investor update"
]
},
"theme": {
"type": "object",
"description": "Two knobs. Every tint in the deck is mixed from the accent, so one hex restyles the whole thing.",
"additionalProperties": true,
"properties": {
"accent": {
"type": "string",
"description": "Accent colour as a CSS colour. Rules, bullet markers, section numbers and link labels are derived from it.",
"default": "#0f766e",
"examples": [
"#0f766e",
"#1d4ed8",
"#b45309"
]
},
"font_pairing": {
"type": "string",
"description": "Display face for headings; body copy is always Inter. `sans` is Inter throughout, `serif` sets headings in an old-style serif, `mono` sets headings and labels in the system monospace.",
"enum": [
"sans",
"serif",
"mono"
],
"default": "sans"
}
}
}
}
},
"slides": {
"type": "array",
"description": "One slide per PDF page, in order.",
"minItems": 1,
"maxItems": 200,
"items": {
"type": "object",
"required": [
"layout"
],
"additionalProperties": true,
"properties": {
"layout": {
"type": "string",
"description": "Which layout renders this slide. `title`, `section` and `closing` are set on a dark ground; `quote` sits on an accent wash; the rest are on white.",
"enum": [
"title",
"section",
"bullets",
"two-column",
"image",
"chart",
"quote",
"closing"
],
"default": "bullets"
},
"title": {
"type": "string",
"maxLength": 200,
"description": "Slide headline. On `title` it defaults to `deck.title`; on `image` it may be omitted for a clean full-bleed frame.",
"examples": [
"Two engines, one API"
]
},
"subtitle": {
"type": "string",
"maxLength": 240,
"description": "`title` layout only: overrides `deck.subtitle` on the cover.",
"examples": [
"Documents for agents. JSON in, PDF out."
]
},
"eyebrow": {
"type": "string",
"maxLength": 60,
"description": "Small label above the headline. On `section` it is set large as the part number, e.g. \"02\".",
"examples": [
"02",
"The wedge"
]
},
"lead": {
"type": "string",
"maxLength": 400,
"description": "One-sentence standfirst under the headline.",
"examples": [
"Every render is deterministic, cached and signed."
]
},
"bullets": {
"type": "array",
"description": "`bullets` layout. Plain strings, or objects with a supporting `detail` line. Six or more steps the type down a size so nothing falls off the slide.",
"maxItems": 12,
"items": {
"type": [
"string",
"object"
],
"description": "A bullet: either the string itself or `{ text, detail }`.",
"additionalProperties": true,
"properties": {
"text": {
"type": "string",
"description": "The bullet.",
"examples": [
"Deterministic cache key"
]
},
"detail": {
"type": "string",
"description": "Supporting line under the bullet, set smaller and muted.",
"examples": [
"Hash of template version + data + options. Cache hits are free."
]
}
}
}
},
"columns": {
"type": "array",
"description": "`two-column` layout: exactly two entries, left then right.",
"maxItems": 2,
"items": {
"type": "object",
"additionalProperties": true,
"properties": {
"heading": {
"type": "string",
"maxLength": 80,
"description": "Small accent label at the top of the column.",
"examples": [
"Chromium"
]
},
"body": {
"type": "string",
"maxLength": 600,
"description": "Paragraph of body copy for the column.",
"examples": [
"Full HTML and CSS, print stylesheets, page breaks, PDF or raster."
]
},
"bullets": {
"type": "array",
"description": "Points inside the column. Same shape as slide bullets.",
"maxItems": 8,
"items": {
"type": [
"string",
"object"
],
"description": "A bullet: either the string itself or `{ text, detail }`.",
"additionalProperties": true,
"properties": {
"text": {
"type": "string",
"description": "The bullet.",
"examples": [
"Multi-page PDF"
]
},
"detail": {
"type": "string",
"description": "Supporting line under the bullet.",
"examples": [
"Repeating table headers, controlled breaks."
]
}
}
}
}
}
}
},
"image": {
"type": "string",
"description": "`image` layout: an https URL fetched through the SSRF-safe fetcher, or an inline `data:` URI.",
"examples": [
"https://example.com/architecture.png",
"data:image/svg+xml;base64,PHN2Zy8+"
]
},
"alt": {
"type": "string",
"maxLength": 200,
"description": "Alternative text for the image. Defaults to the slide title.",
"examples": [
"Request path from MCP tool call to signed URL"
]
},
"image_fit": {
"type": "string",
"description": "How the image fills its frame.",
"enum": [
"cover",
"contain"
],
"default": "cover"
},
"full_bleed": {
"type": "boolean",
"description": "`image` layout: run the image to all four edges and set the title and caption over a scrim.",
"default": false
},
"svg": {
"type": "string",
"description": "`chart` layout: a ready-made SVG string, dropped in unescaped and scaled to the plot area. Give it a `viewBox` and no fixed width or height.",
"examples": [
"<svg viewBox=\"0 0 800 360\" xmlns=\"http://www.w3.org/2000/svg\"><rect x=\"0\" y=\"300\" width=\"60\" height=\"60\" fill=\"#0f766e\"/></svg>"
]
},
"legend": {
"type": "array",
"description": "`chart` layout: swatch and label pairs under the plot.",
"maxItems": 8,
"items": {
"type": "object",
"additionalProperties": true,
"properties": {
"label": {
"type": "string",
"description": "Series name.",
"examples": [
"Chromium renders"
]
},
"color": {
"type": "string",
"description": "Swatch colour. Defaults to the deck accent.",
"examples": [
"#0f766e"
]
}
}
}
},
"quote": {
"type": "string",
"maxLength": 400,
"description": "`quote` layout: the pull quote itself, without surrounding quotation marks.",
"examples": [
"Every agent that can write a document should be able to hand you the file."
]
},
"attribution": {
"type": "string",
"maxLength": 120,
"description": "`quote` layout: who said it.",
"examples": [
"Galley Render"
]
},
"role": {
"type": "string",
"maxLength": 160,
"description": "`quote` layout: their title or the source, set under the attribution.",
"examples": [
"Product thesis, 2026"
]
},
"links": {
"type": "array",
"description": "`closing` layout: a row of label and value pairs — docs, install line, contact.",
"maxItems": 4,
"items": {
"type": "object",
"additionalProperties": true,
"properties": {
"label": {
"type": "string",
"maxLength": 40,
"description": "Small accent label.",
"examples": [
"Docs"
]
},
"value": {
"type": "string",
"maxLength": 120,
"description": "The thing itself: a URL, an address, a command.",
"examples": [
"galleyrender.com/docs"
]
}
}
}
},
"caption": {
"type": "string",
"maxLength": 300,
"description": "Small muted line under the image, chart or columns. Sources and footnotes go here.",
"examples": [
"Source: platform metrics, Oct 2025 – Sep 2026."
]
},
"notes": {
"type": "string",
"maxLength": 4000,
"description": "Speaker notes. Kept with the deck, never rendered into the PDF.",
"examples": [
"Land the cost line here, then move on — the demo is the next slide."
]
}
}
}
}
}
}