@velora-cms/server 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (367) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +65 -0
  3. package/dist/admin-bundle/assets/__virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react__loadShare__.js_commonjs-proxy-Ng0fOpdU.js +1 -0
  4. package/dist/admin-bundle/assets/__virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react_mf_2_dom__loadShare__.js_commonjs-proxy-oc1i_iBU.js +9 -0
  5. package/dist/admin-bundle/assets/_virtual_mf-localSharedImportMap___mfe_internal__velora_admin__mf_owner__1-DOKdWYyj.js +1 -0
  6. package/dist/admin-bundle/assets/_virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare___mf_0_velora_mf_2_cms_mf_1_plugin_mf_2_sdk__loadShare__.js-C1-ZAxwO.js +2 -0
  7. package/dist/admin-bundle/assets/_virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react__loadShare__.js-BuUBskgj.js +9 -0
  8. package/dist/admin-bundle/assets/_virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-DMaku-HC.js +9 -0
  9. package/dist/admin-bundle/assets/_virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react_mf_2_dom__loadShare__.js-DYfqn12F.js +1 -0
  10. package/dist/admin-bundle/assets/_virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-BPoj3DC9.js +25 -0
  11. package/dist/admin-bundle/assets/content-trash.schema-DfXHkC-w.js +72 -0
  12. package/dist/admin-bundle/assets/hostInit--C4xK6nj.js +1 -0
  13. package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
  14. package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
  15. package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
  16. package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
  17. package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
  18. package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
  19. package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
  20. package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
  21. package/dist/admin-bundle/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  22. package/dist/admin-bundle/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  23. package/dist/admin-bundle/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  24. package/dist/admin-bundle/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  25. package/dist/admin-bundle/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
  26. package/dist/admin-bundle/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
  27. package/dist/admin-bundle/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
  28. package/dist/admin-bundle/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
  29. package/dist/admin-bundle/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
  30. package/dist/admin-bundle/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
  31. package/dist/admin-bundle/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
  32. package/dist/admin-bundle/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
  33. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-400-normal-CyU-ddYS.woff2 +0 -0
  34. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-400-normal-EA1o-u_Q.woff +0 -0
  35. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-500-normal-C4MQITzh.woff2 +0 -0
  36. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-500-normal-DPXcppbm.woff +0 -0
  37. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-600-normal-0pRdybE_.woff2 +0 -0
  38. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-600-normal-MJZ0liF5.woff +0 -0
  39. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-400-normal-Bo5KPYvw.woff2 +0 -0
  40. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-400-normal-CFZh1unN.woff +0 -0
  41. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-500-normal-BletK1Hp.woff +0 -0
  42. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-500-normal-Cd6jVIg7.woff2 +0 -0
  43. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-600-normal-Dk5eISD_.woff +0 -0
  44. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-600-normal-KrqB56Mw.woff2 +0 -0
  45. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-400-normal-BeVLg08x.woff +0 -0
  46. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-400-normal-BpLfq-63.woff2 +0 -0
  47. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-500-normal-Ck_W4G78.woff2 +0 -0
  48. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-500-normal-eJxPnL1P.woff +0 -0
  49. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-600-normal-DT29Q8HW.woff +0 -0
  50. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-600-normal-DjDAeNQD.woff2 +0 -0
  51. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-400-normal-BTotfTJu.woff +0 -0
  52. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-400-normal-DZqxrq2p.woff2 +0 -0
  53. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-500-normal-ByOcLdNv.woff +0 -0
  54. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-500-normal-CocWQlwt.woff2 +0 -0
  55. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-600-normal-71GNu3SW.woff2 +0 -0
  56. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-600-normal-BGq0mW3O.woff +0 -0
  57. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-400-normal-Dsrv2Tcn.woff +0 -0
  58. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-400-normal-g30qAdWV.woff2 +0 -0
  59. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-500-normal-Cs5J6C77.woff2 +0 -0
  60. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-500-normal-DB5PtV2g.woff +0 -0
  61. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-600-normal-Bz0x94Yp.woff +0 -0
  62. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-600-normal-DUMzJB7m.woff2 +0 -0
  63. package/dist/admin-bundle/assets/ibm-plex-sans-greek-400-normal-D9ESIMu3.woff +0 -0
  64. package/dist/admin-bundle/assets/ibm-plex-sans-greek-400-normal-_efipK4i.woff2 +0 -0
  65. package/dist/admin-bundle/assets/ibm-plex-sans-greek-500-normal-CuWXN6rf.woff +0 -0
  66. package/dist/admin-bundle/assets/ibm-plex-sans-greek-500-normal-JMMifIXV.woff2 +0 -0
  67. package/dist/admin-bundle/assets/ibm-plex-sans-greek-600-normal-D-CqTdkO.woff +0 -0
  68. package/dist/admin-bundle/assets/ibm-plex-sans-greek-600-normal-DzTrcv_p.woff2 +0 -0
  69. package/dist/admin-bundle/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
  70. package/dist/admin-bundle/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
  71. package/dist/admin-bundle/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
  72. package/dist/admin-bundle/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
  73. package/dist/admin-bundle/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
  74. package/dist/admin-bundle/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
  75. package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-400-normal-C5H60-Va.woff2 +0 -0
  76. package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-400-normal-RBey6euL.woff +0 -0
  77. package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-500-normal-D0aIdm-b.woff +0 -0
  78. package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-500-normal-DakdToA3.woff2 +0 -0
  79. package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-600-normal-DIrixKbi.woff +0 -0
  80. package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-600-normal-DOrvGEcy.woff2 +0 -0
  81. package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-400-normal-DG4YqDda.woff2 +0 -0
  82. package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-400-normal-fK1oJ5dG.woff +0 -0
  83. package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-500-normal-BEb3_waV.woff +0 -0
  84. package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-500-normal-e4dixQRQ.woff2 +0 -0
  85. package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-600-normal-DgdngZtN.woff +0 -0
  86. package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-600-normal-DpPYBSTl.woff2 +0 -0
  87. package/dist/admin-bundle/assets/index-BIAF0ShP.js +542 -0
  88. package/dist/admin-bundle/assets/index-DhMY-0sC.js +148 -0
  89. package/dist/admin-bundle/assets/index-DnZV_gsg.css +1 -0
  90. package/dist/admin-bundle/assets/index-Do71v6NZ.js +7 -0
  91. package/dist/admin-bundle/assets/literata-cyrillic-ext-opsz-italic-5yUt4G_9.woff2 +0 -0
  92. package/dist/admin-bundle/assets/literata-cyrillic-ext-opsz-normal-C5tfwALZ.woff2 +0 -0
  93. package/dist/admin-bundle/assets/literata-cyrillic-opsz-italic-Bj0ndbyp.woff2 +0 -0
  94. package/dist/admin-bundle/assets/literata-cyrillic-opsz-normal-DgmypKXX.woff2 +0 -0
  95. package/dist/admin-bundle/assets/literata-greek-ext-opsz-italic-DFpO5j-Z.woff2 +0 -0
  96. package/dist/admin-bundle/assets/literata-greek-ext-opsz-normal-DGm64vGR.woff2 +0 -0
  97. package/dist/admin-bundle/assets/literata-greek-opsz-italic-CHERNRFq.woff2 +0 -0
  98. package/dist/admin-bundle/assets/literata-greek-opsz-normal-DFglUmAd.woff2 +0 -0
  99. package/dist/admin-bundle/assets/literata-latin-ext-opsz-italic-4y8iAqqs.woff2 +0 -0
  100. package/dist/admin-bundle/assets/literata-latin-ext-opsz-normal-STiTPs0F.woff2 +0 -0
  101. package/dist/admin-bundle/assets/literata-latin-opsz-italic-CJ93D38p.woff2 +0 -0
  102. package/dist/admin-bundle/assets/literata-latin-opsz-normal-BHZsWIlc.woff2 +0 -0
  103. package/dist/admin-bundle/assets/literata-vietnamese-opsz-italic-BdcrZMcO.woff2 +0 -0
  104. package/dist/admin-bundle/assets/literata-vietnamese-opsz-normal-Gqw5JQlq.woff2 +0 -0
  105. package/dist/admin-bundle/assets/mf-entry-bootstrap-0-aef015cd.js +41 -0
  106. package/dist/admin-bundle/assets/remoteEntry-CNyN68bc.js +14 -0
  107. package/dist/admin-bundle/assets/virtualExposes-BDCSo5OS.js +1 -0
  108. package/dist/admin-bundle/assets/vite-preload-helper-Cgledu0r.js +1 -0
  109. package/dist/admin-bundle/index.html +16 -0
  110. package/dist/app.d.ts +8 -0
  111. package/dist/app.js +252 -0
  112. package/dist/auth/api-content-author.d.ts +2 -0
  113. package/dist/auth/api-content-author.js +27 -0
  114. package/dist/auth/api-keys.d.ts +7 -0
  115. package/dist/auth/api-keys.js +85 -0
  116. package/dist/auth/jwt-payload.d.ts +16 -0
  117. package/dist/auth/jwt-payload.js +1 -0
  118. package/dist/auth/preview-tokens.d.ts +7 -0
  119. package/dist/auth/preview-tokens.js +76 -0
  120. package/dist/auth/rate-limit.d.ts +16 -0
  121. package/dist/auth/rate-limit.js +283 -0
  122. package/dist/auth/require-auth.d.ts +4 -0
  123. package/dist/auth/require-auth.js +28 -0
  124. package/dist/auth/route-rate-limits.d.ts +31 -0
  125. package/dist/auth/route-rate-limits.js +155 -0
  126. package/dist/auth/tokens.d.ts +15 -0
  127. package/dist/auth/tokens.js +38 -0
  128. package/dist/bin.d.ts +2 -0
  129. package/dist/bin.js +6 -0
  130. package/dist/boot/core-migration-gate.d.ts +12 -0
  131. package/dist/boot/core-migration-gate.js +52 -0
  132. package/dist/boot/resolve-boot-mode.d.ts +6 -0
  133. package/dist/boot/resolve-boot-mode.js +206 -0
  134. package/dist/bundled-templates/com.velora.blog/manifest.json +39 -0
  135. package/dist/bundled-templates/com.velora.blog/package.tar.gz +0 -0
  136. package/dist/bundled-templates/com.velora.blog/sample-content.json +14 -0
  137. package/dist/config/admin-ui.d.ts +1 -0
  138. package/dist/config/admin-ui.js +10 -0
  139. package/dist/config/cors.d.ts +7 -0
  140. package/dist/config/cors.js +53 -0
  141. package/dist/config/env-file-path.d.ts +2 -0
  142. package/dist/config/env-file-path.js +22 -0
  143. package/dist/config/graphql-limits.d.ts +2 -0
  144. package/dist/config/graphql-limits.js +26 -0
  145. package/dist/config/headless.d.ts +1 -0
  146. package/dist/config/headless.js +8 -0
  147. package/dist/config/license-revalidation.d.ts +2 -0
  148. package/dist/config/license-revalidation.js +16 -0
  149. package/dist/config/load-env.d.ts +1 -0
  150. package/dist/config/load-env.js +53 -0
  151. package/dist/config/marketplace.d.ts +11 -0
  152. package/dist/config/marketplace.js +19 -0
  153. package/dist/config/preview.d.ts +3 -0
  154. package/dist/config/preview.js +39 -0
  155. package/dist/config/public-docs.d.ts +1 -0
  156. package/dist/config/public-docs.js +9 -0
  157. package/dist/config/site.d.ts +7 -0
  158. package/dist/config/site.js +12 -0
  159. package/dist/config/trust-proxy.d.ts +2 -0
  160. package/dist/config/trust-proxy.js +42 -0
  161. package/dist/data-types/validate-data-type-settings.d.ts +1 -0
  162. package/dist/data-types/validate-data-type-settings.js +33 -0
  163. package/dist/db/content-db.d.ts +16 -0
  164. package/dist/db/content-db.js +109 -0
  165. package/dist/db/core-migrations/index.d.ts +14 -0
  166. package/dist/db/core-migrations/index.js +184 -0
  167. package/dist/db/core-migrations/steps.d.ts +4 -0
  168. package/dist/db/core-migrations/steps.js +66 -0
  169. package/dist/db/load-prisma-client.d.ts +2 -0
  170. package/dist/db/load-prisma-client.js +44 -0
  171. package/dist/db/prisma.d.ts +3 -0
  172. package/dist/db/prisma.js +67 -0
  173. package/dist/db/resolve-client-dir.d.ts +2 -0
  174. package/dist/db/resolve-client-dir.js +83 -0
  175. package/dist/docs/filter-public-api-spec.d.ts +2 -0
  176. package/dist/docs/filter-public-api-spec.js +52 -0
  177. package/dist/docs-bundle/404.html +41 -0
  178. package/dist/docs-bundle/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.BcSo_yiZ.js +1 -0
  179. package/dist/docs-bundle/_astro/Search.astro_astro_type_script_index_0_lang.CavldbAs.js +1 -0
  180. package/dist/docs-bundle/_astro/TableOfContents.astro_astro_type_script_index_0_lang.Csloo1VZ.js +1 -0
  181. package/dist/docs-bundle/_astro/common.DTXA7sXM.css +3 -0
  182. package/dist/docs-bundle/_astro/ec.0vx5m.js +3 -0
  183. package/dist/docs-bundle/_astro/ec.w36nc.css +1 -0
  184. package/dist/docs-bundle/_astro/page.Dwipeu-R.js +1 -0
  185. package/dist/docs-bundle/_astro/print.ehPL0gv-.css +1 -0
  186. package/dist/docs-bundle/_astro/starlight-toc.CTCQXEge.js +1 -0
  187. package/dist/docs-bundle/_astro/ui-core.D-7Scnhs.js +2 -0
  188. package/dist/docs-bundle/favicon.svg +1 -0
  189. package/dist/docs-bundle/index.html +1 -0
  190. package/dist/docs-bundle/pagefind/fragment/en_18a36d7.pf_fragment +0 -0
  191. package/dist/docs-bundle/pagefind/fragment/en_1d9eb24.pf_fragment +0 -0
  192. package/dist/docs-bundle/pagefind/fragment/en_1ef320c.pf_fragment +0 -0
  193. package/dist/docs-bundle/pagefind/fragment/en_3099406.pf_fragment +0 -0
  194. package/dist/docs-bundle/pagefind/fragment/en_4a5b6fe.pf_fragment +0 -0
  195. package/dist/docs-bundle/pagefind/fragment/en_9aa652e.pf_fragment +0 -0
  196. package/dist/docs-bundle/pagefind/fragment/en_baccdf6.pf_fragment +0 -0
  197. package/dist/docs-bundle/pagefind/fragment/en_d81f3fe.pf_fragment +0 -0
  198. package/dist/docs-bundle/pagefind/fragment/en_d855550.pf_fragment +0 -0
  199. package/dist/docs-bundle/pagefind/fragment/en_e6d88d8.pf_fragment +0 -0
  200. package/dist/docs-bundle/pagefind/fragment/en_e9d3189.pf_fragment +0 -0
  201. package/dist/docs-bundle/pagefind/index/en_5feeed7.pf_index +0 -0
  202. package/dist/docs-bundle/pagefind/index/en_c6981f9.pf_index +0 -0
  203. package/dist/docs-bundle/pagefind/index/en_ce4f6bf.pf_index +0 -0
  204. package/dist/docs-bundle/pagefind/pagefind-component-ui.css +1509 -0
  205. package/dist/docs-bundle/pagefind/pagefind-component-ui.js +56 -0
  206. package/dist/docs-bundle/pagefind/pagefind-entry.json +1 -0
  207. package/dist/docs-bundle/pagefind/pagefind-highlight.js +1070 -0
  208. package/dist/docs-bundle/pagefind/pagefind-modular-ui.css +214 -0
  209. package/dist/docs-bundle/pagefind/pagefind-modular-ui.js +8 -0
  210. package/dist/docs-bundle/pagefind/pagefind-ui.css +1 -0
  211. package/dist/docs-bundle/pagefind/pagefind-ui.js +2 -0
  212. package/dist/docs-bundle/pagefind/pagefind-worker.js +6 -0
  213. package/dist/docs-bundle/pagefind/pagefind.en_1e94abd852.pf_meta +0 -0
  214. package/dist/docs-bundle/pagefind/pagefind.js +6 -0
  215. package/dist/docs-bundle/pagefind/wasm.en.pagefind +0 -0
  216. package/dist/docs-bundle/pagefind/wasm.unknown.pagefind +0 -0
  217. package/dist/docs-bundle/v1/index.html +86 -0
  218. package/dist/docs-bundle/v1/using-velora/build-a-headless-frontend/index.html +413 -0
  219. package/dist/docs-bundle/v1/using-velora/content-model/index.html +858 -0
  220. package/dist/docs-bundle/v1/using-velora/editing/index.html +748 -0
  221. package/dist/docs-bundle/v1/using-velora/getting-started/index.html +588 -0
  222. package/dist/docs-bundle/v1/using-velora/headless-api/index.html +1620 -0
  223. package/dist/docs-bundle/v1/using-velora/index.html +215 -0
  224. package/dist/docs-bundle/v1/using-velora/installation/index.html +886 -0
  225. package/dist/docs-bundle/v1/using-velora/installing-plugins/index.html +836 -0
  226. package/dist/docs-bundle/v1/using-velora/media/index.html +468 -0
  227. package/dist/docs-bundle/v1/using-velora/users-and-roles/index.html +561 -0
  228. package/dist/graphql/field-count-rule.d.ts +5 -0
  229. package/dist/graphql/field-count-rule.js +59 -0
  230. package/dist/graphql/schema.d.ts +12 -0
  231. package/dist/graphql/schema.js +330 -0
  232. package/dist/index.d.ts +1 -0
  233. package/dist/index.js +180 -0
  234. package/dist/lib.d.ts +2 -0
  235. package/dist/lib.js +39 -0
  236. package/dist/marketplace/install-bundle-members.d.ts +33 -0
  237. package/dist/marketplace/install-bundle-members.js +137 -0
  238. package/dist/marketplace/install-from-marketplace.d.ts +60 -0
  239. package/dist/marketplace/install-from-marketplace.js +581 -0
  240. package/dist/marketplace/license-revalidation-job.d.ts +38 -0
  241. package/dist/marketplace/license-revalidation-job.js +139 -0
  242. package/dist/marketplace/marketplace-client.d.ts +43 -0
  243. package/dist/marketplace/marketplace-client.js +228 -0
  244. package/dist/marketplace/revalidate-license.d.ts +39 -0
  245. package/dist/marketplace/revalidate-license.js +103 -0
  246. package/dist/media/media-service.d.ts +36 -0
  247. package/dist/media/media-service.js +162 -0
  248. package/dist/plugins/boot-plugin-loader.d.ts +32 -0
  249. package/dist/plugins/boot-plugin-loader.js +57 -0
  250. package/dist/plugins/hook-dispatcher.d.ts +13 -0
  251. package/dist/plugins/hook-dispatcher.js +68 -0
  252. package/dist/plugins/install-built-package.d.ts +36 -0
  253. package/dist/plugins/install-built-package.js +199 -0
  254. package/dist/plugins/install-package-tarball.d.ts +25 -0
  255. package/dist/plugins/install-package-tarball.js +73 -0
  256. package/dist/plugins/plugin-context.d.ts +5 -0
  257. package/dist/plugins/plugin-context.js +26 -0
  258. package/dist/plugins/plugin-package-store.d.ts +31 -0
  259. package/dist/plugins/plugin-package-store.js +64 -0
  260. package/dist/plugins/plugin-registry-service.d.ts +227 -0
  261. package/dist/plugins/plugin-registry-service.js +636 -0
  262. package/dist/plugins/seed-template-content.d.ts +23 -0
  263. package/dist/plugins/seed-template-content.js +198 -0
  264. package/dist/plugins/server-serializers.d.ts +3 -0
  265. package/dist/plugins/server-serializers.js +16 -0
  266. package/dist/plugins/simple-storage-api.d.ts +9 -0
  267. package/dist/plugins/simple-storage-api.js +26 -0
  268. package/dist/plugins/snapshot-groups.d.ts +16 -0
  269. package/dist/plugins/snapshot-groups.js +81 -0
  270. package/dist/plugins/structured-query-builder.d.ts +21 -0
  271. package/dist/plugins/structured-query-builder.js +60 -0
  272. package/dist/plugins/structured-snapshot-service.d.ts +16 -0
  273. package/dist/plugins/structured-snapshot-service.js +286 -0
  274. package/dist/plugins/structured-storage-api.d.ts +22 -0
  275. package/dist/plugins/structured-storage-api.js +91 -0
  276. package/dist/plugins/structured-storage-transaction.d.ts +22 -0
  277. package/dist/plugins/structured-storage-transaction.js +63 -0
  278. package/dist/plugins/structured-table-naming.d.ts +1 -0
  279. package/dist/plugins/structured-table-naming.js +18 -0
  280. package/dist/plugins/trusted-keys.d.ts +11 -0
  281. package/dist/plugins/trusted-keys.js +67 -0
  282. package/dist/plugins/trusted-signing-keys.d.ts +27 -0
  283. package/dist/plugins/trusted-signing-keys.js +40 -0
  284. package/dist/plugins/uninstall-bundle.d.ts +29 -0
  285. package/dist/plugins/uninstall-bundle.js +150 -0
  286. package/dist/plugins/verify-package.d.ts +36 -0
  287. package/dist/plugins/verify-package.js +121 -0
  288. package/dist/routes/admin-static.d.ts +8 -0
  289. package/dist/routes/admin-static.js +196 -0
  290. package/dist/routes/api-keys.d.ts +2 -0
  291. package/dist/routes/api-keys.js +95 -0
  292. package/dist/routes/api-playground.d.ts +2 -0
  293. package/dist/routes/api-playground.js +21 -0
  294. package/dist/routes/auth.d.ts +2 -0
  295. package/dist/routes/auth.js +102 -0
  296. package/dist/routes/content.d.ts +3 -0
  297. package/dist/routes/content.js +471 -0
  298. package/dist/routes/data-types.d.ts +3 -0
  299. package/dist/routes/data-types.js +100 -0
  300. package/dist/routes/docs-static.d.ts +8 -0
  301. package/dist/routes/docs-static.js +137 -0
  302. package/dist/routes/document-types.d.ts +3 -0
  303. package/dist/routes/document-types.js +149 -0
  304. package/dist/routes/headless-bridge.d.ts +10 -0
  305. package/dist/routes/headless-bridge.js +207 -0
  306. package/dist/routes/health.d.ts +2 -0
  307. package/dist/routes/health.js +15 -0
  308. package/dist/routes/marketplace-proxy.d.ts +8 -0
  309. package/dist/routes/marketplace-proxy.js +135 -0
  310. package/dist/routes/media.d.ts +6 -0
  311. package/dist/routes/media.js +93 -0
  312. package/dist/routes/plugin-api-routes.d.ts +12 -0
  313. package/dist/routes/plugin-api-routes.js +224 -0
  314. package/dist/routes/plugin-assets.d.ts +8 -0
  315. package/dist/routes/plugin-assets.js +51 -0
  316. package/dist/routes/plugin-management.d.ts +7 -0
  317. package/dist/routes/plugin-management.js +525 -0
  318. package/dist/routes/plugin-storage.d.ts +8 -0
  319. package/dist/routes/plugin-storage.js +104 -0
  320. package/dist/routes/plugins.d.ts +2 -0
  321. package/dist/routes/plugins.js +36 -0
  322. package/dist/routes/public-api.d.ts +9 -0
  323. package/dist/routes/public-api.js +545 -0
  324. package/dist/routes/published-item.d.ts +57 -0
  325. package/dist/routes/published-item.js +236 -0
  326. package/dist/routes/setup.d.ts +9 -0
  327. package/dist/routes/setup.js +477 -0
  328. package/dist/routes/snapshots.d.ts +3 -0
  329. package/dist/routes/snapshots.js +65 -0
  330. package/dist/routes/templates.d.ts +3 -0
  331. package/dist/routes/templates.js +37 -0
  332. package/dist/routes/themes.d.ts +2 -0
  333. package/dist/routes/themes.js +30 -0
  334. package/dist/routes/users.d.ts +2 -0
  335. package/dist/routes/users.js +113 -0
  336. package/dist/sandbox/register-local-plugin.d.ts +36 -0
  337. package/dist/sandbox/register-local-plugin.js +108 -0
  338. package/dist/sandbox/seed-sandbox.d.ts +20 -0
  339. package/dist/sandbox/seed-sandbox.js +122 -0
  340. package/dist/scripts/copy-admin-bundle.d.ts +1 -0
  341. package/dist/scripts/copy-admin-bundle.js +24 -0
  342. package/dist/scripts/copy-docs-bundle.d.ts +1 -0
  343. package/dist/scripts/copy-docs-bundle.js +22 -0
  344. package/dist/scripts/create-api-key.d.ts +5 -0
  345. package/dist/scripts/create-api-key.js +56 -0
  346. package/dist/scripts/generate-prisma-clients.d.ts +6 -0
  347. package/dist/scripts/generate-prisma-clients.js +125 -0
  348. package/dist/scripts/pack-bundled-templates.d.ts +1 -0
  349. package/dist/scripts/pack-bundled-templates.js +87 -0
  350. package/dist/setup/build-connection-string.d.ts +19 -0
  351. package/dist/setup/build-connection-string.js +14 -0
  352. package/dist/setup/bundled-templates.d.ts +19 -0
  353. package/dist/setup/bundled-templates.js +121 -0
  354. package/dist/setup/ensure-prisma-client.d.ts +3 -0
  355. package/dist/setup/ensure-prisma-client.js +23 -0
  356. package/dist/setup/install-starter.d.ts +14 -0
  357. package/dist/setup/install-starter.js +76 -0
  358. package/dist/setup/normalize-database-url.d.ts +2 -0
  359. package/dist/setup/normalize-database-url.js +35 -0
  360. package/dist/setup/persist-env-config.d.ts +2 -0
  361. package/dist/setup/persist-env-config.js +63 -0
  362. package/dist/setup/provision-database.d.ts +2 -0
  363. package/dist/setup/provision-database.js +89 -0
  364. package/dist/setup-app.d.ts +4 -0
  365. package/dist/setup-app.js +43 -0
  366. package/package.json +96 -0
  367. package/prisma/schema.prisma +303 -0
