[data-reveal]{opacity:1!important;transform:none!important}
Extending
Builder

Extending

Drive the builder from another module through @antelopejs/interface-dms-builder, or from any client through its HTTP API.

The builder UI is one client of the builder engine. Other modules (the AI assistant, your own tooling) drive the same operations through the interface or the HTTP API.

The interface

@antelopejs/interface-dms-builder exposes every operation as an InterfaceFunction: import it and call it like any async function from a module in the same process.

src/seed-page.ts
import { AddBlock } from "@antelopejs/interface-dms-builder";

const result = await AddBlock({
  page: "/revenue",
  name: "total",
  type: "KpiCard",
  config: { title: "Total revenue", icon: "i-ph-currency-eur" },
});
if (result.ok) {
  console.log(result.data.path); // "/revenue#total"
}

Mutations return an OpResult: { ok: true, data, changes } with the files changed, or { ok: false, error }. Its reference covers:

TopicRead
Addressing pages, blocks, resources and queriesAddressing and structure
The block and DataType catalogCatalog
Creating and editing pagesPage operations
Tables with their APIResources
Data sourcesQueries, A chart without code
Drafts, preview and expectedVersionDrafts and preview
ErrorsResults and errors

The HTTP API

All routes sit under /api/builder and require an authenticated tenant owner. Operations answer with the interface's OpResult, so a client branches on ok rather than on the status code. The reads that only describe the project (/tables/usage, /page/impact, /page/access, /permissions) answer with their value.

MethodPathBody / queryOperation
GET/statusWhether the API is live, and the project root it edits
GET/catalogGetCatalog
GET / POST/pagesCreatePageInputListPages / CreatePage
GET/categoriesListCategories
GET / DELETE/page?ref=GetPageStructure / DeletePage
POST/page/configure{ page, patch, expectedVersion? }ConfigurePage (an option sent as null is removed)
POST/page/preview{ page, draft }PreviewLayout
POST/page/blocks{ page, draft, expectedVersion? }SetPageBlocks
GET/page/impact?ref=What deleting the page takes
GET/page/access?ref=&permission=Who reaches the page and each of its blocks
GET/permissionsThe permissions the DMS registered
GET / POST/resourcesCreateResourceInputListResources / CreateResource
GET/resource?ref=GetResourceStructure
POST/resource/configure{ resource, patch, expectedVersion? }ConfigureResource (the routes a table serves)
POST / PUT / DELETE/resource/fieldsfield input / { path, patch } / ?path=AddField / ConfigureField / RemoveField
GET/tables/usageThe pages reading each table, and its rows
GET/query-templatesListQueryTemplates
POST / PUT / DELETE/queries{ page, input } / { query, patch } / ?ref=AddQuery / ConfigureQuery / RemoveQuery
POST/refreshRefreshSourceIndex

Edit a whole page

An editor holds a draft rather than replaying edits: read the structure, edit locally, preview as often as needed, and write once.

Read

GET /api/builder/page?ref=/dashboard returns the PageStructure, with its version.

Preview

POST /api/builder/page/preview returns the serialized layout of the draft.

Write

POST /api/builder/page/blocks with expectedVersion writes the draft in one transaction and one typecheck. If the page changed underneath, it fails with stale instead of overwriting.

Make your blocks editable

The builder reads blocks back by shape: a hand-written declaration that matches the grammar of a DMS factory call is as editable as a generated one; one that drifts out of it is shown as opaque rather than rewritten. Custom blocks registered with RegisterBlockType appear in the catalog with a panel generated from their schema; give them .meta({ name, description, icon }) so the builder and the role editor name them.

Frontend module

frontend-vue/dms.frontend.ts registers the DmsBuilder components and the client plugin at priority 100, only when the backend's manifest entry carries dmsBuilder.enabled === true.