{"openapi":"3.0.3","info":{"title":"Render MCP API","version":"0.2.0","description":"HTML-to-image rendering API. Create a free key with an email, then send HTML/CSS or template+data and receive a hosted PNG/SVG URL. Free beta."},"servers":[{"url":"https://rendermcp.com"}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"API key from POST /v1/keys"}}},"paths":{"/v1/keys":{"post":{"operationId":"createOrAddKey","summary":"No Authorization header: create a brand-new account and its first key (errors if the email is already registered). With Authorization: Bearer <api_key>: add another named key to that already-proven account instead. Secrets are shown once and never retrievable. Not exposed as an MCP tool beyond the no-header (brand-new account) case; adding a key to an existing account is REST/UI-only.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"Required when creating a brand-new account; ignored when a valid key header is present."},"name":{"type":"string","description":"Label for the new key when adding to an existing account (e.g. \"Laptop\", \"CI\"). Defaults to a date-stamped name if omitted."}}}}}},"responses":{"200":{"description":"{api_key, email, key_id, note} for a brand-new account, or {api_key, key_id, name, note} when adding to an existing one"},"400":{"description":"Invalid email, email already registered (no header), unknown key (header present), or key cap reached"}}},"get":{"operationId":"listKeys","summary":"List active keys on the authenticated account: id, name, created_at. Never returns key material.","security":[{"bearer":[]}],"responses":{"200":{"description":"{keys: [{id, name, created_at}]}"},"400":{"description":"Missing or invalid key"}}}},"/v1/keys/{id}":{"delete":{"operationId":"revokeKey","summary":"Revoke one specific key by id (Authorization: Bearer required, any active key on the same account). Stops that key immediately; other keys on the account are unaffected. This revokes a credential, not full data erasure: aggregate monthly render counts for the account are retained.","security":[{"bearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"{revoked: true, key_id}"},"400":{"description":"Missing/invalid key, or key_id not found/already revoked/not owned by this account"}}}},"/v1/magic-link":{"post":{"operationId":"requestMagicLink","summary":"Email a 15-minute recovery link for an account you no longer hold a key for. Always returns the same response regardless of whether the email is registered, to prevent account enumeration; only existing accounts actually receive mail, subject to a cooldown and outstanding-link cap.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"}},"required":["email"]}}}},"responses":{"200":{"description":"{sent: true}"},"400":{"description":"Invalid email"}}}},"/v1/magic-link/status":{"post":{"operationId":"magicLinkStatus","summary":"Check a recovery token and return account usage plus its active key list. Read-only.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"}},"required":["token"]}}}},"responses":{"200":{"description":"{email, exists, created_at?, month?, renders_this_month?, renders_total?, monthly_quota?, remaining?, keys?: [{id, name, created_at}]}"},"400":{"description":"Token invalid or expired"}}}},"/v1/magic-link/keys":{"post":{"operationId":"magicLinkAddKey","summary":"Add a named key to the token's account. The token stays valid for the full 15-minute window (not single-use), so several keys can be managed in one recovery visit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"name":{"type":"string","description":"Label for the new key; defaults to a date-stamped name if omitted."}},"required":["token"]}}}},"responses":{"200":{"description":"{api_key, key_id, name, note}"},"400":{"description":"Token invalid or expired, or key cap reached"}}}},"/v1/magic-link/keys/revoke":{"post":{"operationId":"magicLinkRevokeKey","summary":"Revoke one specific key on the token's account by id.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"key_id":{"type":"string"}},"required":["token","key_id"]}}}},"responses":{"200":{"description":"{revoked: true, key_id}"},"400":{"description":"Token invalid/expired, or key_id not found/already revoked"}}}},"/v1/magic-link/exchange":{"post":{"operationId":"exchangeMagicLink","summary":"Exchange a still-valid 15-minute recovery token for a 15-day session token, so the account page doesn't need a fresh email every visit. Returns the same fields as magicLinkStatus plus session_token.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"}},"required":["token"]}}}},"responses":{"200":{"description":"{session_token, email, exists, created_at?, month?, renders_this_month?, renders_total?, monthly_quota?, remaining?, keys?: [{id, name, created_at}]}"},"400":{"description":"Token invalid or expired"}}}},"/v1/session/status":{"post":{"operationId":"sessionStatus","summary":"Check a session token and return account usage plus its active key list. Read-only.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"session_token":{"type":"string"}},"required":["session_token"]}}}},"responses":{"200":{"description":"{email, exists, created_at?, month?, renders_this_month?, renders_total?, monthly_quota?, remaining?, keys?: [{id, name, created_at}]}"},"400":{"description":"Session invalid or expired"}}}},"/v1/session/keys":{"post":{"operationId":"sessionAddKey","summary":"Add a named key to the session's account.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"session_token":{"type":"string"},"name":{"type":"string","description":"Label for the new key; defaults to a date-stamped name if omitted."}},"required":["session_token"]}}}},"responses":{"200":{"description":"{api_key, key_id, name, note}"},"400":{"description":"Session invalid or expired, or key cap reached"}}}},"/v1/session/keys/revoke":{"post":{"operationId":"sessionRevokeKey","summary":"Revoke one specific key on the session's account by id.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"session_token":{"type":"string"},"key_id":{"type":"string"}},"required":["session_token","key_id"]}}}},"responses":{"200":{"description":"{revoked: true, key_id}"},"400":{"description":"Session invalid/expired, or key_id not found/already revoked"}}}},"/v1/session/revoke":{"post":{"operationId":"revokeSession","summary":"Log out: invalidate a session token immediately. Always returns {revoked: true}, whether or not the token existed, so this can't be used to probe for valid tokens.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"session_token":{"type":"string"}},"required":["session_token"]}}}},"responses":{"200":{"description":"{revoked: true}"}}}},"/v1/render":{"post":{"operationId":"render","summary":"Render HTML or a template to an image (Authorization: Bearer header required)","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"html":{"type":"string","description":"Raw HTML. Satori CSS subset: flexbox only, no grid. Required unless template is set."},"css":{"type":"string","description":"Optional CSS appended in a <style> block."},"template":{"type":"string","description":"Template name (see /v1/templates). Required unless html is set."},"data":{"type":"object","description":"Template data fields."},"brand":{"type":"object","properties":{"name":{"type":"string"},"primary_color":{"type":"string","description":"Hex color, e.g. #5138EE"},"theme":{"type":"string","enum":["indigo","emerald","crimson","amber","ocean","mono"],"description":"Named accent preset; ignored when primary_color is set."},"logo_url":{"type":"string","description":"Public https image URL, fetched and inlined (max 2MB)."},"logo_data":{"type":"string","description":"data:image/... URI used directly as the logo."},"font":{"type":"string","enum":["Inter","Plus Jakarta Sans","Space Grotesk","Poppins","Lora","Playfair Display","Merriweather","Outfit"],"description":"Display font for templates (default Inter)."}}},"width":{"type":"integer","default":1200,"maximum":4096},"height":{"type":"integer","default":630,"maximum":4096},"format":{"type":"string","enum":["png","svg"],"default":"png"},"response":{"type":"string","enum":["url","image"],"default":"url"}}}}}},"responses":{"200":{"description":"Rendered image","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"},"width":{"type":"integer"},"height":{"type":"integer"},"format":{"type":"string"},"render_ms":{"type":"integer"}}}},"image/png":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request"},"401":{"description":"Missing or invalid Authorization header"}}}},"/v1/usage":{"get":{"operationId":"getUsage","summary":"Render counts for the authenticated account (quota is shared across every key on it), plus its active key list.","security":[{"bearer":[]}],"responses":{"200":{"description":"{email, month, renders_this_month, renders_total, monthly_quota, remaining, created_at, keys: [{id, name, created_at}]}"}}}},"/v1/templates":{"get":{"operationId":"listTemplates","summary":"List templates with fields and examples (no key needed)","responses":{"200":{"description":"Template catalog"}}}},"/r/{key}":{"get":{"operationId":"getRender","summary":"Fetch a previously rendered image (valid 24h)","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Image bytes"},"404":{"description":"Not found or expired"}}}}}}