{"openapi":"3.1.0","info":{"title":"VectorFast Render API","version":"0.1.0","summary":"Render on-brand images from official templates and JSON params.","description":"Authenticate every `/v1` call except this document with an API key created in your account: `Authorization: Bearer vf_live_…`.\n\nEach key is limited to 60 requests per 60 seconds. Errors always have the shape `{\"error\": {\"code\", \"message\", \"docs\"}}`.\n\n**Billing is not connected yet** (plan T2.4): `credits_charged` is always 0 and `GET /v1/usage` reports `credits_remaining: null`.\n\nTemplates: launch-post, blog-post, social-square, achievement-card, invite-card, stat-card, changelog, quote-card. `GET /v1/templates/{id}` returns each one's params schema, examples and an `agent_example` (a prompt and the render call it should become). A template listed with `status: \"planned\"` cannot be rendered yet.","termsOfService":"https://vectorfast.ai/terms"},"servers":[{"url":"https://vectorfast.ai","description":"Production (same origin as the signed image URLs)"}],"security":[{"apiKey":[]}],"tags":[{"name":"templates","description":"Official templates, their param schemas and preview images."},{"name":"renders","description":"Synchronous renders and watermarked previews."},{"name":"brand-kits","description":"Versioned brand kits: colors, fonts and a logo that templates render with."},{"name":"account","description":"Usage for the current API key's account."},{"name":"meta","description":"This document."}],"paths":{"/v1/templates":{"get":{"tags":["templates"],"operationId":"listTemplates","summary":"List templates","description":"All catalog templates. `preview_url` is a signed `/i/` URL of the English example at the default size.","responses":{"200":{"description":"The catalog.","content":{"application/json":{"schema":{"type":"object","required":["templates"],"properties":{"templates":{"type":"array","items":{"$ref":"#/components/schemas/TemplateListing"}}}}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 60 requests in 60 seconds for this API key (`rate_limited`); honour `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The API is not configured or a dependency is down (`render_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/templates/{id}":{"get":{"tags":["templates"],"operationId":"getTemplate","summary":"Describe a template","description":"Params JSON Schema (draft 2020-12, generated from the template's zod schema), sizes, examples and an agent prompt.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","enum":["launch-post","blog-post","social-square","achievement-card","invite-card","stat-card","changelog","quote-card"]}}],"responses":{"200":{"description":"The template.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateDescription"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The id is not in the catalog (`template_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 60 requests in 60 seconds for this API key (`rate_limited`); honour `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The API is not configured or a dependency is down (`render_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/renders":{"post":{"tags":["renders"],"operationId":"createRender","summary":"Render an image","description":"Validates the template, params, format and size, then answers from the edge cache, the R2 cache or a fresh render (`cached` tells which).\n\nSend `Idempotency-Key` to make retries safe: the same key with the same body returns the stored JSON for 24 hours (header `Idempotent-Replayed: true`); the same key with a different body answers 409 `invalid_params`.\n\nEvery call records one usage row (template, format, cached); params are never stored.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255},"description":"Client-chosen retry key, printable ASCII without spaces."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderRequest"},"examples":{"launch-post":{"summary":"Launch post","value":{"template":"launch-post@1","format":"png","params":{"title":"Introducing scheduled renders","subtitle":"Ship on-brand images from your CI, every time a release goes out.","badge":"New"},"response":"url"}},"blog-post":{"summary":"Blog post","value":{"template":"blog-post@1","format":"png","params":{"title":"Why we moved image generation into CI","excerpt":"Deterministic templates, one brand kit, and no designer in the loop for release-day assets.","author":"Mia Chen","category":"Engineering","readTime":"6 min read"},"response":"url"}},"social-square":{"summary":"Social square","value":{"template":"social-square@1","format":"png","params":{"headline":"Your agent now ships on-brand images","body":"Connect once, pick a template, pass the copy. Every render matches the brand kit.","handle":"@vectorfast","cta":"Join the waitlist"},"response":"url"}},"achievement-card":{"summary":"Achievement card","value":{"template":"achievement-card@1","format":"png","params":{"title":"First 1,000 images rendered","subtitle":"Your brand kit has been busy. Here is to the next thousand.","metric":"1K","date":"Oct 30, 2026","user_name":"Ana Lima"},"response":"url"}},"invite-card":{"summary":"Invite card","value":{"template":"invite-card@1","format":"png","params":{"headline":"Join the team on VectorFast Render","inviter_name":"Mia Chen","code":"TEAM-42","cta":"Accept invite","expires":"Expires Nov 30"},"response":"url"}},"stat-card":{"summary":"Stat card","value":{"template":"stat-card@1","format":"png","params":{"value":"98%","label":"of renders served from cache","delta":"+4 pts","period":"October 2026","footnote":"Edge and R2 hits over all signed-URL requests."},"response":"url"}},"changelog":{"summary":"Changelog","value":{"template":"changelog@1","format":"png","params":{"version":"v2.4.0","date":"Oct 30, 2026","tag":"Release","items":["Remote MCP server for Claude, Cursor and Codex","Signed image URLs that never expire","WebP and JPEG output next to PNG"]},"response":"url"}},"quote-card":{"summary":"Quote card","value":{"template":"quote-card@1","format":"png","params":{"quote":"Our release images used to take a designer half a day. Now the agent ships them with the changelog.","author":"Priya Nair","role":"Head of Growth","company":"Northwind"},"response":"url"}}}}}},"responses":{"200":{"description":"The render. With `response: \"url\"` a JSON body; with `response: \"binary\"` the image bytes and the same facts in headers.","headers":{"X-Cache-Key":{"schema":{"type":"string"}},"X-Cache":{"schema":{"type":"string","enum":["hit-edge","hit-r2","miss","replay"]}},"X-Credits-Charged":{"schema":{"type":"string","const":"0"}},"X-Render-Id":{"schema":{"type":"string"}},"Idempotent-Replayed":{"schema":{"type":"string","const":"true"},"description":"Present when an earlier response was replayed."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderResponse"}},"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/webp":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request body, params, size or Idempotency-Key (`invalid_params`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown or planned template, or unknown brand kit (`template_not_found`, `brand_kit_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`Idempotency-Key` reused with a different body (`invalid_params`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body larger than 256 KiB (`invalid_params`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 60 requests in 60 seconds for this API key (`rate_limited`); honour `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The renderer failed (`render_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Fonts could not be loaded (`render_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The API is not configured or a dependency is down (`render_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/previews":{"post":{"tags":["renders"],"operationId":"createPreview","summary":"Render a watermarked preview","description":"Same body and validation as `/v1/renders`; the image carries a translucent PREVIEW label, is cached at the edge only (never in R2), charges nothing and records no usage. `Idempotency-Key` is ignored.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderRequest"},"examples":{"launch-post":{"summary":"Launch post","value":{"template":"launch-post@1","format":"png","params":{"title":"Introducing scheduled renders","subtitle":"Ship on-brand images from your CI, every time a release goes out.","badge":"New"},"response":"url"}},"blog-post":{"summary":"Blog post","value":{"template":"blog-post@1","format":"png","params":{"title":"Why we moved image generation into CI","excerpt":"Deterministic templates, one brand kit, and no designer in the loop for release-day assets.","author":"Mia Chen","category":"Engineering","readTime":"6 min read"},"response":"url"}},"social-square":{"summary":"Social square","value":{"template":"social-square@1","format":"png","params":{"headline":"Your agent now ships on-brand images","body":"Connect once, pick a template, pass the copy. Every render matches the brand kit.","handle":"@vectorfast","cta":"Join the waitlist"},"response":"url"}},"achievement-card":{"summary":"Achievement card","value":{"template":"achievement-card@1","format":"png","params":{"title":"First 1,000 images rendered","subtitle":"Your brand kit has been busy. Here is to the next thousand.","metric":"1K","date":"Oct 30, 2026","user_name":"Ana Lima"},"response":"url"}},"invite-card":{"summary":"Invite card","value":{"template":"invite-card@1","format":"png","params":{"headline":"Join the team on VectorFast Render","inviter_name":"Mia Chen","code":"TEAM-42","cta":"Accept invite","expires":"Expires Nov 30"},"response":"url"}},"stat-card":{"summary":"Stat card","value":{"template":"stat-card@1","format":"png","params":{"value":"98%","label":"of renders served from cache","delta":"+4 pts","period":"October 2026","footnote":"Edge and R2 hits over all signed-URL requests."},"response":"url"}},"changelog":{"summary":"Changelog","value":{"template":"changelog@1","format":"png","params":{"version":"v2.4.0","date":"Oct 30, 2026","tag":"Release","items":["Remote MCP server for Claude, Cursor and Codex","Signed image URLs that never expire","WebP and JPEG output next to PNG"]},"response":"url"}},"quote-card":{"summary":"Quote card","value":{"template":"quote-card@1","format":"png","params":{"quote":"Our release images used to take a designer half a day. Now the agent ships them with the changelog.","author":"Priya Nair","role":"Head of Growth","company":"Northwind"},"response":"url"}}}}}},"responses":{"200":{"description":"The preview; `watermark: true` in JSON responses.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderResponse"}},"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/webp":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request body, params, size or Idempotency-Key (`invalid_params`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown or planned template, or unknown brand kit (`template_not_found`, `brand_kit_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body larger than 256 KiB (`invalid_params`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 60 requests in 60 seconds for this API key (`rate_limited`); honour `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The renderer failed (`render_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The API is not configured or a dependency is down (`render_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/usage":{"get":{"tags":["account"],"operationId":"getUsage","summary":"Usage for the current period","description":"Renders counted in the current UTC calendar month for the key's account. `credits_charged` is 0 and `credits_remaining` is `null` until T2.4 connects the credit ledger, after which the period follows the subscription.","responses":{"200":{"description":"Usage.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Usage"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 60 requests in 60 seconds for this API key (`rate_limited`); honour `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The API is not configured or a dependency is down (`render_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/brand-kits":{"get":{"tags":["brand-kits"],"operationId":"listBrandKits","summary":"List brand kits","description":"Your kits at their current version, oldest first (the first is the default for renders). With no kits of your own the list holds the read-only built-in `default` kit. `limit` is how many kits your plan includes.","responses":{"200":{"description":"The kits.","content":{"application/json":{"schema":{"type":"object","required":["brand_kits","limit"],"properties":{"brand_kits":{"type":"array","items":{"$ref":"#/components/schemas/BrandKit"}},"limit":{"type":"integer"}}}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 60 requests in 60 seconds for this API key (`rate_limited`); honour `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The API is not configured or a dependency is down (`render_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["brand-kits"],"operationId":"createBrandKit","summary":"Create a brand kit","description":"Creates version 1. Colors and fonts you leave out start from the built-in kit. Font families must be Google Fonts families that load at weights 400 and 700; custom font uploads are not available yet.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandKitCreate"}}}},"responses":{"201":{"description":"The new kit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandKit"}}}},"400":{"description":"Invalid request body, params, size or Idempotency-Key (`invalid_params`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your plan's brand-kit limit is reached (`brand_kit_limit_reached`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body larger than 256 KiB (`invalid_params`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 60 requests in 60 seconds for this API key (`rate_limited`); honour `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The API is not configured or a dependency is down (`render_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/brand-kits/{id}":{"get":{"tags":["brand-kits"],"operationId":"getBrandKit","summary":"Read a brand kit","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^(bk_[a-z2-7]{16}|default)(?:@(\\d{1,9}))?$"},"description":"Brand kit id. `GET` also accepts a pinned version such as `bk_…@2` and the built-in `default`."}],"responses":{"200":{"description":"The kit at its current (or pinned) version.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandKit"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such kit or version on your account (`brand_kit_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 60 requests in 60 seconds for this API key (`rate_limited`); honour `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The API is not configured or a dependency is down (`render_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"tags":["brand-kits"],"operationId":"updateBrandKit","summary":"Update a brand kit","description":"Saves a new version. Fields you leave out keep their current values; the logo only changes through `POST /v1/brand-kits/{id}/logo`. Earlier versions stay readable and renderable when pinned.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^(bk_[a-z2-7]{16}|default)(?:@(\\d{1,9}))?$"},"description":"Brand kit id. `GET` also accepts a pinned version such as `bk_…@2` and the built-in `default`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandKitUpdate"}}}},"responses":{"200":{"description":"The kit at its new version.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandKit"}}}},"400":{"description":"Invalid body, or the read-only built-in kit (`invalid_params`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such kit on your account (`brand_kit_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The kit changed while the request ran (`brand_kit_conflict`); read it and retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body larger than 256 KiB (`invalid_params`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 60 requests in 60 seconds for this API key (`rate_limited`); honour `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The API is not configured or a dependency is down (`render_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["brand-kits"],"operationId":"deleteBrandKit","summary":"Delete a brand kit","description":"Soft delete: the kit stops counting toward your limit, renders that name it answer 404, and its signed image URLs stop rendering on a cache miss.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^(bk_[a-z2-7]{16}|default)(?:@(\\d{1,9}))?$"},"description":"Brand kit id. `GET` also accepts a pinned version such as `bk_…@2` and the built-in `default`."}],"responses":{"200":{"description":"Deleted.","content":{"application/json":{"schema":{"type":"object","required":["id","deleted"],"properties":{"id":{"type":"string"},"deleted":{"type":"boolean","const":true}}}}}},"400":{"description":"The read-only built-in kit (`invalid_params`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such kit on your account (`brand_kit_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 60 requests in 60 seconds for this API key (`rate_limited`); honour `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The API is not configured or a dependency is down (`render_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/brand-kits/{id}/logo":{"post":{"tags":["brand-kits"],"operationId":"uploadBrandKitLogo","summary":"Upload a logo","description":"Send the file as the raw body with `Content-Type: image/png`, `image/jpeg` or `image/svg+xml`, or as `multipart/form-data` with a part named `file`. At most 5 MB and 4096×4096 pixels; the file name is ignored and never stored.\n\nPNG and JPEG logos are traced to SVG with the same engine as the free VectorFast vectorizer, then cleaned (tiny fragments, near-duplicate colors, coordinate precision); the untouched trace is kept too. SVG logos skip tracing and are sanitized (scripts, event handlers, external links and embedded HTML are removed). Both get a 2x PNG. Tracing is a best-effort candidate, not an exact reconstruction of the original artwork.\n\nSaves a new kit version; renders and signed URLs that name the kit use it from then on. Limited to 3 uploads a minute and 10 an hour per API key.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^(bk_[a-z2-7]{16}|default)(?:@(\\d{1,9}))?$"},"description":"Brand kit id. `GET` also accepts a pinned version such as `bk_…@2` and the built-in `default`."}],"requestBody":{"required":true,"content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/svg+xml":{"schema":{"type":"string","format":"binary"}},"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"200":{"description":"The kit at the version that carries the logo.","content":{"application/json":{"schema":{"type":"object","required":["brand_kit","version","logo"],"properties":{"brand_kit":{"$ref":"#/components/schemas/BrandKit"},"version":{"type":"string"},"logo":{"$ref":"#/components/schemas/BrandKit/properties/logo"}}}}}},"400":{"description":"Over 5 MB or 4096×4096, a body that does not match its Content-Type, an unreadable image, or the read-only built-in kit (`invalid_params`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such kit on your account (`brand_kit_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The kit changed while the upload ran (`brand_kit_conflict`); upload again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Content-Type is not PNG, JPEG, SVG or multipart (`invalid_params`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 3 uploads a minute or 10 an hour for this API key (`rate_limited`); honour `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The pipeline or storage failed (`render_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The API is not configured or a dependency is down (`render_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/openapi.json":{"get":{"tags":["meta"],"operationId":"getOpenApi","summary":"This document","security":[],"responses":{"200":{"description":"The OpenAPI 3.1 document.","content":{"application/json":{"schema":{"type":"object"}}}}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"vf_live_<key_id><secret>","description":"Create keys in your VectorFast account. Keys are shown once; revoking one takes effect on the next request."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","docs"],"properties":{"code":{"type":"string","enum":["invalid_params","unauthorized","insufficient_credits","template_not_found","brand_kit_not_found","brand_kit_limit_reached","brand_kit_conflict","not_found","rate_limited","render_failed","render_timeout","render_unavailable"]},"message":{"type":"string","description":"Public-safe; names a field or rule, never echoes a value."},"docs":{"type":"string","format":"uri","examples":["https://vectorfast.ai/render/docs/errors#invalid_params"]}}}}},"RenderRequest":{"type":"object","properties":{"template":{"type":"string","pattern":"^([a-z0-9-]{1,64})(?:@(\\d{1,3}))?$"},"format":{"default":"png","type":"string","enum":["png","jpeg","webp"]},"width":{"type":"integer","minimum":16,"maximum":4096},"height":{"type":"integer","minimum":16,"maximum":4096},"scale":{"default":1,"anyOf":[{"type":"number","const":1},{"type":"number","const":2}]},"brand_kit":{"type":"string","minLength":1,"maxLength":64},"params":{"type":"object","propertyNames":{"type":"string","maxLength":64},"additionalProperties":{}},"response":{"default":"url","type":"string","enum":["url","binary"]}},"required":["template","params"],"title":"RenderRequest","description":"Body of `POST /v1/renders` and `POST /v1/previews`. `params` must satisfy the template's own schema (see `GET /v1/templates/{id}` → `params_schema`, or the `TemplateParams_*` components)."},"RenderResponse":{"type":"object","required":["id","url","cache_key","cached","credits_charged","width","height","scale","format","render_ms","template_version"],"properties":{"id":{"type":"string","pattern":"^rnd_[0-9a-f]{32}$","description":"Render id; stable across idempotent replays."},"url":{"type":"string","format":"uri","description":"Signed `/i/{template}.{format}` URL serving the same pixels. It never expires."},"cache_key":{"type":"string","pattern":"^[0-9a-f]{64}$"},"cached":{"type":"boolean","description":"True when a cache layer answered and nothing was rendered."},"credits_charged":{"type":"integer","const":0,"description":"Always 0 in this phase: credits are connected in T2.4 (plan §9). Cache hits will stay 0."},"width":{"type":"integer"},"height":{"type":"integer"},"scale":{"type":"integer","enum":[1,2]},"format":{"type":"string","enum":["png","jpeg","webp"]},"render_ms":{"type":"number","description":"Renderer time (layout + raster + encode) for this request; 0 on cache hits."},"template_version":{"type":"string","description":"`<id>@<semver>` of the template that produced the pixels.","examples":["launch-post@1.0.0"]},"watermark":{"type":"boolean","const":true,"description":"Present only on `/v1/previews` responses."}}},"BrandKit":{"type":"object","required":["id","name","version","version_number","is_default","read_only","colors","fonts","logo","created_at","updated_at"],"properties":{"id":{"type":"string","description":"`bk_` + 16 base32 characters, or `default` for the built-in kit.","examples":["bk_3k7qz2m4xw5vb6na"]},"name":{"type":"string"},"version":{"type":"string","description":"`<id>@<n>`. Every save creates a new version; renders fold it into the cache key, so old images are never served for a changed kit.","examples":["bk_3k7qz2m4xw5vb6na@2"]},"version_number":{"type":"integer","minimum":1},"is_default":{"type":"boolean","description":"The kit renders use when `brand_kit` is omitted: your oldest kit, or the built-in kit when you have none."},"read_only":{"type":"boolean","description":"True only for the built-in `default` kit."},"colors":{"type":"object","required":["background","foreground","muted","accent","accentForeground","surface"],"properties":{"background":{"type":"string","pattern":"^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$"},"foreground":{"type":"string","pattern":"^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$"},"muted":{"type":"string","pattern":"^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$"},"accent":{"type":"string","pattern":"^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$"},"accentForeground":{"type":"string","pattern":"^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$"},"surface":{"type":"string","pattern":"^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$"}}},"fonts":{"type":"object","required":["heading","body"],"properties":{"heading":{"type":"object","required":["family","source"],"properties":{"family":{"type":"string"},"source":{"type":"string","enum":["google"]}}},"body":{"type":"object","required":["family","source"],"properties":{"family":{"type":"string"},"source":{"type":"string","enum":["google"]}}}}},"logo":{"type":["object","null"],"description":"Set by `POST /v1/brand-kits/{id}/logo`. URLs are signed, immutable and never expire.","required":["svg_url","png_url","source_format","width","height","png_width","png_height","path_count"],"properties":{"svg_url":{"type":["string","null"],"format":"uri"},"png_url":{"type":["string","null"],"format":"uri"},"source_format":{"type":"string","enum":["png","jpeg","svg"]},"width":{"type":"integer"},"height":{"type":"integer"},"png_width":{"type":"integer","description":"Twice `width` (capped at 4096)."},"png_height":{"type":"integer"},"path_count":{"type":"integer"}}},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"}}},"BrandKitCreate":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64},"colors":{"type":"object","properties":{"background":{"type":"string","pattern":"^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$"},"foreground":{"type":"string","pattern":"^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$"},"muted":{"type":"string","pattern":"^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$"},"accent":{"type":"string","pattern":"^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$"},"accentForeground":{"type":"string","pattern":"^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$"},"surface":{"type":"string","pattern":"^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$"}},"additionalProperties":false},"fonts":{"type":"object","properties":{"heading":{"type":"object","properties":{"family":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Z0-9][\\w ]*$"},"source":{"default":"google","type":"string","enum":["google","upload"]}},"required":["family"],"additionalProperties":false},"body":{"type":"object","properties":{"family":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Z0-9][\\w ]*$"},"source":{"default":"google","type":"string","enum":["google","upload"]}},"required":["family"],"additionalProperties":false}},"additionalProperties":false}},"required":["name"],"additionalProperties":false,"title":"BrandKitCreate"},"BrandKitUpdate":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64},"colors":{"type":"object","properties":{"background":{"type":"string","pattern":"^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$"},"foreground":{"type":"string","pattern":"^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$"},"muted":{"type":"string","pattern":"^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$"},"accent":{"type":"string","pattern":"^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$"},"accentForeground":{"type":"string","pattern":"^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$"},"surface":{"type":"string","pattern":"^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$"}},"additionalProperties":false},"fonts":{"type":"object","properties":{"heading":{"type":"object","properties":{"family":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Z0-9][\\w ]*$"},"source":{"default":"google","type":"string","enum":["google","upload"]}},"required":["family"],"additionalProperties":false},"body":{"type":"object","properties":{"family":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Z0-9][\\w ]*$"},"source":{"default":"google","type":"string","enum":["google","upload"]}},"required":["family"],"additionalProperties":false}},"additionalProperties":false}},"additionalProperties":false,"title":"BrandKitUpdate"},"TemplateListing":{"type":"object","required":["id","name","description","status","version","sizes","default_size","formats","preview_url"],"properties":{"id":{"type":"string","enum":["launch-post","blog-post","social-square","achievement-card","invite-card","stat-card","changelog","quote-card"]},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["ready","planned"],"description":"`planned` templates have a contract but no component yet and cannot be rendered."},"version":{"type":["string","null"],"description":"Semver of the component; `null` while planned."},"sizes":{"type":"array","items":{"type":"array","items":{"type":"integer"},"minItems":2,"maxItems":2}},"default_size":{"type":"array","items":{"type":"integer"},"minItems":2,"maxItems":2},"formats":{"type":"array","items":{"type":"string","enum":["png","jpeg","webp"]}},"preview_url":{"type":["string","null"],"format":"uri"}}},"TemplateDescription":{"allOf":[{"$ref":"#/components/schemas/TemplateListing"},{"type":"object","required":["params_schema","examples","agent_prompt","agent_example"],"properties":{"params_schema":{"type":["object","null"],"description":"JSON Schema 2020-12 for `params`; `null` while planned."},"examples":{"type":["object","null"],"properties":{"en":{"type":"object"},"zh":{"type":"object"},"long":{"type":"object"},"minimal":{"type":"object"}}},"agent_prompt":{"type":["string","null"],"description":"One-line prompt an agent could send to get this image; same as `agent_example.prompt`."},"agent_example":{"type":["object","null"],"description":"A prompt and the `POST /v1/renders` / MCP `render` arguments it should become; `null` while planned.","required":["prompt","call"],"properties":{"prompt":{"type":"string"},"call":{"type":"object","required":["template","params"],"properties":{"template":{"type":"string"},"params":{"type":"object"},"format":{"type":"string","enum":["png","jpeg","webp"]},"width":{"type":"integer"},"height":{"type":"integer"}}}}}}}]},"Usage":{"type":"object","required":["period","renders","cached_renders","credits_charged","credits_remaining"],"properties":{"period":{"type":"object","required":["start","end"],"properties":{"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"}}},"renders":{"type":"integer","description":"`POST /v1/renders` calls in the period, cache hits included."},"cached_renders":{"type":"integer"},"credits_charged":{"type":"integer","const":0,"description":"Always 0 until T2.4."},"credits_remaining":{"type":["integer","null"],"description":"`null` until T2.4 connects the credit ledger."}}},"TemplateParams_launch-post":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":120},"subtitle":{"type":"string","maxLength":200},"badge":{"type":"string","maxLength":24}},"required":["title"],"title":"Launch post params","description":"Product or feature launch card: badge, headline and one-line summary."},"TemplateParams_blog-post":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":110},"excerpt":{"type":"string","maxLength":180},"author":{"type":"string","maxLength":60},"category":{"type":"string","maxLength":24},"readTime":{"type":"string","maxLength":20},"coverImage":{"type":"string","maxLength":2048}},"required":["title"],"title":"Blog post params","description":"Open Graph card for an article: category, title, excerpt and byline."},"TemplateParams_social-square":{"type":"object","properties":{"headline":{"type":"string","minLength":1,"maxLength":90},"body":{"type":"string","maxLength":160},"handle":{"type":"string","maxLength":40},"cta":{"type":"string","maxLength":30}},"required":["headline"],"title":"Social square params","description":"Square feed post: large headline, short body, handle and call to action."},"TemplateParams_achievement-card":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":80},"subtitle":{"type":"string","maxLength":140},"metric":{"type":"string","maxLength":10},"date":{"type":"string","maxLength":24},"user_name":{"type":"string","maxLength":40},"avatar_url":{"type":"string","maxLength":2048}},"required":["title"],"title":"Achievement card params","description":"User milestone or badge unlock: a medal with a number, the achievement, and who earned it."},"TemplateParams_invite-card":{"type":"object","properties":{"headline":{"type":"string","minLength":1,"maxLength":90},"inviter_name":{"type":"string","maxLength":40},"code":{"type":"string","maxLength":16},"cta":{"type":"string","maxLength":30},"expires":{"type":"string","maxLength":30}},"required":["headline"],"title":"Invite card params","description":"Invitation or referral card: headline, who invited you, a code on a ticket stub and a call to action."},"TemplateParams_stat-card":{"type":"object","properties":{"value":{"type":"string","minLength":1,"maxLength":12},"label":{"type":"string","minLength":1,"maxLength":80},"delta":{"type":"string","maxLength":16},"period":{"type":"string","maxLength":30},"footnote":{"type":"string","maxLength":120}},"required":["value","label"],"title":"Stat card params","description":"One big number with its label, change and period, for reports, dashboards and year-in-review posts."},"TemplateParams_changelog":{"type":"object","properties":{"version":{"type":"string","minLength":1,"maxLength":24},"date":{"type":"string","maxLength":30},"items":{"anyOf":[{"minItems":1,"maxItems":5,"type":"array","items":{"type":"string","minLength":1,"maxLength":80}},{"type":"string","maxLength":2048}],"description":"One to five highlights, each up to 80 characters. Pass an array of strings; a JSON-encoded array string is also accepted."},"tag":{"type":"string","maxLength":24}},"required":["version","items"],"title":"Changelog params","description":"Release card: version, date, a tag and up to five highlights."},"TemplateParams_quote-card":{"type":"object","properties":{"quote":{"type":"string","minLength":1,"maxLength":240},"author":{"type":"string","minLength":1,"maxLength":60},"role":{"type":"string","maxLength":60},"company":{"type":"string","maxLength":60},"avatar_url":{"type":"string","maxLength":2048}},"required":["quote","author"],"title":"Quote card params","description":"Customer quote or testimonial with the author's name, role and company."}}}}