Skip to content

Pages

Overview

Pages lets you build hosted UI pages backed by data from your Centrali collections. Instead of building a custom frontend from scratch, you define pages declaratively — choosing a page type, configuring data sources, and publishing to a live URL that your users can access.

Pages are ideal for internal tools, customer portals, admin dashboards, and data entry workflows.

What can you build?

Pages supports four page types, each designed for a different use case:

Page Type Purpose Example
List Data tables with sorting, filtering, and row actions Order list, user directory, ticket queue
Detail Single-record view with related data Order detail with line items, customer profile
Form Data entry and editing New order form, support ticket submission
Dashboard Metrics, charts, and activity feeds Sales dashboard, ops overview, KPI tracker

How it works

Building a page follows a straightforward workflow:

graph LR
    A[Create Page] --> B[Configure Sections & Blocks]
    B --> C[Connect Data Sources]
    C --> D[Save Draft]
    D --> E[Publish]
    E --> F[Live at Runtime URL]

1. Create a page

Choose a page type and give it a name and slug. The slug becomes part of the runtime URL.

curl -X POST https://api.centrali.io/workspace/my-workspace/api/v1/pages \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Orders",
    "slug": "orders",
    "pageType": "list",
    "description": "All orders in the workspace"
  }'

2. Configure sections and blocks

A page definition is organized into sections, each containing one or more blocks. Sections control layout; blocks display data.

{
  "sections": [
    {
      "id": "section-1",
      "kind": "content",
      "title": "Orders",
      "layout": "single-column",
      "blocks": [
        {
          "id": "block-1",
          "blockType": "table",
          "dataSource": {
            "type": "structure",
            "ref": "COLLECTION_ID",
            "config": {
              "fields": ["orderNumber", "customer", "total", "status"],
              "sort": [{ "field": "createdAt", "direction": "desc" }],
              "limit": 50
            }
          }
        }
      ]
    }
  ],
  "theme": { "inherit": true }
}

3. Save and publish

Save your definition as a draft version, then publish it to make it live:

# Publish the latest draft
curl -X POST https://api.centrali.io/workspace/my-workspace/api/v1/pages/PAGE_ID/publish \
  -H "Authorization: Bearer YOUR_TOKEN"

4. Access at runtime

Published pages are accessible at your workspace's runtime URL:

https://pages.centrali.io/{workspace-slug}/{page-slug}

Section kinds and layouts

Sections organize your page into logical groups:

Section Kind Purpose
content Primary data display (tables, records, forms)
metrics KPI cards and stat groups
hero Page header with summary information
actions Action buttons and controls
supporting Secondary or contextual content

Each section has a layout that controls how blocks are arranged:

Layout Description
single-column Blocks stacked vertically (default)
two-column Blocks side by side
metric-strip Horizontal row of metric cards
stack Compact vertical stack

Block types

Blocks are the building units of a page. The block type determines what is rendered:

Data blocks

Block Type Used In Description
data-table List, Dashboard Sortable, filterable data table with columns
field-display Detail Read-only field group for single-record views
field-group Form Editable form fields with validation and submit actions
metric-card Dashboard Single KPI with aggregation (count, sum, avg)
stat-group Dashboard Row of related metrics, each with its own data source
chart Dashboard Bar, line, or pie chart with aggregation and groupBy
activity-feed Dashboard, Detail Chronological event list
kanban Dashboard Drag-and-drop board grouped by a status field
calendar Dashboard Date-based event calendar (month/week/day)
related-list Detail Table of related records from another collection
progress-indicator Dashboard Bar, ring, or badge showing progress toward a target
filter-bar Dashboard Date range and dropdown filters for the whole page

Content blocks

Block Type Used In Description
markdown Any Renders markdown content
static-html Any Renders static HTML (sanitized)
data-html Any HTML template with {{field}} placeholders — see below
image Any Image with URL, caption, alignment
video Any YouTube, Vimeo, or direct video embed
modal Any Overlay dialog triggered by an action — supports confirm/cancel flows

Data-bound HTML (data-html)

The data-html block lets you write custom HTML templates with {{fieldName}} placeholders that are replaced with real data at runtime. It supports two modes:

List mode (default): The template renders once per record. Write a single card template and it automatically repeats for each record in the collection. No iteration syntax needed.

<!-- This template renders once for EACH customer -->
<div style="display:inline-block; width:280px; margin:8px; padding:16px; border:1px solid #e5e7eb; border-radius:8px;">
  <h3>{{firstName}} {{lastName}}</h3>
  <p>{{email}}</p>
  <p>{{company}}</p>
</div>

Single mode (mode: "single" with a variable binding): The template renders once for a specific record, typically on detail pages.

<!-- This template renders once for the specific record -->
<div style="padding:16px; border:1px solid #e5e7eb; border-radius:8px;">
  <h2>{{firstName}} {{lastName}}</h2>
  <p>Email: {{email}}</p>
  <p>Company: {{company}} &middot; Tier: {{tier}}</p>
</div>

Auto-flattened records

Records are automatically flattened — use {{firstName}} directly, not {{data.firstName}}. System fields like {{createdAt}} and {{id}} are also available. Dot notation also works for nested access (e.g., {{data.address.city}}).

Placeholders that don't match any field render as empty strings — they won't display raw {{...}} syntax to the user.

All HTML output is sanitized with DOMPurify before rendering. Scripts, iframes, and event handlers are stripped. <style> tags and the target attribute on links are allowed.

Access control

Every page has an access policy that controls who can view it:

Access Mode Behavior
public Anyone with the URL can view the page
authenticated User must be signed in
role-gated User must be signed in and have a specific role
# Set a page to require authentication
curl -X PUT https://api.centrali.io/workspace/my-workspace/api/v1/pages/PAGE_ID/access-policy \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "accessMode": "role-gated",
    "requiredRoles": ["admin", "manager"]
  }'

Default access mode

Pages default to public if no access policy is configured. Always set an appropriate policy before publishing pages that display sensitive data.

When you have multiple pages in a workspace, you can group them into a sidebar navigation shell. Users see a persistent sidebar with links to each page.

To include a page in the navigation shell, set showInNavShell to true:

curl -X PATCH https://api.centrali.io/workspace/my-workspace/api/v1/pages/PAGE_ID \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "showInNavShell": true
  }'

You can customize the navigation order and grouping through the navigation configuration endpoint.

Dynamic data with variable bindings

Pages can use variable bindings to dynamically filter data at runtime. For example, a detail page can read a record ID from the URL, or a list page can show only records belonging to the currently signed-in user.

Variable bindings are configured on each block's data source. See Data Sources & Variable Binding for a complete guide.

Theming

Pages inherit a workspace-level theme by default. You can configure the primary color, accent color, logo, and font family:

curl -X PUT https://api.centrali.io/workspace/my-workspace/api/v1/pages/theme \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "config": {
      "primaryColor": "#1a56db",
      "accentColor": "#7c3aed",
      "logoUrl": "https://example.com/logo.svg",
      "fontFamily": "Inter"
    }
  }'

Versioning

Every save creates a new version. You can view version history and roll back to a previous version by publishing it again. Versions have three states:

State Meaning
draft Work in progress, not visible to end users
published Currently live at the runtime URL
superseded Previously published, replaced by a newer version

Next steps

  1. Configure data sources and variable bindings
  2. Set up page actions