{"openapi":"3.0.3","info":{"title":"Thumbnail Studio API","version":"1.0.0","description":"Server-side thumbnail renderer. Templates are the single source of truth: every template registered in the codebase is listed by /api/templates and renderable via /api/generate."},"tags":[{"name":"Templates","description":"Template discovery"},{"name":"Generate","description":"Image rendering"}],"components":{"schemas":{"GenerateRequest":{"type":"object","properties":{"templateId":{"type":"string","minLength":1,"example":"dark-pro","description":"Id of the template to render. See GET /api/templates."},"modifications":{"$ref":"#/components/schemas/Modifications"},"overrides":{"type":"array","items":{"$ref":"#/components/schemas/ElementOverride"},"description":"Optional per-element layout overrides (transforms, visibility)."},"layerOrder":{"type":"array","items":{"type":"integer","minimum":0},"description":"Optional element index order for z-ordering (bottom-to-top). If provided, elements are drawn in this sequence.","example":[2,0,1]}},"required":["templateId"]},"Modifications":{"type":"object","additionalProperties":{"type":"string"},"description":"Map of customizable field id to new value. Text fields take plain text (use \\n for line breaks); image fields take a publicly reachable image URL.","example":{"main-title":"MY NEW VIDEO\nIS LIVE","subtitle":"Everything you need to know","img-slot-1":"https://example.com/screenshot.png"}},"ElementOverride":{"type":"object","properties":{"index":{"type":"integer","minimum":0,"description":"Element index in the template's `elements` array (see GET /api/templates/{id}).","example":5},"left":{"type":"number"},"top":{"type":"number"},"angle":{"type":"number"},"width":{"type":"number","minimum":0,"exclusiveMinimum":true},"height":{"type":"number","minimum":0,"exclusiveMinimum":true},"rx":{"type":"number","minimum":0},"ry":{"type":"number","minimum":0},"scaleX":{"type":"number","minimum":0,"exclusiveMinimum":true},"scaleY":{"type":"number","minimum":0,"exclusiveMinimum":true},"visible":{"type":"boolean","description":"Set false to remove the element from the render (layer delete)."},"imageX":{"type":"number"},"imageY":{"type":"number"},"imageScale":{"type":"number","minimum":0,"exclusiveMinimum":true}},"required":["index"],"description":"Layout override for one element — move, rotate, resize, or hide it. The editor generates these when layers are transformed or deleted."},"ErrorResponse":{"type":"object","properties":{"error":{"type":"string","example":"Failed to generate image"},"details":{"type":"string","example":"Template 'unknown' not found."}},"required":["error"]},"TemplateList":{"type":"array","items":{"$ref":"#/components/schemas/TemplateMetadata"}},"TemplateMetadata":{"type":"object","properties":{"id":{"type":"string","example":"dark-pro"},"name":{"type":"string","example":"Dark Pro Tech"},"description":{"type":"string","example":"Deep violet gradient with modern bold headlines."},"category":{"type":"string","example":"Tech"},"width":{"type":"integer","example":1280,"description":"Output width in pixels (1280 for YouTube, 1080 for Instagram)."},"height":{"type":"integer","example":720,"description":"Output height in pixels (720 YouTube, 1350 IG portrait, 1080 IG square)."},"accent":{"type":"string","example":"#f59e0b","description":"Accent color used by the editor UI."},"background":{"$ref":"#/components/schemas/Background"},"elements":{"type":"array","items":{"$ref":"#/components/schemas/TemplateElement"},"description":"Template elements for instant preview rendering."},"customizableFields":{"type":"array","items":{"$ref":"#/components/schemas/CustomizableField"}}},"required":["id","name","description","category","width","height","accent","background","customizableFields"]},"Background":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["solid"]},"color":{"type":"string","example":"#0f0c29"}},"required":["type","color"]},{"type":"object","properties":{"type":{"type":"string","enum":["gradient"]},"start":{"type":"string","example":"#0f0c29"},"end":{"type":"string","example":"#24243e"},"angle":{"anyOf":[{"type":"number","enum":[0]},{"type":"number","enum":[45]},{"type":"number","enum":[90]},{"type":"number","enum":[180]},{"type":"number","enum":[270]}]}},"required":["type","start","end","angle"]}],"description":"Canvas background of a template."},"TemplateElement":{"type":"object","properties":{"type":{"type":"string","enum":["rect","triangle","line","polygon","polyline","text","image"]},"id":{"type":"string","description":"Present on elements that can be customized via `modifications`."}},"required":["type"],"additionalProperties":{"nullable":true},"description":"One declarative layout element (geometry, styling, and defaults vary by type)."},"CustomizableField":{"type":"object","properties":{"id":{"type":"string","example":"main-title","description":"Field id — use as a key in `modifications`."},"kind":{"type":"string","enum":["text","image"],"description":"`text` fields take plain text; `image` fields take an image URL."},"label":{"type":"string","example":"Main title"},"description":{"type":"string","example":"Main title text (bold uppercase)"},"multiline":{"type":"boolean","description":"Hint that the text field supports line breaks."}},"required":["id","kind","label","description"]},"TemplateDefinition":{"allOf":[{"$ref":"#/components/schemas/TemplateMetadata"},{"type":"object","properties":{"elements":{"type":"array","items":{"$ref":"#/components/schemas/TemplateElement"},"description":"Ordered layout elements — array order is z-order."}},"required":["elements"]}]}},"parameters":{}},"paths":{"/api/templates":{"get":{"tags":["Templates"],"summary":"List all templates","description":"Returns metadata for every registered template, including its category and the customizable field ids accepted by the generate endpoints. Current categories: Tech News, Cloud & Architecture, Software Engineering, Tech Leadership, System Design, AI & Emerging Tech, Cybersecurity, Breaking News.","responses":{"200":{"description":"Template metadata list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateList"}}}}}},"post":{"tags":["Templates"],"summary":"Register or update a JSON template","description":"Dynamically register a new JSON template definition or update an existing one.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateDefinition"}}}},"responses":{"201":{"description":"Template registered successfully."},"400":{"description":"Invalid template definition.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/templates/{id}":{"get":{"tags":["Templates"],"summary":"Get one template with full layout","description":"Returns the complete declarative definition (metadata + layout elements). Used by canvas editors that render templates client-side.","parameters":[{"schema":{"type":"string","description":"Template id. One of: news-breaking-dark, news-headline-light, news-launch-light, news-minimal-light, news-editorial-serif, news-leak-exclusive, news-ai-spotlight, news-tech-gradient, news-dark-glass, news-quote-dark, news-gadget-review, news-benchmark-vs, news-update-amber, news-security-bulletin, news-alert-red, news-app-spotlight, news-feature-listicle, news-deal-funding, news-stat-infographic.","example":"dark-pro"},"required":true,"description":"Template id. One of: news-breaking-dark, news-headline-light, news-launch-light, news-minimal-light, news-editorial-serif, news-leak-exclusive, news-ai-spotlight, news-tech-gradient, news-dark-glass, news-quote-dark, news-gadget-review, news-benchmark-vs, news-update-amber, news-security-bulletin, news-alert-red, news-app-spotlight, news-feature-listicle, news-deal-funding, news-stat-infographic.","name":"id","in":"path"}],"responses":{"200":{"description":"Full template definition.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateDefinition"}}}},"404":{"description":"Template not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/generate":{"get":{"tags":["Generate"],"summary":"Render a thumbnail (query params)","description":"Convenience endpoint usable directly in an <img src>. Pass modifications as a URL-encoded JSON string.","parameters":[{"schema":{"type":"string","description":"Template id. One of: news-breaking-dark, news-headline-light, news-launch-light, news-minimal-light, news-editorial-serif, news-leak-exclusive, news-ai-spotlight, news-tech-gradient, news-dark-glass, news-quote-dark, news-gadget-review, news-benchmark-vs, news-update-amber, news-security-bulletin, news-alert-red, news-app-spotlight, news-feature-listicle, news-deal-funding, news-stat-infographic.","example":"dark-pro"},"required":true,"description":"Template id. One of: news-breaking-dark, news-headline-light, news-launch-light, news-minimal-light, news-editorial-serif, news-leak-exclusive, news-ai-spotlight, news-tech-gradient, news-dark-glass, news-quote-dark, news-gadget-review, news-benchmark-vs, news-update-amber, news-security-bulletin, news-alert-red, news-app-spotlight, news-feature-listicle, news-deal-funding, news-stat-infographic.","name":"templateId","in":"query"},{"schema":{"type":"string","description":"URL-encoded JSON object mapping field ids to values.","example":"{\"main-title\":\"HELLO WORLD\"}"},"required":false,"description":"URL-encoded JSON object mapping field ids to values.","name":"modifications","in":"query"},{"schema":{"type":"string","description":"URL-encoded JSON array of ElementOverride objects (transforms / layer visibility).","example":"[{\"index\":5,\"visible\":false}]"},"required":false,"description":"URL-encoded JSON array of ElementOverride objects (transforms / layer visibility).","name":"overrides","in":"query"}],"responses":{"200":{"description":"Rendered PNG at the template's own dimensions (see `width`/`height` in GET /api/templates).","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request (unknown template, unknown field id, or malformed body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Rendering failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Generate"],"summary":"Render a thumbnail (JSON body)","description":"Preferred endpoint for programmatic use.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateRequest"}}}},"responses":{"200":{"description":"Rendered PNG at the template's own dimensions (see `width`/`height` in GET /api/templates).","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request (unknown template, unknown field id, or malformed body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Rendering failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}