[{"data":1,"prerenderedAt":7591},["ShallowReactive",2],{"docs-index":3},[4,1010,1267,1922,2318,2507,3229,3619,3819,4356,5212,5593,6097,6350,6784,6941,7109],{"id":5,"title":6,"body":7,"category":1000,"date":1001,"description":1002,"extension":1003,"heroImage":1004,"meta":1005,"navigation":130,"path":1006,"readingMinutes":121,"seo":1007,"stem":1008,"tool":1000,"__hash__":1009},"docs\u002Fdocs\u002Fbatch-animate-videos.md","Batch animate videos",{"type":8,"value":9,"toc":993},"minimark",[10,15,19,27,31,34,692,696,699,926,933,937,969,973,989],[11,12,14],"h2",{"id":13},"one-template-many-videos","One template, many videos",[16,17,18],"p",{},"Personalized video is normally a per-clip cost: an editor or an agency makes each one. With Bluepic it is a loop. You animate a template once, then send one render request per row of data. Each row produces its own MP4: one per recipient, product, city, or language.",[16,20,21,22,26],{},"The template does not change between rows. Only the ",[23,24,25],"code",{},"data"," does. The clip is a pure function of the values you send.",[11,28,30],{"id":29},"the-pattern","The pattern",[16,32,33],{},"Read your rows (from a CSV, a database, a CRM export), and render one video per row. Here it is over the demo template, which needs no API key:",[35,36,41],"pre",{"className":37,"code":38,"language":39,"meta":40,"style":40},"language-js shiki shiki-themes github-dark","const rows = [\n  { text1_text: \"ALEX RIVERA\", image4_image: { src: \"https:\u002F\u002F…\u002Falex.jpg\" } },\n  { text1_text: \"SAM CHEN\", image4_image: { src: \"https:\u002F\u002F…\u002Fsam.jpg\" } },\n  { text1_text: \"MIA LOPEZ\", image4_image: { src: \"https:\u002F\u002F…\u002Fmia.jpg\" } },\n  \u002F\u002F …hundreds more\n];\n\nasync function renderOne(data) {\n  const res = await fetch(\"https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Frender\", {\n    method: \"POST\",\n    headers: { Authorization: \"\u003Cyour API key>\", \"Content-Type\": \"application\u002Fjson\" },\n    body: JSON.stringify({\n      templateId: \"7ccf097c-10f2-4efc-bd03-8abe967c4711\",\n      data,\n      format: \"mp4\",\n    }),\n  });\n  \u002F\u002F The video endpoint streams; read to the `end` event for the hosted URL.\n  const reader = res.body.getReader();\n  const decoder = new TextDecoder();\n  let buffer = \"\";\n  for (;;) {\n    const { value, done } = await reader.read();\n    if (value) buffer += decoder.decode(value, { stream: true });\n    let i;\n    while ((i = buffer.indexOf(\"\\n\\n\")) !== -1) {\n      const line = buffer.slice(0, i).split(\"\\n\").find((l) => l.startsWith(\"data:\"));\n      buffer = buffer.slice(i + 2);\n      if (line) {\n        const evt = JSON.parse(line.slice(5).trim());\n        if (evt.type === \"end\") return evt.result;\n        if (evt.type === \"error\") throw new Error(evt.message);\n      }\n    }\n    if (done) break;\n  }\n  throw new Error(\"no result\");\n}\n","js","",[23,42,43,63,82,97,112,119,125,132,154,179,191,215,233,244,250,261,267,273,279,298,316,334,343,375,402,411,452,520,544,553,590,613,638,644,650,663,669,686],{"__ignoreMap":40},[44,45,48,52,56,59],"span",{"class":46,"line":47},"line",1,[44,49,51],{"class":50},"snl16","const",[44,53,55],{"class":54},"sDLfK"," rows",[44,57,58],{"class":50}," =",[44,60,62],{"class":61},"s95oV"," [\n",[44,64,66,69,73,76,79],{"class":46,"line":65},2,[44,67,68],{"class":61},"  { text1_text: ",[44,70,72],{"class":71},"sU2Wk","\"ALEX RIVERA\"",[44,74,75],{"class":61},", image4_image: { src: ",[44,77,78],{"class":71},"\"https:\u002F\u002F…\u002Falex.jpg\"",[44,80,81],{"class":61}," } },\n",[44,83,85,87,90,92,95],{"class":46,"line":84},3,[44,86,68],{"class":61},[44,88,89],{"class":71},"\"SAM CHEN\"",[44,91,75],{"class":61},[44,93,94],{"class":71},"\"https:\u002F\u002F…\u002Fsam.jpg\"",[44,96,81],{"class":61},[44,98,100,102,105,107,110],{"class":46,"line":99},4,[44,101,68],{"class":61},[44,103,104],{"class":71},"\"MIA LOPEZ\"",[44,106,75],{"class":61},[44,108,109],{"class":71},"\"https:\u002F\u002F…\u002Fmia.jpg\"",[44,111,81],{"class":61},[44,113,115],{"class":46,"line":114},5,[44,116,118],{"class":117},"sAwPA","  \u002F\u002F …hundreds more\n",[44,120,122],{"class":46,"line":121},6,[44,123,124],{"class":61},"];\n",[44,126,128],{"class":46,"line":127},7,[44,129,131],{"emptyLinePlaceholder":130},true,"\n",[44,133,135,138,141,145,148,151],{"class":46,"line":134},8,[44,136,137],{"class":50},"async",[44,139,140],{"class":50}," function",[44,142,144],{"class":143},"svObZ"," renderOne",[44,146,147],{"class":61},"(",[44,149,25],{"class":150},"s9osk",[44,152,153],{"class":61},") {\n",[44,155,157,160,163,165,168,171,173,176],{"class":46,"line":156},9,[44,158,159],{"class":50},"  const",[44,161,162],{"class":54}," res",[44,164,58],{"class":50},[44,166,167],{"class":50}," await",[44,169,170],{"class":143}," fetch",[44,172,147],{"class":61},[44,174,175],{"class":71},"\"https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Frender\"",[44,177,178],{"class":61},", {\n",[44,180,182,185,188],{"class":46,"line":181},10,[44,183,184],{"class":61},"    method: ",[44,186,187],{"class":71},"\"POST\"",[44,189,190],{"class":61},",\n",[44,192,194,197,200,203,206,209,212],{"class":46,"line":193},11,[44,195,196],{"class":61},"    headers: { Authorization: ",[44,198,199],{"class":71},"\"\u003Cyour API key>\"",[44,201,202],{"class":61},", ",[44,204,205],{"class":71},"\"Content-Type\"",[44,207,208],{"class":61},": ",[44,210,211],{"class":71},"\"application\u002Fjson\"",[44,213,214],{"class":61}," },\n",[44,216,218,221,224,227,230],{"class":46,"line":217},12,[44,219,220],{"class":61},"    body: ",[44,222,223],{"class":54},"JSON",[44,225,226],{"class":61},".",[44,228,229],{"class":143},"stringify",[44,231,232],{"class":61},"({\n",[44,234,236,239,242],{"class":46,"line":235},13,[44,237,238],{"class":61},"      templateId: ",[44,240,241],{"class":71},"\"7ccf097c-10f2-4efc-bd03-8abe967c4711\"",[44,243,190],{"class":61},[44,245,247],{"class":46,"line":246},14,[44,248,249],{"class":61},"      data,\n",[44,251,253,256,259],{"class":46,"line":252},15,[44,254,255],{"class":61},"      format: ",[44,257,258],{"class":71},"\"mp4\"",[44,260,190],{"class":61},[44,262,264],{"class":46,"line":263},16,[44,265,266],{"class":61},"    }),\n",[44,268,270],{"class":46,"line":269},17,[44,271,272],{"class":61},"  });\n",[44,274,276],{"class":46,"line":275},18,[44,277,278],{"class":117},"  \u002F\u002F The video endpoint streams; read to the `end` event for the hosted URL.\n",[44,280,282,284,287,289,292,295],{"class":46,"line":281},19,[44,283,159],{"class":50},[44,285,286],{"class":54}," reader",[44,288,58],{"class":50},[44,290,291],{"class":61}," res.body.",[44,293,294],{"class":143},"getReader",[44,296,297],{"class":61},"();\n",[44,299,301,303,306,308,311,314],{"class":46,"line":300},20,[44,302,159],{"class":50},[44,304,305],{"class":54}," decoder",[44,307,58],{"class":50},[44,309,310],{"class":50}," new",[44,312,313],{"class":143}," TextDecoder",[44,315,297],{"class":61},[44,317,319,322,325,328,331],{"class":46,"line":318},21,[44,320,321],{"class":50},"  let",[44,323,324],{"class":61}," buffer ",[44,326,327],{"class":50},"=",[44,329,330],{"class":71}," \"\"",[44,332,333],{"class":61},";\n",[44,335,337,340],{"class":46,"line":336},22,[44,338,339],{"class":50},"  for",[44,341,342],{"class":61}," (;;) {\n",[44,344,346,349,352,355,357,360,363,365,367,370,373],{"class":46,"line":345},23,[44,347,348],{"class":50},"    const",[44,350,351],{"class":61}," { ",[44,353,354],{"class":54},"value",[44,356,202],{"class":61},[44,358,359],{"class":54},"done",[44,361,362],{"class":61}," } ",[44,364,327],{"class":50},[44,366,167],{"class":50},[44,368,369],{"class":61}," reader.",[44,371,372],{"class":143},"read",[44,374,297],{"class":61},[44,376,378,381,384,387,390,393,396,399],{"class":46,"line":377},24,[44,379,380],{"class":50},"    if",[44,382,383],{"class":61}," (value) buffer ",[44,385,386],{"class":50},"+=",[44,388,389],{"class":61}," decoder.",[44,391,392],{"class":143},"decode",[44,394,395],{"class":61},"(value, { stream: ",[44,397,398],{"class":54},"true",[44,400,401],{"class":61}," });\n",[44,403,405,408],{"class":46,"line":404},25,[44,406,407],{"class":50},"    let",[44,409,410],{"class":61}," i;\n",[44,412,414,417,420,422,425,428,430,433,436,438,441,444,447,450],{"class":46,"line":413},26,[44,415,416],{"class":50},"    while",[44,418,419],{"class":61}," ((i ",[44,421,327],{"class":50},[44,423,424],{"class":61}," buffer.",[44,426,427],{"class":143},"indexOf",[44,429,147],{"class":61},[44,431,432],{"class":71},"\"",[44,434,435],{"class":54},"\\n\\n",[44,437,432],{"class":71},[44,439,440],{"class":61},")) ",[44,442,443],{"class":50},"!==",[44,445,446],{"class":50}," -",[44,448,449],{"class":54},"1",[44,451,153],{"class":61},[44,453,455,458,461,463,465,468,470,473,476,479,481,483,486,488,491,494,497,500,503,506,509,512,514,517],{"class":46,"line":454},27,[44,456,457],{"class":50},"      const",[44,459,460],{"class":54}," line",[44,462,58],{"class":50},[44,464,424],{"class":61},[44,466,467],{"class":143},"slice",[44,469,147],{"class":61},[44,471,472],{"class":54},"0",[44,474,475],{"class":61},", i).",[44,477,478],{"class":143},"split",[44,480,147],{"class":61},[44,482,432],{"class":71},[44,484,485],{"class":54},"\\n",[44,487,432],{"class":71},[44,489,490],{"class":61},").",[44,492,493],{"class":143},"find",[44,495,496],{"class":61},"((",[44,498,499],{"class":150},"l",[44,501,502],{"class":61},") ",[44,504,505],{"class":50},"=>",[44,507,508],{"class":61}," l.",[44,510,511],{"class":143},"startsWith",[44,513,147],{"class":61},[44,515,516],{"class":71},"\"data:\"",[44,518,519],{"class":61},"));\n",[44,521,523,526,528,530,532,535,538,541],{"class":46,"line":522},28,[44,524,525],{"class":61},"      buffer ",[44,527,327],{"class":50},[44,529,424],{"class":61},[44,531,467],{"class":143},[44,533,534],{"class":61},"(i ",[44,536,537],{"class":50},"+",[44,539,540],{"class":54}," 2",[44,542,543],{"class":61},");\n",[44,545,547,550],{"class":46,"line":546},29,[44,548,549],{"class":50},"      if",[44,551,552],{"class":61}," (line) {\n",[44,554,556,559,562,564,567,569,572,575,577,579,582,584,587],{"class":46,"line":555},30,[44,557,558],{"class":50},"        const",[44,560,561],{"class":54}," evt",[44,563,58],{"class":50},[44,565,566],{"class":54}," JSON",[44,568,226],{"class":61},[44,570,571],{"class":143},"parse",[44,573,574],{"class":61},"(line.",[44,576,467],{"class":143},[44,578,147],{"class":61},[44,580,581],{"class":54},"5",[44,583,490],{"class":61},[44,585,586],{"class":143},"trim",[44,588,589],{"class":61},"());\n",[44,591,593,596,599,602,605,607,610],{"class":46,"line":592},31,[44,594,595],{"class":50},"        if",[44,597,598],{"class":61}," (evt.type ",[44,600,601],{"class":50},"===",[44,603,604],{"class":71}," \"end\"",[44,606,502],{"class":61},[44,608,609],{"class":50},"return",[44,611,612],{"class":61}," evt.result;\n",[44,614,616,618,620,622,625,627,630,632,635],{"class":46,"line":615},32,[44,617,595],{"class":50},[44,619,598],{"class":61},[44,621,601],{"class":50},[44,623,624],{"class":71}," \"error\"",[44,626,502],{"class":61},[44,628,629],{"class":50},"throw",[44,631,310],{"class":50},[44,633,634],{"class":143}," Error",[44,636,637],{"class":61},"(evt.message);\n",[44,639,641],{"class":46,"line":640},33,[44,642,643],{"class":61},"      }\n",[44,645,647],{"class":46,"line":646},34,[44,648,649],{"class":61},"    }\n",[44,651,653,655,658,661],{"class":46,"line":652},35,[44,654,380],{"class":50},[44,656,657],{"class":61}," (done) ",[44,659,660],{"class":50},"break",[44,662,333],{"class":61},[44,664,666],{"class":46,"line":665},36,[44,667,668],{"class":61},"  }\n",[44,670,672,675,677,679,681,684],{"class":46,"line":671},37,[44,673,674],{"class":50},"  throw",[44,676,310],{"class":50},[44,678,634],{"class":143},[44,680,147],{"class":61},[44,682,683],{"class":71},"\"no result\"",[44,685,543],{"class":61},[44,687,689],{"class":46,"line":688},38,[44,690,691],{"class":61},"}\n",[11,693,695],{"id":694},"run-the-batch-with-a-concurrency-limit","Run the batch with a concurrency limit",[16,697,698],{},"Do not fire all rows at once. Video renders are heavier than stills, so cap how many run in parallel and let the rest queue. A small worker pool is enough:",[35,700,702],{"className":37,"code":701,"language":39,"meta":40,"style":40},"async function renderBatch(rows, concurrency = 4) {\n  const results = [];\n  let next = 0;\n  async function worker() {\n    while (next \u003C rows.length) {\n      const i = next++;\n      try {\n        results[i] = await renderOne(rows[i]);\n      } catch (err) {\n        results[i] = { error: String(err) };\n      }\n    }\n  }\n  await Promise.all(Array.from({ length: concurrency }, worker));\n  return results; \u002F\u002F one hosted MP4 URL (or an error) per input row\n}\n\nconst urls = await renderBatch(rows, 4);\n",[23,703,704,730,742,756,769,787,804,812,826,837,852,856,860,864,886,897,901,905],{"__ignoreMap":40},[44,705,706,708,710,713,715,718,720,723,725,728],{"class":46,"line":47},[44,707,137],{"class":50},[44,709,140],{"class":50},[44,711,712],{"class":143}," renderBatch",[44,714,147],{"class":61},[44,716,717],{"class":150},"rows",[44,719,202],{"class":61},[44,721,722],{"class":150},"concurrency",[44,724,58],{"class":50},[44,726,727],{"class":54}," 4",[44,729,153],{"class":61},[44,731,732,734,737,739],{"class":46,"line":65},[44,733,159],{"class":50},[44,735,736],{"class":54}," results",[44,738,58],{"class":50},[44,740,741],{"class":61}," [];\n",[44,743,744,746,749,751,754],{"class":46,"line":84},[44,745,321],{"class":50},[44,747,748],{"class":61}," next ",[44,750,327],{"class":50},[44,752,753],{"class":54}," 0",[44,755,333],{"class":61},[44,757,758,761,763,766],{"class":46,"line":99},[44,759,760],{"class":50},"  async",[44,762,140],{"class":50},[44,764,765],{"class":143}," worker",[44,767,768],{"class":61},"() {\n",[44,770,771,773,776,779,782,785],{"class":46,"line":114},[44,772,416],{"class":50},[44,774,775],{"class":61}," (next ",[44,777,778],{"class":50},"\u003C",[44,780,781],{"class":61}," rows.",[44,783,784],{"class":54},"length",[44,786,153],{"class":61},[44,788,789,791,794,796,799,802],{"class":46,"line":121},[44,790,457],{"class":50},[44,792,793],{"class":54}," i",[44,795,58],{"class":50},[44,797,798],{"class":61}," next",[44,800,801],{"class":50},"++",[44,803,333],{"class":61},[44,805,806,809],{"class":46,"line":127},[44,807,808],{"class":50},"      try",[44,810,811],{"class":61}," {\n",[44,813,814,817,819,821,823],{"class":46,"line":134},[44,815,816],{"class":61},"        results[i] ",[44,818,327],{"class":50},[44,820,167],{"class":50},[44,822,144],{"class":143},[44,824,825],{"class":61},"(rows[i]);\n",[44,827,828,831,834],{"class":46,"line":156},[44,829,830],{"class":61},"      } ",[44,832,833],{"class":50},"catch",[44,835,836],{"class":61}," (err) {\n",[44,838,839,841,843,846,849],{"class":46,"line":181},[44,840,816],{"class":61},[44,842,327],{"class":50},[44,844,845],{"class":61}," { error: ",[44,847,848],{"class":143},"String",[44,850,851],{"class":61},"(err) };\n",[44,853,854],{"class":46,"line":193},[44,855,643],{"class":61},[44,857,858],{"class":46,"line":217},[44,859,649],{"class":61},[44,861,862],{"class":46,"line":235},[44,863,668],{"class":61},[44,865,866,869,872,874,877,880,883],{"class":46,"line":246},[44,867,868],{"class":50},"  await",[44,870,871],{"class":54}," Promise",[44,873,226],{"class":61},[44,875,876],{"class":143},"all",[44,878,879],{"class":61},"(Array.",[44,881,882],{"class":143},"from",[44,884,885],{"class":61},"({ length: concurrency }, worker));\n",[44,887,888,891,894],{"class":46,"line":252},[44,889,890],{"class":50},"  return",[44,892,893],{"class":61}," results; ",[44,895,896],{"class":117},"\u002F\u002F one hosted MP4 URL (or an error) per input row\n",[44,898,899],{"class":46,"line":263},[44,900,691],{"class":61},[44,902,903],{"class":46,"line":269},[44,904,131],{"emptyLinePlaceholder":130},[44,906,907,909,912,914,916,918,921,924],{"class":46,"line":275},[44,908,51],{"class":50},[44,910,911],{"class":54}," urls",[44,913,58],{"class":50},[44,915,167],{"class":50},[44,917,712],{"class":143},[44,919,920],{"class":61},"(rows, ",[44,922,923],{"class":54},"4",[44,925,543],{"class":61},[16,927,928,929,932],{},"Each entry in ",[23,930,931],{},"urls"," lines up with a row in your data. Store them, attach them to your emails, post them, or hand them to whatever pipeline triggered the batch.",[11,934,936],{"id":935},"what-to-keep-in-mind","What to keep in mind",[938,939,940,954,960],"ul",{},[941,942,943,947,948,953],"li",{},[944,945,946],"strong",{},"Credits."," Each video is metered by length and resolution (1 credit per second of 2K at 24fps), so a batch's cost is the sum of its clips. See ",[949,950,952],"a",{"href":951},"\u002Fdocs\u002Fvideo-credits","how video credits work"," to estimate before a large job.",[941,955,956,959],{},[944,957,958],{},"Aspect ratios."," Need square for a feed and vertical for stories? Build one template per ratio and run the same batch against each; the data stays identical.",[941,961,962,965,966,968],{},[944,963,964],{},"Idempotency."," The same ",[23,967,25],{}," always produces the same video (rendering is deterministic), so you can safely cache results by input and skip re-rendering rows that have not changed.",[11,970,972],{"id":971},"next","Next",[938,974,975,982],{},[941,976,977,978,226],{},"The single-request version: ",[949,979,981],{"href":980},"\u002Fdocs\u002Fgenerate-video-via-api","generate video via API",[941,983,984,985,226],{},"Build the template you are batching: ",[949,986,988],{"href":987},"\u002Fdocs\u002Fhow-to-video-templates","how to build a video template in Studio",[990,991,992],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}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);}",{"title":40,"searchDepth":65,"depth":65,"links":994},[995,996,997,998,999],{"id":13,"depth":65,"text":14},{"id":29,"depth":65,"text":30},{"id":694,"depth":65,"text":695},{"id":935,"depth":65,"text":936},{"id":971,"depth":65,"text":972},"platform","2026-07-26","Generate hundreds of personalized videos from one template. Loop over your data, one render request per row, and collect a hosted MP4 for each. Personalized video at scale, from a spreadsheet or a database.","md","\u002Fdocs-images\u002Fbatch-animate-videos.svg",{},"\u002Fdocs\u002Fbatch-animate-videos",{"title":6,"description":1002},"docs\u002Fbatch-animate-videos","YNpHev1Meom1K_hl_HqlvPILWjPHIQGbDZU9BLvxQ5g",{"id":1011,"title":1012,"body":1013,"category":1000,"date":1001,"description":1260,"extension":1003,"heroImage":1261,"meta":1262,"navigation":130,"path":1263,"readingMinutes":121,"seo":1264,"stem":1265,"tool":1000,"__hash__":1266},"docs\u002Fdocs\u002Fgenerate-video-templates.md","Generate video templates",{"type":8,"value":1014,"toc":1253},[1015,1019,1022,1041,1044,1048,1056,1108,1113,1117,1127,1201,1211,1215,1239,1243,1250],[11,1016,1018],{"id":1017},"what-a-video-template-is","What a video template is",[16,1020,1021],{},"A video template is a reusable design that becomes a different video every time you feed it new data. In Bluepic it is not a special object: it is a normal, reactive template with two things added.",[1023,1024,1025,1035],"ol",{},[941,1026,1027,1030,1031,1034],{},[944,1028,1029],{},"An animation."," A duration and a frame rate, and one or more properties that change over a ",[23,1032,1033],{},"TIME"," clock.",[941,1036,1037,1040],{},[944,1038,1039],{},"Connected input fields."," The parts you want to vary per render (a headline, a photo, a price) exposed as fields your data fills.",[16,1042,1043],{},"Because the template is reactive, its properties are expressions, not fixed values. That is what lets one template produce a thousand on-brand clips: change the data, get a fresh video, with the layout re-fitting itself so long or translated text never clips.",[11,1045,1047],{"id":1046},"how-to-create-one","How to create one",[16,1049,1050,1051,1055],{},"You build a video template visually in ",[949,1052,1054],{"href":1053},"\u002Fdocs\u002Fbuilding-templates-in-bluepic-studio","Bluepic Studio",". The short version:",[1023,1057,1058,1064,1082,1093,1099],{},[941,1059,1060,1063],{},[944,1061,1062],{},"Design the layout"," and connect the fields you want to fill from data (text, images).",[941,1065,1066,1069,1070,1073,1074,1077,1078,1081],{},[944,1067,1068],{},"Set a video format"," (",[23,1071,1072],{},"mp4",") in Project Settings, then set a ",[944,1075,1076],{},"Duration"," and ",[944,1079,1080],{},"FPS",". That turns the design into a video template.",[941,1083,1084,1087,1088,1092],{},[944,1085,1086],{},"Animate a property."," Click the ",[1089,1090,1091],"em",{},"Animate a property"," button, click a numeric property (position, size, rotation, opacity), and Studio freezes its current value as the first keyframe.",[941,1094,1095,1098],{},[944,1096,1097],{},"Keyframe it on the timeline."," Move the playhead, change the value, and add keyframes with linear or eased interpolation.",[941,1100,1101,1104,1105,226],{},[944,1102,1103],{},"Preview"," by scrubbing, then ",[944,1106,1107],{},"publish",[16,1109,1110,1111,226],{},"For the full walkthrough with screenshots of each step, follow ",[949,1112,988],{"href":987},[11,1114,1116],{"id":1115},"how-to-drive-it","How to drive it",[16,1118,1119,1120,1123,1124,1126],{},"Once published, a video template is rendered from your code by its ",[23,1121,1122],{},"templateId",", with the fields supplied as ",[23,1125,25],{},":",[35,1128,1132],{"className":1129,"code":1130,"language":1131,"meta":40,"style":40},"language-bash shiki shiki-themes github-dark","curl -N -X POST \"https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Frender\" \\\n  -H \"Authorization: \u003Cyour API key>\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"templateId\": \"\u003Cyour template id>\",\n    \"data\": { \"headline\": \"Launch day\", \"photo\": { \"src\": \"https:\u002F\u002F…\u002Fimg.jpg\" } },\n    \"format\": \"mp4\"\n  }'\n","bash",[23,1133,1134,1154,1164,1173,1181,1186,1191,1196],{"__ignoreMap":40},[44,1135,1136,1139,1142,1145,1148,1151],{"class":46,"line":47},[44,1137,1138],{"class":143},"curl",[44,1140,1141],{"class":54}," -N",[44,1143,1144],{"class":54}," -X",[44,1146,1147],{"class":71}," POST",[44,1149,1150],{"class":71}," \"https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Frender\"",[44,1152,1153],{"class":54}," \\\n",[44,1155,1156,1159,1162],{"class":46,"line":65},[44,1157,1158],{"class":54},"  -H",[44,1160,1161],{"class":71}," \"Authorization: \u003Cyour API key>\"",[44,1163,1153],{"class":54},[44,1165,1166,1168,1171],{"class":46,"line":84},[44,1167,1158],{"class":54},[44,1169,1170],{"class":71}," \"Content-Type: application\u002Fjson\"",[44,1172,1153],{"class":54},[44,1174,1175,1178],{"class":46,"line":99},[44,1176,1177],{"class":54},"  -d",[44,1179,1180],{"class":71}," '{\n",[44,1182,1183],{"class":46,"line":114},[44,1184,1185],{"class":71},"    \"templateId\": \"\u003Cyour template id>\",\n",[44,1187,1188],{"class":46,"line":121},[44,1189,1190],{"class":71},"    \"data\": { \"headline\": \"Launch day\", \"photo\": { \"src\": \"https:\u002F\u002F…\u002Fimg.jpg\" } },\n",[44,1192,1193],{"class":46,"line":127},[44,1194,1195],{"class":71},"    \"format\": \"mp4\"\n",[44,1197,1198],{"class":46,"line":134},[44,1199,1200],{"class":71},"  }'\n",[16,1202,1203,1204,1206,1207,1210],{},"The response streams progress and ends with a hosted MP4 URL. See ",[949,1205,981],{"href":980}," for the response shape, and ",[949,1208,1209],{"href":1006},"batch animate videos"," to render one per row of data.",[11,1212,1214],{"id":1213},"why-templates-not-one-off-edits","Why templates, not one-off edits",[938,1216,1217,1223,1229],{},[941,1218,1219,1222],{},[944,1220,1221],{},"One design, every variation."," A single template covers every recipient, product, or language. You maintain one thing, not a folder of exported clips.",[941,1224,1225,1228],{},[944,1226,1227],{},"On-brand by construction."," The layout, fonts, and colours are fixed in the template; only the data changes, so nothing drifts off-brand.",[941,1230,1231,1234,1235,226],{},[944,1232,1233],{},"Stills and video from the same source."," The same template renders a PNG or an MP4 by switching the format, so your static and motion assets always match. See ",[949,1236,1238],{"href":1237},"\u002Fdocs\u002Fone-template-two-outputs","one template, two outputs",[11,1240,1242],{"id":1241},"what-a-video-template-is-not","What a video template is not",[16,1244,1245,1246,226],{},"It is not a video editor. A Bluepic video template animates a designed layout; it does not cut, trim, or stitch footage. If your job is assembling clips on a timeline, a timeline-editing tool fits better. See the ",[949,1247,1249],{"href":1248},"\u002Fcompare\u002Fvideo-generation-apis","honest comparison of video-generation APIs",[990,1251,1252],{},"html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#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);}",{"title":40,"searchDepth":65,"depth":65,"links":1254},[1255,1256,1257,1258,1259],{"id":1017,"depth":65,"text":1018},{"id":1046,"depth":65,"text":1047},{"id":1115,"depth":65,"text":1116},{"id":1213,"depth":65,"text":1214},{"id":1241,"depth":65,"text":1242},"What a video template is and how to create one you can drive from data. Design a reactive layout in Studio, animate its properties over a TIME clock, connect input fields, then render a personalized MP4 per data row over the API.","\u002Fdocs-images\u002Fgenerate-video-templates.svg",{},"\u002Fdocs\u002Fgenerate-video-templates",{"title":1012,"description":1260},"docs\u002Fgenerate-video-templates","dOPBX-VBKf2uKaHWSt90qJuxEnvZ7l1riLk6afZVK80",{"id":1268,"title":1269,"body":1270,"category":1000,"date":1001,"description":1916,"extension":1003,"heroImage":1917,"meta":1918,"navigation":130,"path":980,"readingMinutes":114,"seo":1919,"stem":1920,"tool":1000,"__hash__":1921},"docs\u002Fdocs\u002Fgenerate-video-via-api.md","Generate video via API",{"type":8,"value":1271,"toc":1909},[1272,1276,1286,1294,1306,1310,1323,1389,1393,1404,1410,1421,1825,1829,1888,1890,1906],[11,1273,1275],{"id":1274},"the-whole-loop","The whole loop",[16,1277,1278,1279,1281,1282,1285],{},"Generating video with Bluepic is one request. You POST the ",[23,1280,1122],{}," of an animated template plus the data to fill it, ask for a video ",[23,1283,1284],{},"format",", and the API renders it and hands back a hosted MP4.",[35,1287,1292],{"className":1288,"code":1290,"language":1291},[1289],"language-text","POST https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Frender\n{ \"templateId\": \"…\", \"data\": { … }, \"format\": \"mp4\" }\n","text",[23,1293,1290],{"__ignoreMap":40},[16,1295,1296,1297,1300,1301,1303,1304,226],{},"An animated template is just a normal template with an ",[23,1298,1299],{},"animation"," (a duration and a frame rate) whose properties move over a ",[23,1302,1033],{}," clock. If you have not built one yet, see ",[949,1305,988],{"href":987},[11,1307,1309],{"id":1308},"try-it-with-no-key","Try it with no key",[16,1311,1312,1313,1316,1317,202,1320,490],{},"There is a public demo video template you can render with no API key and no credit cost. Its inputs are a photo (",[23,1314,1315],{},"image4_image",") and two lines of text (",[23,1318,1319],{},"text2_text",[23,1321,1322],{},"text1_text",[35,1324,1326],{"className":1129,"code":1325,"language":1131,"meta":40,"style":40},"curl -N -X POST \"https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Frender\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"templateId\": \"7ccf097c-10f2-4efc-bd03-8abe967c4711\",\n    \"data\": {\n      \"text2_text\": \"HEAR ME SPEAK AT\",\n      \"text1_text\": \"THE BLUEPIC EVENT!\"\n    },\n    \"format\": \"mp4\"\n  }'\n",[23,1327,1328,1342,1350,1356,1361,1366,1371,1376,1381,1385],{"__ignoreMap":40},[44,1329,1330,1332,1334,1336,1338,1340],{"class":46,"line":47},[44,1331,1138],{"class":143},[44,1333,1141],{"class":54},[44,1335,1144],{"class":54},[44,1337,1147],{"class":71},[44,1339,1150],{"class":71},[44,1341,1153],{"class":54},[44,1343,1344,1346,1348],{"class":46,"line":65},[44,1345,1158],{"class":54},[44,1347,1170],{"class":71},[44,1349,1153],{"class":54},[44,1351,1352,1354],{"class":46,"line":84},[44,1353,1177],{"class":54},[44,1355,1180],{"class":71},[44,1357,1358],{"class":46,"line":99},[44,1359,1360],{"class":71},"    \"templateId\": \"7ccf097c-10f2-4efc-bd03-8abe967c4711\",\n",[44,1362,1363],{"class":46,"line":114},[44,1364,1365],{"class":71},"    \"data\": {\n",[44,1367,1368],{"class":46,"line":121},[44,1369,1370],{"class":71},"      \"text2_text\": \"HEAR ME SPEAK AT\",\n",[44,1372,1373],{"class":46,"line":127},[44,1374,1375],{"class":71},"      \"text1_text\": \"THE BLUEPIC EVENT!\"\n",[44,1377,1378],{"class":46,"line":134},[44,1379,1380],{"class":71},"    },\n",[44,1382,1383],{"class":46,"line":156},[44,1384,1195],{"class":71},[44,1386,1387],{"class":46,"line":181},[44,1388,1200],{"class":71},[11,1390,1392],{"id":1391},"the-response-is-a-stream","The response is a stream",[16,1394,1395,1396,1399,1400,1403],{},"A video takes longer than a still to produce (render every frame, then encode), so the endpoint does not make you wait blind. It returns a ",[944,1397,1398],{},"Server-Sent Events"," stream: live progress while it works, then a final event with the hosted URL. That is what ",[23,1401,1402],{},"-N"," is for, so curl does not buffer the events.",[35,1405,1408],{"className":1406,"code":1407,"language":1291},[1289],"data: {\"type\":\"init\",\"sessionId\":\"…\"}\ndata: {\"type\":\"keyframe\",\"frame\":48,\"totalFrames\":192}\ndata: {\"type\":\"encode_video\",\"frame\":140,\"totalFrames\":192}\ndata: {\"type\":\"end\",\"result\":\"https:\u002F\u002F…\u002Fresult.mp4\"}\n",[23,1409,1407],{"__ignoreMap":40},[16,1411,1412,1413,1416,1417,1420],{},"Read the stream until the ",[23,1414,1415],{},"end"," event and take its ",[23,1418,1419],{},"result",". That is your video's URL.",[35,1422,1424],{"className":37,"code":1423,"language":39,"meta":40,"style":40},"const res = await fetch(\"https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Frender\", {\n  method: \"POST\",\n  headers: { Authorization: \"\u003Cyour API key>\", \"Content-Type\": \"application\u002Fjson\" },\n  body: JSON.stringify({\n    templateId: \"7ccf097c-10f2-4efc-bd03-8abe967c4711\",\n    data: { text1_text: \"THE BLUEPIC EVENT!\" },\n    format: \"mp4\",\n  }),\n});\n\nconst reader = res.body.getReader();\nconst decoder = new TextDecoder();\nlet buffer = \"\";\nlet url = \"\";\nfor (;;) {\n  const { value, done } = await reader.read();\n  if (value) buffer += decoder.decode(value, { stream: true });\n  let i;\n  while ((i = buffer.indexOf(\"\\n\\n\")) !== -1) {\n    const line = buffer.slice(0, i).split(\"\\n\").find((l) => l.startsWith(\"data:\"));\n    buffer = buffer.slice(i + 2);\n    if (!line) continue;\n    const evt = JSON.parse(line.slice(5).trim());\n    if (evt.type === \"end\") url = evt.result;\n    \u002F\u002F otherwise evt.frame \u002F evt.totalFrames drives a progress bar\n  }\n  if (done) break;\n}\n\u002F\u002F `url` is your rendered MP4\n",[23,1425,1426,1444,1453,1470,1483,1492,1502,1511,1516,1521,1525,1539,1553,1566,1579,1586,1610,1629,1635,1666,1716,1735,1752,1780,1797,1802,1806,1816,1820],{"__ignoreMap":40},[44,1427,1428,1430,1432,1434,1436,1438,1440,1442],{"class":46,"line":47},[44,1429,51],{"class":50},[44,1431,162],{"class":54},[44,1433,58],{"class":50},[44,1435,167],{"class":50},[44,1437,170],{"class":143},[44,1439,147],{"class":61},[44,1441,175],{"class":71},[44,1443,178],{"class":61},[44,1445,1446,1449,1451],{"class":46,"line":65},[44,1447,1448],{"class":61},"  method: ",[44,1450,187],{"class":71},[44,1452,190],{"class":61},[44,1454,1455,1458,1460,1462,1464,1466,1468],{"class":46,"line":84},[44,1456,1457],{"class":61},"  headers: { Authorization: ",[44,1459,199],{"class":71},[44,1461,202],{"class":61},[44,1463,205],{"class":71},[44,1465,208],{"class":61},[44,1467,211],{"class":71},[44,1469,214],{"class":61},[44,1471,1472,1475,1477,1479,1481],{"class":46,"line":99},[44,1473,1474],{"class":61},"  body: ",[44,1476,223],{"class":54},[44,1478,226],{"class":61},[44,1480,229],{"class":143},[44,1482,232],{"class":61},[44,1484,1485,1488,1490],{"class":46,"line":114},[44,1486,1487],{"class":61},"    templateId: ",[44,1489,241],{"class":71},[44,1491,190],{"class":61},[44,1493,1494,1497,1500],{"class":46,"line":121},[44,1495,1496],{"class":61},"    data: { text1_text: ",[44,1498,1499],{"class":71},"\"THE BLUEPIC EVENT!\"",[44,1501,214],{"class":61},[44,1503,1504,1507,1509],{"class":46,"line":127},[44,1505,1506],{"class":61},"    format: ",[44,1508,258],{"class":71},[44,1510,190],{"class":61},[44,1512,1513],{"class":46,"line":134},[44,1514,1515],{"class":61},"  }),\n",[44,1517,1518],{"class":46,"line":156},[44,1519,1520],{"class":61},"});\n",[44,1522,1523],{"class":46,"line":181},[44,1524,131],{"emptyLinePlaceholder":130},[44,1526,1527,1529,1531,1533,1535,1537],{"class":46,"line":193},[44,1528,51],{"class":50},[44,1530,286],{"class":54},[44,1532,58],{"class":50},[44,1534,291],{"class":61},[44,1536,294],{"class":143},[44,1538,297],{"class":61},[44,1540,1541,1543,1545,1547,1549,1551],{"class":46,"line":217},[44,1542,51],{"class":50},[44,1544,305],{"class":54},[44,1546,58],{"class":50},[44,1548,310],{"class":50},[44,1550,313],{"class":143},[44,1552,297],{"class":61},[44,1554,1555,1558,1560,1562,1564],{"class":46,"line":235},[44,1556,1557],{"class":50},"let",[44,1559,324],{"class":61},[44,1561,327],{"class":50},[44,1563,330],{"class":71},[44,1565,333],{"class":61},[44,1567,1568,1570,1573,1575,1577],{"class":46,"line":246},[44,1569,1557],{"class":50},[44,1571,1572],{"class":61}," url ",[44,1574,327],{"class":50},[44,1576,330],{"class":71},[44,1578,333],{"class":61},[44,1580,1581,1584],{"class":46,"line":252},[44,1582,1583],{"class":50},"for",[44,1585,342],{"class":61},[44,1587,1588,1590,1592,1594,1596,1598,1600,1602,1604,1606,1608],{"class":46,"line":263},[44,1589,159],{"class":50},[44,1591,351],{"class":61},[44,1593,354],{"class":54},[44,1595,202],{"class":61},[44,1597,359],{"class":54},[44,1599,362],{"class":61},[44,1601,327],{"class":50},[44,1603,167],{"class":50},[44,1605,369],{"class":61},[44,1607,372],{"class":143},[44,1609,297],{"class":61},[44,1611,1612,1615,1617,1619,1621,1623,1625,1627],{"class":46,"line":269},[44,1613,1614],{"class":50},"  if",[44,1616,383],{"class":61},[44,1618,386],{"class":50},[44,1620,389],{"class":61},[44,1622,392],{"class":143},[44,1624,395],{"class":61},[44,1626,398],{"class":54},[44,1628,401],{"class":61},[44,1630,1631,1633],{"class":46,"line":275},[44,1632,321],{"class":50},[44,1634,410],{"class":61},[44,1636,1637,1640,1642,1644,1646,1648,1650,1652,1654,1656,1658,1660,1662,1664],{"class":46,"line":281},[44,1638,1639],{"class":50},"  while",[44,1641,419],{"class":61},[44,1643,327],{"class":50},[44,1645,424],{"class":61},[44,1647,427],{"class":143},[44,1649,147],{"class":61},[44,1651,432],{"class":71},[44,1653,435],{"class":54},[44,1655,432],{"class":71},[44,1657,440],{"class":61},[44,1659,443],{"class":50},[44,1661,446],{"class":50},[44,1663,449],{"class":54},[44,1665,153],{"class":61},[44,1667,1668,1670,1672,1674,1676,1678,1680,1682,1684,1686,1688,1690,1692,1694,1696,1698,1700,1702,1704,1706,1708,1710,1712,1714],{"class":46,"line":300},[44,1669,348],{"class":50},[44,1671,460],{"class":54},[44,1673,58],{"class":50},[44,1675,424],{"class":61},[44,1677,467],{"class":143},[44,1679,147],{"class":61},[44,1681,472],{"class":54},[44,1683,475],{"class":61},[44,1685,478],{"class":143},[44,1687,147],{"class":61},[44,1689,432],{"class":71},[44,1691,485],{"class":54},[44,1693,432],{"class":71},[44,1695,490],{"class":61},[44,1697,493],{"class":143},[44,1699,496],{"class":61},[44,1701,499],{"class":150},[44,1703,502],{"class":61},[44,1705,505],{"class":50},[44,1707,508],{"class":61},[44,1709,511],{"class":143},[44,1711,147],{"class":61},[44,1713,516],{"class":71},[44,1715,519],{"class":61},[44,1717,1718,1721,1723,1725,1727,1729,1731,1733],{"class":46,"line":318},[44,1719,1720],{"class":61},"    buffer ",[44,1722,327],{"class":50},[44,1724,424],{"class":61},[44,1726,467],{"class":143},[44,1728,534],{"class":61},[44,1730,537],{"class":50},[44,1732,540],{"class":54},[44,1734,543],{"class":61},[44,1736,1737,1739,1741,1744,1747,1750],{"class":46,"line":336},[44,1738,380],{"class":50},[44,1740,1069],{"class":61},[44,1742,1743],{"class":50},"!",[44,1745,1746],{"class":61},"line) ",[44,1748,1749],{"class":50},"continue",[44,1751,333],{"class":61},[44,1753,1754,1756,1758,1760,1762,1764,1766,1768,1770,1772,1774,1776,1778],{"class":46,"line":345},[44,1755,348],{"class":50},[44,1757,561],{"class":54},[44,1759,58],{"class":50},[44,1761,566],{"class":54},[44,1763,226],{"class":61},[44,1765,571],{"class":143},[44,1767,574],{"class":61},[44,1769,467],{"class":143},[44,1771,147],{"class":61},[44,1773,581],{"class":54},[44,1775,490],{"class":61},[44,1777,586],{"class":143},[44,1779,589],{"class":61},[44,1781,1782,1784,1786,1788,1790,1793,1795],{"class":46,"line":377},[44,1783,380],{"class":50},[44,1785,598],{"class":61},[44,1787,601],{"class":50},[44,1789,604],{"class":71},[44,1791,1792],{"class":61},") url ",[44,1794,327],{"class":50},[44,1796,612],{"class":61},[44,1798,1799],{"class":46,"line":404},[44,1800,1801],{"class":117},"    \u002F\u002F otherwise evt.frame \u002F evt.totalFrames drives a progress bar\n",[44,1803,1804],{"class":46,"line":413},[44,1805,668],{"class":61},[44,1807,1808,1810,1812,1814],{"class":46,"line":454},[44,1809,1614],{"class":50},[44,1811,657],{"class":61},[44,1813,660],{"class":50},[44,1815,333],{"class":61},[44,1817,1818],{"class":46,"line":522},[44,1819,691],{"class":61},[44,1821,1822],{"class":46,"line":546},[44,1823,1824],{"class":117},"\u002F\u002F `url` is your rendered MP4\n",[11,1826,1828],{"id":1827},"formats-keys-and-credits","Formats, keys, and credits",[938,1830,1831,1858,1880],{},[941,1832,1833,1836,1837,1077,1839,1842,1843,202,1846,202,1849,202,1852,1855,1856,226],{},[944,1834,1835],{},"Formats."," ",[23,1838,1072],{},[23,1840,1841],{},"mov"," produce video. The image formats (",[23,1844,1845],{},"png",[23,1847,1848],{},"jpeg",[23,1850,1851],{},"pdf",[23,1853,1854],{},"svg",") render the same template as a still. See ",[949,1857,1238],{"href":1237},[941,1859,1860,1863,1864,1867,1868,1871,1872,1876,1877,1879],{},[944,1861,1862],{},"Your own templates."," Pass your API key as a raw ",[23,1865,1866],{},"Authorization"," header (no ",[23,1869,1870],{},"Bearer"," prefix). Issue one on the ",[949,1873,1875],{"href":1874},"\u002Fidml\u002Faccount","account page",". Any template with an ",[23,1878,1299],{}," renders as video.",[941,1881,1882,1884,1885,1887],{},[944,1883,946],{}," A still is 1 credit; a video is metered by length and resolution, at 1 credit per second of 2K at 24fps (",[949,1886,952],{"href":951},"). The public demo templates are free.",[11,1889,972],{"id":971},[938,1891,1892,1898],{},[941,1893,1894,1895,226],{},"Personalize and ",[949,1896,1897],{"href":1006},"batch many videos from one template",[941,1899,1900,1901,1905],{},"Understand ",[949,1902,1904],{"href":1903},"\u002Fdocs\u002Fexpression-engine-over-time","the expression engine"," that drives the motion.",[990,1907,1908],{},"html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#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 pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}",{"title":40,"searchDepth":65,"depth":65,"links":1910},[1911,1912,1913,1914,1915],{"id":1274,"depth":65,"text":1275},{"id":1308,"depth":65,"text":1309},{"id":1391,"depth":65,"text":1392},{"id":1827,"depth":65,"text":1828},{"id":971,"depth":65,"text":972},"How to generate an MP4 from a Bluepic template with one API call. POST a templateId and your data, read the streamed render, and get a hosted video URL back. Includes a free public demo you can call with no key.","\u002Fdocs-images\u002Fgenerate-video-via-api.svg",{},{"title":1269,"description":1916},"docs\u002Fgenerate-video-via-api","nd5mtlC-nnUxDpFennX1iW-m8vDt-mWoFOiP52SpZ78",{"id":1923,"title":1924,"body":1925,"category":1000,"date":1001,"description":2312,"extension":1003,"heroImage":2313,"meta":2314,"navigation":130,"path":987,"readingMinutes":127,"seo":2315,"stem":2316,"tool":1000,"__hash__":2317},"docs\u002Fdocs\u002Fhow-to-video-templates.md","How to: build a video template in Studio",{"type":8,"value":1926,"toc":2301},[1927,1931,1940,1943,1947,1954,1957,1964,1968,1988,2002,2005,2011,2015,2021,2034,2040,2044,2050,2056,2094,2100,2107,2113,2122,2126,2129,2150,2156,2170,2173,2179,2183,2189,2193,2199,2241,2252,2256,2265,2271,2286,2299],[11,1928,1930],{"id":1929},"what-you-are-building","What you are building",[16,1932,1933,1934,1936,1937,1939],{},"A video template is not a separate kind of thing. It is a normal Bluepic template with an ",[944,1935,1299],{},": a duration, a frame rate, and one or more properties that change over time. You build it visually in ",[949,1938,1054],{"href":1053},", and you render it from your code by changing one field on the request. No timeline export, no second tool.",[16,1941,1942],{},"This guide takes a static template and animates it, step by step.",[11,1944,1946],{"id":1945},"step-1-design-the-template","Step 1: Design the template",[16,1948,1949,1950,1953],{},"Start from a normal template. Add your text, images, and shapes, connect the fields you want to fill from data, and get the layout looking right at rest. If you are new to this, follow ",[949,1951,1952],{"href":1053},"Creating templates in Bluepic Studio"," first, then come back.",[16,1955,1956],{},"Everything you already know about templates still applies. Animation is something you add on top, not a different mode you design in.",[16,1958,1959],{},[1960,1961],"img",{"alt":1962,"src":1963},"The Bluepic Studio editor: the design canvas, the layer list on the right, and the tools in the top toolbar.","\u002Fscreenshots\u002Fstudio-video-editor.png",[11,1965,1967],{"id":1966},"step-2-switch-the-format-to-video","Step 2: Switch the format to video",[16,1969,1970,1971,1974,1975,1978,1979,202,1981,1983,1984,1987],{},"Open ",[944,1972,1973],{},"Project Settings"," (the gear icon, top right) and set the output ",[944,1976,1977],{},"Format"," to a video format (",[23,1980,1072],{},[23,1982,1841],{},", or ",[23,1985,1986],{},"webm","). Two things happen:",[938,1989,1990,1997],{},[941,1991,1992,1993,1996],{},"An ",[944,1994,1995],{},"Animation"," section appears in Project Settings.",[941,1998,1992,1999,2001],{},[944,2000,1091],{}," button appears in the editor toolbar.",[16,2003,2004],{},"Setting a video format is what tells Studio you intend to animate. You can switch back to a still format at any time.",[16,2006,2007],{},[1960,2008],{"alt":2009,"src":2010},"Project Settings with the format set to MP4 and the Animation section showing Duration and FPS below it.","\u002Fscreenshots\u002Fstudio-video-project-settings.png",[11,2012,2014],{"id":2013},"step-3-set-the-duration-and-frame-rate","Step 3: Set the duration and frame rate",[16,2016,2017,2018,2020],{},"In the ",[944,2019,1995],{}," section of Project Settings (shown above), set:",[938,2022,2023,2029],{},[941,2024,2025,2028],{},[944,2026,2027],{},"Duration (s)",": how long the clip runs, in seconds.",[941,2030,2031,2033],{},[944,2032,1080],{},": frames per second (24 and 30 are typical).",[16,2035,2036,2037,226],{},"That is the clip's timeline. An 8-second clip at 24fps is 192 frames, and the engine will evaluate your whole template once per frame when it renders. Click ",[944,2038,2039],{},"Apply",[11,2041,2043],{"id":2042},"step-4-turn-a-property-into-an-animation","Step 4: Turn a property into an animation",[16,2045,2046,2047,2049],{},"This is the core move. In the top toolbar, click the ",[944,2048,1091],{}," button (hover a toolbar button to see its name). Studio enters animate mode and highlights the properties that can be animated. Then click the property you want to move.",[16,2051,2052],{},[1960,2053],{"alt":2054,"src":2055},"The \"Animate a property\" button in the top toolbar, with its tooltip.","\u002Fscreenshots\u002Fstudio-video-animate-button.png",[16,2057,2058,2059,2062,2063,202,2066,2069,2070,202,2073,2076,2077,202,2080,202,2083,2086,2087,2090,2091,226],{},"Only ",[944,2060,2061],{},"numeric"," properties are animatable, because animation interpolates a number between values: position (",[23,2064,2065],{},"x",[23,2067,2068],{},"y","), size (",[23,2071,2072],{},"width",[23,2074,2075],{},"height","), the transform values (",[23,2078,2079],{},"translateX\u002FY",[23,2081,2082],{},"scaleX\u002FY",[23,2084,2085],{},"rotate","), and ",[23,2088,2089],{},"opacity",". Text content and colours are not interpolated this way. You will find these in the element's properties panel on the left, for example under ",[944,2092,2093],{},"Transforms",[16,2095,2096],{},[1960,2097],{"alt":2098,"src":2099},"An element selected, with its numeric properties (Transforms: Translate, Scale, Rotate) in the left panel.","\u002Fscreenshots\u002Fstudio-video-transforms.png",[16,2101,2102,2103,2106],{},"The moment you click a property, Studio ",[944,2104,2105],{},"freezes its current value as the first keyframe"," and rewrites the property as an expression:",[35,2108,2111],{"className":2109,"code":2110,"language":1291},[1289],"position.x = ANIMATE(TIME, LINEAR(0, 750))\n",[23,2112,2110],{"__ignoreMap":40},[16,2114,2115,2116,2118,2119,2121],{},"Read that as: \"at ",[23,2117,1033],{}," 0 seconds, ",[23,2120,2065],{}," is 750.\" Nothing moves yet, because there is only one keyframe. The timeline opens, focused on this property, ready for you to add more.",[11,2123,2125],{"id":2124},"step-5-add-keyframes-on-the-timeline","Step 5: Add keyframes on the timeline",[16,2127,2128],{},"Now build the motion:",[1023,2130,2131,2137,2147],{},[941,2132,2133,2136],{},[944,2134,2135],{},"Move the playhead"," on the timeline to a later moment, say 2 seconds.",[941,2138,2139,2142,2143,2146],{},[944,2140,2141],{},"Change the property's value"," (drag the element, or type a new number). Studio records a ",[944,2144,2145],{},"new keyframe"," at that time.",[941,2148,2149],{},"Repeat for each moment you want to pin down.",[16,2151,2152,2153,1126],{},"For each keyframe you can choose how the value ",[944,2154,2155],{},"eases into it",[938,2157,2158,2164],{},[941,2159,2160,2163],{},[944,2161,2162],{},"Linear",": a straight, constant-speed move.",[941,2165,2166,2169],{},[944,2167,2168],{},"Bezier",": an eased move (slow-in, slow-out, or an overshoot), using a cubic-bezier curve, exactly like CSS easing.",[16,2171,2172],{},"Between keyframes, the value interpolates. Before the first keyframe and after the last, it holds. Animate as many properties on as many elements as you like; each gets its own track.",[16,2174,2175],{},[1960,2176],{"alt":2177,"src":2178},"The timeline keyframe editor: a property track (image4 · translateX) with keyframes, the Time and Value fields, and the Linear \u002F Bezier interpolation buttons.","\u002Fscreenshots\u002Fstudio-video-timeline.png",[11,2180,2182],{"id":2181},"step-6-preview","Step 6: Preview",[16,2184,2185,2186,2188],{},"Scrub the timeline. As you drag, Studio sets ",[23,2187,1033],{}," and re-evaluates every expression live, so you see the exact frames the render will produce. This preview is instant and free; nothing is rendered server-side yet.",[11,2190,2192],{"id":2191},"step-7-render-the-video-over-the-api","Step 7: Render the video over the API",[16,2194,2195,2196,2198],{},"When it looks right, ",[944,2197,1107],{}," the template. Now render it from your code exactly like any other template, but ask for a video format:",[35,2200,2202],{"className":1129,"code":2201,"language":1131,"meta":40,"style":40},"curl -N -X POST \"https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Frender\" \\\n  -H \"Authorization: \u003Cyour API key>\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{ \"templateId\": \"\u003Cyour template id>\", \"data\": { }, \"format\": \"mp4\" }'\n",[23,2203,2204,2218,2226,2234],{"__ignoreMap":40},[44,2205,2206,2208,2210,2212,2214,2216],{"class":46,"line":47},[44,2207,1138],{"class":143},[44,2209,1141],{"class":54},[44,2211,1144],{"class":54},[44,2213,1147],{"class":71},[44,2215,1150],{"class":71},[44,2217,1153],{"class":54},[44,2219,2220,2222,2224],{"class":46,"line":65},[44,2221,1158],{"class":54},[44,2223,1161],{"class":71},[44,2225,1153],{"class":54},[44,2227,2228,2230,2232],{"class":46,"line":84},[44,2229,1158],{"class":54},[44,2231,1170],{"class":71},[44,2233,1153],{"class":54},[44,2235,2236,2238],{"class":46,"line":99},[44,2237,1177],{"class":54},[44,2239,2240],{"class":71}," '{ \"templateId\": \"\u003Cyour template id>\", \"data\": { }, \"format\": \"mp4\" }'\n",[16,2242,2243,2244,2246,2247,2249,2250,226],{},"Fill ",[23,2245,25],{}," with the fields you connected in Step 1, one clip per data row. A still render is 1 credit; a video is metered by length and resolution (",[949,2248,952],{"href":951},"). For the response shape (video streams progress and returns a hosted URL), see ",[949,2251,1238],{"href":1237},[11,2253,2255],{"id":2254},"how-the-animation-works-briefly","How the animation works, briefly",[16,2257,2258,2259,2261,2262,1126],{},"Every keyframe you place is compiled into one expression on the property. The ",[23,2260,1091],{}," button and the timeline are a visual front end for ",[23,2263,2264],{},"ANIMATE",[35,2266,2269],{"className":2267,"code":2268,"language":1291},[1289],"ANIMATE(TIME, LINEAR(0, 750), CUBIC(2, 900, …), LINEAR(4, 750))\n",[23,2270,2268],{"__ignoreMap":40},[16,2272,2273,2275,2276,2278,2279,1077,2282,2285],{},[23,2274,2264],{}," takes ",[23,2277,1033],{}," and your keyframes and returns the value at that moment. ",[23,2280,2281],{},"LINEAR(t, v)",[23,2283,2284],{},"CUBIC(t, v, …)"," are the straight and eased keyframes you set on the timeline. You never have to type this; it is what Studio writes as you work.",[16,2287,2288,2289,202,2292,2295,2296,226],{},"If you want the full model, including composition (",[23,2290,2291],{},"750 + ANIMATE(...)",[23,2293,2294],{},"840 * ANIMATE(...)",") and how frames are sampled, read ",[949,2297,2298],{"href":1903},"the expression engine over TIME",[990,2300,1252],{},{"title":40,"searchDepth":65,"depth":65,"links":2302},[2303,2304,2305,2306,2307,2308,2309,2310,2311],{"id":1929,"depth":65,"text":1930},{"id":1945,"depth":65,"text":1946},{"id":1966,"depth":65,"text":1967},{"id":2013,"depth":65,"text":2014},{"id":2042,"depth":65,"text":2043},{"id":2124,"depth":65,"text":2125},{"id":2181,"depth":65,"text":2182},{"id":2191,"depth":65,"text":2192},{"id":2254,"depth":65,"text":2255},"A step-by-step guide to turning a Bluepic template into an animated video, visually in Studio. Set a duration, click a property into an animation, keyframe it on the timeline, then render an MP4 over the API.","\u002Fdocs-images\u002Fhow-to-video-templates.svg",{},{"title":1924,"description":2312},"docs\u002Fhow-to-video-templates","F82tzQm4KFzUIOC3BnY6tXYDPRgi4FwGX14u3IS2YqM",{"id":2319,"title":2320,"body":2321,"category":1000,"date":1001,"description":2501,"extension":1003,"heroImage":2502,"meta":2503,"navigation":130,"path":951,"readingMinutes":99,"seo":2504,"stem":2505,"tool":1000,"__hash__":2506},"docs\u002Fdocs\u002Fvideo-credits.md","How video credits work",{"type":8,"value":2322,"toc":2493},[2323,2327,2334,2342,2345,2349,2355,2362,2376,2380,2447,2450,2454,2461,2465,2477,2479],[11,2324,2326],{"id":2325},"the-unit","The unit",[16,2328,2329,2330,2333],{},"A still image render costs ",[944,2331,2332],{},"1 credit",". Video is metered by how much you actually render, on one simple unit:",[2335,2336,2337],"blockquote",{},[16,2338,2339],{},[944,2340,2341],{},"1 credit = 1 second of 2K video at 24 fps.",[16,2343,2344],{},"\"2K\" here is a frame area of 2048 × 1080 pixels. A render at that size and frame rate costs exactly 1 credit per second. Larger frames or higher frame rates cost proportionally more; smaller or slower ones cost less.",[11,2346,2348],{"id":2347},"the-formula","The formula",[35,2350,2353],{"className":2351,"code":2352,"language":1291},[1289],"credits = ceil( seconds\n                × (width × height) \u002F (2048 × 1080)\n                × (fps \u002F 24) )\n",[23,2354,2352],{"__ignoreMap":40},[16,2356,2357,2358,2361],{},"That is it. The cost is your clip's ",[944,2359,2360],{},"total rendered pixels"," (area × frame rate × duration) measured against one 2K second. Two consequences worth knowing:",[938,2363,2364,2370],{},[941,2365,2366,2367,226],{},"A render always costs ",[944,2368,2369],{},"at least 1 credit",[941,2371,2372,2373,2375],{},"The per-second rate (the ",[23,2374,449],{}," in \"1 credit per second\") is a single number we can adjust. The shape of the formula does not change.",[11,2377,2379],{"id":2378},"worked-examples","Worked examples",[2381,2382,2383,2396],"table",{},[2384,2385,2386],"thead",{},[2387,2388,2389,2393],"tr",{},[2390,2391,2392],"th",{},"Output",[2390,2394,2395],{},"Credits",[2397,2398,2399,2408,2415,2423,2431,2439],"tbody",{},[2387,2400,2401,2405],{},[2402,2403,2404],"td",{},"720p (1280×720), 24fps, 6s",[2402,2406,2407],{},"3",[2387,2409,2410,2413],{},[2402,2411,2412],{},"2K (2048×1080), 24fps, 1s",[2402,2414,449],{},[2387,2416,2417,2420],{},[2402,2418,2419],{},"2K (2048×1080), 24fps, 10s",[2402,2421,2422],{},"10",[2387,2424,2425,2428],{},[2402,2426,2427],{},"1080p (1920×1080), 30fps, 15s",[2402,2429,2430],{},"18",[2387,2432,2433,2436],{},[2402,2434,2435],{},"Square 1500×1500, 24fps, 8s (the demo)",[2402,2437,2438],{},"9",[2387,2440,2441,2444],{},[2402,2442,2443],{},"4K (3840×2160), 30fps, 10s",[2402,2445,2446],{},"47",[16,2448,2449],{},"Read the pattern: doubling the duration doubles the credits; a 4K frame is about four 2K frames, so it costs about four times as much per second; 30fps costs 25% more than 24fps.",[11,2451,2453],{"id":2452},"why-meter-this-way","Why meter this way",[16,2455,2456,2457,2460],{},"Rendering a video means rasterizing every frame and then encoding them, so the real work scales with the number of pixels processed: ",[944,2458,2459],{},"resolution × frame rate × duration",". Metering on that exact quantity keeps the price honest in both directions. A short, small clip is genuinely cheap, and a long 4K clip pays for the resources it actually uses, rather than every render costing the same flat amount regardless of size.",[11,2462,2464],{"id":2463},"estimating-before-you-render","Estimating before you render",[16,2466,2467,2468,2470,2471,2473,2474,2476],{},"Because the formula only needs the output size, frame rate, and duration, you can compute the cost yourself before calling the API. The template's own ",[23,2469,1299],{}," (its duration and fps) and dimensions are what the meter reads, unless you override ",[23,2472,2072],{}," or ",[23,2475,2075],{}," on the request.",[11,2478,972],{"id":971},[938,2480,2481,2486],{},[941,2482,2483,2485],{},[949,2484,1269],{"href":980},": the request and the streamed response.",[941,2487,2488,2492],{},[949,2489,2491],{"href":2490},"\u002Fdocs\u002Frender-your-first-template","Render your first template",": the quickstart, including your free starting credits.",{"title":40,"searchDepth":65,"depth":65,"links":2494},[2495,2496,2497,2498,2499,2500],{"id":2325,"depth":65,"text":2326},{"id":2347,"depth":65,"text":2348},{"id":2378,"depth":65,"text":2379},{"id":2452,"depth":65,"text":2453},{"id":2463,"depth":65,"text":2464},{"id":971,"depth":65,"text":972},"Video rendering is metered transparently. One credit is one second of 2K video at 24fps, and the cost scales linearly with resolution and frame rate. Here is the exact formula, with worked examples.","\u002Fdocs-images\u002Fvideo-credits.svg",{},{"title":2320,"description":2501},"docs\u002Fvideo-credits","ev9FQTJKBEQJalDWCzqcIaV_aGiyM1YNYLXGVnhsvvs",{"id":2508,"title":2509,"body":2510,"category":1000,"date":3222,"description":3223,"extension":1003,"heroImage":3224,"meta":3225,"navigation":130,"path":1237,"readingMinutes":121,"seo":3226,"stem":3227,"tool":1000,"__hash__":3228},"docs\u002Fdocs\u002Fone-template-two-outputs.md","One template, two outputs: still and video",{"type":8,"value":2511,"toc":3215},[2512,2516,2523,2532,2555,2564,2566,2583,2599,2675,2682,2690,2750,2755,2759,2765,2774,2779,2786,3168,3172,3193,3199,3203,3206,3213],[11,2513,2515],{"id":2514},"the-idea","The idea",[16,2517,2518,2519,2522],{},"A Bluepic template is ",[944,2520,2521],{},"reactive",". Position, scale, opacity, colour, and text are expressions, not fixed values. That is what lets one template render a thousand on-brand images from your data.",[16,2524,2525,2526,2528,2529,2531],{},"Add one input, a ",[23,2527,1033],{}," clock, and those same expressions become an animation. So the template you already use for images is also a video template. You do not build a second thing. You change the ",[23,2530,1284],{}," on the same request.",[938,2533,2534,2547],{},[941,2535,2536,2539,2540,202,2542,202,2544,2546],{},[23,2537,2538],{},"format: \"png\""," (or ",[23,2541,1848],{},[23,2543,1851],{},[23,2545,1854],{},") gives you a still.",[941,2548,2549,2539,2552,2554],{},[23,2550,2551],{},"format: \"mp4\"",[23,2553,1841],{},") gives you the animated clip.",[16,2556,2557,2558,2560,2561,2563],{},"Same ",[23,2559,1122],{},", same ",[23,2562,25],{},". One design, two outputs.",[11,2565,1309],{"id":1308},[16,2567,2568,2569,2572,2573,2576,2577,1316,2579,202,2581,490],{},"There is a public demo ",[944,2570,2571],{},"video"," template you can render with no API key and no credit cost. It is the same one on the ",[949,2574,2575],{"href":2575},"\u002Fvideo"," playground. Its inputs are a photo (",[23,2578,1315],{},[23,2580,1319],{},[23,2582,1322],{},[16,2584,2585,2586,2589,2590,2592,2593,2595,2596,2598],{},"First, render a ",[944,2587,2588],{},"still",". An animated template is a function of ",[23,2591,1033],{},", so pick the frame you want by passing ",[23,2594,1033],{}," in ",[23,2597,25],{}," (in seconds):",[35,2600,2602],{"className":1129,"code":2601,"language":1131,"meta":40,"style":40},"curl -X POST \"https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Frender\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"templateId\": \"7ccf097c-10f2-4efc-bd03-8abe967c4711\",\n    \"data\": {\n      \"TIME\": 4,\n      \"text2_text\": \"HEAR ME SPEAK AT\",\n      \"text1_text\": \"THE BLUEPIC EVENT!\"\n    },\n    \"format\": \"png\"\n  }' \\\n  --output frame.png\n",[23,2603,2604,2616,2624,2630,2634,2638,2643,2647,2651,2655,2660,2667],{"__ignoreMap":40},[44,2605,2606,2608,2610,2612,2614],{"class":46,"line":47},[44,2607,1138],{"class":143},[44,2609,1144],{"class":54},[44,2611,1147],{"class":71},[44,2613,1150],{"class":71},[44,2615,1153],{"class":54},[44,2617,2618,2620,2622],{"class":46,"line":65},[44,2619,1158],{"class":54},[44,2621,1170],{"class":71},[44,2623,1153],{"class":54},[44,2625,2626,2628],{"class":46,"line":84},[44,2627,1177],{"class":54},[44,2629,1180],{"class":71},[44,2631,2632],{"class":46,"line":99},[44,2633,1360],{"class":71},[44,2635,2636],{"class":46,"line":114},[44,2637,1365],{"class":71},[44,2639,2640],{"class":46,"line":121},[44,2641,2642],{"class":71},"      \"TIME\": 4,\n",[44,2644,2645],{"class":46,"line":127},[44,2646,1370],{"class":71},[44,2648,2649],{"class":46,"line":134},[44,2650,1375],{"class":71},[44,2652,2653],{"class":46,"line":156},[44,2654,1380],{"class":71},[44,2656,2657],{"class":46,"line":181},[44,2658,2659],{"class":71},"    \"format\": \"png\"\n",[44,2661,2662,2665],{"class":46,"line":193},[44,2663,2664],{"class":71},"  }'",[44,2666,1153],{"class":54},[44,2668,2669,2672],{"class":46,"line":217},[44,2670,2671],{"class":54},"  --output",[44,2673,2674],{"class":71}," frame.png\n",[16,2676,2677,2678,2681],{},"You get ",[23,2679,2680],{},"frame.png",", the design at the 4-second mark.",[16,2683,2684,2685,2687,2688,1126],{},"Now render the ",[944,2686,2571],{},". Same template, same data, ",[23,2689,2551],{},[35,2691,2692],{"className":1129,"code":1325,"language":1131,"meta":40,"style":40},[23,2693,2694,2708,2716,2722,2726,2730,2734,2738,2742,2746],{"__ignoreMap":40},[44,2695,2696,2698,2700,2702,2704,2706],{"class":46,"line":47},[44,2697,1138],{"class":143},[44,2699,1141],{"class":54},[44,2701,1144],{"class":54},[44,2703,1147],{"class":71},[44,2705,1150],{"class":71},[44,2707,1153],{"class":54},[44,2709,2710,2712,2714],{"class":46,"line":65},[44,2711,1158],{"class":54},[44,2713,1170],{"class":71},[44,2715,1153],{"class":54},[44,2717,2718,2720],{"class":46,"line":84},[44,2719,1177],{"class":54},[44,2721,1180],{"class":71},[44,2723,2724],{"class":46,"line":99},[44,2725,1360],{"class":71},[44,2727,2728],{"class":46,"line":114},[44,2729,1365],{"class":71},[44,2731,2732],{"class":46,"line":121},[44,2733,1370],{"class":71},[44,2735,2736],{"class":46,"line":127},[44,2737,1375],{"class":71},[44,2739,2740],{"class":46,"line":134},[44,2741,1380],{"class":71},[44,2743,2744],{"class":46,"line":156},[44,2745,1195],{"class":71},[44,2747,2748],{"class":46,"line":181},[44,2749,1200],{"class":71},[16,2751,2752,2753,226],{},"That is the whole point. Nothing changed but the ",[23,2754,1284],{},[11,2756,2758],{"id":2757},"video-streams-stills-come-back-whole","Video streams; stills come back whole",[16,2760,2761,2762,226],{},"A still is small, so the request returns the file bytes directly. That is why the PNG example uses ",[23,2763,2764],{},"--output",[16,2766,2767,2768,2770,2771,2773],{},"A video takes longer to render (frames first, then encode), so instead of making you wait blind, the request returns a ",[944,2769,1398],{}," stream. You get live progress events and, at the end, a hosted URL for the finished MP4. That is what ",[23,2772,1402],{}," does: it tells curl not to buffer, so the events arrive as they happen.",[35,2775,2777],{"className":2776,"code":1407,"language":1291},[1289],[23,2778,1407],{"__ignoreMap":40},[16,2780,1412,2781,1416,2783,2785],{},[23,2782,1415],{},[23,2784,1419],{},". That is your video's URL. In JavaScript:",[35,2787,2788],{"className":37,"code":1423,"language":39,"meta":40,"style":40},[23,2789,2790,2808,2816,2832,2844,2852,2860,2868,2872,2876,2880,2894,2908,2920,2932,2938,2962,2980,2986,3016,3066,3084,3098,3126,3142,3146,3150,3160,3164],{"__ignoreMap":40},[44,2791,2792,2794,2796,2798,2800,2802,2804,2806],{"class":46,"line":47},[44,2793,51],{"class":50},[44,2795,162],{"class":54},[44,2797,58],{"class":50},[44,2799,167],{"class":50},[44,2801,170],{"class":143},[44,2803,147],{"class":61},[44,2805,175],{"class":71},[44,2807,178],{"class":61},[44,2809,2810,2812,2814],{"class":46,"line":65},[44,2811,1448],{"class":61},[44,2813,187],{"class":71},[44,2815,190],{"class":61},[44,2817,2818,2820,2822,2824,2826,2828,2830],{"class":46,"line":84},[44,2819,1457],{"class":61},[44,2821,199],{"class":71},[44,2823,202],{"class":61},[44,2825,205],{"class":71},[44,2827,208],{"class":61},[44,2829,211],{"class":71},[44,2831,214],{"class":61},[44,2833,2834,2836,2838,2840,2842],{"class":46,"line":99},[44,2835,1474],{"class":61},[44,2837,223],{"class":54},[44,2839,226],{"class":61},[44,2841,229],{"class":143},[44,2843,232],{"class":61},[44,2845,2846,2848,2850],{"class":46,"line":114},[44,2847,1487],{"class":61},[44,2849,241],{"class":71},[44,2851,190],{"class":61},[44,2853,2854,2856,2858],{"class":46,"line":121},[44,2855,1496],{"class":61},[44,2857,1499],{"class":71},[44,2859,214],{"class":61},[44,2861,2862,2864,2866],{"class":46,"line":127},[44,2863,1506],{"class":61},[44,2865,258],{"class":71},[44,2867,190],{"class":61},[44,2869,2870],{"class":46,"line":134},[44,2871,1515],{"class":61},[44,2873,2874],{"class":46,"line":156},[44,2875,1520],{"class":61},[44,2877,2878],{"class":46,"line":181},[44,2879,131],{"emptyLinePlaceholder":130},[44,2881,2882,2884,2886,2888,2890,2892],{"class":46,"line":193},[44,2883,51],{"class":50},[44,2885,286],{"class":54},[44,2887,58],{"class":50},[44,2889,291],{"class":61},[44,2891,294],{"class":143},[44,2893,297],{"class":61},[44,2895,2896,2898,2900,2902,2904,2906],{"class":46,"line":217},[44,2897,51],{"class":50},[44,2899,305],{"class":54},[44,2901,58],{"class":50},[44,2903,310],{"class":50},[44,2905,313],{"class":143},[44,2907,297],{"class":61},[44,2909,2910,2912,2914,2916,2918],{"class":46,"line":235},[44,2911,1557],{"class":50},[44,2913,324],{"class":61},[44,2915,327],{"class":50},[44,2917,330],{"class":71},[44,2919,333],{"class":61},[44,2921,2922,2924,2926,2928,2930],{"class":46,"line":246},[44,2923,1557],{"class":50},[44,2925,1572],{"class":61},[44,2927,327],{"class":50},[44,2929,330],{"class":71},[44,2931,333],{"class":61},[44,2933,2934,2936],{"class":46,"line":252},[44,2935,1583],{"class":50},[44,2937,342],{"class":61},[44,2939,2940,2942,2944,2946,2948,2950,2952,2954,2956,2958,2960],{"class":46,"line":263},[44,2941,159],{"class":50},[44,2943,351],{"class":61},[44,2945,354],{"class":54},[44,2947,202],{"class":61},[44,2949,359],{"class":54},[44,2951,362],{"class":61},[44,2953,327],{"class":50},[44,2955,167],{"class":50},[44,2957,369],{"class":61},[44,2959,372],{"class":143},[44,2961,297],{"class":61},[44,2963,2964,2966,2968,2970,2972,2974,2976,2978],{"class":46,"line":269},[44,2965,1614],{"class":50},[44,2967,383],{"class":61},[44,2969,386],{"class":50},[44,2971,389],{"class":61},[44,2973,392],{"class":143},[44,2975,395],{"class":61},[44,2977,398],{"class":54},[44,2979,401],{"class":61},[44,2981,2982,2984],{"class":46,"line":275},[44,2983,321],{"class":50},[44,2985,410],{"class":61},[44,2987,2988,2990,2992,2994,2996,2998,3000,3002,3004,3006,3008,3010,3012,3014],{"class":46,"line":281},[44,2989,1639],{"class":50},[44,2991,419],{"class":61},[44,2993,327],{"class":50},[44,2995,424],{"class":61},[44,2997,427],{"class":143},[44,2999,147],{"class":61},[44,3001,432],{"class":71},[44,3003,435],{"class":54},[44,3005,432],{"class":71},[44,3007,440],{"class":61},[44,3009,443],{"class":50},[44,3011,446],{"class":50},[44,3013,449],{"class":54},[44,3015,153],{"class":61},[44,3017,3018,3020,3022,3024,3026,3028,3030,3032,3034,3036,3038,3040,3042,3044,3046,3048,3050,3052,3054,3056,3058,3060,3062,3064],{"class":46,"line":300},[44,3019,348],{"class":50},[44,3021,460],{"class":54},[44,3023,58],{"class":50},[44,3025,424],{"class":61},[44,3027,467],{"class":143},[44,3029,147],{"class":61},[44,3031,472],{"class":54},[44,3033,475],{"class":61},[44,3035,478],{"class":143},[44,3037,147],{"class":61},[44,3039,432],{"class":71},[44,3041,485],{"class":54},[44,3043,432],{"class":71},[44,3045,490],{"class":61},[44,3047,493],{"class":143},[44,3049,496],{"class":61},[44,3051,499],{"class":150},[44,3053,502],{"class":61},[44,3055,505],{"class":50},[44,3057,508],{"class":61},[44,3059,511],{"class":143},[44,3061,147],{"class":61},[44,3063,516],{"class":71},[44,3065,519],{"class":61},[44,3067,3068,3070,3072,3074,3076,3078,3080,3082],{"class":46,"line":318},[44,3069,1720],{"class":61},[44,3071,327],{"class":50},[44,3073,424],{"class":61},[44,3075,467],{"class":143},[44,3077,534],{"class":61},[44,3079,537],{"class":50},[44,3081,540],{"class":54},[44,3083,543],{"class":61},[44,3085,3086,3088,3090,3092,3094,3096],{"class":46,"line":336},[44,3087,380],{"class":50},[44,3089,1069],{"class":61},[44,3091,1743],{"class":50},[44,3093,1746],{"class":61},[44,3095,1749],{"class":50},[44,3097,333],{"class":61},[44,3099,3100,3102,3104,3106,3108,3110,3112,3114,3116,3118,3120,3122,3124],{"class":46,"line":345},[44,3101,348],{"class":50},[44,3103,561],{"class":54},[44,3105,58],{"class":50},[44,3107,566],{"class":54},[44,3109,226],{"class":61},[44,3111,571],{"class":143},[44,3113,574],{"class":61},[44,3115,467],{"class":143},[44,3117,147],{"class":61},[44,3119,581],{"class":54},[44,3121,490],{"class":61},[44,3123,586],{"class":143},[44,3125,589],{"class":61},[44,3127,3128,3130,3132,3134,3136,3138,3140],{"class":46,"line":377},[44,3129,380],{"class":50},[44,3131,598],{"class":61},[44,3133,601],{"class":50},[44,3135,604],{"class":71},[44,3137,1792],{"class":61},[44,3139,327],{"class":50},[44,3141,612],{"class":61},[44,3143,3144],{"class":46,"line":404},[44,3145,1801],{"class":117},[44,3147,3148],{"class":46,"line":413},[44,3149,668],{"class":61},[44,3151,3152,3154,3156,3158],{"class":46,"line":454},[44,3153,1614],{"class":50},[44,3155,657],{"class":61},[44,3157,660],{"class":50},[44,3159,333],{"class":61},[44,3161,3162],{"class":46,"line":522},[44,3163,691],{"class":61},[44,3165,3166],{"class":46,"line":546},[44,3167,1824],{"class":117},[11,3169,3171],{"id":3170},"on-your-own-templates","On your own templates",[16,3173,3174,3175,3177,3178,3180,3181,3183,3184,3186,3187,3189,3190,3192],{},"To render one of your own templates, add your API key (issue one on the ",[949,3176,1875],{"href":1874}," and pass it as a raw ",[23,3179,1866],{}," header, with no ",[23,3182,1870],{}," prefix) and use its ",[23,3185,1122],{},". Any template you built in ",[949,3188,1054],{"href":1053}," with an ",[23,3191,1299],{}," (a duration and a frame rate) renders as video. Every other template renders as a still.",[16,3194,3195,3196,3198],{},"Metering is simple. A still costs 1 credit; a video is metered by length and resolution (1 credit per second of 2K at 24fps, see ",[949,3197,952],{"href":951},"). The two public demo templates (this one and the image demo) are free and unmetered.",[11,3200,3202],{"id":3201},"why-this-matters","Why this matters",[16,3204,3205],{},"Personalized video is normally a separate, expensive pipeline: a video editor, a timeline API, or a render farm you operate. Here it is the template you already have and the request you already make, with one field changed. Change the data, get a fresh clip. One per recipient, product, city, or language.",[16,3207,3208,3209,2595,3211,226],{},"Next, read how any property animates as a function of ",[23,3210,1033],{},[949,3212,1904],{"href":1903},[990,3214,1908],{},{"title":40,"searchDepth":65,"depth":65,"links":3216},[3217,3218,3219,3220,3221],{"id":2514,"depth":65,"text":2515},{"id":1308,"depth":65,"text":1309},{"id":2757,"depth":65,"text":2758},{"id":3170,"depth":65,"text":3171},{"id":3201,"depth":65,"text":3202},"2026-07-25","The same Bluepic template renders a PNG or an animated MP4. Change one thing, the format, and the same design becomes a personalized video. No second tool, no separate video template.","\u002Fdocs-images\u002Fone-template-two-outputs.svg",{},{"title":2509,"description":3223},"docs\u002Fone-template-two-outputs","QXKN2F5Ma6tZMrbeUdictR2dWtCzPblUFwIEq3kVE-M",{"id":3230,"title":3231,"body":3232,"category":1000,"date":3612,"description":3613,"extension":1003,"heroImage":3614,"meta":3615,"navigation":130,"path":1903,"readingMinutes":127,"seo":3616,"stem":3617,"tool":1000,"__hash__":3618},"docs\u002Fdocs\u002Fexpression-engine-over-time.md","The expression engine: animate any property over TIME",{"type":8,"value":3233,"toc":3603},[3234,3242,3246,3253,3270,3286,3295,3299,3308,3314,3321,3345,3348,3365,3369,3379,3385,3403,3407,3414,3420,3450,3454,3457,3490,3502,3506,3515,3518,3588,3592,3601],[2335,3235,3236],{},[16,3237,3238,3239,3241],{},"Looking for the visual, step-by-step version? Start with ",[949,3240,1924],{"href":987},". This article is the model underneath that workflow, for when you want to understand exactly what the timeline is writing.",[11,3243,3245],{"id":3244},"properties-are-expressions-not-constants","Properties are expressions, not constants",[16,3247,3248,3249,3252],{},"In most template tools a property is a fixed number. ",[23,3250,3251],{},"x = 750",". That is why those templates cannot move: there is nothing to animate.",[16,3254,3255,3256,3259,3260,3262,3263,1983,3266,3269],{},"In a Bluepic template, a property is an ",[944,3257,3258],{},"expression"," that is evaluated for every render. ",[23,3261,2065],{}," might be ",[23,3264,3265],{},"750",[23,3267,3268],{},"750 + logoWidth",", or a function of your data. That is already what makes a template survive real content: when the text is longer than the placeholder, the expressions re-fit the layout instead of overflowing.",[16,3271,3272,3273,3275,3276,202,3279,202,3282,3285],{},"Animation adds exactly one thing to that model: a variable called ",[23,3274,1033],{},", in seconds. Evaluate the same expressions at ",[23,3277,3278],{},"TIME = 0",[23,3280,3281],{},"TIME = 0.04",[23,3283,3284],{},"TIME = 0.08",", and so on across the clip, and the design moves. A video is the template sampled across a timeline it always had.",[16,3287,3288,3289,3294],{},"You build these expressions ",[944,3290,3291,3292],{},"visually in ",[949,3293,1054],{"href":1053},"; you rarely type them by hand. But understanding the model is what makes the rest of the platform make sense, so here is what is under the hood.",[11,3296,3298],{"id":3297},"animate-a-keyframe-track","ANIMATE: a keyframe track",[16,3300,3301,3302,3304,3305,3307],{},"The workhorse is ",[23,3303,2264],{},". You give it ",[23,3306,1033],{}," and a list of keyframes, and it returns the value at that moment.",[35,3309,3312],{"className":3310,"code":3311,"language":1291},[1289],"ANIMATE(TIME, keyframe, keyframe, keyframe, …)\n",[23,3313,3311],{"__ignoreMap":40},[16,3315,3316,3317,3320],{},"A keyframe is a ",[23,3318,3319],{},"(time, value)"," pair. There are two kinds:",[938,3322,3323,3336],{},[941,3324,3325,3327,3328,3331,3332,3335],{},[23,3326,2281],{}," is a keyframe at time ",[23,3329,3330],{},"t"," (seconds) with value ",[23,3333,3334],{},"v",", reached by straight interpolation.",[941,3337,3338,3341,3342,490],{},[23,3339,3340],{},"CUBIC(t, v, x1, y1, x2, y2)"," is the same keyframe, but reached with a cubic-bezier easing curve (the four numbers are its control points, exactly like CSS ",[23,3343,3344],{},"cubic-bezier",[16,3346,3347],{},"Two rules are worth remembering:",[1023,3349,3350,3359],{},[941,3351,3352,3355,3356,226],{},[944,3353,3354],{},"Interpolation happens between neighbours."," Between two keyframes, the value moves from the earlier one's value to the later one's. The interpolation style (straight or eased) is set by the keyframe you are moving ",[944,3357,3358],{},"toward",[941,3360,3361,3364],{},[944,3362,3363],{},"The value holds at the ends."," Before the first keyframe it stays at the first value; after the last keyframe it stays at the last value. Nothing extrapolates off into infinity.",[11,3366,3368],{"id":3367},"straight-motion-linear","Straight motion: LINEAR",[16,3370,3371,3372,3375,3376,1126],{},"Here is the headline text in the demo video. It starts off-screen to the left (",[23,3373,3374],{},"-1500","), slides in, holds, then slides back out. This is its ",[23,3377,3378],{},"translateX",[35,3380,3383],{"className":3381,"code":3382,"language":1291},[1289],"translateX = ANIMATE(TIME,\n  LINEAR(2.31, -1500),   \u002F\u002F holds off-screen until 2.31s\n  LINEAR(2.85, 0),       \u002F\u002F slides in: 2.31s to 2.85s\n  LINEAR(7,    0),       \u002F\u002F holds in place: 2.85s to 7s\n  LINEAR(7.9,  -1500))   \u002F\u002F slides back out: 7s to 7.9s\n",[23,3384,3382],{"__ignoreMap":40},[16,3386,3387,3388,3390,3391,3393,3394,3396,3397,3399,3400,3402],{},"Read it as a track. Until 2.31 seconds the value is pinned at ",[23,3389,3374],{}," (the \"before the first keyframe it holds\" rule). From 2.31 to 2.85 it moves ",[23,3392,3374],{}," to ",[23,3395,472],{},". From 2.85 to 7 it stays at ",[23,3398,472],{}," (two keyframes with the same value is a hold). From 7 to 7.9 it moves back to ",[23,3401,3374],{},". After 7.9 it holds off-screen. The intro line above it runs the same track shifted a few tenths of a second earlier, so the two lines slide in one after the other.",[11,3404,3406],{"id":3405},"eased-motion-cubic","Eased motion: CUBIC",[16,3408,3409,3410,3413],{},"Straight motion looks mechanical. ",[23,3411,3412],{},"CUBIC"," gives it weight. Here is the speaker photo scaling up with a slight overshoot, then holding, then shrinking away:",[35,3415,3418],{"className":3416,"code":3417,"language":1291},[1289],"width = 840 * ANIMATE(TIME,\n  LINEAR(0.3, 0),                             \u002F\u002F stays at 0 until 0.3s\n  CUBIC(1.3, 1, 0.5, 0.5, 0.72, 1.66),        \u002F\u002F pops to full size, overshooting past 1\n  LINEAR(7, 1),                               \u002F\u002F holds full size\n  CUBIC(7.9, 0, 0.89, 0.28, 0.89, 0.28))      \u002F\u002F eases back to 0\n",[23,3419,3417],{"__ignoreMap":40},[16,3421,3422,3423,3393,3425,3427,3428,3431,3432,3434,3435,3393,3437,3439,3440,3442,3443,3445,3446,3449],{},"The value here runs from ",[23,3424,472],{},[23,3426,449],{},", and the whole thing is multiplied by ",[23,3429,3430],{},"840",", so ",[23,3433,2072],{}," runs from ",[23,3436,472],{},[23,3438,3430],{},". The ",[23,3441,3412],{}," reaching ",[23,3444,449],{}," at 1.3s has a control point with ",[23,3447,3448],{},"y2 = 1.66",": the easing curve briefly goes past its target, which is what makes the photo \"pop\" and settle instead of arriving flatly. That one number is the difference between a corporate fade and something that feels alive.",[11,3451,3453],{"id":3452},"composition-base-track-or-base-track","Composition: base + track, or base × track",[16,3455,3456],{},"Notice the two patterns above:",[938,3458,3459,3478],{},[941,3460,3461,3465,3466,3469,3470,1126,3472],{},[944,3462,3463],{},[23,3464,2291],{}," offsets a base position. The track oscillates around zero and rides on top of a fixed anchor. That is how the photo gently bobs ",[23,3467,3468],{},"±15px"," around ",[23,3471,3251],{},[35,3473,3476],{"className":3474,"code":3475,"language":1291},[1289],"x = 750 + ANIMATE(TIME,\n  LINEAR(0, 0), CUBIC(2, 15, …), CUBIC(4, 0, …),\n  CUBIC(6, -15, …), CUBIC(8, 0, …))\n",[23,3477,3475],{"__ignoreMap":40},[941,3479,3480,3484,3485,3393,3487,3489],{},[944,3481,3482],{},[23,3483,2294],{}," scales a base size. The track runs from ",[23,3486,472],{},[23,3488,449],{}," and multiplies a dimension.",[16,3491,3492,3493,3496,3497,202,3499,3501],{},"Because these are ordinary expressions, you compose them like any other math. And because ",[1089,3494,3495],{},"every"," property is an expression, the same idea drives ",[23,3498,2089],{},[23,3500,2085],{},", colour, and even text, not just position.",[11,3503,3505],{"id":3504},"from-expressions-to-frames","From expressions to frames",[16,3507,3508,3509,3511,3512,3514],{},"When you request ",[23,3510,2551],{},", the engine samples these expressions at the template's frame rate. An 8-second clip at 24fps is 192 evaluations of the whole template, encoded into a video. Rendering is deterministic: the same ",[23,3513,1033],{}," always produces the same frame, so the output is stable and cache-friendly at volume.",[16,3516,3517],{},"Two ways to see it for yourself:",[938,3519,3520,3532],{},[941,3521,3522,3528,3529,3531],{},[944,3523,3524,3525,226],{},"Scrub the ",[949,3526,3527],{"href":2575},"\u002Fvideo playground"," Dragging the timeline sets ",[23,3530,1033],{}," and re-evaluates every expression live, in your browser. No render, no credits.",[941,3533,3534,3537,3538,3540,3541,1126,3543,3584,3587],{},[944,3535,3536],{},"Freeze a frame over the API."," Because ",[23,3539,1033],{}," is just an input, you can render any single moment as a still by passing it in ",[23,3542,25],{},[35,3544,3546],{"className":1129,"code":3545,"language":1131,"meta":40,"style":40},"curl -X POST \"https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Frender\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{ \"templateId\": \"7ccf097c-10f2-4efc-bd03-8abe967c4711\", \"data\": { \"TIME\": 1.3 }, \"format\": \"png\" }' \\\n  --output pop.png\n",[23,3547,3548,3560,3568,3577],{"__ignoreMap":40},[44,3549,3550,3552,3554,3556,3558],{"class":46,"line":47},[44,3551,1138],{"class":143},[44,3553,1144],{"class":54},[44,3555,1147],{"class":71},[44,3557,1150],{"class":71},[44,3559,1153],{"class":54},[44,3561,3562,3564,3566],{"class":46,"line":65},[44,3563,1158],{"class":54},[44,3565,1170],{"class":71},[44,3567,1153],{"class":54},[44,3569,3570,3572,3575],{"class":46,"line":84},[44,3571,1177],{"class":54},[44,3573,3574],{"class":71}," '{ \"templateId\": \"7ccf097c-10f2-4efc-bd03-8abe967c4711\", \"data\": { \"TIME\": 1.3 }, \"format\": \"png\" }'",[44,3576,1153],{"class":54},[44,3578,3579,3581],{"class":46,"line":99},[44,3580,2671],{"class":54},[44,3582,3583],{"class":71}," pop.png\n",[3585,3586],"br",{},"That gives you the photo mid-pop, at the 1.3-second mark.",[11,3589,3591],{"id":3590},"why-this-is-the-whole-story","Why this is the whole story",[16,3593,3594,3595,3597,3598,3600],{},"\"Personalized video from a template\" sounds like it needs a video pipeline. It does not. It needs properties that are functions of your data and one more axis, ",[23,3596,1033],{},", that they can also be functions of. That is the entire mechanism behind ",[949,3599,1238],{"href":1237},": the still and the clip come from the same expressions, evaluated at one moment or across many.",[990,3602,1252],{},{"title":40,"searchDepth":65,"depth":65,"links":3604},[3605,3606,3607,3608,3609,3610,3611],{"id":3244,"depth":65,"text":3245},{"id":3297,"depth":65,"text":3298},{"id":3367,"depth":65,"text":3368},{"id":3405,"depth":65,"text":3406},{"id":3452,"depth":65,"text":3453},{"id":3504,"depth":65,"text":3505},{"id":3590,"depth":65,"text":3591},"2026-07-24","How a Bluepic template becomes an animation. Every property is an expression, TIME is the only extra input, and ANIMATE turns keyframes into motion with straight or eased interpolation.","\u002Fdocs-images\u002Fexpression-engine-time.svg",{},{"title":3231,"description":3613},"docs\u002Fexpression-engine-over-time","DSG65P7Kq7C86LkSzu9me3ZwvErQYidqxkEM2e2dwgc",{"id":3620,"title":3621,"body":3622,"category":3809,"date":3810,"description":3811,"extension":1003,"heroImage":3812,"meta":3813,"navigation":130,"path":3814,"readingMinutes":99,"seo":3815,"stem":3816,"tool":3817,"__hash__":3818},"docs\u002Fdocs\u002Fconvert-idml-online.md","Convert an IDML file online",{"type":8,"value":3623,"toc":3801},[3624,3628,3646,3649,3653,3661,3675,3678,3682,3685,3707,3710,3714,3717,3755,3762,3766,3782,3786],[11,3625,3627],{"id":3626},"what-you-need","What you need",[16,3629,3630,3631,3634,3635,3638,3639,1077,3642,3645],{},"An InDesign ",[23,3632,3633],{},".idml"," file. If you have the full package (the folder InDesign creates with ",[944,3636,3637],{},"File → Package","), even better, because it also holds the ",[23,3640,3641],{},"Links",[23,3643,3644],{},"Document fonts"," folders the layout depends on.",[16,3647,3648],{},"You do not need InDesign, an account, or an upload. The tool runs entirely in your browser.",[11,3650,3652],{"id":3651},"step-1-open-the-file","Step 1: Open the file",[16,3654,3655,3656,3660],{},"Go to the ",[949,3657,3659],{"href":3658},"\u002Fidml","IDML converter"," and drop your file onto the drop zone. Two options:",[938,3662,3663,3669],{},[941,3664,3665,3668],{},[944,3666,3667],{},"Choose folder"," picks up the whole package, so linked images and fonts resolve automatically. This gives the most faithful result.",[941,3670,3671,3674],{},[944,3672,3673],{},"Choose .idml file"," loads a lone document. It still renders, using any fonts it can resolve.",[16,3676,3677],{},"Once it parses, you see a live preview of every page and a summary of how many pages, images and fonts were found.",[11,3679,3681],{"id":3680},"step-2-edit-the-content-optional","Step 2: Edit the content (optional)",[16,3683,3684],{},"Everything is editable in the panel next to the preview:",[938,3686,3687,3693],{},[941,3688,3689,3692],{},[944,3690,3691],{},"Text",": type over any field. Because the engine re-fits text to its frame, a longer or translated string never clips or overflows. The layout stays intact.",[941,3694,3695,3698,3699,3702,3703,3706],{},[944,3696,3697],{},"Images",": press ",[944,3700,3701],{},"Replace"," to swap in your own photo, then ",[944,3704,3705],{},"Crop"," to reframe it. Images are compressed for the size they are displayed at, so exports stay light.",[16,3708,3709],{},"The preview updates live as you edit.",[11,3711,3713],{"id":3712},"step-3-export","Step 3: Export",[16,3715,3716],{},"Pick your format:",[938,3718,3719,3728,3737,3746],{},[941,3720,3721,3727],{},[949,3722,3724],{"href":3723},"\u002Fidml-to-pdf",[944,3725,3726],{},"PDF",": every page merged into one print-ready file, with real vector text.",[941,3729,3730,3736],{},[949,3731,3733],{"href":3732},"\u002Fidml-to-png",[944,3734,3735],{},"PNG",": high-resolution raster, with transparency where the layout has no background.",[941,3738,3739,3745],{},[949,3740,3742],{"href":3741},"\u002Fidml-to-jpg",[944,3743,3744],{},"JPG",": a smaller, flat image for email and uploads.",[941,3747,3748,3754],{},[949,3749,3751],{"href":3750},"\u002Fidml-to-svg",[944,3752,3753],{},"SVG",": true vector, with a choice of live (editable) text or outlined paths.",[16,3756,3757,3758,3761],{},"Use the primary button for the page's default format, or the ",[944,3759,3760],{},"Download"," menu for any other format. Multi-page documents export page by page, except PDF, which merges them.",[11,3763,3765],{"id":3764},"just-want-to-look-at-it","Just want to look at it?",[16,3767,3768,3769,3771,3772,3776,3777,3781],{},"If someone sent you an ",[23,3770,3633],{}," and you only need to see it, the ",[949,3773,3775],{"href":3774},"\u002Fidml-viewer","IDML viewer"," opens it straight to a preview. To change the text or images first, the ",[949,3778,3780],{"href":3779},"\u002Fidml-editor","IDML editor"," is the same tool framed for editing.",[11,3783,3785],{"id":3784},"doing-this-at-scale","Doing this at scale?",[16,3787,3788,3789,3793,3794,3796,3797,226],{},"The exact same rendering engine is available as a hosted ",[949,3790,3792],{"href":3791},"\u002Fidml\u002Fapi","render API",". POST an IDML with its assets and per-language text, and get PNG, JPEG, PDF or SVG back, with 100 free render credits per month. See ",[949,3795,2491],{"href":2490}," or the ",[949,3798,3800],{"href":3799},"\u002Fdocs\u002Fapi-reference","API reference",{"title":40,"searchDepth":65,"depth":65,"links":3802},[3803,3804,3805,3806,3807,3808],{"id":3626,"depth":65,"text":3627},{"id":3651,"depth":65,"text":3652},{"id":3680,"depth":65,"text":3681},{"id":3712,"depth":65,"text":3713},{"id":3764,"depth":65,"text":3765},{"id":3784,"depth":65,"text":3785},"reference","2026-07-17","A step-by-step walkthrough of the free IDML tool, opening a file, editing text and images, and exporting to PNG, JPG, PDF or SVG, all in your browser without InDesign.","\u002Fdocs-images\u002Fconvert-idml-online.svg",{},"\u002Fdocs\u002Fconvert-idml-online",{"title":3621,"description":3811},"docs\u002Fconvert-idml-online","indesign","oAkPJW-6ZlIFIx2PSz4cFoZ5XewZdjmxHF4hSjXQgXI",{"id":3820,"title":2491,"body":3821,"category":1000,"date":3810,"description":4350,"extension":1003,"heroImage":4351,"meta":4352,"navigation":130,"path":2490,"readingMinutes":114,"seo":4353,"stem":4354,"tool":1000,"__hash__":4355},"docs\u002Fdocs\u002Frender-your-first-template.md",{"type":8,"value":3822,"toc":4339},[3823,3825,3834,3836,3839,3892,3899,3903,3906,3921,3933,3937,3947,4013,4019,4023,4162,4166,4169,4214,4226,4230,4249,4287,4290,4294,4313,4315,4336],[11,3824,2515],{"id":2514},[16,3826,3827,3828,3830,3831,3833],{},"You design a template once in ",[949,3829,1054],{"href":1053},", where every field is an input. Then you render it from your own code by sending its ",[23,3832,1122],{}," and the data to drop in, and you get an image (or, if the template is animated, a video) back. The engine re-fits text and images so the layout stays correct no matter what you send.",[11,3835,1309],{"id":1308},[16,3837,3838],{},"There is one public demo template you can render with no API key and no credit cost. Copy this:",[35,3840,3842],{"className":1129,"code":3841,"language":1131,"meta":40,"style":40},"curl -X POST \"https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Frender\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"templateId\": \"efa8ff2c-262b-4488-b8cf-e01521c6fbb5\",\n    \"format\": \"png\"\n  }' \\\n  --output render.png\n",[23,3843,3844,3856,3864,3870,3875,3879,3885],{"__ignoreMap":40},[44,3845,3846,3848,3850,3852,3854],{"class":46,"line":47},[44,3847,1138],{"class":143},[44,3849,1144],{"class":54},[44,3851,1147],{"class":71},[44,3853,1150],{"class":71},[44,3855,1153],{"class":54},[44,3857,3858,3860,3862],{"class":46,"line":65},[44,3859,1158],{"class":54},[44,3861,1170],{"class":71},[44,3863,1153],{"class":54},[44,3865,3866,3868],{"class":46,"line":84},[44,3867,1177],{"class":54},[44,3869,1180],{"class":71},[44,3871,3872],{"class":46,"line":99},[44,3873,3874],{"class":71},"    \"templateId\": \"efa8ff2c-262b-4488-b8cf-e01521c6fbb5\",\n",[44,3876,3877],{"class":46,"line":114},[44,3878,2659],{"class":71},[44,3880,3881,3883],{"class":46,"line":121},[44,3882,2664],{"class":71},[44,3884,1153],{"class":54},[44,3886,3887,3889],{"class":46,"line":127},[44,3888,2671],{"class":54},[44,3890,3891],{"class":71}," render.png\n",[16,3893,3894,3895,3898],{},"You get a ",[23,3896,3897],{},"render.png"," back. That is the whole loop: one POST, one image.",[11,3900,3902],{"id":3901},"get-your-api-key","Get your API key",[16,3904,3905],{},"For your own templates you need a key.",[1023,3907,3908,3915],{},[941,3909,3910,3911,3914],{},"Sign in at ",[949,3912,3913],{"href":1874},"your account",". A key is issued automatically the first time.",[941,3916,3917,3918,490],{},"Copy the key (it starts with ",[23,3919,3920],{},"bpk_",[16,3922,3923,3924,3926,3927,1836,3930,3932],{},"Pass it as the ",[23,3925,1866],{}," header, with ",[944,3928,3929],{},"no",[23,3931,1870],{}," prefix.",[11,3934,3936],{"id":3935},"render-your-own-template","Render your own template",[16,3938,3939,3940,3942,3943,3946],{},"Once you have built and saved a template in Studio, grab its ",[23,3941,1122],{}," (the Studio editor's ",[944,3944,3945],{},"Code"," button shows a ready-made snippet for that template). Then:",[35,3948,3950],{"className":1129,"code":3949,"language":1131,"meta":40,"style":40},"curl -X POST \"https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Frender\" \\\n  -H \"Authorization: bpk_your_key_here\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"templateId\": \"YOUR_TEMPLATE_ID\",\n    \"data\": { \"headline\": \"Hello from the API\" },\n    \"format\": \"png\"\n  }' \\\n  --output render.png\n",[23,3951,3952,3964,3973,3981,3987,3992,3997,4001,4007],{"__ignoreMap":40},[44,3953,3954,3956,3958,3960,3962],{"class":46,"line":47},[44,3955,1138],{"class":143},[44,3957,1144],{"class":54},[44,3959,1147],{"class":71},[44,3961,1150],{"class":71},[44,3963,1153],{"class":54},[44,3965,3966,3968,3971],{"class":46,"line":65},[44,3967,1158],{"class":54},[44,3969,3970],{"class":71}," \"Authorization: bpk_your_key_here\"",[44,3972,1153],{"class":54},[44,3974,3975,3977,3979],{"class":46,"line":84},[44,3976,1158],{"class":54},[44,3978,1170],{"class":71},[44,3980,1153],{"class":54},[44,3982,3983,3985],{"class":46,"line":99},[44,3984,1177],{"class":54},[44,3986,1180],{"class":71},[44,3988,3989],{"class":46,"line":114},[44,3990,3991],{"class":71},"    \"templateId\": \"YOUR_TEMPLATE_ID\",\n",[44,3993,3994],{"class":46,"line":121},[44,3995,3996],{"class":71},"    \"data\": { \"headline\": \"Hello from the API\" },\n",[44,3998,3999],{"class":46,"line":127},[44,4000,2659],{"class":71},[44,4002,4003,4005],{"class":46,"line":134},[44,4004,2664],{"class":71},[44,4006,1153],{"class":54},[44,4008,4009,4011],{"class":46,"line":156},[44,4010,2671],{"class":54},[44,4012,3891],{"class":71},[16,4014,4015,4016,4018],{},"The ",[23,4017,25],{}," object holds one entry per input field in your template. Any field you leave out keeps its default.",[11,4020,4022],{"id":4021},"from-javascript","From JavaScript",[35,4024,4026],{"className":37,"code":4025,"language":39,"meta":40,"style":40},"const res = await fetch(\"https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Frender\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": \"bpk_your_key_here\",\n    \"Content-Type\": \"application\u002Fjson\",\n  },\n  body: JSON.stringify({\n    templateId: \"YOUR_TEMPLATE_ID\",\n    data: { headline: \"Hello from the API\" },\n    format: \"png\",\n  }),\n});\n\nconst blob = await res.blob(); \u002F\u002F the rendered image\n",[23,4027,4028,4046,4054,4059,4071,4082,4087,4099,4108,4118,4127,4131,4135,4139],{"__ignoreMap":40},[44,4029,4030,4032,4034,4036,4038,4040,4042,4044],{"class":46,"line":47},[44,4031,51],{"class":50},[44,4033,162],{"class":54},[44,4035,58],{"class":50},[44,4037,167],{"class":50},[44,4039,170],{"class":143},[44,4041,147],{"class":61},[44,4043,175],{"class":71},[44,4045,178],{"class":61},[44,4047,4048,4050,4052],{"class":46,"line":65},[44,4049,1448],{"class":61},[44,4051,187],{"class":71},[44,4053,190],{"class":61},[44,4055,4056],{"class":46,"line":84},[44,4057,4058],{"class":61},"  headers: {\n",[44,4060,4061,4064,4066,4069],{"class":46,"line":99},[44,4062,4063],{"class":71},"    \"Authorization\"",[44,4065,208],{"class":61},[44,4067,4068],{"class":71},"\"bpk_your_key_here\"",[44,4070,190],{"class":61},[44,4072,4073,4076,4078,4080],{"class":46,"line":114},[44,4074,4075],{"class":71},"    \"Content-Type\"",[44,4077,208],{"class":61},[44,4079,211],{"class":71},[44,4081,190],{"class":61},[44,4083,4084],{"class":46,"line":121},[44,4085,4086],{"class":61},"  },\n",[44,4088,4089,4091,4093,4095,4097],{"class":46,"line":127},[44,4090,1474],{"class":61},[44,4092,223],{"class":54},[44,4094,226],{"class":61},[44,4096,229],{"class":143},[44,4098,232],{"class":61},[44,4100,4101,4103,4106],{"class":46,"line":134},[44,4102,1487],{"class":61},[44,4104,4105],{"class":71},"\"YOUR_TEMPLATE_ID\"",[44,4107,190],{"class":61},[44,4109,4110,4113,4116],{"class":46,"line":156},[44,4111,4112],{"class":61},"    data: { headline: ",[44,4114,4115],{"class":71},"\"Hello from the API\"",[44,4117,214],{"class":61},[44,4119,4120,4122,4125],{"class":46,"line":181},[44,4121,1506],{"class":61},[44,4123,4124],{"class":71},"\"png\"",[44,4126,190],{"class":61},[44,4128,4129],{"class":46,"line":193},[44,4130,1515],{"class":61},[44,4132,4133],{"class":46,"line":217},[44,4134,1520],{"class":61},[44,4136,4137],{"class":46,"line":235},[44,4138,131],{"emptyLinePlaceholder":130},[44,4140,4141,4143,4146,4148,4150,4153,4156,4159],{"class":46,"line":246},[44,4142,51],{"class":50},[44,4144,4145],{"class":54}," blob",[44,4147,58],{"class":50},[44,4149,167],{"class":50},[44,4151,4152],{"class":61}," res.",[44,4154,4155],{"class":143},"blob",[44,4157,4158],{"class":61},"(); ",[44,4160,4161],{"class":117},"\u002F\u002F the rendered image\n",[11,4163,4165],{"id":4164},"render-a-video","Render a video",[16,4167,4168],{},"If your template is animated, ask for a video format and you get an MP4 instead. There is a public video demo you can call with no key:",[35,4170,4172],{"className":1129,"code":4171,"language":1131,"meta":40,"style":40},"curl -N -X POST \"https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Frender\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"templateId\": \"7ccf097c-10f2-4efc-bd03-8abe967c4711\",\n    \"format\": \"mp4\"\n  }'\n",[23,4173,4174,4188,4196,4202,4206,4210],{"__ignoreMap":40},[44,4175,4176,4178,4180,4182,4184,4186],{"class":46,"line":47},[44,4177,1138],{"class":143},[44,4179,1141],{"class":54},[44,4181,1144],{"class":54},[44,4183,1147],{"class":71},[44,4185,1150],{"class":71},[44,4187,1153],{"class":54},[44,4189,4190,4192,4194],{"class":46,"line":65},[44,4191,1158],{"class":54},[44,4193,1170],{"class":71},[44,4195,1153],{"class":54},[44,4197,4198,4200],{"class":46,"line":84},[44,4199,1177],{"class":54},[44,4201,1180],{"class":71},[44,4203,4204],{"class":46,"line":99},[44,4205,1360],{"class":71},[44,4207,4208],{"class":46,"line":114},[44,4209,1195],{"class":71},[44,4211,4212],{"class":46,"line":121},[44,4213,1200],{"class":71},[16,4215,4216,4217,4219,4220,4222,4223,4225],{},"A video takes longer to produce, so the response is a stream: live progress events, then a final event carrying the hosted MP4 URL. That is what ",[23,4218,1402],{}," is for. See ",[949,4221,981],{"href":980}," for how to read the stream, and ",[949,4224,1238],{"href":1237}," for the still-vs-video idea.",[11,4227,4229],{"id":4228},"formats-and-options","Formats and options",[16,4231,4232,4234,4235,202,4237,202,4239,202,4241,4243,4244,1077,4246,4248],{},[23,4233,1284],{}," can be ",[23,4236,1845],{},[23,4238,1848],{},[23,4240,1851],{},[23,4242,1854],{},", or the video formats ",[23,4245,1072],{},[23,4247,1841],{},". A few optional fields:",[938,4250,4251,4259,4267,4275],{},[941,4252,4253,4255,4256,4258],{},[23,4254,2072],{}," \u002F ",[23,4257,2075],{},": override the output size in pixels.",[941,4260,4261,4264,4265,226],{},[23,4262,4263],{},"dpi",": resolution for ",[23,4266,1851],{},[941,4268,4269,4272,4273,226],{},[23,4270,4271],{},"quality",": 0 to 1 for ",[23,4274,1848],{},[941,4276,4277,208,4280,4282,4283,4286],{},[23,4278,4279],{},"svgVectorizeText",[23,4281,398],{}," outlines text in SVG output, ",[23,4284,4285],{},"false"," keeps it as live text.",[16,4288,4289],{},"Video formats return a Server-Sent Events stream rather than the file bytes; the image formats return the file directly.",[11,4291,4293],{"id":4292},"credits-and-limits","Credits and limits",[16,4295,4296,4297,4300,4301,4303,4304,4307,4308,4310,4311,226],{},"Every account includes ",[944,4298,4299],{},"100 free render credits per month",". An image render is ",[944,4302,2332],{},"; a video is metered by length and resolution, at ",[944,4305,4306],{},"1 credit per second of 2K at 24fps"," (see ",[949,4309,952],{"href":951},"). The public demo templates above are free and unmetered. Check your balance any time on the ",[949,4312,1875],{"href":1874},[11,4314,972],{"id":971},[938,4316,4317,4325,4331],{},[941,4318,4319,4320,1983,4322,226],{},"Render a personalized clip: ",[949,4321,981],{"href":980},[949,4323,4324],{"href":1006},"batch many at once",[941,4326,4327,4328,4330],{},"The complete ",[949,4329,3800],{"href":3799},", including rendering a raw IDML file and every error you'll hit.",[941,4332,4333,226],{},[949,4334,4335],{"href":1053},"Building templates in Bluepic Studio",[990,4337,4338],{},"html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#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 pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}",{"title":40,"searchDepth":65,"depth":65,"links":4340},[4341,4342,4343,4344,4345,4346,4347,4348,4349],{"id":2514,"depth":65,"text":2515},{"id":1308,"depth":65,"text":1309},{"id":3901,"depth":65,"text":3902},{"id":3935,"depth":65,"text":3936},{"id":4021,"depth":65,"text":4022},{"id":4164,"depth":65,"text":4165},{"id":4228,"depth":65,"text":4229},{"id":4292,"depth":65,"text":4293},{"id":971,"depth":65,"text":972},"A five-minute API quickstart. Get a key, POST a templateId with your data, and get a rendered image or video back. Includes public demo templates you can call with no key at all.","\u002Fdocs-images\u002Frender-first-template.svg",{},{"title":2491,"description":4350},"docs\u002Frender-your-first-template","PSe61GFjP7LMKvsOLNdHmSZKr1neP9jtfdo__kHhlUo",{"id":4357,"title":3800,"body":4358,"category":1000,"date":5205,"description":5206,"extension":1003,"heroImage":5207,"meta":5208,"navigation":130,"path":3799,"readingMinutes":127,"seo":5209,"stem":5210,"tool":1000,"__hash__":5211},"docs\u002Fdocs\u002Fapi-reference.md",{"type":8,"value":4359,"toc":5195},[4360,4364,4370,4377,4381,4384,4448,4454,4460,4470,4474,4480,4486,4567,4708,4719,4823,4827,4833,4839,4845,4852,4941,4950,4954,4960,5055,5062,5065,5085,5089,5164,5168,5192],[11,4361,4363],{"id":4362},"base-url","Base URL",[35,4365,4368],{"className":4366,"code":4367,"language":1291},[1289],"https:\u002F\u002Fapi.bluepic.io\n",[23,4369,4367],{"__ignoreMap":40},[16,4371,4372,4373,4376],{},"Every path below is relative to that. There's no staging\u002Fsandbox host: the same API key works everywhere, and the free monthly credits (see ",[949,4374,2395],{"href":4375},"#credits",") are the only thing separating experimentation from production traffic.",[11,4378,4380],{"id":4379},"authentication","Authentication",[16,4382,4383],{},"There are two different credentials in play, and mixing them up is the most common integration mistake:",[2381,4385,4386,4398],{},[2384,4387,4388],{},[2387,4389,4390,4392,4395],{},[2390,4391],{},[2390,4393,4394],{},"Used for",[2390,4396,4397],{},"Header",[2397,4399,4400,4427],{},[2387,4401,4402,4407,4416],{},[2402,4403,4404],{},[944,4405,4406],{},"API key",[2402,4408,4409,202,4412,4415],{},[23,4410,4411],{},"POST \u002Fapi\u002Frender",[23,4413,4414],{},"POST \u002Fapi\u002Fidml\u002Frender",": the endpoints your backend calls",[2402,4417,4418,202,4421,1836,4423,4426],{},[23,4419,4420],{},"Authorization: \u003Ckey>",[944,4422,3929],{},[23,4424,4425],{},"Bearer "," prefix",[2387,4428,4429,4434,4443],{},[2402,4430,4431],{},[944,4432,4433],{},"Studio session token",[2402,4435,4436,202,4439,4442],{},[23,4437,4438],{},"\u002Fapi\u002Fapi-keys",[23,4440,4441],{},"GET \u002Fapi\u002Fidml\u002Fme",": managing keys and checking balance from your own account",[2402,4444,4445],{},[23,4446,4447],{},"Authorization: Bearer \u003Ctoken>",[16,4449,4450,4451,4453],{},"For almost every integration, you only need the first row. Grab your key once from the ",[949,4452,1875],{"href":1874}," (it's created for you on first visit) and treat it like any other server-side secret: never ship it to a browser or mobile client.",[35,4455,4458],{"className":4456,"code":4457,"language":1291},[1289],"POST https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Frender\nAuthorization: bpk_xxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n",[23,4459,4457],{"__ignoreMap":40},[16,4461,4462,4463,4465,4466,4469],{},"A key that starts with ",[23,4464,4425],{}," is rejected: that prefix is reserved for the session-token routes above, which authenticate ",[1089,4467,4468],{},"you",", not your backend.",[11,4471,4473],{"id":4472},"render-a-studio-template","Render a Studio template",[35,4475,4478],{"className":4476,"code":4477,"language":1291},[1289],"POST \u002Fapi\u002Frender\n",[23,4479,4477],{"__ignoreMap":40},[16,4481,4482,4483,4485],{},"This is the endpoint for templates you built visually in ",[949,4484,1054],{"href":1053},". You send the template's ID and the field values to fill in; Bluepic renders it server-side and hands back the image.",[35,4487,4491],{"className":4488,"code":4489,"language":4490,"meta":40,"style":40},"language-json shiki shiki-themes github-dark","{\n  \"templateId\": \"efa8ff2c-262b-4488-b8cf-e01521c6fbb5\",\n  \"data\": {\n    \"headline_text_content\": \"Ship it Friday\",\n    \"avatar_image\": { \"src\": \"https:\u002F\u002Fcdn.example.com\u002Favatar.jpg\" }\n  },\n  \"format\": \"png\"\n}\n","json",[23,4492,4493,4498,4510,4518,4530,4549,4553,4563],{"__ignoreMap":40},[44,4494,4495],{"class":46,"line":47},[44,4496,4497],{"class":61},"{\n",[44,4499,4500,4503,4505,4508],{"class":46,"line":65},[44,4501,4502],{"class":54},"  \"templateId\"",[44,4504,208],{"class":61},[44,4506,4507],{"class":71},"\"efa8ff2c-262b-4488-b8cf-e01521c6fbb5\"",[44,4509,190],{"class":61},[44,4511,4512,4515],{"class":46,"line":84},[44,4513,4514],{"class":54},"  \"data\"",[44,4516,4517],{"class":61},": {\n",[44,4519,4520,4523,4525,4528],{"class":46,"line":99},[44,4521,4522],{"class":54},"    \"headline_text_content\"",[44,4524,208],{"class":61},[44,4526,4527],{"class":71},"\"Ship it Friday\"",[44,4529,190],{"class":61},[44,4531,4532,4535,4538,4541,4543,4546],{"class":46,"line":114},[44,4533,4534],{"class":54},"    \"avatar_image\"",[44,4536,4537],{"class":61},": { ",[44,4539,4540],{"class":54},"\"src\"",[44,4542,208],{"class":61},[44,4544,4545],{"class":71},"\"https:\u002F\u002Fcdn.example.com\u002Favatar.jpg\"",[44,4547,4548],{"class":61}," }\n",[44,4550,4551],{"class":46,"line":121},[44,4552,4086],{"class":61},[44,4554,4555,4558,4560],{"class":46,"line":127},[44,4556,4557],{"class":54},"  \"format\"",[44,4559,208],{"class":61},[44,4561,4562],{"class":71},"\"png\"\n",[44,4564,4565],{"class":46,"line":134},[44,4566,691],{"class":61},[2381,4568,4569,4585],{},[2384,4570,4571],{},[2387,4572,4573,4576,4579,4582],{},[2390,4574,4575],{},"Field",[2390,4577,4578],{},"Type",[2390,4580,4581],{},"Default",[2390,4583,4584],{},"Notes",[2397,4586,4587,4602,4619,4648,4675,4692],{},[2387,4588,4589,4593,4596,4599],{},[2402,4590,4591],{},[23,4592,1122],{},[2402,4594,4595],{},"string",[2402,4597,4598],{},"required",[2402,4600,4601],{},"The UUID from the template's editor URL. Must belong to your account.",[2387,4603,4604,4608,4611,4616],{},[2402,4605,4606],{},[23,4607,25],{},[2402,4609,4610],{},"object",[2402,4612,4613],{},[23,4614,4615],{},"{}",[2402,4617,4618],{},"Keys are the input-field names you defined in Studio. Anything you omit keeps its design-time default.",[2387,4620,4621,4625,4642,4646],{},[2402,4622,4623],{},[23,4624,1284],{},[2402,4626,4627,4629,4630,4629,4632,4629,4634,4629,4636,4629,4639],{},[23,4628,1845],{}," | ",[23,4631,1848],{},[23,4633,1851],{},[23,4635,1854],{},[23,4637,4638],{},"webp",[23,4640,4641],{},"gif",[2402,4643,4644],{},[23,4645,1845],{},[2402,4647],{},[2387,4649,4650,4655,4663,4667],{},[2402,4651,4652],{},[23,4653,4654],{},"engine",[2402,4656,4657,4629,4660],{},[23,4658,4659],{},"puppeteer",[23,4661,4662],{},"resvg",[2402,4664,4665],{},[23,4666,4659],{},[2402,4668,4669,4670,4674],{},"See ",[949,4671,4673],{"href":4672},"\u002Fdocs\u002Fautomate-indesign-projects#engine-fidelity-vs-throughput","engine trade-offs",": same choice applies here.",[2387,4676,4677,4683,4686,4689],{},[2402,4678,4679,4255,4681],{},[23,4680,2072],{},[23,4682,2075],{},[2402,4684,4685],{},"number",[2402,4687,4688],{},"template size",[2402,4690,4691],{},"Override the rendered pixel size.",[2387,4693,4694,4700,4702,4705],{},[2402,4695,4696,4255,4698],{},[23,4697,4263],{},[23,4699,4271],{},[2402,4701,4685],{},[2402,4703,4704],{},"none",[2402,4706,4707],{},"Resolution and compression, where the format supports them.",[16,4709,4710,4711,4714,4715,4718],{},"Response is the rendered file's raw bytes with a matching ",[23,4712,4713],{},"Content-Type",": no wrapper JSON, no polling. Fetch it, save it, or pipe it straight into an ",[23,4716,4717],{},"\u003Cimg>"," or email attachment.",[35,4720,4722],{"className":37,"code":4721,"language":39,"meta":40,"style":40},"const res = await fetch('https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Frender', {\n  method: 'POST',\n  headers: { Authorization: apiKey, 'Content-Type': 'application\u002Fjson' },\n  body: JSON.stringify({\n    templateId: 'efa8ff2c-262b-4488-b8cf-e01521c6fbb5',\n    data: { headline_text_content: 'Ship it Friday' },\n  }),\n});\nconst png = await res.blob();\n",[23,4723,4724,4743,4752,4767,4779,4788,4798,4802,4806],{"__ignoreMap":40},[44,4725,4726,4728,4730,4732,4734,4736,4738,4741],{"class":46,"line":47},[44,4727,51],{"class":50},[44,4729,162],{"class":54},[44,4731,58],{"class":50},[44,4733,167],{"class":50},[44,4735,170],{"class":143},[44,4737,147],{"class":61},[44,4739,4740],{"class":71},"'https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Frender'",[44,4742,178],{"class":61},[44,4744,4745,4747,4750],{"class":46,"line":65},[44,4746,1448],{"class":61},[44,4748,4749],{"class":71},"'POST'",[44,4751,190],{"class":61},[44,4753,4754,4757,4760,4762,4765],{"class":46,"line":84},[44,4755,4756],{"class":61},"  headers: { Authorization: apiKey, ",[44,4758,4759],{"class":71},"'Content-Type'",[44,4761,208],{"class":61},[44,4763,4764],{"class":71},"'application\u002Fjson'",[44,4766,214],{"class":61},[44,4768,4769,4771,4773,4775,4777],{"class":46,"line":99},[44,4770,1474],{"class":61},[44,4772,223],{"class":54},[44,4774,226],{"class":61},[44,4776,229],{"class":143},[44,4778,232],{"class":61},[44,4780,4781,4783,4786],{"class":46,"line":114},[44,4782,1487],{"class":61},[44,4784,4785],{"class":71},"'efa8ff2c-262b-4488-b8cf-e01521c6fbb5'",[44,4787,190],{"class":61},[44,4789,4790,4793,4796],{"class":46,"line":121},[44,4791,4792],{"class":61},"    data: { headline_text_content: ",[44,4794,4795],{"class":71},"'Ship it Friday'",[44,4797,214],{"class":61},[44,4799,4800],{"class":46,"line":127},[44,4801,1515],{"class":61},[44,4803,4804],{"class":46,"line":134},[44,4805,1520],{"class":61},[44,4807,4808,4810,4813,4815,4817,4819,4821],{"class":46,"line":156},[44,4809,51],{"class":50},[44,4811,4812],{"class":54}," png",[44,4814,58],{"class":50},[44,4816,167],{"class":50},[44,4818,4152],{"class":61},[44,4820,4155],{"class":143},[44,4822,297],{"class":61},[11,4824,4826],{"id":4825},"render-a-raw-idml-file","Render a raw IDML file",[35,4828,4831],{"className":4829,"code":4830,"language":1291},[1289],"POST \u002Fapi\u002Fidml\u002Frender\nContent-Type: multipart\u002Fform-data\n",[23,4832,4830],{"__ignoreMap":40},[16,4834,4835,4836,4838],{},"Use this when your source of truth is an InDesign file rather than a Studio template: no upload step, no template to manage, just the ",[23,4837,3633],{}," export and whatever assets it links to, on every request.",[35,4840,4843],{"className":4841,"code":4842,"language":1291},[1289],"idml=@certificate.idml\nassets=@seal.png\nassets=@Signature-Font.ttf\n",[23,4844,4842],{"__ignoreMap":40},[16,4846,4847,4848,4851],{},"Assets are matched back to the document ",[944,4849,4850],{},"by filename",". Options go as query params:",[2381,4853,4854,4864],{},[2384,4855,4856],{},[2387,4857,4858,4861],{},[2390,4859,4860],{},"Param",[2390,4862,4863],{},"What it does",[2397,4865,4866,4882,4897,4911,4922],{},[2387,4867,4868,4872],{},[2402,4869,4870],{},[23,4871,1284],{},[2402,4873,4874,202,4876,202,4878,2473,4880],{},[23,4875,1845],{},[23,4877,1848],{},[23,4879,1851],{},[23,4881,1854],{},[2387,4883,4884,4889],{},[2402,4885,4886],{},[23,4887,4888],{},"pages",[2402,4890,4891,4893,4894],{},[23,4892,876],{},", or a 1-based list like ",[23,4895,4896],{},"1,3,5",[2387,4898,4899,4903],{},[2402,4900,4901],{},[23,4902,4654],{},[2402,4904,4905,4907,4908,4910],{},[23,4906,4659],{}," (default, full fidelity) or ",[23,4909,4662],{}," (faster raster, no Chromium)",[2387,4912,4913,4919],{},[2402,4914,4915,4255,4917],{},[23,4916,4263],{},[23,4918,4271],{},[2402,4920,4921],{},"Output resolution \u002F compression",[2387,4923,4924,4929],{},[2402,4925,4926],{},[23,4927,4928],{},"textOverrides",[2402,4930,4931,4932,4935,4936,4940],{},"JSON ",[23,4933,4934],{},"{\"\u003CpageIndex>:\u003CelementId>\": \"value\"}",", see ",[949,4937,4939],{"href":4938},"\u002Fdocs\u002Fautomate-indesign-projects","Automate InDesign projects"," for the full walkthrough",[16,4942,4943,4944,4947,4948,226],{},"Multiple pages come back as one merged PDF (",[23,4945,4946],{},"format=pdf",") or a zip (everything else): same one-request, one-response shape as the Studio endpoint. Full detail, including a complete pipeline example, is in ",[949,4949,4939],{"href":4938},[11,4951,4953],{"id":4952},"check-your-balance","Check your balance",[35,4955,4958],{"className":4956,"code":4957,"language":1291},[1289],"GET \u002Fapi\u002Fidml\u002Fme\nAuthorization: Bearer \u003Cstudio session token>\n",[23,4959,4957],{"__ignoreMap":40},[35,4961,4963],{"className":4488,"code":4962,"language":4490,"meta":40,"style":40},"{\n  \"plan\": \"free\",\n  \"monthlyCredits\": 100,\n  \"period\": \"2026-07\",\n  \"used\": 34,\n  \"remaining\": 66,\n  \"resetsAt\": \"2026-08-01T00:00:00.000Z\",\n  \"upgradeUrl\": \"https:\u002F\u002Fbluepic.io\u002Fpricing\"\n}\n",[23,4964,4965,4969,4981,4993,5005,5017,5029,5041,5051],{"__ignoreMap":40},[44,4966,4967],{"class":46,"line":47},[44,4968,4497],{"class":61},[44,4970,4971,4974,4976,4979],{"class":46,"line":65},[44,4972,4973],{"class":54},"  \"plan\"",[44,4975,208],{"class":61},[44,4977,4978],{"class":71},"\"free\"",[44,4980,190],{"class":61},[44,4982,4983,4986,4988,4991],{"class":46,"line":84},[44,4984,4985],{"class":54},"  \"monthlyCredits\"",[44,4987,208],{"class":61},[44,4989,4990],{"class":54},"100",[44,4992,190],{"class":61},[44,4994,4995,4998,5000,5003],{"class":46,"line":99},[44,4996,4997],{"class":54},"  \"period\"",[44,4999,208],{"class":61},[44,5001,5002],{"class":71},"\"2026-07\"",[44,5004,190],{"class":61},[44,5006,5007,5010,5012,5015],{"class":46,"line":114},[44,5008,5009],{"class":54},"  \"used\"",[44,5011,208],{"class":61},[44,5013,5014],{"class":54},"34",[44,5016,190],{"class":61},[44,5018,5019,5022,5024,5027],{"class":46,"line":121},[44,5020,5021],{"class":54},"  \"remaining\"",[44,5023,208],{"class":61},[44,5025,5026],{"class":54},"66",[44,5028,190],{"class":61},[44,5030,5031,5034,5036,5039],{"class":46,"line":127},[44,5032,5033],{"class":54},"  \"resetsAt\"",[44,5035,208],{"class":61},[44,5037,5038],{"class":71},"\"2026-08-01T00:00:00.000Z\"",[44,5040,190],{"class":61},[44,5042,5043,5046,5048],{"class":46,"line":134},[44,5044,5045],{"class":54},"  \"upgradeUrl\"",[44,5047,208],{"class":61},[44,5049,5050],{"class":71},"\"https:\u002F\u002Fbluepic.io\u002Fpricing\"\n",[44,5052,5053],{"class":46,"line":156},[44,5054,691],{"class":61},[16,5056,5057,5058,5061],{},"This one uses your session token, not your API key: it's meant for your own dashboard or a build script running as you, not for a server checking on behalf of end users. Despite the ",[23,5059,5060],{},"\u002Fidml\u002F"," in the path, it covers both render endpoints: 1 credit is 1 rendered page, regardless of which one produced it.",[11,5063,2395],{"id":5064},"credits",[16,5066,5067,5068,5070,5071,1077,5074,5077,5078,5081,5082,5084],{},"Every account starts with ",[944,5069,4299],{},", shared across ",[23,5072,5073],{},"\u002Fapi\u002Frender",[23,5075,5076],{},"\u002Fapi\u002Fidml\u002Frender",". A render that produces multiple pages costs one credit per page. Once you're out, both endpoints return ",[23,5079,5080],{},"402","; check your balance on the ",[949,5083,1875],{"href":1874}," or get in touch for more.",[11,5086,5088],{"id":5087},"errors","Errors",[2381,5090,5091,5101],{},[2384,5092,5093],{},[2387,5094,5095,5098],{},[2390,5096,5097],{},"Status",[2390,5099,5100],{},"Meaning",[2397,5102,5103,5113,5126,5138,5150],{},[2387,5104,5105,5110],{},[2402,5106,5107],{},[23,5108,5109],{},"400",[2402,5111,5112],{},"Malformed request: check the field against the tables above.",[2387,5114,5115,5120],{},[2402,5116,5117],{},[23,5118,5119],{},"401",[2402,5121,5122,5123,5125],{},"Missing or invalid ",[23,5124,1866],{}," header.",[2387,5127,5128,5132],{},[2402,5129,5130],{},[23,5131,5080],{},[2402,5133,5134,5135,226],{},"Out of credits this period. Body includes ",[23,5136,5137],{},"upgradeUrl",[2387,5139,5140,5145],{},[2402,5141,5142],{},[23,5143,5144],{},"404",[2402,5146,5147,5149],{},[23,5148,1122],{}," doesn't exist, or isn't owned by your account.",[2387,5151,5152,5157],{},[2402,5153,5154],{},[23,5155,5156],{},"500",[2402,5158,5159,5160,5163],{},"Render failed server-side. Body includes ",[23,5161,5162],{},"details",", worth attaching to a support request.",[11,5165,5167],{"id":5166},"next-steps","Next steps",[938,5169,5170,5178,5186],{},[941,5171,5172,5175,5176,226],{},[949,5173,5174],{"href":1053},"Build a template visually in Bluepic Studio",", then drive it with ",[23,5177,5073],{},[941,5179,5180,5182,5183,5185],{},[949,5181,4939],{"href":4938}," for the full ",[23,5184,5076],{}," walkthrough, including a batch-pipeline example.",[941,5187,5188,5189,226],{},"Try requests live, with your own key, in the ",[949,5190,5191],{"href":3791},"interactive playground",[990,5193,5194],{},"html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#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 pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}",{"title":40,"searchDepth":65,"depth":65,"links":5196},[5197,5198,5199,5200,5201,5202,5203,5204],{"id":4362,"depth":65,"text":4363},{"id":4379,"depth":65,"text":4380},{"id":4472,"depth":65,"text":4473},{"id":4825,"depth":65,"text":4826},{"id":4952,"depth":65,"text":4953},{"id":5064,"depth":65,"text":2395},{"id":5087,"depth":65,"text":5088},{"id":5166,"depth":65,"text":5167},"2026-07-16","Every endpoint in the Bluepic developer API, authentication, rendering a Studio template, rendering a raw IDML file, checking your credit balance, and the errors you'll actually hit.","\u002Fdocs-images\u002Fapi-reference.svg",{},{"title":3800,"description":5206},"docs\u002Fapi-reference","JLLkWfvg1YOeIGbYTwBV0hYkI6v5QOWN_niJggHsQmo",{"id":5213,"title":5214,"body":5215,"category":5584,"date":5205,"description":5585,"extension":1003,"heroImage":5586,"meta":5587,"navigation":130,"path":5588,"readingMinutes":121,"seo":5589,"stem":5590,"tool":5591,"__hash__":5592},"docs\u002Fdocs\u002Fautomate-figma-photoshop-canva-illustrator.md","Automate Figma, Photoshop, Canva & Illustrator graphics",{"type":8,"value":5216,"toc":5576},[5217,5221,5224,5227,5229,5235,5248,5255,5260,5500,5503,5507,5514,5518,5528,5530,5537,5539,5573],[11,5218,5220],{"id":5219},"the-problem","The problem",[16,5222,5223],{},"You designed a social post in Canva, a banner in Figma, a product shot in Photoshop, an icon set in Illustrator. Now marketing wants fifty versions with different names, fifty different sizes, or a weekly refresh with new numbers, and none of those tools are built to be called from a script. Someone opens the app and does it by hand, every time.",[16,5225,5226],{},"Bluepic's IDML API is a plain HTTP endpoint built for exactly this: render a template with different field values, at scale, from your own automation. It doesn't speak Figma's or Canva's file formats directly, but a thin InDesign wrapper around your existing artwork (five minutes to set up, once) turns any of them into an automatable pipeline.",[11,5228,30],{"id":29},[16,5230,5231,5234],{},[944,5232,5233],{},"1. Export your artwork."," From Figma or Canva, export PNG, JPG or PDF. From Photoshop or Illustrator, keep the native PSD\u002FAI or export flattened, either works. PDF is the simplest universal choice if you're unsure.",[16,5236,5237,1836,5240,5243,5244,5247],{},[944,5238,5239],{},"2. Wrap it in InDesign, once.",[23,5241,5242],{},"File → Place"," the exported artwork into an InDesign document, add the text frames (or other elements) you want to control programmatically (a name, a price, a date, a location), then ",[23,5245,5246],{},"File → Export → InDesign Markup (IDML)",". This file is your reusable template from now on.",[16,5249,5250,5251,226],{},"Draw each of those frames with headroom for your longest real value (a long store name, a long price string): the frame's size is a hard ceiling for anything you substitute in later, not just a starting point. See ",[949,5252,5254],{"href":5253},"\u002Fdocs\u002Ftext-frames-and-best-practices","Text frames, bounds & best practices",[16,5256,5257],{},[944,5258,5259],{},"3. Render it from code.",[35,5261,5263],{"className":37,"code":5262,"language":39,"meta":40,"style":40},"const variants = await getVariantsFromYourData(); \u002F\u002F e.g. 50 store locations, 50 SKUs, 50 event dates\n\nfor (const variant of variants) {\n  const textOverrides = {\n    '0:TextFrame_headline': variant.title,\n    '0:TextFrame_price': variant.price,\n  };\n\n  const form = new FormData();\n  form.append('idml', bannerIdml);\n  form.append('assets', backgroundArt, 'background.pdf'); \u002F\u002F your Figma\u002FPhotoshop\u002FCanva\u002FIllustrator export\n\n  const res = await fetch(\n    `https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Fidml\u002Frender?format=png&textOverrides=${encodeURIComponent(JSON.stringify(textOverrides))}`,\n    { method: 'POST', headers: { Authorization: apiKey }, body: form },\n  );\n\n  await saveFile(`banner-${variant.id}.png`, await res.blob());\n}\n",[23,5264,5265,5284,5288,5305,5316,5324,5332,5337,5341,5357,5373,5396,5400,5415,5443,5453,5458,5462,5496],{"__ignoreMap":40},[44,5266,5267,5269,5272,5274,5276,5279,5281],{"class":46,"line":47},[44,5268,51],{"class":50},[44,5270,5271],{"class":54}," variants",[44,5273,58],{"class":50},[44,5275,167],{"class":50},[44,5277,5278],{"class":143}," getVariantsFromYourData",[44,5280,4158],{"class":61},[44,5282,5283],{"class":117},"\u002F\u002F e.g. 50 store locations, 50 SKUs, 50 event dates\n",[44,5285,5286],{"class":46,"line":65},[44,5287,131],{"emptyLinePlaceholder":130},[44,5289,5290,5292,5294,5296,5299,5302],{"class":46,"line":84},[44,5291,1583],{"class":50},[44,5293,1069],{"class":61},[44,5295,51],{"class":50},[44,5297,5298],{"class":54}," variant",[44,5300,5301],{"class":50}," of",[44,5303,5304],{"class":61}," variants) {\n",[44,5306,5307,5309,5312,5314],{"class":46,"line":99},[44,5308,159],{"class":50},[44,5310,5311],{"class":54}," textOverrides",[44,5313,58],{"class":50},[44,5315,811],{"class":61},[44,5317,5318,5321],{"class":46,"line":114},[44,5319,5320],{"class":71},"    '0:TextFrame_headline'",[44,5322,5323],{"class":61},": variant.title,\n",[44,5325,5326,5329],{"class":46,"line":121},[44,5327,5328],{"class":71},"    '0:TextFrame_price'",[44,5330,5331],{"class":61},": variant.price,\n",[44,5333,5334],{"class":46,"line":127},[44,5335,5336],{"class":61},"  };\n",[44,5338,5339],{"class":46,"line":134},[44,5340,131],{"emptyLinePlaceholder":130},[44,5342,5343,5345,5348,5350,5352,5355],{"class":46,"line":156},[44,5344,159],{"class":50},[44,5346,5347],{"class":54}," form",[44,5349,58],{"class":50},[44,5351,310],{"class":50},[44,5353,5354],{"class":143}," FormData",[44,5356,297],{"class":61},[44,5358,5359,5362,5365,5367,5370],{"class":46,"line":181},[44,5360,5361],{"class":61},"  form.",[44,5363,5364],{"class":143},"append",[44,5366,147],{"class":61},[44,5368,5369],{"class":71},"'idml'",[44,5371,5372],{"class":61},", bannerIdml);\n",[44,5374,5375,5377,5379,5381,5384,5387,5390,5393],{"class":46,"line":193},[44,5376,5361],{"class":61},[44,5378,5364],{"class":143},[44,5380,147],{"class":61},[44,5382,5383],{"class":71},"'assets'",[44,5385,5386],{"class":61},", backgroundArt, ",[44,5388,5389],{"class":71},"'background.pdf'",[44,5391,5392],{"class":61},"); ",[44,5394,5395],{"class":117},"\u002F\u002F your Figma\u002FPhotoshop\u002FCanva\u002FIllustrator export\n",[44,5397,5398],{"class":46,"line":217},[44,5399,131],{"emptyLinePlaceholder":130},[44,5401,5402,5404,5406,5408,5410,5412],{"class":46,"line":235},[44,5403,159],{"class":50},[44,5405,162],{"class":54},[44,5407,58],{"class":50},[44,5409,167],{"class":50},[44,5411,170],{"class":143},[44,5413,5414],{"class":61},"(\n",[44,5416,5417,5420,5423,5425,5427,5429,5431,5433,5435,5438,5441],{"class":46,"line":246},[44,5418,5419],{"class":71},"    `https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Fidml\u002Frender?format=png&textOverrides=${",[44,5421,5422],{"class":143},"encodeURIComponent",[44,5424,147],{"class":71},[44,5426,223],{"class":54},[44,5428,226],{"class":71},[44,5430,229],{"class":143},[44,5432,147],{"class":71},[44,5434,4928],{"class":61},[44,5436,5437],{"class":71},"))",[44,5439,5440],{"class":71},"}`",[44,5442,190],{"class":61},[44,5444,5445,5448,5450],{"class":46,"line":252},[44,5446,5447],{"class":61},"    { method: ",[44,5449,4749],{"class":71},[44,5451,5452],{"class":61},", headers: { Authorization: apiKey }, body: form },\n",[44,5454,5455],{"class":46,"line":263},[44,5456,5457],{"class":61},"  );\n",[44,5459,5460],{"class":46,"line":269},[44,5461,131],{"emptyLinePlaceholder":130},[44,5463,5464,5466,5469,5471,5474,5477,5479,5482,5485,5487,5490,5492,5494],{"class":46,"line":275},[44,5465,868],{"class":50},[44,5467,5468],{"class":143}," saveFile",[44,5470,147],{"class":61},[44,5472,5473],{"class":71},"`banner-${",[44,5475,5476],{"class":61},"variant",[44,5478,226],{"class":71},[44,5480,5481],{"class":61},"id",[44,5483,5484],{"class":71},"}.png`",[44,5486,202],{"class":61},[44,5488,5489],{"class":50},"await",[44,5491,4152],{"class":61},[44,5493,4155],{"class":143},[44,5495,589],{"class":61},[44,5497,5498],{"class":46,"line":281},[44,5499,691],{"class":61},[16,5501,5502],{},"Linked PDF, AI, EPS and PSD assets are rasterized automatically on Bluepic's servers: nothing from Adobe or Figma needs to be installed anywhere near this script.",[11,5504,5506],{"id":5505},"multiple-output-sizes-from-one-file","Multiple output sizes from one file",[16,5508,5509,1077,5511,5513],{},[23,5510,2072],{},[23,5512,2075],{}," query parameters override the rendered size independent of the document's own dimensions: useful if the same layout needs to ship as a square post and a story-format crop. Render the same request twice with different dimensions rather than maintaining two separate template files.",[11,5515,5517],{"id":5516},"where-the-field-keys-come-from","Where the field keys come from",[16,5519,5520,5521,5524,5525,5527],{},"Load your wrapper IDML into the ",[949,5522,5523],{"href":3791},"interactive API playground"," once, tick the override checkbox, and it lists every text element with an empty input: type into the ones you want to control and copy the generated ",[23,5526,4928],{}," JSON as your starting shape. From there, driving the same keys with real data from your own system is the whole job.",[11,5529,2395],{"id":5064},[16,5531,5067,5532,5534,5535,226],{},[944,5533,4299],{},", 1 credit per rendered output. Fifty banners in three sizes is 150 renders; check usage or ask about higher volume on the ",[949,5536,1875],{"href":1874},[11,5538,5167],{"id":5166},[938,5540,5541,5546,5553,5566],{},[941,5542,5543,5545],{},[949,5544,3902],{"href":3791}," and try the playground with your own artwork.",[941,5547,5548,5549,226],{},"If the automation is specifically about shipping the same design in multiple languages rather than multiple data variants, see ",[949,5550,5552],{"href":5551},"\u002Fdocs\u002Ftranslate-figma-photoshop-canva-illustrator","Translate Figma, Photoshop, Canva & Illustrator graphics automatically",[941,5554,5555,5556,5559,5560,202,5562,1077,5564,226],{},"Read the ",[949,5557,5558],{"href":3791},"full API reference"," for every parameter, including ",[23,5561,4888],{},[23,5563,4263],{},[23,5565,4654],{},[941,5567,5568,5569,226],{},"If a field needs mixed formatting (part bold, a different color), see ",[949,5570,5572],{"href":5571},"\u002Fdocs\u002Frichtext-override-format","Rich text format for overrides",[990,5574,5575],{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#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);}",{"title":40,"searchDepth":65,"depth":65,"links":5577},[5578,5579,5580,5581,5582,5583],{"id":5219,"depth":65,"text":5220},{"id":29,"depth":65,"text":30},{"id":5505,"depth":65,"text":5506},{"id":5516,"depth":65,"text":5517},{"id":5064,"depth":65,"text":2395},{"id":5166,"depth":65,"text":5167},"automate","Batch-generate variants of artwork made in Figma, Photoshop, Canva or Illustrator (personalized fields, multiple sizes, hundreds of outputs) with no design software running in your pipeline.","\u002Fdocs-images\u002Fautomate-figma-photoshop-canva-illustrator.svg",{},"\u002Fdocs\u002Fautomate-figma-photoshop-canva-illustrator",{"title":5214,"description":5585},"docs\u002Fautomate-figma-photoshop-canva-illustrator","other-tools","1hZCNbOPV43Y1TGxH97X7zVS0HqPzgyQj_Jdm_O-jv0",{"id":5594,"title":4939,"body":5595,"category":5584,"date":5205,"description":6091,"extension":1003,"heroImage":6092,"meta":6093,"navigation":130,"path":4938,"readingMinutes":121,"seo":6094,"stem":6095,"tool":3817,"__hash__":6096},"docs\u002Fdocs\u002Fautomate-indesign-projects.md",{"type":8,"value":5596,"toc":6082},[5597,5599,5602,5609,5613,5622,5627,5633,5651,5657,5734,5740,5746,5764,5773,5777,6017,6020,6026,6036,6038,6048,6050,6080],[11,5598,5220],{"id":5219},[16,5600,5601],{},"InDesign is a design tool, not a rendering server. The moment you need to generate a hundred name badges, a thousand certificates, or a weekly batch of social posts from one master layout, you're stuck: either someone opens InDesign and does it by hand, or you buy and run InDesign Server: a license, a machine, and an operations burden, just to turn a template into pixels.",[16,5603,5604,5605,5608],{},"Bluepic's IDML renderer is a plain HTTP API that does exactly that one job: take an InDesign file (exported as IDML) and a set of field values, and hand back rendered output. No InDesign install, no server to maintain, no license: a ",[23,5606,5607],{},"POST"," request from wherever your automation already lives (a cron job, a serverless function, a CI pipeline, your app's backend).",[11,5610,5612],{"id":5611},"the-building-blocks","The building blocks",[16,5614,5615,5618,5619,5621],{},[944,5616,5617],{},"1. Export once."," Design your master in InDesign, ",[23,5620,5246],{},". This is your template: the thing you POST every time.",[16,5623,5624],{},[944,5625,5626],{},"2. Render it.",[35,5628,5631],{"className":5629,"code":5630,"language":1291},[1289],"POST https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Fidml\u002Frender\nAuthorization: \u003Cyour API key>\nContent-Type: multipart\u002Fform-data\n\nidml=@certificate.idml\nassets=@seal.png\nassets=@Signature-Font.ttf\n",[23,5632,5630],{"__ignoreMap":40},[16,5634,5635,5636,5639,5640,5642,5643,5646,5647,5650],{},"Every asset the IDML links to (images, fonts) goes in a repeated ",[23,5637,5638],{},"assets"," field, matched back to the document ",[944,5641,4850],{},", so name each part after what the IDML actually links (",[23,5644,5645],{},"hero.jpg",", not ",[23,5648,5649],{},"hero-final-v2.jpg","). Anything you leave out renders as a placeholder rather than failing the request, so partial asset sets are safe to iterate on.",[16,5652,5653,5656],{},[944,5654,5655],{},"3. Pick your output."," Query parameters control the render:",[2381,5658,5659,5667],{},[2384,5660,5661],{},[2387,5662,5663,5665],{},[2390,5664,4860],{},[2390,5666,4863],{},[2397,5668,5669,5685,5697,5711,5723],{},[2387,5670,5671,5675],{},[2402,5672,5673],{},[23,5674,1284],{},[2402,5676,5677,202,5679,202,5681,2473,5683],{},[23,5678,1845],{},[23,5680,1848],{},[23,5682,1851],{},[23,5684,1854],{},[2387,5686,5687,5691],{},[2402,5688,5689],{},[23,5690,4888],{},[2402,5692,5693,4893,5695],{},[23,5694,876],{},[23,5696,4896],{},[2387,5698,5699,5703],{},[2402,5700,5701],{},[23,5702,4654],{},[2402,5704,5705,5707,5708,5710],{},[23,5706,4659],{}," (full fidelity, the default) or ",[23,5709,4662],{}," (faster PNG\u002FJPEG, no Chromium)",[2387,5712,5713,5717],{},[2402,5714,5715],{},[23,5716,4263],{},[2402,5718,5719,5720],{},"Output resolution for raster and PDF, default ",[23,5721,5722],{},"300",[2387,5724,5725,5731],{},[2402,5726,5727,4255,5729],{},[23,5728,2072],{},[23,5730,2075],{},[2402,5732,5733],{},"Override the rendered size in px",[16,5735,5736,5737,5739],{},"Multiple pages come back as a single merged PDF (for ",[23,5738,4946],{},") or a zip with one file per page (PNG\u002FJPEG\u002FSVG): one request, one response, no follow-up polling.",[16,5741,5742],{},[1960,5743],{"alt":5744,"src":5745},"The API playground's Files and Options cards, the same parameters as the table above, live","\u002Fscreenshots\u002Fapi-files-options.png",[16,5747,5748,5751,5752,5754,5755,5757,5758,5760,5761,5763],{},[944,5749,5750],{},"4. Fill in the dynamic fields."," This is what turns a converter into automation: ",[23,5753,4928],{},", a JSON object of ",[23,5756,4934],{}," pairs. Any text element you name gets replaced; anything you don't name keeps its source value. The ",[949,5759,5191],{"href":3791}," generates this JSON for you: load your IDML once, type into the fields you want to control, and copy the live-updating ",[23,5762,1138],{}," command as your starting point.",[16,5765,5766,5767,5769,5770,5772],{},"One field you'll almost always vary this way is a name, and names vary a lot in length. The text frame's size in InDesign is the hard ceiling any override gets fit inside (it shrinks or wraps, it never grows the frame), so size name\u002Fvalue fields generously for your longest real record, not just your test data. Full detail in ",[949,5768,5254],{"href":5253},". If a field has mixed formatting inside it, its override needs the array shape from ",[949,5771,5572],{"href":5571}," instead of a plain string.",[11,5774,5776],{"id":5775},"a-concrete-pipeline-personalized-certificates","A concrete pipeline: personalized certificates",[35,5778,5780],{"className":37,"code":5779,"language":39,"meta":40,"style":40},"const attendees = await getAttendeesFromYourDatabase(); \u002F\u002F [{ name, date, score }, ...]\n\nfor (const attendee of attendees) {\n  const textOverrides = {\n    '0:TextFrame_name': attendee.name,\n    '0:TextFrame_date': attendee.date,\n    '0:TextFrame_score': `${attendee.score}%`,\n  };\n\n  const form = new FormData();\n  form.append('idml', certificateIdml);\n  form.append('assets', sealImage, 'seal.png');\n\n  const res = await fetch(\n    `https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Fidml\u002Frender?format=pdf&textOverrides=${encodeURIComponent(JSON.stringify(textOverrides))}`,\n    { method: 'POST', headers: { Authorization: apiKey }, body: form },\n  );\n\n  await saveFile(`certificate-${attendee.name}.pdf`, await res.blob());\n}\n",[23,5781,5782,5801,5805,5821,5831,5839,5847,5870,5874,5878,5892,5905,5923,5927,5941,5966,5974,5978,5982,6013],{"__ignoreMap":40},[44,5783,5784,5786,5789,5791,5793,5796,5798],{"class":46,"line":47},[44,5785,51],{"class":50},[44,5787,5788],{"class":54}," attendees",[44,5790,58],{"class":50},[44,5792,167],{"class":50},[44,5794,5795],{"class":143}," getAttendeesFromYourDatabase",[44,5797,4158],{"class":61},[44,5799,5800],{"class":117},"\u002F\u002F [{ name, date, score }, ...]\n",[44,5802,5803],{"class":46,"line":65},[44,5804,131],{"emptyLinePlaceholder":130},[44,5806,5807,5809,5811,5813,5816,5818],{"class":46,"line":84},[44,5808,1583],{"class":50},[44,5810,1069],{"class":61},[44,5812,51],{"class":50},[44,5814,5815],{"class":54}," attendee",[44,5817,5301],{"class":50},[44,5819,5820],{"class":61}," attendees) {\n",[44,5822,5823,5825,5827,5829],{"class":46,"line":99},[44,5824,159],{"class":50},[44,5826,5311],{"class":54},[44,5828,58],{"class":50},[44,5830,811],{"class":61},[44,5832,5833,5836],{"class":46,"line":114},[44,5834,5835],{"class":71},"    '0:TextFrame_name'",[44,5837,5838],{"class":61},": attendee.name,\n",[44,5840,5841,5844],{"class":46,"line":121},[44,5842,5843],{"class":71},"    '0:TextFrame_date'",[44,5845,5846],{"class":61},": attendee.date,\n",[44,5848,5849,5852,5854,5857,5860,5862,5865,5868],{"class":46,"line":127},[44,5850,5851],{"class":71},"    '0:TextFrame_score'",[44,5853,208],{"class":61},[44,5855,5856],{"class":71},"`${",[44,5858,5859],{"class":61},"attendee",[44,5861,226],{"class":71},[44,5863,5864],{"class":61},"score",[44,5866,5867],{"class":71},"}%`",[44,5869,190],{"class":61},[44,5871,5872],{"class":46,"line":134},[44,5873,5336],{"class":61},[44,5875,5876],{"class":46,"line":156},[44,5877,131],{"emptyLinePlaceholder":130},[44,5879,5880,5882,5884,5886,5888,5890],{"class":46,"line":181},[44,5881,159],{"class":50},[44,5883,5347],{"class":54},[44,5885,58],{"class":50},[44,5887,310],{"class":50},[44,5889,5354],{"class":143},[44,5891,297],{"class":61},[44,5893,5894,5896,5898,5900,5902],{"class":46,"line":193},[44,5895,5361],{"class":61},[44,5897,5364],{"class":143},[44,5899,147],{"class":61},[44,5901,5369],{"class":71},[44,5903,5904],{"class":61},", certificateIdml);\n",[44,5906,5907,5909,5911,5913,5915,5918,5921],{"class":46,"line":217},[44,5908,5361],{"class":61},[44,5910,5364],{"class":143},[44,5912,147],{"class":61},[44,5914,5383],{"class":71},[44,5916,5917],{"class":61},", sealImage, ",[44,5919,5920],{"class":71},"'seal.png'",[44,5922,543],{"class":61},[44,5924,5925],{"class":46,"line":235},[44,5926,131],{"emptyLinePlaceholder":130},[44,5928,5929,5931,5933,5935,5937,5939],{"class":46,"line":246},[44,5930,159],{"class":50},[44,5932,162],{"class":54},[44,5934,58],{"class":50},[44,5936,167],{"class":50},[44,5938,170],{"class":143},[44,5940,5414],{"class":61},[44,5942,5943,5946,5948,5950,5952,5954,5956,5958,5960,5962,5964],{"class":46,"line":252},[44,5944,5945],{"class":71},"    `https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Fidml\u002Frender?format=pdf&textOverrides=${",[44,5947,5422],{"class":143},[44,5949,147],{"class":71},[44,5951,223],{"class":54},[44,5953,226],{"class":71},[44,5955,229],{"class":143},[44,5957,147],{"class":71},[44,5959,4928],{"class":61},[44,5961,5437],{"class":71},[44,5963,5440],{"class":71},[44,5965,190],{"class":61},[44,5967,5968,5970,5972],{"class":46,"line":263},[44,5969,5447],{"class":61},[44,5971,4749],{"class":71},[44,5973,5452],{"class":61},[44,5975,5976],{"class":46,"line":269},[44,5977,5457],{"class":61},[44,5979,5980],{"class":46,"line":275},[44,5981,131],{"emptyLinePlaceholder":130},[44,5983,5984,5986,5988,5990,5993,5995,5997,6000,6003,6005,6007,6009,6011],{"class":46,"line":281},[44,5985,868],{"class":50},[44,5987,5468],{"class":143},[44,5989,147],{"class":61},[44,5991,5992],{"class":71},"`certificate-${",[44,5994,5859],{"class":61},[44,5996,226],{"class":71},[44,5998,5999],{"class":61},"name",[44,6001,6002],{"class":71},"}.pdf`",[44,6004,202],{"class":61},[44,6006,5489],{"class":50},[44,6008,4152],{"class":61},[44,6010,4155],{"class":143},[44,6012,589],{"class":61},[44,6014,6015],{"class":46,"line":300},[44,6016,691],{"class":61},[16,6018,6019],{},"Same shape works for event badges, invoice-style documents, personalized social graphics, or a weekly report that only changes three numbers: anywhere you'd otherwise open InDesign, change some text, and export by hand.",[11,6021,6023,6025],{"id":6022},"engine-fidelity-vs-throughput",[23,6024,4654],{},": fidelity vs. throughput",[16,6027,6028,6029,6031,6032,6035],{},"For a handful of renders, the default ",[23,6030,4659],{}," engine (a real, full layout engine, the same technology behind the free converter) is the right call: full fidelity, every feature. For a large batch of simple PNG\u002FJPEG exports where you're rendering hundreds or thousands of variants, ",[23,6033,6034],{},"engine=resvg"," skips the Chromium instance entirely and renders faster, worth benchmarking against your own document before committing to it for a production batch job.",[11,6037,2395],{"id":5064},[16,6039,5067,6040,6042,6043,6045,6046,226],{},[944,6041,4299],{},": 1 credit per rendered page, regardless of format or how many ",[23,6044,4928],{}," you send. A 500-badge batch job is 500 credits; check usage and get in touch for higher volume from the ",[949,6047,1875],{"href":1874},[11,6049,5167],{"id":5166},[938,6051,6052,6057,6062,6073],{},[941,6053,6054,6056],{},[949,6055,3902],{"href":3791}," and try the playground against your own IDML.",[941,6058,5555,6059,6061],{},[949,6060,5558],{"href":3791}," for every parameter.",[941,6063,6064,6065,6069,6070,6072],{},"If your automation is specifically about shipping the same layout in multiple languages, see ",[949,6066,6068],{"href":6067},"\u002Fdocs\u002Ftranslate-indesign-automatically","Translate InDesign projects automatically",": same API, same ",[23,6071,4928],{}," mechanism.",[941,6074,6075,1077,6077,6079],{},[949,6076,5254],{"href":5253},[949,6078,5572],{"href":5571},": worth reading before your first production batch.",[990,6081,5575],{},{"title":40,"searchDepth":65,"depth":65,"links":6083},[6084,6085,6086,6087,6089,6090],{"id":5219,"depth":65,"text":5220},{"id":5611,"depth":65,"text":5612},{"id":5775,"depth":65,"text":5776},{"id":6022,"depth":65,"text":6088},"engine: fidelity vs. throughput",{"id":5064,"depth":65,"text":2395},{"id":5166,"depth":65,"text":5167},"Turn a single InDesign layout into a rendering pipeline, batch outputs, dynamic fields, and PDF\u002FPNG\u002FJPEG\u002FSVG generation with no InDesign install anywhere.","\u002Fdocs-images\u002Fautomate-indesign-projects.svg",{},{"title":4939,"description":6091},"docs\u002Fautomate-indesign-projects","Bx-sw09gO46SUTjOGAHWTUf0rWUZA83aXGABw0lzLgM",{"id":6098,"title":4335,"body":6099,"category":1000,"date":5205,"description":6344,"extension":1003,"heroImage":6345,"meta":6346,"navigation":130,"path":1053,"readingMinutes":121,"seo":6347,"stem":6348,"tool":1000,"__hash__":6349},"docs\u002Fdocs\u002Fbuilding-templates-in-bluepic-studio.md",{"type":8,"value":6100,"toc":6335},[6101,6105,6116,6121,6125,6132,6152,6160,6164,6167,6186,6190,6203,6260,6266,6270,6276,6282,6294,6298,6308,6310,6332],[11,6102,6104],{"id":6103},"design-once-render-forever","Design once, render forever",[16,6106,6107,6112,6113,6115],{},[949,6108,1054],{"href":6109,"rel":6110},"https:\u002F\u002Fbx-studio.bluepic.io",[6111],"nofollow"," is where the actual design work happens: the layout, the fonts, the exact pixel a logo sits at. Everything downstream (the ",[949,6114,3792],{"href":3799},", batch jobs, personalization) just fills in values on a template you already built visually. You never hand-write the JSON a template is made of; Studio is the editor for it.",[16,6117,6118,6119,226],{},"This guide covers the Studio side specifically: creating a template, wiring up the parts that should change per-render, and finding the ID your code needs. If you're looking for the raw-IDML path instead (importing an existing InDesign file without touching Studio's canvas), see ",[949,6120,4939],{"href":4938},[11,6122,6124],{"id":6123},"create-a-template","Create a template",[16,6126,6127,6128,6131],{},"From the templates view, ",[944,6129,6130],{},"Create Template"," opens three starting points:",[938,6133,6134,6140,6146],{},[941,6135,6136,6139],{},[944,6137,6138],{},"Empty Template",": a blank canvas at a size you pick.",[941,6141,6142,6145],{},[944,6143,6144],{},"Presets",": common starting layouts.",[941,6147,6148,6151],{},[944,6149,6150],{},"Import from IDML",": bring in an existing InDesign export as an editable Studio template, rather than rendering it raw.",[16,6153,6154,6155,6157,6158,226],{},"Pick a size from the aspect-ratio grid (square, portrait 4:5, story 9:16, and a handful of others), or set exact dimensions: this is the pixel size every render comes back at unless a request overrides ",[23,6156,2072],{},"\u002F",[23,6159,2075],{},[11,6161,6163],{"id":6162},"the-editor","The editor",[16,6165,6166],{},"The canvas works like any layered design tool: text, image, shape, and pen tools on the toolbar, a layers panel on the right, and a property panel on the left for whatever's selected: position and size, fill and stroke, opacity, and (for text) content, font, size and alignment.",[16,6168,6169,6170,6176,6177,6180,6181,6185],{},"The part that matters for automation is the ",[944,6171,6172,6175],{},[23,6173,6174],{},"fx"," expression icon"," in the toolbar. Any numeric property (position, size, rotation, opacity) can be bound to an expression instead of a fixed number, and an expression can repeat one element a computed number of times: a rectangle whose count is ",[23,6178,6179],{},"body.lines.length",", say, drawn once and multiplied automatically at render time. Design the element once; let the expression handle \"one per line,\" \"one per row,\" or \"one per item in the data\": see the ",[949,6182,6184],{"href":6183},"\u002F#how-it-works","live example on the homepage"," for exactly this pattern.",[11,6187,6189],{"id":6188},"connect-input-fields","Connect input fields",[16,6191,6192,6195,6196,6199,6200,6202],{},[944,6193,6194],{},"Add Input Field",", then click the element you want to expose. That element's editable property (its text content, its image source, sometimes its fill color) becomes a ",[944,6197,6198],{},"named field",". The name you give it here is the exact key your API request's ",[23,6201,25],{}," object uses later:",[35,6204,6206],{"className":4488,"code":6205,"language":4490,"meta":40,"style":40},"{\n  \"templateId\": \"efa8ff2c-262b-4488-b8cf-e01521c6fbb5\",\n  \"data\": {\n    \"headline_text_content\": \"Ship it Friday\",\n    \"avatar_image\": { \"src\": \"https:\u002F\u002Fcdn.example.com\u002Favatar.jpg\" }\n  }\n}\n",[23,6207,6208,6212,6222,6228,6238,6252,6256],{"__ignoreMap":40},[44,6209,6210],{"class":46,"line":47},[44,6211,4497],{"class":61},[44,6213,6214,6216,6218,6220],{"class":46,"line":65},[44,6215,4502],{"class":54},[44,6217,208],{"class":61},[44,6219,4507],{"class":71},[44,6221,190],{"class":61},[44,6223,6224,6226],{"class":46,"line":84},[44,6225,4514],{"class":54},[44,6227,4517],{"class":61},[44,6229,6230,6232,6234,6236],{"class":46,"line":99},[44,6231,4522],{"class":54},[44,6233,208],{"class":61},[44,6235,4527],{"class":71},[44,6237,190],{"class":61},[44,6239,6240,6242,6244,6246,6248,6250],{"class":46,"line":114},[44,6241,4534],{"class":54},[44,6243,4537],{"class":61},[44,6245,4540],{"class":54},[44,6247,208],{"class":61},[44,6249,4545],{"class":71},[44,6251,4548],{"class":61},[44,6253,6254],{"class":46,"line":121},[44,6255,668],{"class":61},[44,6257,6258],{"class":46,"line":127},[44,6259,691],{"class":61},[16,6261,6262,6265],{},[944,6263,6264],{},"Show Input Fields"," toggles a preview of every field you've wired up so far, letting you check names and defaults before you publish. Anything you don't expose as a field stays fixed: logos, backgrounds, decorative shapes never need a field at all.",[11,6267,6269],{"id":6268},"publish-and-find-your-template-id","Publish, and find your template ID",[16,6271,6272,6275],{},[944,6273,6274],{},"Publish"," makes the template renderable via the API. The ID itself is simpler than a separate \"copy ID\" step: it's the UUID already sitting in your browser's address bar while you're editing:",[35,6277,6280],{"className":6278,"code":6279,"language":1291},[1289],"https:\u002F\u002Fbx-studio.bluepic.io\u002Feditor\u002Fefa8ff2c-262b-4488-b8cf-e01521c6fbb5\n                                       └──────────────── templateId ────────────────┘\n",[23,6281,6279],{"__ignoreMap":40},[16,6283,6284,6285,6287,6288,6293],{},"The same string goes straight into ",[23,6286,1122],{}," on a ",[949,6289,6291],{"href":6290},"\u002Fdocs\u002Fapi-reference#render-a-studio-template",[23,6292,4411],{}," call.",[11,6295,6297],{"id":6296},"sharing-url-vs-the-api","Sharing URL vs. the API",[16,6299,6300,6301,6304,6305,6307],{},"Studio's settings panel (the gear icon next to Publish) also offers a ",[944,6302,6303],{},"Sharing URL",": a public, no-code page where anyone can fill in the fields and export an image by hand, useful for handing a template to someone who isn't writing code at all. That's a different mechanism from calling ",[23,6306,5073],{}," directly: the sharing URL is for a human filling in a form once; the API is for your own backend rendering the same template hundreds of times from data it already has. Both read the same published template and the same input fields: pick whichever matches who's actually triggering the render.",[11,6309,5167],{"id":5166},[938,6311,6312,6322,6327],{},[941,6313,6314,6316,6317,6319,6320,226],{},[949,6315,3800],{"href":3799},": the exact request shape for ",[23,6318,5073],{},", once you have a ",[23,6321,1122],{},[941,6323,6324,6325,226],{},"Try it immediately with the demo template in the ",[949,6326,5191],{"href":3791},[941,6328,6329,6330,226],{},"Need the raw-IDML path instead of designing in Studio? See ",[949,6331,4939],{"href":4938},[990,6333,6334],{},"html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#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);}",{"title":40,"searchDepth":65,"depth":65,"links":6336},[6337,6338,6339,6340,6341,6342,6343],{"id":6103,"depth":65,"text":6104},{"id":6123,"depth":65,"text":6124},{"id":6162,"depth":65,"text":6163},{"id":6188,"depth":65,"text":6189},{"id":6268,"depth":65,"text":6269},{"id":6296,"depth":65,"text":6297},{"id":5166,"depth":65,"text":5167},"Design a template visually in Bluepic Studio (elements, expressions and input fields), then find its ID and drive it from your own code with a single POST \u002Fapi\u002Frender call.","\u002Fdocs-images\u002Fbuilding-templates-in-bluepic-studio.svg",{},{"title":4335,"description":6344},"docs\u002Fbuilding-templates-in-bluepic-studio","t6Fw-uWR6Od40KHnm9oGrVmUTjmb6w_lZKAb0U2kuMs",{"id":6351,"title":5572,"body":6352,"category":3809,"date":5205,"description":6778,"extension":1003,"heroImage":6779,"meta":6780,"navigation":130,"path":5571,"readingMinutes":114,"seo":6781,"stem":6782,"tool":3817,"__hash__":6783},"docs\u002Fdocs\u002Frichtext-override-format.md",{"type":8,"value":6353,"toc":6769},[6354,6364,6368,6378,6382,6385,6476,6496,6502,6505,6603,6620,6624,6636,6640,6646,6749,6751,6767],[16,6355,6356,6357,6359,6360,6363],{},"Most text elements are plain: a single string is all ",[23,6358,4928],{}," needs for them. But when a text element has ",[944,6361,6362],{},"mixed formatting inside it"," (part bold, a different color on one word, a linked phrase), it's in \"rich text\" mode, and its override value needs to be a small array of formatted runs instead of a plain string.",[11,6365,6367],{"id":6366},"how-to-tell-which-mode-an-element-needs","How to tell which mode an element needs",[16,6369,6370,6371,3796,6374,6377],{},"You don't have to inspect the IDML for this: load the file into the ",[949,6372,6373],{"href":3658},"Convert tool",[949,6375,6376],{"href":3791},"API playground"," and open the text-overrides table. Plain elements get a normal text box; rich-text elements get a small formatting toolbar on their input automatically. If you see the toolbar, that row needs the array shape below, not a string.",[11,6379,6381],{"id":6380},"the-shape","The shape",[16,6383,6384],{},"A rich-text override value is an array of runs. Each run is a chunk of text plus the formatting that applies to just that chunk:",[35,6386,6388],{"className":4488,"code":6387,"language":4490,"meta":40,"style":40},"[\n  { \"text\": \"Save \", \"format\": {} },\n  { \"text\": \"30%\", \"format\": { \"fontWeight\": 700, \"color\": \"#e0392b\" } },\n  { \"text\": \" this week only.\", \"format\": {} }\n]\n",[23,6389,6390,6395,6416,6453,6471],{"__ignoreMap":40},[44,6391,6392],{"class":46,"line":47},[44,6393,6394],{"class":61},"[\n",[44,6396,6397,6400,6403,6405,6408,6410,6413],{"class":46,"line":65},[44,6398,6399],{"class":61},"  { ",[44,6401,6402],{"class":54},"\"text\"",[44,6404,208],{"class":61},[44,6406,6407],{"class":71},"\"Save \"",[44,6409,202],{"class":61},[44,6411,6412],{"class":54},"\"format\"",[44,6414,6415],{"class":61},": {} },\n",[44,6417,6418,6420,6422,6424,6427,6429,6431,6433,6436,6438,6441,6443,6446,6448,6451],{"class":46,"line":84},[44,6419,6399],{"class":61},[44,6421,6402],{"class":54},[44,6423,208],{"class":61},[44,6425,6426],{"class":71},"\"30%\"",[44,6428,202],{"class":61},[44,6430,6412],{"class":54},[44,6432,4537],{"class":61},[44,6434,6435],{"class":54},"\"fontWeight\"",[44,6437,208],{"class":61},[44,6439,6440],{"class":54},"700",[44,6442,202],{"class":61},[44,6444,6445],{"class":54},"\"color\"",[44,6447,208],{"class":61},[44,6449,6450],{"class":71},"\"#e0392b\"",[44,6452,81],{"class":61},[44,6454,6455,6457,6459,6461,6464,6466,6468],{"class":46,"line":99},[44,6456,6399],{"class":61},[44,6458,6402],{"class":54},[44,6460,208],{"class":61},[44,6462,6463],{"class":71},"\" this week only.\"",[44,6465,202],{"class":61},[44,6467,6412],{"class":54},[44,6469,6470],{"class":61},": {} }\n",[44,6472,6473],{"class":46,"line":114},[44,6474,6475],{"class":61},"]\n",[16,6477,6478,6479,6481,6482,6485,6486,6488,6489,6492,6493,6495],{},"Concatenate every run's ",[23,6480,1291],{}," in order and you get the plain-text content: ",[23,6483,6484],{},"\"Save 30% this week only.\""," here. The ",[23,6487,1284],{}," object carries only what's ",[1089,6490,6491],{},"different"," from the element's own base style; an empty ",[23,6494,4615],{}," just means \"use whatever the element already looks like.\"",[11,6497,6499,6501],{"id":6498},"format-fields",[23,6500,1284],{}," fields",[16,6503,6504],{},"Every field is optional: omit one to inherit the element's base value rather than overriding it.",[2381,6506,6507,6518],{},[2384,6508,6509],{},[2387,6510,6511,6513,6515],{},[2390,6512,4575],{},[2390,6514,4578],{},[2390,6516,6517],{},"Effect",[2397,6519,6520,6533,6554,6567,6579,6591],{},[2387,6521,6522,6527,6530],{},[2402,6523,6524],{},[23,6525,6526],{},"fontWeight",[2402,6528,6529],{},"number (100–900)",[2402,6531,6532],{},"Weight of just this run.",[2387,6534,6535,6540,6551],{},[2402,6536,6537],{},[23,6538,6539],{},"fontStyle",[2402,6541,6542,4629,6545,4629,6548],{},[23,6543,6544],{},"'normal'",[23,6546,6547],{},"'italic'",[23,6549,6550],{},"'condensed'",[2402,6552,6553],{},"Style of just this run.",[2387,6555,6556,6561,6564],{},[2402,6557,6558],{},[23,6559,6560],{},"fontSize",[2402,6562,6563],{},"number (px)",[2402,6565,6566],{},"Size of just this run.",[2387,6568,6569,6574,6576],{},[2402,6570,6571],{},[23,6572,6573],{},"letterSpacing",[2402,6575,4685],{},[2402,6577,6578],{},"Tracking, as a ratio (InDesign-style), for just this run.",[2387,6580,6581,6586,6588],{},[2402,6582,6583],{},[23,6584,6585],{},"color",[2402,6587,4595],{},[2402,6589,6590],{},"Any CSS color for just this run's text.",[2387,6592,6593,6598,6600],{},[2402,6594,6595],{},[23,6596,6597],{},"href",[2402,6599,4595],{},[2402,6601,6602],{},"Wraps this run in a link (carries through to PDF export).",[16,6604,6605,6608,6609,6612,6613,6616,6617,6619],{},[23,6606,6607],{},"fontFamily"," and a horizontal ",[23,6610,6611],{},"scale"," also exist on the underlying type, but there's no need to set them per run: a font-family or scale difference always causes the conversion to split that content into a ",[944,6614,6615],{},"separate element"," rather than a mixed run within one element (see ",[949,6618,5254],{"href":5253},"), so within a single rich-text array, every run already shares the same family and scale by construction.",[11,6621,6623],{"id":6622},"line-breaks","Line breaks",[16,6625,6626,6627,6629,6630,6632,6633,6635],{},"A literal ",[23,6628,485],{}," inside a run's ",[23,6631,1291],{}," is a valid line break: you don't need a separate array entry per line, just embed ",[23,6634,485],{}," wherever a break belongs within a run.",[11,6637,6639],{"id":6638},"a-full-request-example","A full request example",[16,6641,6642,6643,6645],{},"Plain and rich-text keys sit side by side in the same ",[23,6644,4928],{}," object without any special marker, the shape of each value is all that distinguishes them:",[35,6647,6649],{"className":4488,"code":6648,"language":4490,"meta":40,"style":40},"{\n  \"0:TextFrame_headline\": \"Willkommen in Berlin\",\n  \"0:TextFrame_promo\": [\n    { \"text\": \"Save \", \"format\": {} },\n    { \"text\": \"30%\", \"format\": { \"fontWeight\": 700, \"color\": \"#e0392b\" } },\n    { \"text\": \" this week only.\", \"format\": {} }\n  ]\n}\n",[23,6650,6651,6655,6667,6675,6692,6724,6740,6745],{"__ignoreMap":40},[44,6652,6653],{"class":46,"line":47},[44,6654,4497],{"class":61},[44,6656,6657,6660,6662,6665],{"class":46,"line":65},[44,6658,6659],{"class":54},"  \"0:TextFrame_headline\"",[44,6661,208],{"class":61},[44,6663,6664],{"class":71},"\"Willkommen in Berlin\"",[44,6666,190],{"class":61},[44,6668,6669,6672],{"class":46,"line":84},[44,6670,6671],{"class":54},"  \"0:TextFrame_promo\"",[44,6673,6674],{"class":61},": [\n",[44,6676,6677,6680,6682,6684,6686,6688,6690],{"class":46,"line":99},[44,6678,6679],{"class":61},"    { ",[44,6681,6402],{"class":54},[44,6683,208],{"class":61},[44,6685,6407],{"class":71},[44,6687,202],{"class":61},[44,6689,6412],{"class":54},[44,6691,6415],{"class":61},[44,6693,6694,6696,6698,6700,6702,6704,6706,6708,6710,6712,6714,6716,6718,6720,6722],{"class":46,"line":114},[44,6695,6679],{"class":61},[44,6697,6402],{"class":54},[44,6699,208],{"class":61},[44,6701,6426],{"class":71},[44,6703,202],{"class":61},[44,6705,6412],{"class":54},[44,6707,4537],{"class":61},[44,6709,6435],{"class":54},[44,6711,208],{"class":61},[44,6713,6440],{"class":54},[44,6715,202],{"class":61},[44,6717,6445],{"class":54},[44,6719,208],{"class":61},[44,6721,6450],{"class":71},[44,6723,81],{"class":61},[44,6725,6726,6728,6730,6732,6734,6736,6738],{"class":46,"line":121},[44,6727,6679],{"class":61},[44,6729,6402],{"class":54},[44,6731,208],{"class":61},[44,6733,6463],{"class":71},[44,6735,202],{"class":61},[44,6737,6412],{"class":54},[44,6739,6470],{"class":61},[44,6741,6742],{"class":46,"line":127},[44,6743,6744],{"class":61},"  ]\n",[44,6746,6747],{"class":46,"line":134},[44,6748,691],{"class":61},[11,6750,5167],{"id":5166},[938,6752,6753,6758],{},[941,6754,6755,6757],{},[949,6756,5254],{"href":5253},": why an element ends up plain vs. rich text in the first place.",[941,6759,6760,6763,6764,6766],{},[949,6761,6762],{"href":3791},"Full API reference"," for ",[23,6765,4928],{}," and every other render option.",[990,6768,6334],{},{"title":40,"searchDepth":65,"depth":65,"links":6770},[6771,6772,6773,6775,6776,6777],{"id":6366,"depth":65,"text":6367},{"id":6380,"depth":65,"text":6381},{"id":6498,"depth":65,"text":6774},"format fields",{"id":6622,"depth":65,"text":6623},{"id":6638,"depth":65,"text":6639},{"id":5166,"depth":65,"text":5167},"The exact JSON shape textOverrides expects when a text element has mixed formatting inside it, fields, defaults, line breaks, and a full worked example.","\u002Fdocs-images\u002Frichtext-override-format.svg",{},{"title":5572,"description":6778},"docs\u002Frichtext-override-format","1JsaGZqipDJaEz3q6n3zqSYgdijsUpHvGqMsKuyYsBY",{"id":6785,"title":5254,"body":6786,"category":3809,"date":5205,"description":6935,"extension":1003,"heroImage":6936,"meta":6937,"navigation":130,"path":5253,"readingMinutes":121,"seo":6938,"stem":6939,"tool":3817,"__hash__":6940},"docs\u002Fdocs\u002Ftext-frames-and-best-practices.md",{"type":8,"value":6787,"toc":6929},[6788,6791,6795,6810,6813,6823,6827,6837,6840,6856,6863,6868,6894,6902,6906,6912,6914],[16,6789,6790],{},"Overriding text (a translation, a name, a price) looks simple from the API side: you send a string, you get pixels back. Two things the engine does under the hood decide whether that string actually lands the way you expect: how big the text is allowed to get, and how many pieces your InDesign text frame turned into. Both are worth understanding before you build automation around a document.",[11,6792,6794],{"id":6793},"a-frames-size-is-the-maximum-bound-not-a-suggestion","A frame's size is the maximum bound, not a suggestion",[16,6796,6797,6798,1077,6800,6802,6803,6805,6806,6809],{},"When an InDesign text frame is converted, it becomes a serial text element with a fixed ",[23,6799,2072],{},[23,6801,2075],{},", captured once, at conversion time, from the original frame. Every render after that, including every ",[23,6804,4928],{}," value you ever send, works ",[944,6807,6808],{},"inside that same box",". The engine has exactly two levers to make text fit: shrink the font size, and wrap into more lines. It never grows the box itself, no matter how long the replacement text is.",[16,6811,6812],{},"That means: if your English placeholder fits snugly and the German translation runs 30% longer, the frame doesn't expand to make room: the text shrinks (or wraps further) to stay inside the space you gave it in InDesign. This is precisely what makes the auto-fit engine useful (no manual resizing per language), but it also means the frame you draw is a real ceiling, not a hint.",[16,6814,6815,6818,6819,6822],{},[944,6816,6817],{},"Best practice:"," size your text frames with headroom for the ",[1089,6820,6821],{},"longest"," value you'll ever send through them (the longest language, the longest name, the longest price string), not just the sample text you happened to design with. Auto-fit will shrink shorter values back down automatically; it can't invent space that was never there.",[11,6824,6826],{"id":6825},"one-text-frame-doesnt-always-mean-one-override-key","One text frame doesn't always mean one override key",[16,6828,6829,6830,6833,6834,6836],{},"The converter doesn't always turn one InDesign text frame into one serial element. Depending on what's inside it, a single frame can become ",[944,6831,6832],{},"several"," elements, and each one gets its own key in ",[23,6835,4928],{},". This catches people off guard more than the bounds issue above, because it's invisible until you actually look at the converted result.",[16,6838,6839],{},"By default, a frame's content only splits into a new element at an existing paragraph break or forced line break, and only when:",[938,6841,6842,6848,6853],{},[941,6843,6844,6845],{},"the character formatting (font, size, weight, style, color, letter-spacing) or line spacing differs across that break, ",[944,6846,6847],{},"or",[941,6849,6850,6851],{},"it's a blank line, ",[944,6852,6847],{},[941,6854,6855],{},"InDesign paragraph spacing (space-before\u002Fspace-after) is set on either paragraph.",[16,6857,6858,6859,6862],{},"Consistently-formatted paragraphs with no blank line and no paragraph spacing between them are actually ",[944,6860,6861],{},"merged"," into one multi-line element: a subtlety worth knowing, since it means simply pressing Enter between two lines doesn't automatically split them.",[16,6864,6865],{},[944,6866,6867],{},"What this means in practice:",[938,6869,6870,6880,6887],{},[941,6871,6872,6873,6875,6876,6879],{},"If you want a single ",[23,6874,4928],{}," key to control a whole heading or paragraph, keep its formatting uniform end to end and avoid paragraph spacing or blank lines inside it. A stray bold word or a different-colored phrase is fine to ",[1089,6877,6878],{},"want",", but if it sits at a line break, it becomes a split point: what you meant as \"one sentence\" quietly becomes two override keys.",[941,6881,6882,6883,6886],{},"If two parts of what looks like one frame are conceptually different content (a headline and a caption, say), make them ",[944,6884,6885],{},"separate InDesign text frames"," from the start. It's cleaner to design that intent explicitly than to have the converter split a single frame for you and have to discover the extra key afterwards.",[941,6888,6889,6890,6893],{},"In short: ",[944,6891,6892],{},"one text frame per what is, in fact, one piece of text."," If it reads as one idea, keep it one frame with uniform formatting; if it's really two ideas, give it two frames.",[16,6895,6896,6897,3796,6899,6901],{},"You don't have to guess at any of this: load your file into the ",[949,6898,6373],{"href":3658},[949,6900,6376],{"href":3791}," and open the text-overrides table. It shows exactly one row per element the conversion actually produced, with the key each one will use: check it before you wire up automation, not after.",[11,6903,6905],{"id":6904},"mixed-formatting-needs-a-different-override-shape","Mixed formatting needs a different override shape",[16,6907,6908,6909,6911],{},"If a text element does end up with mixed formatting inside it (a bold word, a linked phrase), it's in \"rich text\" mode, and overriding it takes a small structured array instead of a plain string: see ",[949,6910,5572],{"href":5571}," for the exact shape.",[11,6913,5167],{"id":5166},[938,6915,6916,6923],{},[941,6917,6918,1077,6920,6922],{},[949,6919,6068],{"href":6067},[949,6921,4939],{"href":4938},": put this into practice.",[941,6924,6925,6763,6927,6766],{},[949,6926,6762],{"href":3791},[23,6928,4928],{},{"title":40,"searchDepth":65,"depth":65,"links":6930},[6931,6932,6933,6934],{"id":6793,"depth":65,"text":6794},{"id":6825,"depth":65,"text":6826},{"id":6904,"depth":65,"text":6905},{"id":5166,"depth":65,"text":5167},"How the rendering engine and the InDesign→serial conversion actually interact, why a text frame's size is the hard ceiling for any override, and how to structure InDesign text so overrides stay clean and predictable.","\u002Fdocs-images\u002Ftext-frames-and-best-practices.svg",{},{"title":5254,"description":6935},"docs\u002Ftext-frames-and-best-practices","YBjAouAGj9bB6PojkrNfYDjk3vqH90zepIO0MPkikhg",{"id":6942,"title":5552,"body":6943,"category":7102,"date":5205,"description":7103,"extension":1003,"heroImage":7104,"meta":7105,"navigation":130,"path":5551,"readingMinutes":121,"seo":7106,"stem":7107,"tool":5591,"__hash__":7108},"docs\u002Fdocs\u002Ftranslate-figma-photoshop-canva-illustrator.md",{"type":8,"value":6944,"toc":7093},[6945,6949,6955,6958,6962,6965,6996,7003,7007,7015,7020,7024,7027,7033,7045,7049,7060,7064,7067,7069],[11,6946,6948],{"id":6947},"the-honest-starting-point","The honest starting point",[16,6950,6951,6952],{},"Figma, Photoshop, Canva and Illustrator don't export IDML: that's an InDesign-only format, and Bluepic's translation engine works on IDML. So this isn't \"upload your Figma file and get eight languages back.\" It's something more useful once you know it exists: ",[944,6953,6954],{},"your artwork stays exactly as-is, in whatever tool you made it in: InDesign becomes a thin, one-time wrapper around it that carries the translatable text and does the auto-fitting.",[16,6956,6957],{},"This is not a workaround; it's the same production pattern print and marketing teams have used for decades. A poster's photography comes from Photoshop, its icon set from Illustrator, its background from Figma or Canva, and InDesign is the assembly layer that places all of it and adds the type on top. All this tutorial does is point that same assembly layer at Bluepic's API instead of a human's export button.",[11,6959,6961],{"id":6960},"step-1-export-your-artwork-as-a-flat-file","Step 1: export your artwork as a flat file",[16,6963,6964],{},"From whichever tool made the graphic:",[938,6966,6967,6976,6984,6990],{},[941,6968,6969,208,6972,6975],{},[944,6970,6971],{},"Figma",[23,6973,6974],{},"Export"," → PNG, JPG or PDF.",[941,6977,6978,208,6981,6975],{},[944,6979,6980],{},"Canva",[23,6982,6983],{},"Share → Download",[941,6985,6986,6989],{},[944,6987,6988],{},"Photoshop",": save as PSD (kept as layers) or export a flattened PNG\u002FJPG.",[941,6991,6992,6995],{},[944,6993,6994],{},"Illustrator",": save as AI (kept as vector) or export a flattened PNG\u002FJPG\u002FPDF.",[16,6997,6998,6999,7002],{},"If you're not sure which to pick: ",[944,7000,7001],{},"PDF works from all four tools"," and is the simplest universal choice. Photoshop's native PSD and Illustrator's native AI also work directly: you don't have to flatten them first.",[11,7004,7006],{"id":7005},"step-2-place-it-in-a-lightweight-indesign-file","Step 2: place it in a lightweight InDesign file",[16,7008,7009,7010,7012,7013,226],{},"Open (or create) an InDesign document, ",[23,7011,5242],{}," your exported artwork, position it, and add your text frames on top: headline, body copy, call to action, whatever needs to vary per language. This InDesign file doesn't need to be a \"real\" layout in the traditional sense; its only job is to hold your artwork and your translatable text frames in one place. Export it: ",[23,7014,5246],{},[16,7016,7017,7018,226],{},"Two things worth getting right while you're drawing those frames: size each one with headroom for your longest expected language (the frame's size becomes a hard ceiling, not a starting point), and keep one frame's formatting uniform if you want it to stay one overridable unit rather than silently splitting into several. Both are covered in ",[949,7019,5254],{"href":5253},[11,7021,7023],{"id":7022},"step-3-send-it-to-bluepic-same-as-any-idml","Step 3: send it to Bluepic, same as any IDML",[16,7025,7026],{},"This is where it stops being special-cased. Whether the file underneath is a native InDesign document or a thin wrapper around a Photoshop export, the API doesn't know the difference:",[35,7028,7031],{"className":7029,"code":7030,"language":1291},[1289],"POST https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Fidml\u002Frender\nAuthorization: \u003Cyour API key>\nContent-Type: multipart\u002Fform-data\n\nidml=@poster.idml\nassets=@background.pdf\ntextOverrides={\"0:TextFrame_headline\":\"Willkommen in Berlin\"}\n",[23,7032,7030],{"__ignoreMap":40},[16,7034,7035,7038,7039,7041,7042,7044],{},[944,7036,7037],{},"PDF, AI, EPS and PSD assets are rasterized automatically, server-side",": you don't need Photoshop or Illustrator installed anywhere in your pipeline to handle a linked PSD or AI file; Bluepic converts it to a print-quality raster the same way it would if you'd flattened it yourself. Anything you don't mention in ",[23,7040,4928],{}," keeps its source text, so this is the exact same mechanism used in ",[949,7043,6068],{"href":6067},": the artwork's origin doesn't change how translation works at all.",[11,7046,7048],{"id":7047},"what-you-get-for-that-five-minutes-of-setup","What you get for that five minutes of setup",[938,7050,7051,7054,7057],{},[941,7052,7053],{},"Every language re-fits into the same text frames automatically: no manual resizing per locale, whether the underlying art came from Figma, Canva, Photoshop or Illustrator.",[941,7055,7056],{},"The artwork itself is never touched or re-generated: you keep full creative control in the tool you already use.",[941,7058,7059],{},"One API call per language, scriptable from wherever your translations already live (a CMS, a spreadsheet, a translation-memory export).",[11,7061,7063],{"id":7062},"what-this-doesnt-do","What this doesn't do",[16,7065,7066],{},"To be precise about the boundary: this doesn't give you live editing of a Figma or Canva design, and it doesn't reach into a PSD's individual layers to translate text baked into the artwork itself (a headline rendered as pixels inside Photoshop, for example, stays exactly as exported; only text frames you add in the InDesign wrapper are translatable). If your source text is baked into the graphic, pull it out into an InDesign text frame once; from then on it's automatable.",[11,7068,5167],{"id":5166},[938,7070,7071,7077,7082,7087],{},[941,7072,7073,7076],{},[949,7074,7075],{"href":3791},"Try the interactive playground"," with a wrapper file built around your own artwork.",[941,7078,4669,7079,7081],{},[949,7080,5214],{"href":5588}," for the same pattern applied beyond translation: batch variants, personalization, sizes.",[941,7083,5555,7084,7086],{},[949,7085,5558],{"href":3791}," for every render option.",[941,7088,7089,7090,7092],{},"If a text frame needs mixed formatting (part bold, a different color), see ",[949,7091,5572],{"href":5571}," for the value shape it needs.",{"title":40,"searchDepth":65,"depth":65,"links":7094},[7095,7096,7097,7098,7099,7100,7101],{"id":6947,"depth":65,"text":6948},{"id":6960,"depth":65,"text":6961},{"id":7005,"depth":65,"text":7006},{"id":7022,"depth":65,"text":7023},{"id":7047,"depth":65,"text":7048},{"id":7062,"depth":65,"text":7063},{"id":5166,"depth":65,"text":5167},"translate","None of these tools export a translatable, auto-fitting layout, but a thin InDesign wrapper around your existing artwork turns them into one, with zero design rework.","\u002Fdocs-images\u002Ftranslate-figma-photoshop-canva-illustrator.svg",{},{"title":5552,"description":7103},"docs\u002Ftranslate-figma-photoshop-canva-illustrator","JJjVeiNpCNhncVG29JU9ChNHtyBEV3KkTWMFI6b8_i0",{"id":7110,"title":6068,"body":7111,"category":7102,"date":5205,"description":7585,"extension":1003,"heroImage":7586,"meta":7587,"navigation":130,"path":6067,"readingMinutes":127,"seo":7588,"stem":7589,"tool":3817,"__hash__":7590},"docs\u002Fdocs\u002Ftranslate-indesign-automatically.md",{"type":8,"value":7112,"toc":7575},[7113,7115,7118,7121,7131,7135,7145,7171,7177,7180,7184,7187,7193,7199,7205,7215,7220,7239,7245,7254,7257,7511,7518,7520,7527,7531,7534,7536,7572],[11,7114,5220],{"id":5219},[16,7116,7117],{},"You design a flyer, a social post, or a data sheet once, in InDesign. Then someone asks for the German version. Then French. Then Polish. German runs roughly 30% longer than English; Polish and French aren't far behind; Chinese often runs shorter. Every one of those languages either overflows the text frame or leaves an awkward gap, because the layout was only ever tuned for the source language.",[16,7119,7120],{},"The usual fixes are manual: a DTP agency rebuilds each language by hand, or someone eyeballs font sizes in InDesign for every locale, every time the copy changes. Neither scales past a handful of languages or a handful of updates.",[16,7122,7123,7124,1069,7127,7130],{},"Bluepic's IDML renderer takes a different approach: export your file as ",[944,7125,7126],{},"IDML",[23,7128,7129],{},"File → Export → InDesign Markup","), hand it to Bluepic, and swap the text per language. The layout engine re-fits every text frame to the size it was actually designed for, automatically, for each language, every time.",[11,7132,7134],{"id":7133},"option-1-do-it-by-hand-in-the-browser","Option 1: do it by hand, in the browser",[16,7136,7137,7138,7141,7142,7144],{},"The fastest way to see this working is the free converter at ",[949,7139,7140],{"href":3658},"developers.bluepic.io\u002Fidml",". Drop your ",[23,7143,3633],{}," (or the whole package folder, so linked images and fonts come along):",[1023,7146,7147,7154,7157,7160],{},[941,7148,7149,7150,7153],{},"Once it's parsed, the ",[944,7151,7152],{},"Text & overrides"," card shows every text element on the current page, pre-filled with your source copy.",[941,7155,7156],{},"Type your translation directly into any row: the preview on the left updates immediately, live, so you can see the frame re-fit in real time.",[941,7158,7159],{},"Switch pages with the picker in the card header if your document has more than one.",[941,7161,7162,7163,7166,7167,7170],{},"Download the page as PNG, JPEG, SVG or ",[23,7164,7165],{},".bluepic",", or hit ",[944,7168,7169],{},"PDF (all pages)"," once every page looks right.",[16,7172,7173],{},[1960,7174],{"alt":7175,"src":7176},"The Convert tool's Edit & export card, showing the live preview next to editable text rows for every element on the page","\u002Fscreenshots\u002Fconvert-edit-export.png",[16,7178,7179],{},"This is genuinely useful for a one-off translation, or for proving the concept before you wire up automation. But if you're translating the same document into eight languages every month, you want the API.",[11,7181,7183],{"id":7182},"option-2-automate-it-via-the-api","Option 2: automate it via the API",[16,7185,7186],{},"The render API takes the exact same idea and makes it scriptable. One call:",[35,7188,7191],{"className":7189,"code":7190,"language":1291},[1289],"POST https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Fidml\u002Frender\nAuthorization: \u003Cyour API key>            (no \"Bearer \" prefix)\nContent-Type: multipart\u002Fform-data\n\nidml=@brochure.idml\nassets=@hero.jpg\nassets=@Inter.ttf\n",[23,7192,7190],{"__ignoreMap":40},[16,7194,7195,7196,7198],{},"That alone reproduces the source document. To translate it, add one field: ",[23,7197,4928],{},", a JSON object mapping each text element to its translated value:",[35,7200,7203],{"className":7201,"code":7202,"language":1291},[1289],"textOverrides={\"0:TextFrame_a1b2c3\":\"Willkommen in Berlin\",\"1:TextFrame_d4e5f6\":\"Jetzt entdecken\"}\n",[23,7204,7202],{"__ignoreMap":40},[16,7206,7207,7208,7211,7212,7214],{},"Keys are ",[23,7209,7210],{},"\"\u003CpageIndex>:\u003CelementId>\""," (0-based page index, matching the ",[23,7213,4888],{}," you're rendering). Any element you don't mention keeps its source text, so a partial translation is a valid request, not an error.",[7216,7217,7219],"h3",{"id":7218},"where-the-keys-come-from","Where the keys come from",[16,7221,7222,7223,7228,7229,7232,7233,7235,7236,7238],{},"You don't hand-derive those element IDs. Load the same document into the ",[944,7224,7225],{},[949,7226,5523],{"href":7227},"\u002Fidml\u002Fapi#playground",", tick ",[1089,7230,7231],{},"\"Override texts with custom values\"",", and it lists every text element on every page with an empty input next to each one. Type anything into a row and watch the generated ",[23,7234,1138],{}," command update live with the exact ",[23,7237,4928],{}," JSON: copy that shape once, and you have the keys for every future request against that document.",[16,7240,7241],{},[1960,7242],{"alt":7243,"src":7244},"The API playground's override table, one row per text element, an empty box means \"keep source text\"","\u002Fscreenshots\u002Fapi-override-card.png",[16,7246,7247,7248,7250,7251,7253],{},"Two things worth knowing before you build a translation matrix on top of those keys: the frame's size in InDesign is the hard ceiling German\u002FPolish\u002FFrench get shrunk to fit inside (not just a starting point), and one InDesign text frame doesn't always map to exactly one key. See ",[949,7249,5254],{"href":5253}," for both. If a row shows a formatting toolbar instead of a plain box, that element is rich text and needs the array shape from ",[949,7252,5572],{"href":5571}," rather than a plain string.",[16,7255,7256],{},"From there, automating a language is just: keep the keys, swap the values.",[35,7258,7260],{"className":37,"code":7259,"language":39,"meta":40,"style":40},"\u002F\u002F One render per language, same keys, translated values.\nconst translations = {\n  de: { '0:TextFrame_a1b2c3': 'Willkommen in Berlin', '1:TextFrame_d4e5f6': 'Jetzt entdecken' },\n  fr: { '0:TextFrame_a1b2c3': 'Bienvenue à Berlin', '1:TextFrame_d4e5f6': 'Découvrir maintenant' },\n};\n\nfor (const [lang, textOverrides] of Object.entries(translations)) {\n  const form = new FormData();\n  form.append('idml', idmlFile);\n  form.append('assets', heroImage, 'hero.jpg');\n  form.append('assets', interFont, 'Inter.ttf');\n\n  const res = await fetch(\n    `https:\u002F\u002Fapi.bluepic.io\u002Fapi\u002Fidml\u002Frender?format=pdf&textOverrides=${encodeURIComponent(JSON.stringify(textOverrides))}`,\n    { method: 'POST', headers: { Authorization: apiKey }, body: form },\n  );\n  const pdf = await res.blob();\n  \u002F\u002F save `pdf` as `brochure-${lang}.pdf`\n}\n",[23,7261,7262,7267,7278,7303,7326,7331,7335,7368,7382,7395,7413,7431,7435,7449,7473,7481,7485,7502,7507],{"__ignoreMap":40},[44,7263,7264],{"class":46,"line":47},[44,7265,7266],{"class":117},"\u002F\u002F One render per language, same keys, translated values.\n",[44,7268,7269,7271,7274,7276],{"class":46,"line":65},[44,7270,51],{"class":50},[44,7272,7273],{"class":54}," translations",[44,7275,58],{"class":50},[44,7277,811],{"class":61},[44,7279,7280,7283,7286,7288,7291,7293,7296,7298,7301],{"class":46,"line":84},[44,7281,7282],{"class":61},"  de: { ",[44,7284,7285],{"class":71},"'0:TextFrame_a1b2c3'",[44,7287,208],{"class":61},[44,7289,7290],{"class":71},"'Willkommen in Berlin'",[44,7292,202],{"class":61},[44,7294,7295],{"class":71},"'1:TextFrame_d4e5f6'",[44,7297,208],{"class":61},[44,7299,7300],{"class":71},"'Jetzt entdecken'",[44,7302,214],{"class":61},[44,7304,7305,7308,7310,7312,7315,7317,7319,7321,7324],{"class":46,"line":99},[44,7306,7307],{"class":61},"  fr: { ",[44,7309,7285],{"class":71},[44,7311,208],{"class":61},[44,7313,7314],{"class":71},"'Bienvenue à Berlin'",[44,7316,202],{"class":61},[44,7318,7295],{"class":71},[44,7320,208],{"class":61},[44,7322,7323],{"class":71},"'Découvrir maintenant'",[44,7325,214],{"class":61},[44,7327,7328],{"class":46,"line":114},[44,7329,7330],{"class":61},"};\n",[44,7332,7333],{"class":46,"line":121},[44,7334,131],{"emptyLinePlaceholder":130},[44,7336,7337,7339,7341,7343,7346,7349,7351,7353,7356,7359,7362,7365],{"class":46,"line":127},[44,7338,1583],{"class":50},[44,7340,1069],{"class":61},[44,7342,51],{"class":50},[44,7344,7345],{"class":61}," [",[44,7347,7348],{"class":54},"lang",[44,7350,202],{"class":61},[44,7352,4928],{"class":54},[44,7354,7355],{"class":61},"] ",[44,7357,7358],{"class":50},"of",[44,7360,7361],{"class":61}," Object.",[44,7363,7364],{"class":143},"entries",[44,7366,7367],{"class":61},"(translations)) {\n",[44,7369,7370,7372,7374,7376,7378,7380],{"class":46,"line":134},[44,7371,159],{"class":50},[44,7373,5347],{"class":54},[44,7375,58],{"class":50},[44,7377,310],{"class":50},[44,7379,5354],{"class":143},[44,7381,297],{"class":61},[44,7383,7384,7386,7388,7390,7392],{"class":46,"line":156},[44,7385,5361],{"class":61},[44,7387,5364],{"class":143},[44,7389,147],{"class":61},[44,7391,5369],{"class":71},[44,7393,7394],{"class":61},", idmlFile);\n",[44,7396,7397,7399,7401,7403,7405,7408,7411],{"class":46,"line":181},[44,7398,5361],{"class":61},[44,7400,5364],{"class":143},[44,7402,147],{"class":61},[44,7404,5383],{"class":71},[44,7406,7407],{"class":61},", heroImage, ",[44,7409,7410],{"class":71},"'hero.jpg'",[44,7412,543],{"class":61},[44,7414,7415,7417,7419,7421,7423,7426,7429],{"class":46,"line":193},[44,7416,5361],{"class":61},[44,7418,5364],{"class":143},[44,7420,147],{"class":61},[44,7422,5383],{"class":71},[44,7424,7425],{"class":61},", interFont, ",[44,7427,7428],{"class":71},"'Inter.ttf'",[44,7430,543],{"class":61},[44,7432,7433],{"class":46,"line":217},[44,7434,131],{"emptyLinePlaceholder":130},[44,7436,7437,7439,7441,7443,7445,7447],{"class":46,"line":235},[44,7438,159],{"class":50},[44,7440,162],{"class":54},[44,7442,58],{"class":50},[44,7444,167],{"class":50},[44,7446,170],{"class":143},[44,7448,5414],{"class":61},[44,7450,7451,7453,7455,7457,7459,7461,7463,7465,7467,7469,7471],{"class":46,"line":246},[44,7452,5945],{"class":71},[44,7454,5422],{"class":143},[44,7456,147],{"class":71},[44,7458,223],{"class":54},[44,7460,226],{"class":71},[44,7462,229],{"class":143},[44,7464,147],{"class":71},[44,7466,4928],{"class":61},[44,7468,5437],{"class":71},[44,7470,5440],{"class":71},[44,7472,190],{"class":61},[44,7474,7475,7477,7479],{"class":46,"line":252},[44,7476,5447],{"class":61},[44,7478,4749],{"class":71},[44,7480,5452],{"class":61},[44,7482,7483],{"class":46,"line":263},[44,7484,5457],{"class":61},[44,7486,7487,7489,7492,7494,7496,7498,7500],{"class":46,"line":269},[44,7488,159],{"class":50},[44,7490,7491],{"class":54}," pdf",[44,7493,58],{"class":50},[44,7495,167],{"class":50},[44,7497,4152],{"class":61},[44,7499,4155],{"class":143},[44,7501,297],{"class":61},[44,7503,7504],{"class":46,"line":275},[44,7505,7506],{"class":117},"  \u002F\u002F save `pdf` as `brochure-${lang}.pdf`\n",[44,7508,7509],{"class":46,"line":281},[44,7510,691],{"class":61},[16,7512,7513,7514,7517],{},"Pull the ",[23,7515,7516],{},"translations"," object from wherever your copy already lives: a spreadsheet, a CMS, a translation-memory export, or a machine-translation API you run once and review. Bluepic's job is the layout, not the translation itself; feed it whatever text you trust.",[7216,7519,2395],{"id":5064},[16,7521,5067,7522,7524,7525,226],{},[944,7523,4299],{},": 1 credit per rendered page, regardless of how many text overrides you send. Six pages in five languages is 30 rendered outputs, i.e. 30 credits. Check your balance any time on the ",[949,7526,1875],{"href":1874},[11,7528,7530],{"id":7529},"what-auto-fit-actually-means","What \"auto-fit\" actually means",[16,7532,7533],{},"There's an honest caveat here worth stating plainly: there's no such thing as the \"correct\" InDesign rendering of your German copy. Nobody ever laid that version out by hand. What Bluepic's engine does is re-run the same fit\u002Fshrink logic InDesign itself uses for auto-sizing text, against the real translated string, using real font metrics. It's layout-faithful to the ORIGINAL design's intent, not a pixel-for-pixel guess at what a human designer would have done differently for a 30%-longer string. For most marketing collateral (flyers, social posts, data sheets), that's exactly what you want: the master layout, honored, in every language.",[11,7535,5167],{"id":5166},[938,7537,7538,7543,7560,7565],{},[941,7539,7540,7542],{},[949,7541,7075],{"href":3791}," and get your API key (issued automatically on first use).",[941,7544,5555,7545,7547,7548,202,7550,202,7552,202,7554,202,7556,6157,7558,490],{},[949,7546,5558],{"href":3791}," for every render option (",[23,7549,1284],{},[23,7551,4888],{},[23,7553,4263],{},[23,7555,4654],{},[23,7557,2072],{},[23,7559,2075],{},[941,7561,4669,7562,7564],{},[949,7563,4939],{"href":4938}," for the same API used beyond translation: batch variants, dynamic fields, personalization.",[941,7566,7567,1077,7569,7571],{},[949,7568,5254],{"href":5253},[949,7570,5572],{"href":5571},": the two things worth knowing before you scale this to a real translation matrix.",[990,7573,7574],{},"html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}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);}",{"title":40,"searchDepth":65,"depth":65,"links":7576},[7577,7578,7579,7583,7584],{"id":5219,"depth":65,"text":5220},{"id":7133,"depth":65,"text":7134},{"id":7182,"depth":65,"text":7183,"children":7580},[7581,7582],{"id":7218,"depth":84,"text":7219},{"id":5064,"depth":84,"text":2395},{"id":7529,"depth":65,"text":7530},{"id":5166,"depth":65,"text":5167},"Turn one InDesign master layout into every language you need, with the text re-fit automatically, by hand in the browser, or by API for real automation.","\u002Fdocs-images\u002Ftranslate-indesign-automatically.svg",{},{"title":6068,"description":7585},"docs\u002Ftranslate-indesign-automatically","klFjSoJiDPplFqwiH3v_7geuzvqU2XQ9skLBxvxE-0o",1785414149053]