@@ -0,0 +1,858 @@
1
+ <!DOCTYPE html><html lang="en" dir="ltr" data-theme="dark" data-has-toc data-has-sidebar class="astro-ewhz5xxr"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><title>Document types | Velora</title><link rel="shortcut icon" href="/docs/favicon.svg" type="image/svg+xml"/><meta name="generator" content="Astro v7.2.10"/><meta name="generator" content="Starlight v0.41.5"/><meta property="og:title" content="Document types"/><meta property="og:type" content="article"/><meta property="og:locale" content="en"/><meta property="og:description" content="Model your content — the page/element split, fields, tabs, tree rules, data types, and the admin API that creates them."/><meta property="og:site_name" content="Velora"/><meta name="twitter:card" content="summary_large_image"/><meta name="description" content="Model your content — the page/element split, fields, tabs, tree rules, data types, and the admin API that creates them."/><script>
2
+ window.StarlightThemeProvider = (() => {
3
+ const storedTheme =
4
+ typeof localStorage !== 'undefined' && localStorage.getItem('starlight-theme');
5
+ const theme =
6
+ storedTheme ||
7
+ (window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark');
8
+ document.documentElement.dataset.theme = theme === 'light' ? 'light' : 'dark';
9
+ return {
10
+ updatePickers(theme = storedTheme || 'auto') {
11
+ document.querySelectorAll('starlight-theme-select').forEach((picker) => {
12
+ const select = picker.querySelector('select');
13
+ if (select) select.value = theme;
14
+ /** @type {HTMLTemplateElement | null} */
15
+ const tmpl = document.querySelector(`#theme-icons`);
16
+ const newIcon = tmpl && tmpl.content.querySelector('.' + theme);
17
+ if (newIcon) {
18
+ const oldIcon = picker.querySelector('svg.label-icon');
19
+ if (oldIcon) {
20
+ oldIcon.replaceChildren(...newIcon.cloneNode(true).childNodes);
21
+ }
22
+ }
23
+ });
24
+ },
25
+ };
26
+ })();
27
+ </script><template id="theme-icons"><svg aria-hidden="true" class="light astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M5 12a1 1 0 0 0-1-1H3a1 1 0 0 0 0 2h1a1 1 0 0 0 1-1Zm.64 5-.71.71a1 1 0 0 0 0 1.41 1 1 0 0 0 1.41 0l.71-.71A1 1 0 0 0 5.64 17ZM12 5a1 1 0 0 0 1-1V3a1 1 0 0 0-2 0v1a1 1 0 0 0 1 1Zm5.66 2.34a1 1 0 0 0 .7-.29l.71-.71a1 1 0 1 0-1.41-1.41l-.66.71a1 1 0 0 0 0 1.41 1 1 0 0 0 .66.29Zm-12-.29a1 1 0 0 0 1.41 0 1 1 0 0 0 0-1.41l-.71-.71a1.004 1.004 0 1 0-1.43 1.41l.73.71ZM21 11h-1a1 1 0 0 0 0 2h1a1 1 0 0 0 0-2Zm-2.64 6A1 1 0 0 0 17 18.36l.71.71a1 1 0 0 0 1.41 0 1 1 0 0 0 0-1.41l-.76-.66ZM12 6.5a5.5 5.5 0 1 0 5.5 5.5A5.51 5.51 0 0 0 12 6.5Zm0 9a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7Zm0 3.5a1 1 0 0 0-1 1v1a1 1 0 0 0 2 0v-1a1 1 0 0 0-1-1Z"/></svg><svg aria-hidden="true" class="dark astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21.64 13a1 1 0 0 0-1.05-.14 8.049 8.049 0 0 1-3.37.73 8.15 8.15 0 0 1-8.14-8.1 8.59 8.59 0 0 1 .25-2A1 1 0 0 0 8 2.36a10.14 10.14 0 1 0 14 11.69 1 1 0 0 0-.36-1.05Zm-9.5 6.69A8.14 8.14 0 0 1 7.08 5.22v.27a10.15 10.15 0 0 0 10.14 10.14 9.784 9.784 0 0 0 2.1-.22 8.11 8.11 0 0 1-7.18 4.32v-.04Z"/></svg><svg aria-hidden="true" class="auto astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21 14h-1V7a3 3 0 0 0-3-3H7a3 3 0 0 0-3 3v7H3a1 1 0 0 0-1 1v2a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-2a1 1 0 0 0-1-1ZM6 7a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v7H6V7Zm14 10a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-1h16v1Z"/></svg></template><link rel="stylesheet" href="/docs/_astro/print.ehPL0gv-.css" media="print"><link rel="stylesheet" href="/docs/_astro/common.DTXA7sXM.css"><script type="module" src="/docs/_astro/page.Dwipeu-R.js"></script></head><body class="astro-ewhz5xxr"><a class="sl-skip-link astro-zufphfvp" href="#_top">Skip to content</a><div class="page sl-flex astro-5auw3bkx"><header class="header astro-5auw3bkx"><div class="header astro-6ytusalw"><div class="title-wrapper sl-flex astro-6ytusalw"><a href="/docs/" class="site-title sl-flex astro-nu4hftpf"><span class="astro-nu4hftpf" translate="no">Velora</span></a></div><div class="sl-flex print:hidden astro-6ytusalw"><site-search class="astro-6ytusalw astro-zwj6qr56" data-translations="{&quot;placeholder&quot;:&quot;Search&quot;}"><button data-open-modal disabled aria-label="Search" aria-keyshortcuts="Control+K" class="astro-zwj6qr56"><svg aria-hidden="true" class="astro-zwj6qr56 astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21.71 20.29 18 16.61A9 9 0 1 0 16.61 18l3.68 3.68a.999.999 0 0 0 1.42 0 1 1 0 0 0 0-1.39ZM11 18a7 7 0 1 1 0-14 7 7 0 0 1 0 14Z"/></svg><span class="sl-hidden md:sl-block astro-zwj6qr56" aria-hidden="true">Search</span><kbd class="sl-hidden md:sl-flex astro-zwj6qr56" style="display: none;"><kbd class="astro-zwj6qr56">Ctrl</kbd><kbd class="astro-zwj6qr56">K</kbd></kbd></button><dialog style="padding:0" aria-label="Search" class="astro-zwj6qr56"><div class="dialog-frame sl-flex astro-zwj6qr56"><button data-close-modal class="sl-flex md:sl-hidden astro-zwj6qr56">Cancel</button><div class="search-container astro-zwj6qr56"><div id="starlight__search" class="astro-zwj6qr56"></div></div></div></dialog></site-search><script>
28
+ (() => {
29
+ const openBtn = document.querySelector('button[data-open-modal]');
30
+ const shortcut = openBtn?.querySelector('kbd');
31
+ if (!openBtn || !(shortcut instanceof HTMLElement)) return;
32
+ const platformKey = shortcut.querySelector('kbd');
33
+ if (platformKey && /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)) {
34
+ platformKey.textContent = '⌘';
35
+ openBtn.setAttribute('aria-keyshortcuts', 'Meta+K');
36
+ }
37
+ shortcut.style.display = '';
38
+ })();
39
+ </script><script type="module" src="/docs/_astro/Search.astro_astro_type_script_index_0_lang.CavldbAs.js"></script></div><div class="sl-hidden md:sl-flex print:hidden right-group astro-6ytusalw"><div class="sl-flex social-icons astro-6ytusalw"></div><starlight-theme-select><label style="--sl-select-width: 6.25em" class="astro-7uz3f5wr"><span class="sr-only astro-7uz3f5wr">Select theme</span><svg aria-hidden="true" class="icon label-icon astro-7uz3f5wr astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21 14h-1V7a3 3 0 0 0-3-3H7a3 3 0 0 0-3 3v7H3a1 1 0 0 0-1 1v2a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-2a1 1 0 0 0-1-1ZM6 7a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v7H6V7Zm14 10a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-1h16v1Z"/></svg><select autocomplete="off" class="astro-7uz3f5wr"><option value="dark" class="astro-7uz3f5wr">Dark</option><option value="light" class="astro-7uz3f5wr">Light</option><option value="auto" selected class="astro-7uz3f5wr">Auto</option></select><svg aria-hidden="true" class="icon caret astro-7uz3f5wr astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M17 9.17a1 1 0 0 0-1.41 0L12 12.71 8.46 9.17a1 1 0 1 0-1.41 1.42l4.24 4.24a1.002 1.002 0 0 0 1.42 0L17 10.59a1.002 1.002 0 0 0 0-1.42Z"/></svg></label></starlight-theme-select><script>
40
+ StarlightThemeProvider.updatePickers();
41
+ </script><script type="module">var e=`starlight-theme`,t=e=>e===`auto`||e===`dark`||e===`light`?e:`auto`,n=()=>t(typeof localStorage<`u`&&localStorage.getItem(e));function r(t){typeof localStorage<`u`&&localStorage.setItem(e,t===`light`||t===`dark`?t:``)}var i=()=>matchMedia(`(prefers-color-scheme: light)`).matches?`light`:`dark`;function a(e){StarlightThemeProvider.updatePickers(e),document.documentElement.dataset.theme=e===`auto`?i():e,r(e)}matchMedia(`(prefers-color-scheme: light)`).addEventListener(`change`,()=>{n()===`auto`&&a(`auto`)});var o=class extends HTMLElement{constructor(){super(),a(n()),this.querySelector(`select`)?.addEventListener(`change`,e=>{e.currentTarget instanceof HTMLSelectElement&&a(t(e.currentTarget.value))})}};customElements.define(`starlight-theme-select`,o);</script><script type="module">var e=class extends HTMLElement{constructor(){super();let e=this.querySelector(`select`);e&&(e.addEventListener(`change`,e=>{e.currentTarget instanceof HTMLSelectElement&&(window.location.pathname=e.currentTarget.value)}),window.addEventListener(`pageshow`,t=>{if(!t.persisted)return;let n=e.querySelector(`option[selected]`)?.index;n!==e.selectedIndex&&(e.selectedIndex=n??0)}))}};customElements.define(`starlight-lang-select`,e);</script></div></div></header><nav class="sidebar print:hidden astro-5auw3bkx" aria-label="Main"><starlight-menu-button class="print:hidden astro-gnkm6dw5"><button aria-expanded="false" aria-label="Menu" aria-controls="starlight__sidebar" class="sl-flex md:sl-hidden astro-gnkm6dw5"><svg aria-hidden="true" class="open-menu astro-gnkm6dw5 astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M3 8h18a1 1 0 1 0 0-2H3a1 1 0 0 0 0 2Zm18 8H3a1 1 0 0 0 0 2h18a1 1 0 0 0 0-2Zm0-5H3a1 1 0 0 0 0 2h18a1 1 0 0 0 0-2Z"/></svg><svg aria-hidden="true" class="close-menu astro-gnkm6dw5 astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="m13.41 12 6.3-6.29a1.004 1.004 0 1 0-1.42-1.42L12 10.59l-6.29-6.3a1.004 1.004 0 0 0-1.42 1.42l6.3 6.29-6.3 6.29a1 1 0 0 0 0 1.42.998.998 0 0 0 1.42 0l6.29-6.3 6.29 6.3a.999.999 0 0 0 1.42 0 1 1 0 0 0 0-1.42L13.41 12Z"/></svg></button></starlight-menu-button><script type="module">var e=class extends HTMLElement{btn=this.querySelector(`button`);constructor(){super(),this.btn.addEventListener(`click`,()=>this.toggleExpanded());let e=this.closest(`nav`);e&&e.addEventListener(`keyup`,e=>this.closeOnEscape(e)),matchMedia(`(min-width: 50em)`).addEventListener(`change`,()=>this.setExpanded(!1))}setExpanded(e){this.setAttribute(`aria-expanded`,String(e)),document.body.toggleAttribute(`data-mobile-menu-expanded`,e),document.querySelectorAll(`.main-frame, .sl-skip-link`).forEach(t=>{t.toggleAttribute(`inert`,e)})}toggleExpanded(){this.setExpanded(this.getAttribute(`aria-expanded`)!==`true`)}closeOnEscape(e){e.code===`Escape`&&(this.setExpanded(!1),this.btn.focus())}};customElements.define(`starlight-menu-button`,e);</script><div id="starlight__sidebar" class="sidebar-pane astro-5auw3bkx"><div class="sidebar-content sl-flex astro-5auw3bkx"><sl-sidebar-state-persist data-hash="03jp7ll" class="astro-pcs3l5kp"><script aria-hidden="true">
42
+ (() => {
43
+ try {
44
+ if (!matchMedia('(min-width: 50em)').matches) return;
45
+ /** @type {HTMLElement | null} */
46
+ const target = document.querySelector('sl-sidebar-state-persist');
47
+ const state = JSON.parse(sessionStorage.getItem('sl-sidebar-state') || '0');
48
+ if (!target || !state || target.dataset.hash !== state.hash) return;
49
+ window._starlightScrollRestore = state.scroll;
50
+ customElements.define(
51
+ 'sl-sidebar-restore',
52
+ class SidebarRestore extends HTMLElement {
53
+ connectedCallback() {
54
+ try {
55
+ const idx = parseInt(this.dataset.index || '');
56
+ const details = this.closest('details');
57
+ if (details && typeof state.open[idx] === 'boolean') details.open = state.open[idx];
58
+ } catch {}
59
+ }
60
+ }
61
+ );
62
+ } catch {}
63
+ })();
64
+ </script><ul class="top-level astro-ck7l566j"><li class="astro-ck7l566j"><details open class="astro-ck7l566j"><summary class="astro-ck7l566j"><span class="group-label astro-ck7l566j"><span class="large astro-ck7l566j">Using Velora</span></span><svg aria-hidden="true" class="caret astro-ck7l566j astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.25rem;"><path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z"/></svg></summary><sl-sidebar-restore data-index="0"></sl-sidebar-restore><ul class="astro-ck7l566j"><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/" class="astro-ck7l566j"><span class="astro-ck7l566j">Using Velora</span></a></li><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/getting-started/" class="astro-ck7l566j"><span class="astro-ck7l566j">Getting started</span></a></li><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/installation/" class="astro-ck7l566j"><span class="astro-ck7l566j">Installation</span></a></li><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/content-model/" aria-current="page" class="astro-ck7l566j"><span class="astro-ck7l566j">Document types</span></a></li><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/editing/" class="astro-ck7l566j"><span class="astro-ck7l566j">Content editing</span></a></li><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/media/" class="astro-ck7l566j"><span class="astro-ck7l566j">Media</span></a></li><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/users-and-roles/" class="astro-ck7l566j"><span class="astro-ck7l566j">Users and roles</span></a></li><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/installing-plugins/" class="astro-ck7l566j"><span class="astro-ck7l566j">Installing plugins</span></a></li><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/headless-api/" class="astro-ck7l566j"><span class="astro-ck7l566j">Headless API</span></a></li><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/build-a-headless-frontend/" class="astro-ck7l566j"><span class="astro-ck7l566j">Build a headless frontend</span></a></li></ul></details></li></ul><script aria-hidden="true">
65
+ (() => {
66
+ const scroller = document.getElementById('starlight__sidebar');
67
+ if (!window._starlightScrollRestore || !scroller) return;
68
+ scroller.scrollTop = window._starlightScrollRestore;
69
+ delete window._starlightScrollRestore;
70
+ })();
71
+ </script></sl-sidebar-state-persist><div class="md:sl-hidden"><div class="mobile-preferences sl-flex astro-yfet6llj"><div class="social-icons astro-yfet6llj"></div><starlight-theme-select><label style="--sl-select-width: 6.25em" class="astro-7uz3f5wr"><span class="sr-only astro-7uz3f5wr">Select theme</span><svg aria-hidden="true" class="icon label-icon astro-7uz3f5wr astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21 14h-1V7a3 3 0 0 0-3-3H7a3 3 0 0 0-3 3v7H3a1 1 0 0 0-1 1v2a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-2a1 1 0 0 0-1-1ZM6 7a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v7H6V7Zm14 10a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-1h16v1Z"/></svg><select autocomplete="off" class="astro-7uz3f5wr"><option value="dark" class="astro-7uz3f5wr">Dark</option><option value="light" class="astro-7uz3f5wr">Light</option><option value="auto" selected class="astro-7uz3f5wr">Auto</option></select><svg aria-hidden="true" class="icon caret astro-7uz3f5wr astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M17 9.17a1 1 0 0 0-1.41 0L12 12.71 8.46 9.17a1 1 0 1 0-1.41 1.42l4.24 4.24a1.002 1.002 0 0 0 1.42 0L17 10.59a1.002 1.002 0 0 0 0-1.42Z"/></svg></label></starlight-theme-select><script>
72
+ StarlightThemeProvider.updatePickers();
73
+ </script></div></div></div></div></nav><div class="main-frame astro-5auw3bkx"><script type="module">var e=document.getElementById(`starlight__sidebar`),t=e?.querySelector(`sl-sidebar-state-persist`),n=`sl-sidebar-state`,r=()=>{let r=[],i=t?.dataset.hash||``;try{let e=sessionStorage.getItem(n),t=JSON.parse(e||`{}`);Array.isArray(t.open)&&t.hash===i&&(r=t.open)}catch{}return{hash:i,open:r,scroll:e?.scrollTop||0}},i=e=>{try{sessionStorage.setItem(n,JSON.stringify(e))}catch{}},a=()=>i(r()),o=(e,t)=>{let n=r();n.open[t]=e,i(n)};t?.addEventListener(`click`,e=>{if(!(e.target instanceof Element))return;let t=e.target.closest(`summary`)?.closest(`details`);if(!t)return;let n=t.querySelector(`sl-sidebar-restore`),r=parseInt(n?.dataset.index||``);isNaN(r)||o(!t.open,r)}),addEventListener(`visibilitychange`,()=>{document.visibilityState===`hidden`&&a()});</script><div class="lg:sl-flex astro-perbdhd2"><aside class="right-sidebar-container print:hidden astro-perbdhd2"><div class="right-sidebar astro-perbdhd2"><div class="lg:sl-hidden astro-epm5tkrc"><mobile-starlight-toc data-min-h="2" data-max-h="3" class="astro-bnvebtr4"><nav aria-labelledby="starlight__on-this-page--mobile" class="astro-bnvebtr4"><details id="starlight__mobile-toc" class="astro-bnvebtr4"><summary id="starlight__on-this-page--mobile" class="sl-flex astro-bnvebtr4"><span class="toggle sl-flex astro-bnvebtr4">On this page<svg aria-hidden="true" class="caret astro-bnvebtr4 astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1rem;"><path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z"/></svg></span><span class="display-current astro-bnvebtr4"></span></summary><div class="dropdown astro-bnvebtr4"><ul class="isMobile astro-lptfoybu" style="--depth: 0;"><li style="--depth: 0;" class="astro-lptfoybu"><a href="#_top" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Overview</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#page-types-and-element-types" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Page types and element types</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#the-properties-of-a-document-type" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">The properties of a document type</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#fields" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Fields</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#tabs" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Tabs</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#data-types-and-the-built-in-editors" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Data types and the built-in editors</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#tree-rules" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Tree rules</span></a><ul class="isMobile astro-lptfoybu" style="--depth: 1;"><li style="--depth: 1;" class="astro-lptfoybu"><a href="#reserved-root-slugs" style="--depth: 1;" class="astro-lptfoybu"><span style="--depth: 1;" class="astro-lptfoybu">Reserved root slugs</span></a></li></ul></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#locales" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Locales</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#templates-are-inert-in-v1" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Templates are inert in v1</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#a-worked-example-a-page-type-in-the-admin" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">A worked example: a Page type in the admin</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#the-same-thing-over-the-admin-api" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">The same thing over the admin API</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#how-ids-are-minted" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">How ids are minted</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#next" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Next</span></a></li></ul></div></details></nav></mobile-starlight-toc><script type="module" src="/docs/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.BcSo_yiZ.js"></script></div><div class="right-sidebar-panel sl-hidden lg:sl-block astro-epm5tkrc"><div class="sl-container astro-epm5tkrc"><starlight-toc data-min-h="2" data-max-h="3"><nav aria-labelledby="starlight__on-this-page"><h2 id="starlight__on-this-page">On this page</h2><ul class="astro-lptfoybu" style="--depth: 0;"><li style="--depth: 0;" class="astro-lptfoybu"><a href="#_top" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Overview</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#page-types-and-element-types" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Page types and element types</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#the-properties-of-a-document-type" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">The properties of a document type</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#fields" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Fields</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#tabs" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Tabs</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#data-types-and-the-built-in-editors" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Data types and the built-in editors</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#tree-rules" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Tree rules</span></a><ul class="astro-lptfoybu" style="--depth: 1;"><li style="--depth: 1;" class="astro-lptfoybu"><a href="#reserved-root-slugs" style="--depth: 1;" class="astro-lptfoybu"><span style="--depth: 1;" class="astro-lptfoybu">Reserved root slugs</span></a></li></ul></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#locales" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Locales</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#templates-are-inert-in-v1" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Templates are inert in v1</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#a-worked-example-a-page-type-in-the-admin" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">A worked example: a Page type in the admin</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#the-same-thing-over-the-admin-api" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">The same thing over the admin API</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#how-ids-are-minted" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">How ids are minted</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#next" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Next</span></a></li></ul></nav></starlight-toc><script type="module" src="/docs/_astro/TableOfContents.astro_astro_type_script_index_0_lang.Csloo1VZ.js"></script></div></div></div></aside><div class="main-pane astro-perbdhd2"><main data-pagefind-body class="astro-ewhz5xxr" lang="en" dir="ltr"><div class="content-panel astro-ahkzlqop"><div class="sl-container astro-ahkzlqop"><h1 id="_top" class="astro-oef2efwn">Document types</h1></div></div><div class="content-panel astro-ahkzlqop"><div class="sl-container astro-ahkzlqop"><div class="sl-markdown-content"><p>A document type is the shape of a piece of content: which fields it has,
74
+ how those fields are grouped into tabs in the editor, and — for content
75
+ that lives in the tree — where it is allowed to sit. Every content item
76
+ you create is an instance of some document type; the type defines what
77
+ you are allowed to fill in, the item just holds the values.</p>
78
+ <p>Document types are edited in the admin under <strong>Settings → Document
79
+ Types</strong>, and over the admin API at <code dir="auto">/api/document-types</code>. Both go through
80
+ the same service, so nothing below is admin-only behaviour.</p>
81
+ <div class="sl-heading-wrapper level-h2"><h2 id="page-types-and-element-types">Page types and element types</h2><a class="sl-anchor-link" href="#page-types-and-element-types"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “Page types and element types”</span></a></div>
82
+ <p>Every document type is one of two kinds, chosen when it is created and
83
+ fixed afterwards — <code dir="auto">kind</code> is absent from the update request schema
84
+ entirely, so there is no “change the kind” call to make:</p>
85
+
86
+
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+
104
+
105
+
106
+
107
+
108
+ <table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td><code dir="auto">page</code></td><td>literal <code dir="auto">"page"</code></td><td>Lives in the content tree: it has a <code dir="auto">path</code>, a position, and <code dir="auto">treeRules</code> governing where it may sit. This is the kind you create content with.</td><td>A <code dir="auto">Page</code>, <code dir="auto">Post</code>, or <code dir="auto">Author</code> type</td></tr><tr><td><code dir="auto">element</code></td><td>literal <code dir="auto">"element"</code></td><td>A shape of fields with no tree position — no path, no URL, no <code dir="auto">treeRules</code>. Intended for reusable blocks (a call-to-action, a testimonial, an SEO block) that should not clutter the tree.</td><td>An <code dir="auto">SEO block</code> type</td></tr></tbody></table>
109
+ <p>Element types can be defined today — fields, tabs, the same builder a
110
+ page type uses — but there is <strong>no authoring path for them yet</strong>: the
111
+ content service rejects an element type at any tree position including
112
+ the root (<code dir="auto">element-kind-not-placeable</code>), and the admin’s create menu only
113
+ ever offers page types. Defining one today reserves the shape for later;
114
+ it does not let you create content with it.</p>
115
+ <p>Reach for a page type whenever the content needs its own address in the
116
+ tree, which in v1 is every piece of content you will actually author.</p>
117
+ <div class="sl-heading-wrapper level-h2"><h2 id="the-properties-of-a-document-type">The properties of a document type</h2><a class="sl-anchor-link" href="#the-properties-of-a-document-type"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “The properties of a document type”</span></a></div>
118
+ <p>These are the properties <code dir="auto">DocumentTypeDefinitionSchema</code>
119
+ (<code dir="auto">packages/api-schemas/src/document-type.schema.ts</code>) defines — the shape of
120
+ one <strong>item</strong> in a <code dir="auto">GET /api/document-types</code> response, which itself wraps a
121
+ page of these as <code dir="auto">{ items, nextCursor }</code>:</p>
122
+
123
+
124
+
125
+
126
+
127
+
128
+
129
+
130
+
131
+
132
+
133
+
134
+
135
+
136
+
137
+
138
+
139
+
140
+
141
+
142
+
143
+
144
+
145
+
146
+
147
+
148
+
149
+
150
+
151
+
152
+
153
+
154
+
155
+
156
+
157
+
158
+
159
+
160
+
161
+
162
+
163
+
164
+
165
+
166
+
167
+
168
+
169
+
170
+
171
+
172
+
173
+
174
+
175
+
176
+
177
+
178
+
179
+
180
+
181
+ <table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td><code dir="auto">id</code></td><td>string (uuid)</td><td>Server-minted identity. Never sent on create — the service mints it.</td><td><code dir="auto">7a2f1c40-9b31-4d6a-8f0e-2c5b7d1a4e93</code></td></tr><tr><td><code dir="auto">name</code></td><td>string, non-empty</td><td>Display name in the admin and in the create menu. Must be unique within one plugin’s manifest; core does not otherwise enforce uniqueness.</td><td><code dir="auto">Page</code></td></tr><tr><td><code dir="auto">icon</code></td><td>string, may be empty</td><td>Optional icon name, chosen from the admin’s curated picker (kebab-case). The admin shows it in the content tree, the create form and the editor’s info tab; an empty value renders the default document icon. Stored and returned by the API (including the public <code dir="auto">/api/v1/document-types</code>).</td><td><code dir="auto">file-text</code></td></tr><tr><td><code dir="auto">kind</code></td><td><code dir="auto">"page"</code> | <code dir="auto">"element"</code></td><td>The discriminator for the two branches above. Immutable after creation.</td><td><code dir="auto">page</code></td></tr><tr><td><code dir="auto">fields</code></td><td>array of field definitions</td><td>The fields content of this type carries. Order is array order. May be empty.</td><td><code dir="auto">[{ "id": "title", … }]</code></td></tr><tr><td><code dir="auto">tabs</code></td><td>array of tab definitions, default <code dir="auto">[]</code></td><td>Editor tab groups. <code dir="auto">[]</code> renders one flat field list.</td><td><code dir="auto">[]</code></td></tr><tr><td><code dir="auto">treeRules</code></td><td>object, <strong><code dir="auto">page</code> kind only</strong></td><td>Where content of this type may sit. Required on <code dir="auto">page</code>, rejected on <code dir="auto">element</code> — both branches are strict, so a stray key is an error rather than being silently dropped.</td><td><code dir="auto">{ "allowedAtRoot": true, "allowedParentTypes": [], "allowedChildTypes": [] }</code></td></tr><tr><td><code dir="auto">defaultTemplateId</code></td><td>string (uuid), optional, <code dir="auto">page</code> kind only</td><td>Pointer to a template. Never set at create time — a template cannot exist before its document type does; set it afterwards with <code dir="auto">PATCH /api/document-types/:id/default-template</code>.</td><td><code dir="auto">3d9c81e2-5b47-4a10-9f6d-08c2a7b31f45</code></td></tr></tbody></table>
182
+ <div class="sl-heading-wrapper level-h2"><h2 id="fields">Fields</h2><a class="sl-anchor-link" href="#fields"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “Fields”</span></a></div>
183
+ <p>A field is one editable value on the type. Its <code dir="auto">id</code> is the key the value
184
+ is stored under in the content version’s <code dir="auto">data</code> object, which is why
185
+ renaming a field’s display <code dir="auto">name</code> later never orphans existing content —
186
+ only the <code dir="auto">id</code> matters to stored data, and it is the one thing you should
187
+ not change.</p>
188
+
189
+
190
+
191
+
192
+
193
+
194
+
195
+
196
+
197
+
198
+
199
+
200
+
201
+
202
+
203
+
204
+
205
+
206
+
207
+
208
+
209
+
210
+
211
+
212
+
213
+
214
+
215
+
216
+
217
+
218
+
219
+
220
+
221
+
222
+
223
+
224
+
225
+
226
+
227
+
228
+
229
+ <table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td><code dir="auto">id</code></td><td>string matching <code dir="auto">^[a-zA-Z][a-zA-Z0-9_]*$</code></td><td>The data key: starts with a letter, then letters, digits, and underscores. Must be unique within the type — a duplicate is a validation error naming the offending index, because two fields sharing a key would silently collide in stored data.</td><td><code dir="auto">title</code></td></tr><tr><td><code dir="auto">name</code></td><td>string, non-empty</td><td>The label shown above the input in the editor. Free to change at any time.</td><td><code dir="auto">Title</code></td></tr><tr><td><code dir="auto">datatypeId</code></td><td>string, non-empty</td><td>References a <strong>Data Type</strong> configuration id (see below), not an editor id directly. The route rejects the create or update if no data type row has this id.</td><td><code dir="auto">b1e00000-0000-4000-8000-000000000001</code></td></tr><tr><td><code dir="auto">validation</code></td><td>object, optional</td><td>Per-field rules — see the table below. Deliberately per field, not on the Data Type, so the same Data Type can be required on one type and optional on another.</td><td><code dir="auto">{ "required": true, "maxLength": 120 }</code></td></tr><tr><td><code dir="auto">tabId</code></td><td>string (uuid), optional</td><td>Which tab this field renders under. Must reference a tab declared in the same payload, or the request is rejected. Omit it and the field renders in the implicit default group.</td><td><code dir="auto">9c1e5b70-4a62-4f83-b0d1-7e2f6a48c531</code></td></tr></tbody></table>
230
+ <p>The validation object is <code dir="auto">FieldValidationSchema</code>. Every key in it is
231
+ optional, and the whole object may be omitted:</p>
232
+
233
+
234
+
235
+
236
+
237
+
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+
246
+
247
+
248
+
249
+
250
+
251
+
252
+
253
+
254
+
255
+
256
+
257
+
258
+
259
+
260
+
261
+
262
+
263
+
264
+
265
+
266
+
267
+
268
+
269
+
270
+
271
+
272
+
273
+
274
+
275
+
276
+
277
+
278
+
279
+ <table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td><code dir="auto">required</code></td><td>boolean</td><td>Rejects <code dir="auto">undefined</code>, <code dir="auto">null</code>, and <code dir="auto">""</code> on save. Deliberately does <strong>not</strong> reject <code dir="auto">false</code> or <code dir="auto">0</code> — those are real values for a boolean or number field, not emptiness.</td><td><code dir="auto">true</code></td></tr><tr><td><code dir="auto">minLength</code></td><td>integer ≥ 0</td><td>Minimum string length. Applied only when the stored value is a string. Must be ≤ <code dir="auto">maxLength</code>, or the document type itself fails validation.</td><td><code dir="auto">3</code></td></tr><tr><td><code dir="auto">maxLength</code></td><td>integer ≥ 0</td><td>Maximum string length. Distinct from the Text Data Type’s own <code dir="auto">maxLength</code> setting, which physically blocks typing in the browser; this one is a rule checked when content is saved.</td><td><code dir="auto">120</code></td></tr><tr><td><code dir="auto">min</code></td><td>number</td><td>Minimum numeric value. Applied only when the stored value is a number. Must be ≤ <code dir="auto">max</code>.</td><td><code dir="auto">0</code></td></tr><tr><td><code dir="auto">max</code></td><td>number</td><td>Maximum numeric value.</td><td><code dir="auto">100</code></td></tr><tr><td><code dir="auto">pattern</code></td><td>string, non-empty</td><td>A regular expression source (no flags) the value must match. An empty string is left to <code dir="auto">required</code> to judge; a pattern that fails to compile validates as a pass rather than bricking every save of existing content.</td><td><code dir="auto">^[a-z0-9-]+$</code></td></tr></tbody></table>
280
+ <p>These rules are enforced on the server when content is saved, not only in
281
+ the browser — see <a href="/docs/v1/using-velora/editing/">Content editing</a> for the
282
+ error body a failed save returns.</p>
283
+ <div class="sl-heading-wrapper level-h2"><h2 id="tabs">Tabs</h2><a class="sl-anchor-link" href="#tabs"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “Tabs”</span></a></div>
284
+ <p>Tabs group fields into the strip across the top of the content editor.
285
+ They are flat — one level of tabs, not tabs inside groups — and their
286
+ order is their position in the array.</p>
287
+
288
+
289
+
290
+
291
+
292
+
293
+
294
+
295
+
296
+
297
+
298
+
299
+
300
+
301
+
302
+
303
+
304
+
305
+
306
+
307
+
308
+
309
+
310
+ <table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td><code dir="auto">id</code></td><td>string (uuid)</td><td>Referenced by a field’s <code dir="auto">tabId</code>. Purely organizational: unlike a field id it is never a data key, so it is a generated uuid rather than a typed key.</td><td><code dir="auto">9c1e5b70-4a62-4f83-b0d1-7e2f6a48c531</code></td></tr><tr><td><code dir="auto">name</code></td><td>string, non-empty</td><td>The tab label in the editor.</td><td><code dir="auto">Content</code></td></tr></tbody></table>
311
+ <p>A type with <code dir="auto">tabs: []</code> still gets one tab in the editor, labelled
312
+ <strong>Content</strong>, so the tab strip is present on every item.</p>
313
+ <div class="sl-heading-wrapper level-h2"><h2 id="data-types-and-the-built-in-editors">Data types and the built-in editors</h2><a class="sl-anchor-link" href="#data-types-and-the-built-in-editors"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “Data types and the built-in editors”</span></a></div>
314
+ <p>A field does not reference an editor directly. It references a <strong>Data
315
+ Type</strong>: a named, admin-configured instance of a registered editor — “Text
316
+ — 200 char limit” is a configured instance of the Text editor. Configure
317
+ a Data Type once under <strong>Settings → Data Types</strong>, then reuse it across as
318
+ many fields and document types as you like.</p>
319
+
320
+
321
+
322
+
323
+
324
+
325
+
326
+
327
+
328
+
329
+
330
+
331
+
332
+
333
+
334
+
335
+
336
+
337
+
338
+
339
+
340
+
341
+
342
+
343
+
344
+
345
+
346
+
347
+
348
+
349
+
350
+
351
+
352
+
353
+
354
+ <table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td><code dir="auto">id</code></td><td>string (uuid)</td><td>The id a field’s <code dir="auto">datatypeId</code> points at.</td><td><code dir="auto">b1e00000-0000-4000-8000-000000000001</code></td></tr><tr><td><code dir="auto">name</code></td><td>string, non-empty</td><td>How this configuration appears when you pick a datatype in the document type builder.</td><td><code dir="auto">Text — 200 char limit</code></td></tr><tr><td><code dir="auto">editorId</code></td><td>string, non-empty</td><td>The registered editor this configures. <strong>Immutable after creation</strong> — swapping the editor under fields that already store values in the old editor’s shape would corrupt them, so the update schema has no <code dir="auto">editorId</code> key at all.</td><td><code dir="auto">com.velora.text</code></td></tr><tr><td><code dir="auto">settings</code></td><td>object, default <code dir="auto">{}</code></td><td>The editor’s own settings, validated against that editor’s settings schema.</td><td><code dir="auto">{ "maxLength": 200 }</code></td></tr></tbody></table>
355
+ <p>Six editors are built in, and each seeds a default Data Type
356
+ configuration on migration with a <strong>fixed, known id</strong> — so the builder
357
+ works out of the box and you can reference these ids directly without a
358
+ lookup. The Example column below is the seeded configuration id you would
359
+ put in a field’s <code dir="auto">datatypeId</code>:</p>
360
+
361
+
362
+
363
+
364
+
365
+
366
+
367
+
368
+
369
+
370
+
371
+
372
+
373
+
374
+
375
+
376
+
377
+
378
+
379
+
380
+
381
+
382
+
383
+
384
+
385
+
386
+
387
+
388
+
389
+
390
+
391
+
392
+
393
+
394
+
395
+
396
+
397
+
398
+
399
+
400
+
401
+
402
+
403
+
404
+
405
+
406
+
407
+ <table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td>Text</td><td><code dir="auto">com.velora.text</code>, stores a <code dir="auto">string</code></td><td>Single-line text. Settings: <code dir="auto">placeholder</code>, and <code dir="auto">maxLength</code>, which hard-blocks typing past that many characters in the browser.</td><td><code dir="auto">b1e00000-0000-4000-8000-000000000001</code></td></tr><tr><td>Number</td><td><code dir="auto">com.velora.number</code>, stores a <code dir="auto">number</code></td><td>A numeric input. Clearing the input stores <code dir="auto">null</code>, not <code dir="auto">0</code>. No settings.</td><td><code dir="auto">b1e00000-0000-4000-8000-000000000002</code></td></tr><tr><td>Boolean</td><td><code dir="auto">com.velora.boolean</code>, stores a <code dir="auto">boolean</code></td><td>A checkbox; renders as Yes/No when read-only. No settings.</td><td><code dir="auto">b1e00000-0000-4000-8000-000000000003</code></td></tr><tr><td>Date</td><td><code dir="auto">com.velora.date</code>, stores a <code dir="auto">string</code> (<code dir="auto">format: date</code>)</td><td>A native date input, storing the <code dir="auto">YYYY-MM-DD</code> string it produces. No settings.</td><td><code dir="auto">b1e00000-0000-4000-8000-000000000004</code></td></tr><tr><td>Image</td><td><code dir="auto">com.velora.image</code>, stores a <code dir="auto">string</code></td><td>A <strong>media reference id</strong>, not the file itself. The Media Library item is fetched separately by that id. Settings: <code dir="auto">acceptedTypes</code>, <code dir="auto">maxSizeBytes</code>.</td><td><code dir="auto">b1e00000-0000-4000-8000-000000000005</code></td></tr><tr><td>Rich Text</td><td><code dir="auto">com.velora.richtext</code>, stores an <code dir="auto">object</code></td><td>The rich-text editor’s native JSON document shape — a nested object, not an HTML string. No settings.</td><td><code dir="auto">b1e00000-0000-4000-8000-000000000006</code></td></tr></tbody></table>
408
+ <p>Only Text and Image validate their <code dir="auto">settings</code> object server-side today —
409
+ number, boolean, date, and rich text accept whatever <code dir="auto">settings</code> you send
410
+ because they have nothing to configure. These are distinct from a field’s
411
+ own <code dir="auto">validation.maxLength</code> (a save-time content rule, see the Fields
412
+ section above): a Data Type’s <code dir="auto">maxLength</code> physically blocks typing in the
413
+ browser instead.</p>
414
+
415
+
416
+
417
+
418
+
419
+
420
+
421
+
422
+
423
+
424
+
425
+
426
+
427
+
428
+
429
+
430
+
431
+
432
+
433
+
434
+
435
+
436
+
437
+
438
+
439
+
440
+
441
+
442
+
443
+
444
+
445
+
446
+
447
+
448
+
449
+ <table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td><code dir="auto">placeholder</code></td><td>string, optional</td><td>Placeholder text shown in an empty Text input.</td><td><code dir="auto">"Enter a title…"</code></td></tr><tr><td><code dir="auto">maxLength</code></td><td>integer ≥ 1, optional</td><td>Text editor only: hard character cap enforced in the browser input itself.</td><td><code dir="auto">200</code></td></tr><tr><td><code dir="auto">acceptedTypes</code></td><td>array of string, optional</td><td>Image editor only: MIME types the media picker offers for this field.</td><td><code dir="auto">["image/png", "image/jpeg"]</code></td></tr><tr><td><code dir="auto">maxSizeBytes</code></td><td>integer ≥ 1, optional</td><td>Image editor only: largest file the media picker accepts for this field.</td><td><code dir="auto">5242880</code></td></tr></tbody></table>
450
+ <p>New editors come from plugins: install a datatype plugin, then create Data
451
+ Type configurations of it exactly as you would for a built-in. Writing one
452
+ is covered in
453
+ <a href="https://docs.velora-cms.com/v1/building-plugins/datatype-plugin-contract/">Building plugins</a>.</p>
454
+ <div class="sl-heading-wrapper level-h2"><h2 id="tree-rules">Tree rules</h2><a class="sl-anchor-link" href="#tree-rules"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “Tree rules”</span></a></div>
455
+ <p><code dir="auto">treeRules</code> exist on <code dir="auto">page</code> types only, and they answer three questions:
456
+ may this type exist at the root of the tree, which parent types may it be
457
+ created under, and which types may be created under it.</p>
458
+
459
+
460
+
461
+
462
+
463
+
464
+
465
+
466
+
467
+
468
+
469
+
470
+
471
+
472
+
473
+
474
+
475
+
476
+
477
+
478
+
479
+
480
+
481
+
482
+
483
+
484
+
485
+
486
+
487
+ <table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td><code dir="auto">allowedAtRoot</code></td><td>boolean, required</td><td>Whether content of this type may be created with <code dir="auto">parentId: null</code>. <code dir="auto">false</code> means it only ever exists under a parent.</td><td><code dir="auto">true</code></td></tr><tr><td><code dir="auto">allowedParentTypes</code></td><td>array of document type ids, required</td><td>Which types this one may be created under. <strong>An empty array means “any page type”</strong>, not “none”.</td><td><code dir="auto">[]</code></td></tr><tr><td><code dir="auto">allowedChildTypes</code></td><td>array of document type ids, required</td><td>Which types may be created under content of this type. Empty likewise means any page type.</td><td><code dir="auto">["b8e4c0f1-2d63-4a95-8e07-31f9a5c2d648"]</code></td></tr></tbody></table>
488
+ <p>All three keys are required whenever <code dir="auto">treeRules</code> is present, and the whole
489
+ object is required when updating a <code dir="auto">page</code> type — the update schema cannot
490
+ see the stored <code dir="auto">kind</code>, so the route enforces the pairing itself: a <code dir="auto">page</code>
491
+ update without <code dir="auto">treeRules</code> is a <code dir="auto">400</code>, and an <code dir="auto">element</code> update carrying
492
+ <code dir="auto">treeRules</code> is also a <code dir="auto">400</code>.</p>
493
+ <p>The rules are enforced in two places that agree by construction. The
494
+ tree’s per-node create menu offers exactly the types the rules permit, and
495
+ the server re-checks the same rules when content is created — rejecting a
496
+ placement with one of <code dir="auto">not-allowed-at-root</code>, <code dir="auto">parent-forbids-child-type</code>,
497
+ or <code dir="auto">child-forbids-parent-type</code>. Creation is the only enforcement point
498
+ that exists, because there is no move or reparent endpoint in v1.</p>
499
+ <div class="sl-heading-wrapper level-h3"><h3 id="reserved-root-slugs">Reserved root slugs</h3><a class="sl-anchor-link" href="#reserved-root-slugs"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “Reserved root slugs”</span></a></div>
500
+ <p>A handful of root-level paths belong to the server itself, not to
501
+ content, because they are already routes: <code dir="auto">/velora</code> is the admin UI,
502
+ <code dir="auto">/api</code> is the whole API surface, <code dir="auto">/docs</code> is the bundled documentation, and
503
+ <code dir="auto">/plugin-assets</code> serves plugin-contributed static files. Creating a
504
+ root-level (<code dir="auto">parentId: null</code>) page whose first path segment is one of
505
+ these — regardless of case — is rejected on top of the tree-rules check
506
+ above, whether or not <code dir="auto">treeRules.allowedAtRoot</code> permits the type at the
507
+ root:</p>
508
+
509
+
510
+
511
+
512
+
513
+
514
+
515
+
516
+
517
+
518
+
519
+
520
+
521
+
522
+
523
+
524
+
525
+
526
+
527
+
528
+
529
+
530
+
531
+
532
+
533
+ <table><thead><tr><th>Reserved root slug</th><th>Namespace</th></tr></thead><tbody><tr><td><code dir="auto">velora</code></td><td>The admin SPA</td></tr><tr><td><code dir="auto">api</code></td><td>The whole API surface (<code dir="auto">/api/*</code>)</td></tr><tr><td><code dir="auto">docs</code></td><td>The bundled documentation</td></tr><tr><td><code dir="auto">plugin-assets</code></td><td>Plugin-contributed static files</td></tr></tbody></table>
534
+ <p>The rejection is a <code dir="auto">400</code> shaped like any other field validation failure —
535
+ see <a href="/docs/v1/using-velora/editing/">Content editing</a> for the full
536
+ <code dir="auto">fieldErrors</code> shape — with exactly one entry:
537
+ <code dir="auto">fieldErrors[0].fieldId</code> is <code dir="auto">"slug"</code> and <code dir="auto">fieldErrors[0].key</code> is
538
+ <code dir="auto">"contentCreate.errors.reservedSlug"</code>. The check only ever looks at a
539
+ root-level item’s first path segment, so a reserved word deeper in the
540
+ tree (<code dir="auto">/blog/api</code>, say) is unaffected.</p>
541
+ <div class="sl-heading-wrapper level-h2"><h2 id="locales">Locales</h2><a class="sl-anchor-link" href="#locales"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “Locales”</span></a></div>
542
+ <p>Locale is a property of a <em>content item</em>, not of a document type — one
543
+ type serves every language. Translations are separate content nodes
544
+ sharing a <code dir="auto">translationGroupId</code>, one of which carries
545
+ <code dir="auto">isDefaultLocale: true</code>. The admin’s create form always writes
546
+ <code dir="auto">locale: "en"</code> with <code dir="auto">isDefaultLocale: true</code> today; additional translations
547
+ are created over the API by posting another node with the same
548
+ <code dir="auto">translationGroupId</code>. Reading one is a query parameter away —
549
+ <code dir="auto">GET /api/content/:id?locale=fr&#x26;fallback=en</code>:</p>
550
+
551
+
552
+
553
+
554
+
555
+
556
+
557
+
558
+
559
+
560
+
561
+
562
+
563
+
564
+
565
+
566
+
567
+
568
+
569
+
570
+
571
+
572
+
573
+ <table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td><code dir="auto">locale</code></td><td>string, at least 2 characters, optional</td><td>The translation to fetch instead of this node’s own locale. Omit it and the node is returned as-is, with <code dir="auto">fallback</code> ignored entirely.</td><td><code dir="auto">fr</code></td></tr><tr><td><code dir="auto">fallback</code></td><td>string, at least 2 characters, optional</td><td>An explicit fallback locale to serve when <code dir="auto">locale</code> has no matching translation. Omit it and an unmatched <code dir="auto">locale</code> is a plain <code dir="auto">404</code> — explicit means explicit, there is no automatic default-locale fallback here.</td><td><code dir="auto">en</code></td></tr></tbody></table>
574
+ <p>See <a href="/docs/v1/using-velora/editing/">Content editing</a> for how the node this
575
+ route returns relates to its versions.</p>
576
+ <div class="sl-heading-wrapper level-h2"><h2 id="templates-are-inert-in-v1">Templates are inert in v1</h2><a class="sl-anchor-link" href="#templates-are-inert-in-v1"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “Templates are inert in v1”</span></a></div>
577
+ <p>A page type can point at a <code dir="auto">TemplateDefinition</code> — a named record tying a
578
+ template to a document type, with many templates able to target one type:</p>
579
+
580
+
581
+
582
+
583
+
584
+
585
+
586
+
587
+
588
+
589
+
590
+
591
+
592
+
593
+
594
+
595
+
596
+
597
+
598
+
599
+
600
+
601
+
602
+
603
+
604
+
605
+
606
+
607
+
608
+ <table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td><code dir="auto">id</code></td><td>string (uuid)</td><td>The template record’s identity; what <code dir="auto">defaultTemplateId</code> points at.</td><td><code dir="auto">3d9c81e2-5b47-4a10-9f6d-08c2a7b31f45</code></td></tr><tr><td><code dir="auto">name</code></td><td>string, non-empty</td><td>Display name in the document type editor’s Templates tab.</td><td><code dir="auto">Wide page</code></td></tr><tr><td><code dir="auto">documentTypeId</code></td><td>string (uuid)</td><td>The <code dir="auto">page</code> type this template targets.</td><td><code dir="auto">7a2f1c40-9b31-4d6a-8f0e-2c5b7d1a4e93</code></td></tr></tbody></table>
609
+ <p>In v1 that is all a template is: an inert data record. There is no
610
+ rendering engine consuming it, and no “preview the rendered page” button,
611
+ because Velora v1 is headless-first — you build the site with the
612
+ REST/GraphQL API and your own frontend. The default-template pointer is
613
+ backoffice-only: the public document type shape and the public templates
614
+ list both omit <code dir="auto">defaultTemplateId</code>, so a frontend can list a type’s
615
+ templates but cannot learn which one is marked default. Template rendering
616
+ is planned as a later release, not a v1 feature.</p>
617
+ <div class="sl-heading-wrapper level-h2"><h2 id="a-worked-example-a-page-type-in-the-admin">A worked example: a Page type in the admin</h2><a class="sl-anchor-link" href="#a-worked-example-a-page-type-in-the-admin"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “A worked example: a Page type in the admin”</span></a></div>
618
+ <p>The steps below create a <code dir="auto">Page</code> type with two fields, <code dir="auto">title</code> (Text) and
619
+ <code dir="auto">body</code> (Rich Text), allowed at the root of the tree.</p>
620
+ <ol>
621
+ <li>Go to <strong>Settings → Document Types</strong> and use the <strong>+</strong> button to open
622
+ <strong>New document type</strong>.</li>
623
+ <li>Fill in <strong>Name</strong> (<code dir="auto">Page</code>) and <strong>Icon</strong> (<code dir="auto">file-text</code>). Both are
624
+ required — the save button stays disabled until they are set.</li>
625
+ <li>Leave <strong>Kind</strong> on <strong>Page</strong>.</li>
626
+ <li>Select <strong>Add field</strong>. Set <strong>Key</strong> to <code dir="auto">title</code>, <strong>Label</strong> to <code dir="auto">Title</code>,
627
+ and <strong>Datatype</strong> to <strong>Text</strong>. Under <strong>Validation rules</strong>, tick
628
+ <strong>Required</strong> and set <strong>Max length</strong> to <code dir="auto">120</code>.</li>
629
+ <li>Select <strong>Add field</strong> again. Set <strong>Key</strong> to <code dir="auto">body</code>, <strong>Label</strong> to
630
+ <code dir="auto">Body</code>, and <strong>Datatype</strong> to <strong>Rich Text</strong>.</li>
631
+ <li>Leave <strong>Tabs</strong> empty — two fields do not need grouping, and the editor
632
+ renders them under a single <strong>Content</strong> tab.</li>
633
+ <li>Tick <strong>Allowed at root</strong>, and leave <strong>Allowed parent types</strong> and
634
+ <strong>Allowed child types</strong> empty (empty means any type).</li>
635
+ <li>Select <strong>Save</strong>.</li>
636
+ </ol>
637
+ <p>The type appears in the Settings tree, and the content tree’s <strong>+</strong> button
638
+ now offers <strong>Page</strong> at the root.</p>
639
+ <div class="sl-heading-wrapper level-h2"><h2 id="the-same-thing-over-the-admin-api">The same thing over the admin API</h2><a class="sl-anchor-link" href="#the-same-thing-over-the-admin-api"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “The same thing over the admin API”</span></a></div>
640
+ <p>Everything the builder does is an ordinary API call, which is how a
641
+ provisioning script or a migration would do it. The JSON in this section
642
+ is derived from the Zod schemas in <code dir="auto">packages/api-schemas</code> and the shapes
643
+ the route tests assert — it is not a captured transcript, so ids and
644
+ timestamps are illustrative.</p>
645
+ <p>Writes need a signed-in session token — any authenticated user, admin or
646
+ editor, since document type routes only require a session, not the admin
647
+ role — so sign in first:</p>
648
+ <div class="expressive-code"><link rel="stylesheet" href="/docs/_astro/ec.w36nc.css"><script type="module" src="/docs/_astro/ec.0vx5m.js"></script><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">curl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-s</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-X</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">POST</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">http://localhost:3000/api/auth/login</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#F78C6C;--1:#AA0982">\</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#82AAFF;--1:#3B61B0">-H</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#ECC48D;--1:#984E4D">Content-Type: application/json</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#F78C6C;--1:#AA0982">\</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#82AAFF;--1:#3B61B0">-d</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">'</span><span style="--0:#ECC48D;--1:#984E4D">{"email":"you@example.com","password":"correct-horse-8"}</span><span style="--0:#D9F5DD;--1:#111111">'</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="curl -s -X POST http://localhost:3000/api/auth/login \ -H &#x22;Content-Type: application/json&#x22; \ -d &#x27;{&#x22;email&#x22;:&#x22;you@example.com&#x22;,&#x22;password&#x22;:&#x22;correct-horse-8&#x22;}&#x27;"><div></div></button></div></figure></div>
649
+ <p>The response carries the access token — good for 15 minutes — and the
650
+ signed-in user; a refresh cookie rides along in the <code dir="auto">Set-Cookie</code> header:</p>
651
+ <div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="json"><code><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"accessToken"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"user"</span><span style="--0:#D6DEEB;--1:#403F53">: {</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"id"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">0b6d4e21-8c3a-4f57-9d02-6a1e5b7c8d90</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"email"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">you@example.com</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"name"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">Ada Lovelace</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"role"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">admin</span><span style="--0:#D9F5DD;--1:#111111">"</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="{ &#x22;accessToken&#x22;: &#x22;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…&#x22;, &#x22;user&#x22;: { &#x22;id&#x22;: &#x22;0b6d4e21-8c3a-4f57-9d02-6a1e5b7c8d90&#x22;, &#x22;email&#x22;: &#x22;you@example.com&#x22;, &#x22;name&#x22;: &#x22;Ada Lovelace&#x22;, &#x22;role&#x22;: &#x22;admin&#x22; }}"><div></div></button></div></figure></div>
652
+
653
+
654
+
655
+
656
+
657
+
658
+
659
+
660
+
661
+
662
+
663
+
664
+
665
+
666
+
667
+
668
+
669
+
670
+
671
+
672
+
673
+
674
+
675
+
676
+
677
+
678
+
679
+
680
+
681
+
682
+
683
+
684
+
685
+
686
+
687
+
688
+
689
+
690
+
691
+
692
+
693
+
694
+
695
+
696
+
697
+
698
+
699
+
700
+
701
+
702
+
703
+
704
+
705
+ <table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td><code dir="auto">email</code></td><td>string (email), required</td><td>Request only: the account’s email.</td><td><code dir="auto">you@example.com</code></td></tr><tr><td><code dir="auto">password</code></td><td>string, non-empty, required</td><td>Request only.</td><td><code dir="auto">correct-horse-8</code></td></tr><tr><td><code dir="auto">accessToken</code></td><td>string</td><td>Response only: a JWT, good for 15 minutes, sent as <code dir="auto">Authorization: Bearer &#x3C;accessToken></code> on every write below.</td><td><code dir="auto">eyJhbGciOiJIUzI1NiIs…</code></td></tr><tr><td><code dir="auto">user.id</code></td><td>string (uuid)</td><td>Response only: the signed-in user’s id.</td><td><code dir="auto">0b6d4e21-8c3a-4f57-9d02-6a1e5b7c8d90</code></td></tr><tr><td><code dir="auto">user.email</code></td><td>string (email)</td><td>Response only.</td><td><code dir="auto">you@example.com</code></td></tr><tr><td><code dir="auto">user.name</code></td><td>string or <code dir="auto">null</code></td><td>Response only.</td><td><code dir="auto">Ada Lovelace</code></td></tr><tr><td><code dir="auto">user.role</code></td><td><code dir="auto">"admin"</code> | <code dir="auto">"editor"</code></td><td>Response only: determines which routes accept this token — see the write note above.</td><td><code dir="auto">admin</code></td></tr></tbody></table>
706
+ <p>A refresh token rides along in an <code dir="auto">httpOnly</code> <code dir="auto">Set-Cookie</code>, never in the
707
+ response body — the JS client can never read it.</p>
708
+ <p>Next, find the Data Type ids the fields will reference. This read needs no
709
+ token — the document type and data type list routes are unauthenticated on
710
+ the admin origin:</p>
711
+ <div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">curl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-s</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#ECC48D;--1:#984E4D">http://localhost:3000/api/data-types?limit=2</span><span style="--0:#D9F5DD;--1:#111111">"</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="curl -s &#x22;http://localhost:3000/api/data-types?limit=2&#x22;"><div></div></button></div></figure></div>
712
+ <p>It answers a cursor-paginated page of configurations — on a fresh
713
+ instance, the six seeded built-ins, ordered by name:</p>
714
+ <div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="json"><code><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"items"</span><span style="--0:#D6DEEB;--1:#403F53">: [</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">{ </span><span style="--0:#7FDBCA;--1:#096E72">"id"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">b1e00000-0000-4000-8000-000000000003</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#7FDBCA;--1:#096E72">"name"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">Boolean</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#7FDBCA;--1:#096E72">"editorId"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">com.velora.boolean</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#7FDBCA;--1:#096E72">"settings"</span><span style="--0:#D6DEEB;--1:#403F53">: {} },</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">{ </span><span style="--0:#7FDBCA;--1:#096E72">"id"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">b1e00000-0000-4000-8000-000000000004</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#7FDBCA;--1:#096E72">"name"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">Date</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#7FDBCA;--1:#096E72">"editorId"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">com.velora.date</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#7FDBCA;--1:#096E72">"settings"</span><span style="--0:#D6DEEB;--1:#403F53">: {} }</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">],</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"nextCursor"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">b1e00000-0000-4000-8000-000000000004</span><span style="--0:#D9F5DD;--1:#111111">"</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="{ &#x22;items&#x22;: [ { &#x22;id&#x22;: &#x22;b1e00000-0000-4000-8000-000000000003&#x22;, &#x22;name&#x22;: &#x22;Boolean&#x22;, &#x22;editorId&#x22;: &#x22;com.velora.boolean&#x22;, &#x22;settings&#x22;: {} }, { &#x22;id&#x22;: &#x22;b1e00000-0000-4000-8000-000000000004&#x22;, &#x22;name&#x22;: &#x22;Date&#x22;, &#x22;editorId&#x22;: &#x22;com.velora.date&#x22;, &#x22;settings&#x22;: {} } ], &#x22;nextCursor&#x22;: &#x22;b1e00000-0000-4000-8000-000000000004&#x22;}"><div></div></button></div></figure></div>
715
+ <p>The same three pagination parameters appear on every list route in this
716
+ section:</p>
717
+
718
+
719
+
720
+
721
+
722
+
723
+
724
+
725
+
726
+
727
+
728
+
729
+
730
+
731
+
732
+
733
+
734
+
735
+
736
+
737
+
738
+
739
+
740
+
741
+
742
+
743
+
744
+
745
+
746
+ <table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td><code dir="auto">limit</code></td><td>integer 1–100, default <code dir="auto">50</code></td><td>Page size, coerced from the query string. Outside the range is a <code dir="auto">400</code>.</td><td><code dir="auto">2</code></td></tr><tr><td><code dir="auto">cursor</code></td><td>string (uuid), optional</td><td>The <code dir="auto">id</code> of the previous page’s last row. Omit it for the first page.</td><td><code dir="auto">b1e00000-0000-4000-8000-000000000004</code></td></tr><tr><td><code dir="auto">nextCursor</code></td><td>string (uuid) or <code dir="auto">null</code></td><td>Feed it back as <code dir="auto">cursor</code> for the next page. <code dir="auto">null</code> means this was the last page.</td><td><code dir="auto">null</code></td></tr></tbody></table>
747
+ <p>Now create the type. <code dir="auto">POST /api/document-types</code> takes the same shape as
748
+ the definition above <strong>minus <code dir="auto">id</code></strong> — the server mints that — and minus
749
+ <code dir="auto">defaultTemplateId</code>:</p>
750
+ <div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">curl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-s</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-X</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">POST</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">http://localhost:3000/api/document-types</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#F78C6C;--1:#AA0982">\</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#82AAFF;--1:#3B61B0">-H</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#ECC48D;--1:#984E4D">Content-Type: application/json</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#F78C6C;--1:#AA0982">\</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#82AAFF;--1:#3B61B0">-H</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#ECC48D;--1:#984E4D">Authorization: Bearer </span><span style="--0:#C5E478;--1:#3B61B0">$TOKEN</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#F78C6C;--1:#AA0982">\</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#82AAFF;--1:#3B61B0">-d</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">'</span><span style="--0:#ECC48D;--1:#984E4D">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">"name": "Page",</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">"icon": "file-text",</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">"kind": "page",</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">"fields": [</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">"id": "title",</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">"name": "Title",</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">"datatypeId": "b1e00000-0000-4000-8000-000000000001",</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">"validation": { "required": true, "maxLength": 120 }</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">},</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">"id": "body",</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">"name": "Body",</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">"datatypeId": "b1e00000-0000-4000-8000-000000000006"</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">}</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">],</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">"treeRules": { "allowedAtRoot": true, "allowedParentTypes": [], "allowedChildTypes": [] },</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">"tabs": []</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">}</span><span style="--0:#D9F5DD;--1:#111111">'</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="curl -s -X POST http://localhost:3000/api/document-types \ -H &#x22;Content-Type: application/json&#x22; \ -H &#x22;Authorization: Bearer $TOKEN&#x22; \ -d &#x27;{ &#x22;name&#x22;: &#x22;Page&#x22;, &#x22;icon&#x22;: &#x22;file-text&#x22;, &#x22;kind&#x22;: &#x22;page&#x22;, &#x22;fields&#x22;: [ { &#x22;id&#x22;: &#x22;title&#x22;, &#x22;name&#x22;: &#x22;Title&#x22;, &#x22;datatypeId&#x22;: &#x22;b1e00000-0000-4000-8000-000000000001&#x22;, &#x22;validation&#x22;: { &#x22;required&#x22;: true, &#x22;maxLength&#x22;: 120 } }, { &#x22;id&#x22;: &#x22;body&#x22;, &#x22;name&#x22;: &#x22;Body&#x22;, &#x22;datatypeId&#x22;: &#x22;b1e00000-0000-4000-8000-000000000006&#x22; } ], &#x22;treeRules&#x22;: { &#x22;allowedAtRoot&#x22;: true, &#x22;allowedParentTypes&#x22;: [], &#x22;allowedChildTypes&#x22;: [] }, &#x22;tabs&#x22;: [] }&#x27;"><div></div></button></div></figure></div>
751
+ <p>A success is <code dir="auto">201</code> carrying the stored definition, including the id the
752
+ server minted for it:</p>
753
+ <div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="json"><code><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"documentType"</span><span style="--0:#D6DEEB;--1:#403F53">: {</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"id"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">7a2f1c40-9b31-4d6a-8f0e-2c5b7d1a4e93</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"name"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">Page</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"icon"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">file-text</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"kind"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">page</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"fields"</span><span style="--0:#D6DEEB;--1:#403F53">: [</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"id"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">title</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"name"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">Title</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"datatypeId"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">b1e00000-0000-4000-8000-000000000001</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"validation"</span><span style="--0:#D6DEEB;--1:#403F53">: { </span><span style="--0:#7FDBCA;--1:#096E72">"required"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#FF6A83;--1:#A24848">true</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#7FDBCA;--1:#096E72">"maxLength"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#F78C6C;--1:#AA0982">120</span><span style="--0:#D6DEEB;--1:#403F53"> }</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">},</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">{ </span><span style="--0:#7FDBCA;--1:#096E72">"id"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">body</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#7FDBCA;--1:#096E72">"name"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">Body</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#7FDBCA;--1:#096E72">"datatypeId"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">b1e00000-0000-4000-8000-000000000006</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53"> }</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">],</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"treeRules"</span><span style="--0:#D6DEEB;--1:#403F53">: { </span><span style="--0:#7FDBCA;--1:#096E72">"allowedAtRoot"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#FF6A83;--1:#A24848">true</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#7FDBCA;--1:#096E72">"allowedParentTypes"</span><span style="--0:#D6DEEB;--1:#403F53">: [], </span><span style="--0:#7FDBCA;--1:#096E72">"allowedChildTypes"</span><span style="--0:#D6DEEB;--1:#403F53">: [] },</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"tabs"</span><span style="--0:#D6DEEB;--1:#403F53">: []</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="{ &#x22;documentType&#x22;: { &#x22;id&#x22;: &#x22;7a2f1c40-9b31-4d6a-8f0e-2c5b7d1a4e93&#x22;, &#x22;name&#x22;: &#x22;Page&#x22;, &#x22;icon&#x22;: &#x22;file-text&#x22;, &#x22;kind&#x22;: &#x22;page&#x22;, &#x22;fields&#x22;: [ { &#x22;id&#x22;: &#x22;title&#x22;, &#x22;name&#x22;: &#x22;Title&#x22;, &#x22;datatypeId&#x22;: &#x22;b1e00000-0000-4000-8000-000000000001&#x22;, &#x22;validation&#x22;: { &#x22;required&#x22;: true, &#x22;maxLength&#x22;: 120 } }, { &#x22;id&#x22;: &#x22;body&#x22;, &#x22;name&#x22;: &#x22;Body&#x22;, &#x22;datatypeId&#x22;: &#x22;b1e00000-0000-4000-8000-000000000006&#x22; } ], &#x22;treeRules&#x22;: { &#x22;allowedAtRoot&#x22;: true, &#x22;allowedParentTypes&#x22;: [], &#x22;allowedChildTypes&#x22;: [] }, &#x22;tabs&#x22;: [] }}"><div></div></button></div></figure></div>
754
+ <p>Keep the returned <code dir="auto">id</code> — it is what <code dir="auto">POST /api/content</code> needs as
755
+ <code dir="auto">documentTypeId</code> when you create your first item.</p>
756
+ <p>The rest of the resource follows the same shapes:</p>
757
+
758
+
759
+
760
+
761
+
762
+
763
+
764
+
765
+
766
+
767
+
768
+
769
+
770
+
771
+
772
+
773
+
774
+
775
+
776
+
777
+
778
+
779
+
780
+
781
+
782
+
783
+
784
+
785
+
786
+
787
+
788
+
789
+
790
+
791
+
792
+
793
+
794
+
795
+
796
+
797
+
798
+ <table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td><code dir="auto">GET /api/document-types</code></td><td>route</td><td>Cursor-paginated list, <code dir="auto">{ items, nextCursor }</code>. No token required.</td><td><code dir="auto">?limit=50</code></td></tr><tr><td><code dir="auto">POST /api/document-types</code></td><td>route</td><td>Create. <code dir="auto">201</code> with <code dir="auto">{ documentType }</code>; <code dir="auto">400</code> when a <code dir="auto">datatypeId</code> matches no data type row.</td><td>—</td></tr><tr><td><code dir="auto">PUT /api/document-types/:id</code></td><td>route</td><td>Full replace of the mutable definition: <code dir="auto">name</code>, <code dir="auto">icon</code>, <code dir="auto">fields</code>, <code dir="auto">tabs</code>, <code dir="auto">treeRules</code>. <code dir="auto">kind</code> and <code dir="auto">defaultTemplateId</code> are not accepted here.</td><td>—</td></tr><tr><td><code dir="auto">PATCH /api/document-types/:id/default-template</code></td><td>route</td><td>Sets or clears the default template pointer. Body <code dir="auto">{ "templateId": … }</code>, nullable.</td><td><code dir="auto">{"templateId":null}</code></td></tr><tr><td><code dir="auto">DELETE /api/document-types/:id</code></td><td>route</td><td>Deletes the type. <code dir="auto">409</code> if any live content or any template still references it — no cascade, by design.</td><td>—</td></tr></tbody></table>
799
+ <p>Deleting a type is the one place content is genuinely destroyed: the
800
+ delete first purges content of that type <strong>already sitting in the trash</strong>,
801
+ then removes the type row. Live content blocks the delete outright, with a
802
+ message naming how many items are in the way.</p>
803
+ <div class="sl-heading-wrapper level-h2"><h2 id="how-ids-are-minted">How ids are minted</h2><a class="sl-anchor-link" href="#how-ids-are-minted"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “How ids are minted”</span></a></div>
804
+ <p>Four different id conventions live in this model, and mixing them up is
805
+ the usual first mistake:</p>
806
+
807
+
808
+
809
+
810
+
811
+
812
+
813
+
814
+
815
+
816
+
817
+
818
+
819
+
820
+
821
+
822
+
823
+
824
+
825
+
826
+
827
+
828
+
829
+
830
+
831
+
832
+
833
+
834
+
835
+
836
+
837
+
838
+
839
+
840
+
841
+ <table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td>Document type <code dir="auto">id</code></td><td>uuid, minted by the server</td><td>Generated when the type is created, by the same service the admin route and the plugin installer both call. Never supplied by the caller.</td><td><code dir="auto">7a2f1c40-9b31-4d6a-8f0e-2c5b7d1a4e93</code></td></tr><tr><td>Field <code dir="auto">id</code></td><td>a key <strong>you</strong> choose</td><td>Human-typed and stable: it becomes the key in stored content data. Constrained to <code dir="auto">^[a-zA-Z][a-zA-Z0-9_]*$</code>, not a uuid.</td><td><code dir="auto">title</code></td></tr><tr><td>Tab <code dir="auto">id</code></td><td>uuid, minted by the builder</td><td>Generated in the browser when you add a tab. Never a data key — only a field’s <code dir="auto">tabId</code> refers to it.</td><td><code dir="auto">9c1e5b70-4a62-4f83-b0d1-7e2f6a48c531</code></td></tr><tr><td><code dir="auto">datatypeId</code></td><td>uuid of a Data Type configuration</td><td>Points at a row in the data types table, <em>not</em> at an editor id such as <code dir="auto">com.velora.text</code>. An id with no matching row is a <code dir="auto">400</code>.</td><td><code dir="auto">b1e00000-0000-4000-8000-000000000001</code></td></tr></tbody></table>
842
+ <p>Plugins can ship document types too. A <code dir="auto">section</code> or <code dir="auto">template</code> plugin
843
+ declares them in its manifest, and the installer creates them through the
844
+ exact same service the admin route uses — so ids are server-minted there
845
+ as well. Because a manifest cannot know an id that does not exist yet, a
846
+ plugin’s <code dir="auto">treeRules</code> reference its <strong>own</strong> types by <em>name</em>, and the
847
+ installer resolves those names to minted ids in a second pass. A name that
848
+ is not one of that plugin’s declared types fails the install and rolls
849
+ back every type it had created.</p>
850
+ <div class="sl-heading-wrapper level-h2"><h2 id="next">Next</h2><a class="sl-anchor-link" href="#next"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “Next”</span></a></div>
851
+ <ul>
852
+ <li><a href="/docs/v1/using-velora/editing/">Content editing</a> — creating items of these
853
+ types, and what Save, Publish, and rollback actually write.</li>
854
+ <li><a href="/docs/v1/using-velora/headless-api/">Headless API</a> — reading document types
855
+ and content back out, including the narrower public shape.</li>
856
+ <li><a href="/docs/v1/using-velora/media/">Media</a> — where the id an Image field stores
857
+ comes from.</li>
858
+ </ul></div><footer class="sl-flex astro-gjkl2a5w"><div class="meta sl-flex astro-gjkl2a5w"></div><div class="pagination-links print:hidden astro-3tj6ibx2" dir="ltr"><a href="/docs/v1/using-velora/installation/" rel="prev" class="astro-3tj6ibx2"><svg aria-hidden="true" class="astro-3tj6ibx2 astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M17 11H9.41l3.3-3.29a1.004 1.004 0 1 0-1.42-1.42l-5 5a1 1 0 0 0-.21.33 1 1 0 0 0 0 .76 1 1 0 0 0 .21.33l5 5a1.002 1.002 0 0 0 1.639-.325 1 1 0 0 0-.219-1.095L9.41 13H17a1 1 0 0 0 0-2Z"/></svg><span class="astro-3tj6ibx2">Previous<br class="astro-3tj6ibx2"><span class="link-title astro-3tj6ibx2">Installation</span></span></a><a href="/docs/v1/using-velora/editing/" rel="next" class="astro-3tj6ibx2"><svg aria-hidden="true" class="astro-3tj6ibx2 astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M17.92 11.62a1.001 1.001 0 0 0-.21-.33l-5-5a1.003 1.003 0 1 0-1.42 1.42l3.3 3.29H7a1 1 0 0 0 0 2h7.59l-3.3 3.29a1.002 1.002 0 0 0 .325 1.639 1 1 0 0 0 1.095-.219l5-5a1 1 0 0 0 .21-.33 1 1 0 0 0 0-.76Z"/></svg><span class="astro-3tj6ibx2">Next<br class="astro-3tj6ibx2"><span class="link-title astro-3tj6ibx2">Content editing</span></span></a></div></footer><div class="velora-bundle-stamp astro-llmj63sw">Velora 0.9.0 &middot; bundled documentation</div></div></div></main></div></div></div></div></body></html>