Template gallery / slide

slide

A single 1920x1080 slide for social posts, thumbnails and carousels. Same layouts as slide-deck minus chart — title, section, bullets, two-column, image, quote, closing — on the Satori fast path: flexbox subset, inline styles, bundled Inter, no network at render time.

satori default PNG 20 top-level fields 1 unit

Rendered preview of the slide template
Rendered by Galley from the example payload below, at 1920px wide .

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
layout "title" | "section" | "bullets" | "two-column" | "image" | "quote" | "closing" required Which layout renders the slide. `chart` is not available on the Satori fast path — use `slide-deck`, or pass a pre-rendered chart to the `image` layout.
title string optional The headline.
subtitle string optional `title` layout: the standfirst under the headline.
eyebrow string optional Small label next to the accent tick at the top of the slide.
number string optional `section` layout: the oversized part number behind the headline.
lead string optional One-sentence standfirst under the headline.
bullets array of string | object optional `bullets` layout. Plain strings, or objects with a supporting `detail` line. The list steps down a size as it gets heavier so it always lands inside the frame.
columns array of object { heading, body, bullets } optional `two-column` layout: exactly two entries, left then right.
image string optional `image` layout: an https URL inlined through the SSRF-safe fetcher, or a `data:` URI. Satori cannot fetch at render time, so the image is resolved before the slide is drawn.
image_fit "cover" | "contain" optional How the image fills its frame.
full_bleed boolean optional `image` layout: run the image to all four edges and set the eyebrow, title and caption over a scrim at the bottom.
caption string optional `image` layout: the small muted line under the frame, or inside the scrim when full bleed.
quote string optional `quote` layout: the pull quote itself, without surrounding quotation marks.
attribution string optional `quote` layout: who said it.
role string optional `quote` layout: their title or the source.
links array of object { label, value } optional `closing` layout: a row of label and value pairs.
footer string optional Bottom-left running line.
badge string optional Bottom-right line, set in the accent colour. Carousel position, a date, a hashtag.
theme object { mode, accent, background, font_pairing } optional Satori ships Inter only, so `font_pairing` changes weight and letterspacing rather than the family.
notes string optional Speaker notes. Kept with the slide, never rendered into the image.

Default render options

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

width 1920
height 1080

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
{
  "layout": "bullets",
  "eyebrow": "Galley Render",
  "title": "Five things a render has to be",
  "lead": "Anything less and your 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 you set."
    }
  ],
  "footer": "galleyrender.com",
  "badge": "1 / 5",
  "theme": {
    "mode": "dark",
    "accent": "#2dd4bf",
    "font_pairing": "sans"
  },
  "notes": "Opening card of the launch carousel. The next four cards each expand one line."
}

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": "slide@1",
  "format": "png",
  "data": {
    "layout": "bullets",
    "eyebrow": "Galley Render",
    "title": "Five things a render has to be",
    "lead": "Anything less and your 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 you set."
      }
    ],
    "footer": "galleyrender.com",
    "badge": "1 / 5",
    "theme": {
      "mode": "dark",
      "accent": "#2dd4bf",
      "font_pairing": "sans"
    },
    "notes": "Opening card of the launch carousel. The next four cards each expand one line."
  }
}'
With no API key, over MCPshell
# 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",
        "format": "png",
        "data": { … see the example payload above … }
      }
    }
  }'

