Open Graph images & certificates, rendered from a URL.

Upload an HTML template as JSON once. Every combination of parameters becomes an immutable, edge-cacheable webp — social cards, changelog banners, course certificates — no image pipeline to run.

 one GET, one image
GET https://<tenant>.media0.subprocess.io/image/certificate?recipient=Ada+Lovelace&course=Analytical+Engines+101

302 -> /image/t/{templateID}/{version}/webp?course=…&recipient=…
200 image/webp · Cache-Control: public, max-age=86400, immutable

How it works

A template is a single JSON document: Go html/template markup plus declared parameters. Everything downstream is boring, cacheable HTTP.

1

POST a template

One JSON spec: HTML, width/height, and typed params (string, number, image_url). Validate it for free first.

2

Versions are immutable

Every publish mints an ascending version (000001, 000002, …). Old URLs keep rendering exactly what they always did.

3

Aliases 302 to latest

Point /image/certificate at a template. Consumers hit the alias; it redirects to the fully-qualified, immutable render URL.

4

Rendered once, cached hard

Headless Chromium renders on the first request; the webp is cached (in memory and central blob storage) with a 24-hour TTL that every access extends. Immutable URLs mean any CDN in front caches them as an ordinary proxy.

Built agent-first

The whole lifecycle is three HTTP calls with JSON in and JSON out — no dashboard clicks required. AI agents design a spec, dry-run it against /templates/validate, publish, and hand back a URL. The agent guide ships a drop-in system prompt.

Even signup is agent-first: no email verification, no CAPTCHA — a Hashcash-style proof of work (a few seconds of CPU) buys a tenant, a write API key and ten recovery codes in one call. The response includes a claim_url: the agent hands it to its human together with one recovery code, and the human redeems the code in the console to take over — registering passkeys and an email.

# 0. sign up as an agent: solve a proof of work, get an account
curl -s -X POST https://media0.subprocess.io/auth/pow/challenge
# -> {"challenge": "<64 hex>", "bits": 22, "expires_at": "…"}

# brute-force an ASCII nonce until sha256(challenge + ":" + nonce)
# starts with >= bits leading zero bits:
n=0; while true; do
  h=$(printf '%s:%s' "$CHALLENGE" "$n" | sha256sum | cut -c1-6)
  [ "$h" = "000000" ] && break   # 24 bits; server needs 22
  n=$((n+1))
done

curl -s https://media0.subprocess.io/auth/agent/signup \
  -d "{\"challenge\": \"$CHALLENGE\", \"nonce\": \"$n\", \"name\": \"my-bot\"}"
# -> 201 {"tenant_id": …, "api_key": {"secret": "m0_…", "scope": "write"},
#         "recovery_codes": [10 one-time codes], "claim_url": "…/app/#/claim?tenant=…"}
# 1. dry-run: parse + normalize, costs nothing
curl -s https://media0.subprocess.io/api/v1/templates/validate \
  -H "Authorization: Bearer $M0_KEY" \
  -d '{"spec": {"html": "<h1 class=\"og-title\">{{.title}}</h1>",
             "params": {"title": {"required": true}}}}'

# 2. publish
curl -s -X POST https://media0.subprocess.io/api/v1/templates \
  -H "Authorization: Bearer $M0_KEY" -d @spec.json

# 3. name it
curl -s -X PUT https://media0.subprocess.io/api/v1/aliases/launch \
  -H "Authorization: Bearer $M0_KEY" \
  -d '{"template_id": "<id from step 2>"}'

Why agents like it

Specs are self-describing: every parameter carries a type, default and description, so a model can read a template back and know how to call it. Validation returns machine-readable problems, never a rendered bill.

The render sandbox is deterministic by construction: no external network, a shared /lib asset library (CSS utilities, SVG backdrops, laurels and seals for certificates), and image_url params fetched server-side with SSRF guards.

Read keys can list, inspect and validate; write keys publish. Give your agent the scope it needs and nothing more.

Simple limits

Every tenant gets the same generous defaults. Renders are only counted on cache misses — cached traffic is free and unmetered.

WhatLimitNotes
Templates50 per tenantunlimited immutable versions each
Renders10,000 / monthcache-miss renders only; hits are free
Render budget1 secondper render, headless Chromium
Image cache24 h slidingevery access extends the TTL
Outputwebpdefault 1200×630 @ 2× scale, up to 4096 px

Docs

Reference material ships with the repository: docs/API.md (every endpoint with curl examples), docs/AGENT_GUIDE.md (template authoring for AI agents), docs/ARCHITECTURE.md (how the cache hierarchy works) and ready-to-POST specs under examples/.

Get started in the Console