Render pipeline

From a title and a brand kit to a cached PNG \u2014 in four stages.

No canvas editor sits between your agent and the output. Every request runs the same deterministic pipeline: resolve, render, convert, cache.

01 · Request

Agent sends structured fields

A title, a theme, a size, and a brandKitId — nothing else to decide. No prompt to iterate on.

  • Validated against the template's typed schema
  • Missing fields fall back to brand-kit defaults, never a blank
POST /v1/images
{
"template": "blog-gradient-v1",
"title": "How the algorithm ranks posts",
"size": "1200x630",
"brandKitId": "brand_9f2c"
}
02 · Resolve

Props merge with the brand kit

Logo, colors, and font resolve from the brand kit — fetched once at save time, never re-fetched per request.

  • Logo/font fetches are cached and SSRF-guarded
  • Font buffers pre-loaded per workspace, not per call
resolved_props.json
{
"accent": "#2F5BFF",
"font": "Sora-Bold",
"logo": "cache://brand_9f2c/logo"
}
// resolved from brand_9f2c, not the request
03 · Render

Satori renders JSX → SVG

Each template is a typed JSX component. Satori lays it out and produces SVG — no headless browser, no Chromium process to keep warm.

  • resvg converts SVG → PNG/WebP
  • Line balancing and font scaling run automatically on overflow
templates/blog-gradient/index.tsx
export default function Template(props) {
return (
<div style={{ background: props.accent }}>
<h1>{props.title}</h1>
</div>
);
}
04 · Deliver

Cached, CDN’d, returned

The render is keyed by a hash of the fully-resolved input — same title, template, and brand kit hits cache instantly, even across different agents.

  • <300ms on a cache hit, globally distributed
  • Response includes ready-to-embed alt text
200 OK
{
"url": "cdn.imageclaw.dev/9f2c/a83e.png",
"width": 1200, "height": 630,
"cached": true, "ms": 214
}
<1s
cold render
<300ms
cached render
99.9%
availability
P95
<1s API latency
Under the hood

Why it's fast \u2014 and self-hostable.

Satori

No headless browser

Templates render to SVG via layout, not a Chromium instance — dramatically lighter to run and package than Puppeteer-based renderers.

resvg

Fast raster conversion

SVG → PNG/WebP via native Rust bindings, benchmarked against sharp and librsvg for your template complexity.

Cache keys

Hash of resolved input

Template + resolved props + font version — so identical requests from different agents share a cache entry.

Run the whole pipeline yourself.

Render engine, template registry, and CLI are AGPL-licensed. Clone it, point it at your own CDN.

View on GitHub →