[{"data":1,"prerenderedAt":1167},["ShallowReactive",2],{"pricing-plans":3,"content-page-\u002Fdocs\u002Fauthentication":4,"api-docs-metadata":665,"i-ph:list":3,"i-ph:caret-right":3,"i-ph:caret-down":3,"i-ph:twitter-logo":3,"i-ph:youtube-logo":3,"i-ph:copy":3},null,{"id":5,"title":6,"body":7,"description":656,"extension":657,"meta":658,"navigation":315,"path":661,"seo":662,"stem":663,"__hash__":664},"all\u002Fdocs\u002Fauthentication.md","Authentication",{"type":8,"value":9,"toc":642},"minimark",[10,14,32,37,62,65,69,88,103,142,255,270,345,352,360,381,385,391,402,407,428,432,536,543,568,572,616,620,638],[11,12,6],"h1",{"id":13},"authentication",[15,16,17,18,22,23,26,27,31],"p",{},"API requests that modify or render resources require a valid ",[19,20,21],"strong",{},"API token"," (API key). Tokens are scoped to a ",[19,24,25],{},"space"," and used via the ",[28,29,30],"code",{},"Authorization"," header.",[33,34,36],"h2",{"id":35},"api-keys","API keys",[38,39,40,48,55],"ol",{},[41,42,43,44,47],"li",{},"Go to ",[19,45,46],{},"Settings → Space → Developers",".",[41,49,50,51,54],{},"Click ",[19,52,53],{},"Create API key",", give it a name and optional expiry.",[41,56,57,58,61],{},"Copy the token whenever you need it; you can also use ",[19,59,60],{},"Copy"," next to the masked value in the list. Store it securely and avoid sharing it publicly.",[15,63,64],{},"Tokens can be revoked from the same Developers page.",[33,66,68],{"id":67},"demo-tokens-no-account","Demo tokens (no account)",[15,70,71,72,75,76,79,80,83,84,87],{},"For quick demos — and so AI agents can try Bannx without signing up — you can mint a short-lived ",[19,73,74],{},"demo token",". Demo tokens are ",[19,77,78],{},"stored only in Redis"," (never in the database), expire after ",[19,81,82],{},"~1 hour",", and can render ",[19,85,86],{},"public templates only",". They never consume any space's credits.",[15,89,90,91,94,95,98,99,102],{},"To prevent abuse, each client IP can mint at most ",[19,92,93],{},"5 demo tokens per rolling hour","; after that, ",[28,96,97],{},"POST \u002Fapi\u002Fdemo-token"," returns ",[28,100,101],{},"429 Too Many Requests"," until the window resets.",[104,105,110],"pre",{"className":106,"code":107,"language":108,"meta":109,"style":109},"language-bash shiki shiki-themes github-light github-dark","# Mint a demo token (no auth required)\ncurl -s -X POST https:\u002F\u002Fyour-app.bannx.app\u002Fapi\u002Fdemo-token\n","bash","",[28,111,112,121],{"__ignoreMap":109},[113,114,117],"span",{"class":115,"line":116},"line",1,[113,118,120],{"class":119},"sJ8bj","# Mint a demo token (no auth required)\n",[113,122,124,128,132,135,139],{"class":115,"line":123},2,[113,125,127],{"class":126},"sScJk","curl",[113,129,131],{"class":130},"sj4cs"," -s",[113,133,134],{"class":130}," -X",[113,136,138],{"class":137},"sZZnC"," POST",[113,140,141],{"class":137}," https:\u002F\u002Fyour-app.bannx.app\u002Fapi\u002Fdemo-token\n",[104,143,147],{"className":144,"code":145,"language":146,"meta":109,"style":109},"language-json shiki shiki-themes github-light github-dark","{\n  \"token\": \"demo_xxxxxxxxxxxxxxxxxxxxxxxx\",\n  \"tokenType\": \"demo\",\n  \"expiresAt\": 1750000000000,\n  \"ttlSeconds\": 3600,\n  \"quota\": { \"limit\": 5, \"used\": 1, \"remaining\": 4 }\n}\n","json",[28,148,149,155,169,182,195,208,249],{"__ignoreMap":109},[113,150,151],{"class":115,"line":116},[113,152,154],{"class":153},"sVt8B","{\n",[113,156,157,160,163,166],{"class":115,"line":123},[113,158,159],{"class":130},"  \"token\"",[113,161,162],{"class":153},": ",[113,164,165],{"class":137},"\"demo_xxxxxxxxxxxxxxxxxxxxxxxx\"",[113,167,168],{"class":153},",\n",[113,170,172,175,177,180],{"class":115,"line":171},3,[113,173,174],{"class":130},"  \"tokenType\"",[113,176,162],{"class":153},[113,178,179],{"class":137},"\"demo\"",[113,181,168],{"class":153},[113,183,185,188,190,193],{"class":115,"line":184},4,[113,186,187],{"class":130},"  \"expiresAt\"",[113,189,162],{"class":153},[113,191,192],{"class":130},"1750000000000",[113,194,168],{"class":153},[113,196,198,201,203,206],{"class":115,"line":197},5,[113,199,200],{"class":130},"  \"ttlSeconds\"",[113,202,162],{"class":153},[113,204,205],{"class":130},"3600",[113,207,168],{"class":153},[113,209,211,214,217,220,222,225,228,231,233,236,238,241,243,246],{"class":115,"line":210},6,[113,212,213],{"class":130},"  \"quota\"",[113,215,216],{"class":153},": { ",[113,218,219],{"class":130},"\"limit\"",[113,221,162],{"class":153},[113,223,224],{"class":130},"5",[113,226,227],{"class":153},", ",[113,229,230],{"class":130},"\"used\"",[113,232,162],{"class":153},[113,234,235],{"class":130},"1",[113,237,227],{"class":153},[113,239,240],{"class":130},"\"remaining\"",[113,242,162],{"class":153},[113,244,245],{"class":130},"4",[113,247,248],{"class":153}," }\n",[113,250,252],{"class":115,"line":251},7,[113,253,254],{"class":153},"}\n",[15,256,257,258,261,262,265,266,269],{},"Use the token against ",[19,259,260],{},"GET"," ",[28,263,264],{},"\u002Fapi\u002Frender\u002F:pageId"," for a public template, either as a Bearer header or the ",[28,267,268],{},"demo"," query parameter:",[104,271,273],{"className":106,"code":272,"language":108,"meta":109,"style":109},"# Header\ncurl -L -H \"Authorization: Bearer demo_xxxxxxxxxxxxxxxxxxxxxxxx\" \\\n  \"https:\u002F\u002Fyour-app.bannx.app\u002Fapi\u002Frender\u002FPAGE_ID?format=png&output=binary\" \\\n  --output demo.png\n\n# Query param (handy for \u003Cimg> src)\ncurl -L \\\n  \"https:\u002F\u002Fyour-app.bannx.app\u002Fapi\u002Frender\u002FPAGE_ID?format=png&output=binary&demo=demo_xxxxxxxxxxxxxxxxxxxxxxxx\" \\\n  --output demo.png\n",[28,274,275,280,296,303,311,317,322,330,338],{"__ignoreMap":109},[113,276,277],{"class":115,"line":116},[113,278,279],{"class":119},"# Header\n",[113,281,282,284,287,290,293],{"class":115,"line":123},[113,283,127],{"class":126},[113,285,286],{"class":130}," -L",[113,288,289],{"class":130}," -H",[113,291,292],{"class":137}," \"Authorization: Bearer demo_xxxxxxxxxxxxxxxxxxxxxxxx\"",[113,294,295],{"class":130}," \\\n",[113,297,298,301],{"class":115,"line":171},[113,299,300],{"class":137},"  \"https:\u002F\u002Fyour-app.bannx.app\u002Fapi\u002Frender\u002FPAGE_ID?format=png&output=binary\"",[113,302,295],{"class":130},[113,304,305,308],{"class":115,"line":184},[113,306,307],{"class":130},"  --output",[113,309,310],{"class":137}," demo.png\n",[113,312,313],{"class":115,"line":197},[113,314,316],{"emptyLinePlaceholder":315},true,"\n",[113,318,319],{"class":115,"line":210},[113,320,321],{"class":119},"# Query param (handy for \u003Cimg> src)\n",[113,323,324,326,328],{"class":115,"line":251},[113,325,127],{"class":126},[113,327,286],{"class":130},[113,329,295],{"class":130},[113,331,333,336],{"class":115,"line":332},8,[113,334,335],{"class":137},"  \"https:\u002F\u002Fyour-app.bannx.app\u002Fapi\u002Frender\u002FPAGE_ID?format=png&output=binary&demo=demo_xxxxxxxxxxxxxxxxxxxxxxxx\"",[113,337,295],{"class":130},[113,339,341,343],{"class":115,"line":340},9,[113,342,307],{"class":130},[113,344,310],{"class":137},[15,346,347,348,351],{},"Demo tokens are intended for evaluation only. For private templates, higher limits, and credit-backed usage, create an ",[19,349,350],{},"API key"," as described above.",[33,353,355,356,359],{"id":354},"get-render-url-secret-gt","GET render URL secret (",[28,357,358],{},"gt",")",[15,361,362,363,261,365,367,368,371,372,374,375,377,378,380],{},"For ",[19,364,260],{},[28,366,264],{},", when the template requires a GET token (default), you can either use a normal API key in the header or a shorter ",[19,369,370],{},"GET render URL secret"," in the query string as ",[28,373,358],{},". Create, rotate, or clear it under ",[19,376,46],{}," (same page as API keys). The secret is scoped to the ",[19,379,25],{},"; use it only in URLs you control, since query strings can appear in logs and referrers.",[33,382,384],{"id":383},"sending-the-token","Sending the token",[15,386,387,388,390],{},"Send the token in the ",[19,389,30],{}," header as a Bearer token:",[104,392,396],{"className":393,"code":394,"language":395,"meta":109,"style":109},"language-http shiki shiki-themes github-light github-dark","Authorization: Bearer your_api_token_here\n","http",[28,397,398],{"__ignoreMap":109},[113,399,400],{"class":115,"line":116},[113,401,394],{},[403,404,406],"h3",{"id":405},"example-with-curl","Example with cURL",[104,408,410],{"className":106,"code":409,"language":108,"meta":109,"style":109},"curl -H \"Authorization: Bearer YOUR_TOKEN\" \\\n  \"https:\u002F\u002Fyour-app.bannx.app\u002Fapi\u002Frender\u002FPAGE_ID?format=png\"\n",[28,411,412,423],{"__ignoreMap":109},[113,413,414,416,418,421],{"class":115,"line":116},[113,415,127],{"class":126},[113,417,289],{"class":130},[113,419,420],{"class":137}," \"Authorization: Bearer YOUR_TOKEN\"",[113,422,295],{"class":130},[113,424,425],{"class":115,"line":123},[113,426,427],{"class":137},"  \"https:\u002F\u002Fyour-app.bannx.app\u002Fapi\u002Frender\u002FPAGE_ID?format=png\"\n",[403,429,431],{"id":430},"example-with-fetch","Example with fetch",[104,433,437],{"className":434,"code":435,"language":436,"meta":109,"style":109},"language-javascript shiki shiki-themes github-light github-dark","const response = await fetch(\n  `https:\u002F\u002Fyour-app.bannx.app\u002Fapi\u002Frender\u002F${pageId}?format=png`,\n  {\n    headers: {\n      'Authorization': `Bearer ${apiToken}`,\n    },\n  }\n);\nconst imageBlob = await response.blob();\n","javascript",[28,438,439,460,473,478,483,501,506,511,516],{"__ignoreMap":109},[113,440,441,445,448,451,454,457],{"class":115,"line":116},[113,442,444],{"class":443},"szBVR","const",[113,446,447],{"class":130}," response",[113,449,450],{"class":443}," =",[113,452,453],{"class":443}," await",[113,455,456],{"class":126}," fetch",[113,458,459],{"class":153},"(\n",[113,461,462,465,468,471],{"class":115,"line":123},[113,463,464],{"class":137},"  `https:\u002F\u002Fyour-app.bannx.app\u002Fapi\u002Frender\u002F${",[113,466,467],{"class":153},"pageId",[113,469,470],{"class":137},"}?format=png`",[113,472,168],{"class":153},[113,474,475],{"class":115,"line":171},[113,476,477],{"class":153},"  {\n",[113,479,480],{"class":115,"line":184},[113,481,482],{"class":153},"    headers: {\n",[113,484,485,488,490,493,496,499],{"class":115,"line":197},[113,486,487],{"class":137},"      'Authorization'",[113,489,162],{"class":153},[113,491,492],{"class":137},"`Bearer ${",[113,494,495],{"class":153},"apiToken",[113,497,498],{"class":137},"}`",[113,500,168],{"class":153},[113,502,503],{"class":115,"line":210},[113,504,505],{"class":153},"    },\n",[113,507,508],{"class":115,"line":251},[113,509,510],{"class":153},"  }\n",[113,512,513],{"class":115,"line":332},[113,514,515],{"class":153},");\n",[113,517,518,520,523,525,527,530,533],{"class":115,"line":340},[113,519,444],{"class":443},[113,521,522],{"class":130}," imageBlob",[113,524,450],{"class":443},[113,526,453],{"class":443},[113,528,529],{"class":153}," response.",[113,531,532],{"class":126},"blob",[113,534,535],{"class":153},"();\n",[33,537,539,542],{"id":538},"token-query-parameter-not-allowed",[28,540,541],{},"token"," query parameter (not allowed)",[15,544,545,546,549,550,553,554,557,558,562,563,567],{},"Passing an API key as ",[28,547,548],{},"?token=..."," is ",[19,551,552],{},"not allowed"," and returns ",[28,555,556],{},"400 Bad Request",". Use the ",[19,559,560],{},[28,561,30],{}," header for API keys, or the ",[19,564,565],{},[28,566,358],{}," parameter for the dedicated GET render secret.",[33,569,571],{"id":570},"errors","Errors",[573,574,575,588],"table",{},[576,577,578],"thead",{},[579,580,581,585],"tr",{},[582,583,584],"th",{},"Status",[582,586,587],{},"Meaning",[589,590,591,602],"tbody",{},[579,592,593,599],{},[594,595,596],"td",{},[28,597,598],{},"401 Unauthorized",[594,600,601],{},"Missing or invalid token, or token expired",[579,603,604,608],{},[594,605,606],{},[28,607,556],{},[594,609,610,611,613,614],{},"API key was sent as ",[28,612,541],{}," query; use header or ",[28,615,358],{},[33,617,619],{"id":618},"token-scope","Token scope",[621,622,623,629,635],"ul",{},[41,624,625,626,628],{},"Each token is tied to one ",[19,627,25],{},". Rendering and other operations use that space's templates, pages, and credits.",[41,630,631,632,47],{},"Expired tokens (if you set an expiry) are rejected with ",[28,633,634],{},"401",[41,636,637],{},"Revoked keys no longer work; create a new key if needed.",[639,640,641],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":109,"searchDepth":123,"depth":123,"links":643},[644,645,646,648,652,654,655],{"id":35,"depth":123,"text":36},{"id":67,"depth":123,"text":68},{"id":354,"depth":123,"text":647},"GET render URL secret (gt)",{"id":383,"depth":123,"text":384,"children":649},[650,651],{"id":405,"depth":171,"text":406},{"id":430,"depth":171,"text":431},{"id":538,"depth":123,"text":653},"token query parameter (not allowed)",{"id":570,"depth":123,"text":571},{"id":618,"depth":123,"text":619},"API key creation, Bearer token usage, and token scope for the Bannx API.","md",{"layout":659,"updatedAt":660},"docs","2026-06-04","\u002Fdocs\u002Fauthentication",{"title":6,"description":656},"docs\u002Fauthentication","jg62WPD687uBytvuBWTpEuJqsGcepY52jlEOaAuntww",{"success":315,"routes":666,"total":1165,"generatedAt":1166,"canViewAllApiDocs":737},[667,714,725,778,835,856,877,892,908,920,932,949,977,1003,1022,1034,1045,1056,1065,1071,1089,1100,1115,1127,1140],{"path":668,"method":669,"summary":670,"description":671,"tags":672,"group":673,"requestBody":674,"responses":706,"actionName":713},"\u002Fapi\u002Fcode-to-image","POST","Render code snippets as images","Render source code with syntax highlighting to PNG, WebP, or JPEG. No API token required.",[673],"Rendering",{"content":675},{"application\u002Fjson":676},{"schema":677},{"type":678,"required":679,"properties":680},"object",[28],{"code":681,"lang":684,"theme":686,"format":688,"width":694,"height":697,"quality":699,"showLineNumbers":701,"style":704},{"type":682,"description":683},"string","Source code to render",{"type":682,"description":685},"Language for highlighting (e.g. js, ts). Default: js",{"type":682,"description":687},"Shiki theme. Default: github-dark",{"type":682,"enum":689,"description":693},[690,691,692],"png","webp","jpeg","Output format. Default: png (lossless)",{"type":695,"description":696},"number","Optional width",{"type":695,"description":698},"Optional height",{"type":695,"description":700},"Optional quality",{"type":702,"description":703},"boolean","Show 1-based line numbers in a left gutter. Default: false",{"type":678,"description":705},"Optional style options",{"200":707,"400":709,"500":711},{"description":708},"Image (data URL or binary depending on format)",{"description":710},"Missing or invalid code",{"description":712},"Failed to generate code image","codeToImage",{"path":715,"method":669,"summary":716,"description":717,"tags":718,"group":6,"responses":719,"actionName":724},"\u002Fapi\u002Fdemo-token","Create a demo token","Mint a short-lived, IP-throttled demo token (stored only in Redis, ~1h TTL) that can render PUBLIC templates with tweaks — no account or API key required. Each client IP can mint up to 5 tokens per rolling hour. Use the token as `Authorization: Bearer demo_…` or `?demo=demo_…` against GET \u002Fapi\u002Frender\u002F:pageId.",[6],{"200":720,"429":722},{"description":721},"Demo token created",{"description":723},"Per-IP demo token quota reached","createDemoToken",{"path":264,"method":260,"summary":726,"description":727,"tags":728,"group":673,"parameters":729,"responses":770,"actionName":777},"Render a page by ID (GET)","Render by page ID with query parameters. Supports draft (database), publish cache, png\u002Fsvg\u002Fwebp\u002Fpdf, JSON or binary responses, tweaks, and brandTweaks — aligned with POST \u002Fapi\u002Frender. Auth depends on template requireGetRenderToken (Bearer or gt query).",[673],[730,734,744,748,754,758,761,765],{"in":731,"name":467,"required":315,"schema":732,"description":733},"path",{"type":682},"Page ID to render",{"in":735,"name":736,"required":737,"schema":738,"description":743},"query","format",false,{"type":682,"enum":739},[690,740,691,741,742],"svg","pdf","pptx","Output format. Default: png",{"in":735,"name":745,"required":737,"schema":746,"description":747},"draft",{"type":702},"If true, load from database instead of publish cache. Default: false",{"in":735,"name":749,"required":737,"schema":750,"description":753},"output",{"type":682,"enum":751},[146,752],"binary","Response shape: omit or json for metadata + file URL; binary for raw bytes",{"in":735,"name":755,"required":737,"schema":756,"description":757},"pageIds",{"type":682},"Comma-separated page IDs for merged PDF (format=pdf only)",{"in":735,"name":358,"required":737,"schema":759,"description":760},{"type":682},"GET render secret when Bearer is not used",{"in":735,"name":762,"required":737,"schema":763,"description":764},"scale",{"type":695,"minimum":116,"maximum":184},"Output resolution multiplier for png\u002Fwebp. Overrides the template's renderScale. Default 2.",{"in":735,"name":766,"required":737,"schema":767,"description":769},"quality",{"type":695,"minimum":116,"maximum":768},100,"WebP encode quality (webp only). Overrides the template's webpQuality. Default 90.",{"200":771,"401":773,"404":775},{"description":772},"Binary image\u002FPDF or application\u002Fjson with file metadata",{"description":774},"Missing or invalid API token",{"description":776},"Page or template not found","renderPage",{"path":779,"method":669,"summary":780,"description":781,"tags":782,"group":673,"requestBody":783,"responses":827,"actionName":834},"\u002Fapi\u002Frender\u002Findex","Render by page ID","Render a page by ID from the publish cache, or from the database when draft: true. Requires API token. Does not accept full inline page payloads. Optional body field renderType (satori, takumi, html) overrides the template's rendererType. Use format pdf for the HTML + Chrome PDF pipeline (always uses the HTML renderer). For multi-page PDFs, send pageIds and optional tweaksPerPage (same length). Default response is application\u002Fjson with timings and a URL to the uploaded render; set output to binary (or binary: true) to return raw bytes.",[673],{"content":784},{"application\u002Fjson":785},{"schema":786},{"type":678,"description":787,"properties":788},"For raster formats use pageId. For PDF use pageId (single) or pageIds (merged in order).",{"pageId":789,"pageIds":791,"tweaksPerPage":795,"templateId":798,"draft":800,"format":802,"tweaks":806,"brandTweaks":808,"emojiType":810,"output":812,"binary":817,"renderType":819,"scale":824,"quality":826},{"type":682,"description":790},"Page ID to render (required for png\u002Fsvg\u002Fwebp; optional for pdf when pageIds is set)",{"type":792,"items":793,"description":794},"array",{"type":682},"Multiple pages for merged PDF (format pdf only)",{"type":792,"items":796,"description":797},{"type":678},"Per-page tweaks aligned with pageIds when rendering PDF",{"type":682,"description":799},"Optional; when set, validated against the page's template (parallel fetch)",{"type":702,"description":801},"If true, load page and template from database instead of publish cache. Default: false",{"type":682,"enum":803,"description":805},[690,740,691,741,742,804],"html5","Output format. Default: png. pdf and pptx use HTML + Chrome; pptx builds an editable slide per page. html5 returns a self-contained HTML5 display-ad ZIP (Google Ads) for a single page.",{"type":678,"description":807},"Variable overrides; keys may use dot paths for nested values. For Variable[] sources, use \"varId.nested.path\" when patching object values.",{"type":678,"description":809},"Brand token overrides by dot path (e.g. logos.icon or brand.logos.icon). Merged into resolved brand tokens before render.",{"type":682,"description":811},"e.g. twemoji, openmoji. Default from template",{"type":682,"enum":813,"description":816},[146,752,814,815],"image","raw","Response shape: omit or json for application\u002Fjson (default); binary, image, or raw for raw body",{"type":702,"description":818},"If true, same as output binary (raw body)",{"type":682,"enum":820,"description":823},[821,822],"takumi","html","Override rendering engine (takumi or html). Ignored when format is pdf (HTML pipeline). Legacy value 'satori' is accepted and treated as 'takumi'.",{"type":695,"minimum":116,"maximum":184,"description":825},"Output resolution multiplier for png\u002Fwebp (logical layout unchanged, pixels multiplied). Overrides the template's renderScale. Default 2.",{"type":695,"minimum":116,"maximum":768,"description":769},{"200":828,"400":830,"401":832,"404":833},{"description":829},"Default: application\u002Fjson with render metadata and file.url. With output binary: image\u002Fpng, image\u002Fsvg+xml, image\u002Fwebp, or application\u002Fpdf.",{"description":831},"Invalid body (e.g. missing pageId)",{"description":774},{"description":776},"render",{"path":836,"method":260,"summary":837,"description":838,"tags":839,"parameters":840,"responses":850,"actionName":855},"\u002Fapi\u002Frenders\u002Flogs","List render logs","Render request logs for the current space, optionally narrowed to one template. Session-authenticated (dashboard login) — not callable with an API key.",[673],[841,844,847],{"in":735,"name":842,"required":737,"description":843},"templateId","Only logs for this template's pages",{"in":735,"name":845,"required":737,"description":846},"page","Page number",{"in":735,"name":848,"required":737,"description":849},"perPage","Items per page (default 50)",{"200":851,"401":853},{"description":852},"JSON { list, count, pagesCount } of render log entries",{"description":854},"Not logged in","listRenderLogs",{"path":857,"method":260,"summary":858,"description":859,"tags":860,"parameters":862,"responses":873,"actionName":876},"\u002Fapi\u002Ftemplates\u002Fpublic","List public templates","List published community\u002Fgallery templates with their cached pages. No authentication required.",[861],"Templates",[863,866,869,871],{"in":735,"name":864,"required":737,"description":865},"search","Search name + description (max 80 chars)",{"in":735,"name":867,"required":737,"description":868},"category","Category filter, or \"featured\" for the curated list",{"in":735,"name":845,"required":737,"description":870},"Page number (default 1)",{"in":735,"name":848,"required":737,"description":872},"Items per page (max 100)",{"200":874},{"description":875},"JSON { list, count, pagesCount } — templates include their published pages","listPublicTemplates",{"path":878,"method":260,"summary":879,"description":880,"tags":881,"parameters":882,"responses":886,"actionName":891},"\u002Fapi\u002Ftemplates\u002Fpublic\u002F:id","Get a public template","Fetch one published template (with its pages) by ID. Only templates with publicOn are returned; private IDs 404. No authentication required.",[861],[883],{"in":731,"name":884,"required":315,"description":885},"id","Template ID",{"200":887,"404":889},{"description":888},"JSON { doc } — the template including published pages",{"description":890},"Template not found or not public","getPublicTemplateDoc",{"path":893,"method":894,"summary":895,"description":896,"tags":897,"parameters":899,"responses":902,"actionName":907},"\u002Fapi\u002Fv1\u002Fbrands\u002F:id","DELETE","Delete a brand","Soft-delete a brand by ID. Session-authenticated (dashboard login; owner\u002Fadmin\u002Feditor).",[898],"Brands",[900],{"in":731,"name":884,"required":315,"description":901},"Brand ID",{"200":903,"404":905},{"description":904},"JSON { success: true, id }",{"description":906},"Brand not found in your space","deleteBrand",{"path":893,"method":260,"summary":909,"description":910,"tags":911,"parameters":912,"responses":914,"actionName":919},"Get a brand","Fetch one brand by ID. Public brands (publicOn set) need no auth; private brands require a dashboard session in the owning space. For API-key access use the MCP server (get_brand).",[898],[913],{"in":731,"name":884,"required":315,"description":901},{"200":915,"404":917},{"description":916},"JSON { doc } — the brand with its token sets",{"description":918},"Not found or not accessible","getBrand",{"path":893,"method":921,"summary":922,"description":923,"tags":924,"parameters":925,"responses":927,"actionName":931},"PUT","Update a brand","Update a brand by ID (same fields as create, all optional). Session-authenticated (dashboard login; owner\u002Fadmin\u002Feditor). For API-key access use the MCP server (update_brand).",[898],[926],{"in":731,"name":884,"required":315,"description":901},{"200":928,"404":930},{"description":929},"JSON { doc } — the updated brand",{"description":906},"updateBrand",{"path":933,"method":260,"summary":934,"description":935,"tags":936,"parameters":937,"responses":943,"actionName":948},"\u002Fapi\u002Fv1\u002Fbrands\u002Findex","List brands","List brands in the current space. Session-authenticated (dashboard login); pass ?public=true to list public brands anonymously. For API-key access use the MCP server (list_brands).",[898],[938,939,940],{"in":735,"name":845,"required":737,"description":870},{"in":735,"name":848,"required":737,"description":849},{"in":735,"name":941,"required":737,"description":942},"public","\"true\" to list public brands without auth",{"200":944,"401":946},{"description":945},"JSON { list, count, pagesCount } of brands",{"description":947},"Not logged in (when public is not set)","listBrands",{"path":933,"method":669,"summary":950,"description":951,"tags":952,"requestBody":953,"responses":972,"actionName":976},"Create a brand","Create a brand in the current space. Session-authenticated (dashboard login; owner\u002Fadmin\u002Feditor). External image URLs inside tokens are re-uploaded to your space. For API-key access use the MCP server (create_brand).",[898],{"content":954},{"application\u002Fjson":955},{"schema":956},{"type":678,"required":957,"properties":959},[958],"name",{"name":960,"draftTokens":962,"liveTokens":964,"tokens":966,"variants":968,"extra":970},{"type":682,"description":961},"Brand name",{"type":678,"description":963},"Token kit being edited (logos, colors, typography, …)",{"type":678,"description":965},"Token kit used by published renders",{"type":678,"description":967},"Resolved token set",{"type":678,"description":969},"Named variations of the kit",{"type":678,"description":971},"Free-form extras",{"200":973,"401":975},{"description":974},"JSON { doc } — the created brand",{"description":854},"createBrand",{"path":978,"method":260,"summary":979,"description":980,"tags":981,"group":982,"parameters":983,"responses":996,"actionName":1002},"\u002Fapi\u002Fv1\u002Fpublic\u002Fcontent\u002F:collection\u002F:slug","Get an entry","Read a single PUBLISHED entry by slug. Authenticate with a content.read API key (Authorization: Bearer …). The slug is matched against the collection's configured preview.slugField (defaults to \"slug\"). Only liveData is returned; use ?depth= to resolve references and ?variants= for personalization.",[982],"Content",[984,987,990,993],{"in":731,"name":985,"required":315,"description":986},"collection","The collection's API name",{"in":731,"name":988,"required":315,"description":989},"slug","The entry slug",{"in":735,"name":991,"required":737,"description":992},"depth","Resolve reference fields to this depth (default 0)",{"in":735,"name":994,"required":737,"description":995},"variants","Comma-separated variant ids; returns personalization-resolved values",{"200":997,"401":999,"404":1000},{"description":998},"JSON { doc } — the published entry",{"description":774},{"description":1001},"Collection or entry not found","publicGetContentBySlug",{"path":1004,"method":260,"summary":1005,"description":1006,"tags":1007,"group":982,"parameters":1008,"responses":1015,"actionName":1021},"\u002Fapi\u002Fv1\u002Fpublic\u002Fcontent\u002F:collection\u002Findex","List entries","List PUBLISHED entries in a collection. Authenticate with a content.read API key (Authorization: Bearer …). Only liveData is returned — never drafts. Values under `data` are keyed by field API name. Supports pagination, reference resolution (?depth=), and personalization (?variants=).",[982],[1009,1010,1011,1013,1014],{"in":731,"name":985,"required":315,"description":986},{"in":735,"name":845,"required":737,"description":870},{"in":735,"name":848,"required":737,"description":1012},"Items per page (default 20)",{"in":735,"name":991,"required":737,"description":992},{"in":735,"name":994,"required":737,"description":995},{"200":1016,"401":1018,"404":1019},{"description":1017},"JSON { list, count, pagesCount } of published entries",{"description":774},{"description":1020},"Collection not found","publicListContent",{"path":1023,"method":894,"summary":1024,"description":1025,"tags":1026,"responses":1028,"actionName":1033},"\u002Fapi\u002Fv1\u002Fshaders\u002F:id","Delete a shader","Permanently remove a shader from the current space.",[1027],"Shaders",{"200":1029,"404":1031},{"description":1030},"JSON { deleted: true }",{"description":1032},"No such shader","deleteShader",{"path":1023,"method":260,"summary":1035,"description":1036,"tags":1037,"responses":1038,"actionName":1044},"Get a shader","Fetch one shader including its vetted `program` — the only source a client should compile. Status is enforced here on every request, which is what makes revoking a working kill switch: a quarantined or revoked shader returns 410 and stops loading everywhere immediately.",[1027],{"200":1039,"404":1041,"410":1042},{"description":1040},"JSON { doc }",{"description":1032},{"description":1043},"Shader is quarantined or revoked","getShader",{"path":1023,"method":921,"summary":1046,"description":1047,"tags":1048,"responses":1049,"actionName":1055},"Update a shader","Editing source or the manifest re-runs the full vetting pipeline and produces a new content hash — an existing approval is never inherited by code that was not vetted. A rejected edit returns 422 and leaves the stored shader untouched.",[1027],{"200":1050,"404":1052,"422":1053},{"description":1051},"JSON { doc, report }",{"description":1032},{"description":1054},"Rejected by vetting; the stored shader is unchanged","updateShader",{"path":1057,"method":669,"summary":1058,"description":1059,"tags":1060,"responses":1061,"actionName":1064},"\u002Fapi\u002Fv1\u002Fshaders\u002F:id\u002Fstatus","Set a shader status (kill switch)","Revoke, quarantine, or restore a shader. Because GET enforces status on every fetch, revoking stops the shader loading everywhere at once — there is no client deny-list to push and no cache to wait out. Restoring also clears the accumulated watchdog trips.",[1027],{"200":1062,"404":1063},{"description":1040},{"description":1032},"setShaderStatus",{"path":1066,"method":669,"summary":1067,"description":1068,"tags":1069,"actionName":1070},"\u002Fapi\u002Fv1\u002Fshaders\u002F:id\u002Ftelemetry","Report a client watchdog trip","Called by a viewer whose frame watchdog disabled this shader. Enough independent trips ",[1027],"reportShaderTrip",{"path":1072,"method":260,"summary":1073,"description":1074,"tags":1075,"parameters":1076,"responses":1084,"actionName":1088},"\u002Fapi\u002Fv1\u002Fshaders\u002Findex","List shaders","List the current space's shaders. Pass ?public=true to list published shaders anonymously. Program source is omitted from the listing — fetch a single shader to obtain it.",[1027],[1077,1078,1079,1082],{"in":735,"name":845,"required":737,"description":870},{"in":735,"name":848,"required":737,"description":849},{"in":735,"name":1080,"required":737,"description":1081},"status","approved | quarantined | revoked",{"in":735,"name":941,"required":737,"description":1083},"\"true\" to list public shaders without auth",{"200":1085,"401":1087},{"description":1086},"JSON { list, count, pagesCount }",{"description":947},"listShaders",{"path":1072,"method":669,"summary":1090,"description":1091,"tags":1092,"responses":1093,"actionName":1099},"Create a shader","Submit a GLSL fragment body plus a typed input manifest. The shader is vetted before anything is stored: byte caps, a static policy scan, loop normalisation to constant bounds, a cost model, then a real compile and fuzz render inside a sandboxed child process. A rejected submission returns 422 with the stage-by-stage report and is not saved.",[1027],{"200":1094,"401":1096,"422":1097},{"description":1095},"JSON { doc, report } — the stored shader and its vetting report",{"description":854},{"description":1098},"Rejected by vetting; data.report explains which stage refused","createShader",{"path":1101,"method":894,"summary":1102,"description":1103,"tags":1104,"parameters":1106,"responses":1109,"actionName":1114},"\u002Fapi\u002Fv1\u002Ftokens\u002Fapi-keys\u002F:id","Delete an API key","Revoke an API key immediately. Session-authenticated (dashboard login; owner\u002Fadmin).",[1105],"Tokens",[1107],{"in":731,"name":884,"required":315,"description":1108},"Token ID",{"200":1110,"404":1112},{"description":1111},"JSON { message } confirmation",{"description":1113},"Token not found in your space","deleteApiKey",{"path":1101,"method":1116,"summary":1117,"description":1118,"tags":1119,"parameters":1120,"responses":1122,"actionName":1126},"PATCH","Update an API key","Rename, change expiry, or adjust scopes of an API key. Session-authenticated (dashboard login; owner\u002Fadmin).",[1105],[1121],{"in":731,"name":884,"required":315,"description":1108},{"200":1123,"404":1125},{"description":1124},"JSON { doc } — the updated key",{"description":1113},"updateApiKey",{"path":1128,"method":260,"summary":1129,"description":1130,"tags":1131,"parameters":1132,"responses":1135,"actionName":1139},"\u002Fapi\u002Fv1\u002Ftokens\u002Fapi-keys\u002Findex","List API keys","List API keys for the current space. Session-authenticated (dashboard login; owner\u002Fadmin).",[1105],[1133,1134],{"in":735,"name":845,"required":737,"description":870},{"in":735,"name":848,"required":737,"description":1012},{"200":1136,"401":1138},{"description":1137},"JSON { list, count, pagesCount } of API keys",{"description":854},"listApiKeys",{"path":1128,"method":669,"summary":1141,"description":1142,"tags":1143,"requestBody":1144,"responses":1160,"actionName":1164},"Create an API key","Create a space-scoped API key. The plain token is returned once, on creation. Session-authenticated (dashboard login; owner\u002Fadmin).",[1105],{"content":1145},{"application\u002Fjson":1146},{"schema":1147},{"type":678,"required":1148,"properties":1149},[958],{"name":1150,"expiresAt":1152,"neverExpires":1154,"shortToken":1156,"scopes":1158},{"type":682,"description":1151},"Key name (max 200 chars)",{"type":682,"description":1153},"ISO datetime expiry",{"type":702,"description":1155},"No expiry. Default: false",{"type":702,"description":1157},"Shorter ~96-bit token instead of ~256-bit. Default: false",{"type":678,"description":1159},"Optional restrictions: { mcpTools?: string[], apiEndpoints?: string[] }. Omit a list to allow all; [] denies all.",{"200":1161,"401":1163},{"description":1162},"JSON { doc } including the plain token (shown only once)",{"description":854},"createApiKey",25,"2026-08-19T19:21:11.342Z",1787167270508]