Pin the version — slide@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 (20 top-level properties)
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Slide",
  "description": "One 16:9 slide as a 1920x1080 PNG. The fields match a single entry of `slide-deck`'s `slides` array, so a slide can be lifted straight out of a deck and posted on its own.",
  "type": "object",
  "required": [
    "layout"
  ],
  "additionalProperties": true,
  "properties": {
    "layout": {
      "type": "string",
      "description": "Which layout renders the slide. `chart` is not available on the Satori fast path — use `slide-deck`, or pass a pre-rendered chart to the `image` layout.",
      "enum": [
        "title",
        "section",
        "bullets",
        "two-column",
        "image",
        "quote",
        "closing"
      ],
      "default": "bullets"
    },
    "title": {
      "type": "string",
      "maxLength": 200,
      "description": "The headline.",
      "examples": [
        "Five things a render has to be"
      ]
    },
    "subtitle": {
      "type": "string",
      "maxLength": 240,
      "description": "`title` layout: the standfirst under the headline.",
      "examples": [
        "Documents for agents. JSON in, PDF out."
      ]
    },
    "eyebrow": {
      "type": "string",
      "maxLength": 60,
      "description": "Small label next to the accent tick at the top of the slide.",
      "examples": [
        "Galley Render"
      ]
    },
    "number": {
      "type": "string",
      "maxLength": 8,
      "description": "`section` layout: the oversized part number behind the headline.",
      "examples": [
        "02"
      ]
    },
    "lead": {
      "type": "string",
      "maxLength": 400,
      "description": "One-sentence standfirst under the headline.",
      "examples": [
        "Anything less and the agent is back to gluing a headless browser together."
      ]
    },
    "bullets": {
      "type": "array",
      "description": "`bullets` layout. Plain strings, or objects with a supporting `detail` line. The list steps down a size as it gets heavier so it always lands inside the frame.",
      "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": [
              "Deterministic"
            ]
          },
          "detail": {
            "type": "string",
            "description": "Supporting line under the bullet, set smaller and muted.",
            "examples": [
              "The cache key hashes template version, data and options."
            ]
          }
        }
      }
    },
    "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": 400,
            "description": "Paragraph of body copy for the column.",
            "examples": [
              "A pooled headless browser for anything that has to paginate or print."
            ]
          },
          "bullets": {
            "type": "array",
            "description": "Points inside the column. Detail lines are not shown in the column layout.",
            "maxItems": 5,
            "items": {
              "type": [
                "string",
                "object"
              ],
              "description": "A bullet: either the string itself or `{ text }`.",
              "additionalProperties": true,
              "properties": {
                "text": {
                  "type": "string",
                  "description": "The bullet.",
                  "examples": [
                    "PDF, PNG, JPG, WebP"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "image": {
      "type": "string",
      "description": "`image` layout: an https URL inlined through the SSRF-safe fetcher, or a `data:` URI. Satori cannot fetch at render time, so the image is resolved before the slide is drawn.",
      "examples": [
        "https://example.com/screenshot.png",
        "data:image/svg+xml;base64,PHN2Zy8+"
      ]
    },
    "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 eyebrow, title and caption over a scrim at the bottom.",
      "default": false
    },
    "caption": {
      "type": "string",
      "maxLength": 300,
      "description": "`image` layout: the small muted line under the frame, or inside the scrim when full bleed.",
      "examples": [
        "Rendered by Galley, 1920x1080, in about 40 ms."
      ]
    },
    "quote": {
      "type": "string",
      "maxLength": 300,
      "description": "`quote` layout: the pull quote itself, without surrounding quotation marks.",
      "examples": [
        "Documents for agents. JSON in, PDF out."
      ]
    },
    "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.",
      "examples": [
        "Product thesis, 2026"
      ]
    },
    "links": {
      "type": "array",
      "description": "`closing` layout: a row of label and value pairs.",
      "maxItems": 3,
      "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"
            ]
          }
        }
      }
    },
    "footer": {
      "type": "string",
      "maxLength": 80,
      "description": "Bottom-left running line.",
      "default": "galleyrender.com"
    },
    "badge": {
      "type": "string",
      "maxLength": 40,
      "description": "Bottom-right line, set in the accent colour. Carousel position, a date, a hashtag.",
      "examples": [
        "1 / 5"
      ]
    },
    "theme": {
      "type": "object",
      "description": "Satori ships Inter only, so `font_pairing` changes weight and letterspacing rather than the family.",
      "additionalProperties": true,
      "properties": {
        "mode": {
          "type": "string",
          "description": "Dark ground or light ground. The default accent changes with it.",
          "enum": [
            "dark",
            "light"
          ],
          "default": "dark"
        },
        "accent": {
          "type": "string",
          "description": "Accent colour as a CSS colour. Defaults to #2dd4bf on dark, #0f766e on light.",
          "examples": [
            "#2dd4bf",
            "#0f766e",
            "#f59e0b"
          ]
        },
        "background": {
          "type": "string",
          "description": "Overrides the ground colour for the chosen mode.",
          "examples": [
            "#0b0f14",
            "#ffffff"
          ]
        },
        "font_pairing": {
          "type": "string",
          "description": "Headline treatment: `sans` is bold and tightly tracked, `serif` is light and airy, `mono` is semibold with neutral tracking.",
          "enum": [
            "sans",
            "serif",
            "mono"
          ],
          "default": "sans"
        }
      }
    },
    "notes": {
      "type": "string",
      "maxLength": 4000,
      "description": "Speaker notes. Kept with the slide, never rendered into the image.",
      "examples": [
        "Post this one first — it is the hook for the thread."
      ]
    }
  }
}