Builder Guide
Binding Contracts
Use the binding catalog and live checklist/contacts page contract to connect React template blocks to bounded, authorized server data and actions.
A binding contract connects a React template block to an installed, bounded server operation. The page stores a public bindingKey and an optional dataKey; it stores no SQL, database route, tenant identity, credential or authorization rule.
The binding catalog is an authoring allowlist. It tells Puck which installed keys and child data paths a builder may select. It does not grant access to records or actions.
Binding catalog
The current local Puck catalog is developer-kit/local-puck/binding-catalog.json. Each entry gives the stable binding name and the bounded child documents that blocks may select with dataKey.
| Binding key | Purpose | Available data keys |
|---|---|---|
work-orders.presentation | Work Orders presentation | metrics, byStatus, recent, trend, table, timeline |
checklists-signoffs.presentation | Checklists & Signoffs presentation | tasks |
contacts.presentation | Contacts presentation | table |
company-news.presentation | Company News presentation | table, feed, metrics |
Copy the complete binding catalog
{
"schemaVersion": 1,
"bindings": [
{
"bindingKey": "work-orders.presentation",
"title": "Work Orders presentation",
"dataKeys": ["metrics", "byStatus", "recent", "trend", "table", "timeline"]
},
{
"bindingKey": "checklists-signoffs.presentation",
"title": "Checklists & Signoffs presentation",
"dataKeys": ["tasks"]
},
{
"bindingKey": "contacts.presentation",
"title": "Contacts presentation",
"dataKeys": ["table"]
},
{
"bindingKey": "company-news.presentation",
"title": "Company News presentation",
"dataKeys": ["table", "feed", "metrics"]
}
]
}Several blocks can reuse one binding. For example, Work Orders can render metrics, a status distribution, recent records, a trend, a grid and a timeline from one authorized presentation document. Reuse avoids issuing a broad record query for every visual block.
Live checklist and Contacts page contract
The shipped pages/live-checklist-contacts.json example composes two independent live contracts. The checklist task list uses checklists-signoffs.presentation with dataKey: tasks. The Contacts grid uses contacts.presentation with dataKey: table.
Copy the complete live checklist and Contacts page JSON
{
"blocks": [
{
"_id": "live-actions-header",
"_type": "core.page-header",
"props": {
"title": "Live checklist and contact actions",
"lead": "These components receive only the records and actions authorized for the signed-in user. Checklist toggles use optimistic concurrency; contact actions refetch their scoped presentation after each write."
}
},
{
"_id": "live-actions-layout",
"_type": "core.container",
"props": {
"layout": "grid",
"gridMode": "twelve-column",
"gap": "normal"
},
"children": [
{
"_id": "live-actions-checklist-card",
"_type": "core.card",
"props": {
"title": "Checklist work",
"description": "Items outside the user's DataRole and Location scope are never sent to the browser.",
"responsive": {
"desktop": { "span": 5 },
"tablet": { "span": 12 },
"mobile": { "span": 12 }
}
},
"children": [
{
"_id": "live-actions-checklist",
"_type": "core.task-list",
"bindingKey": "checklists-signoffs.presentation",
"dataKey": "tasks",
"props": {}
}
]
},
{
"_id": "live-actions-contacts-card",
"_type": "core.card",
"props": {
"title": "Recent contacts",
"description": "The grid is bounded to 25 visible contacts and each row declares only its currently valid favorite action.",
"responsive": {
"desktop": { "span": 7 },
"tablet": { "span": 12 },
"mobile": { "span": 12 }
}
},
"children": [
{
"_id": "live-actions-contacts",
"_type": "core.presentation-grid",
"bindingKey": "contacts.presentation",
"dataKey": "table",
"props": {}
}
]
}
]
}
]
}The layout and presentation remain editable. Changing card widths, titles, responsive spans or component styling does not change the server contract. Changing a bindingKey or dataKey must still reference an installed catalog entry and pass server validation.
How the runtime resolves a binding
- The page renderer collects unique binding keys from visible, registered blocks.
- The native runtime requests
GET /api/runtime/data/<bindingKey>under the current same-origin user session. Headless applications can resolve 1–24 keys withPOST /v1/apps/{saasAppId}/runtime/bindingsand a delegated-user credential. - The server resolves the tenant from the real host and requires the authenticated SaaS app, AppAccount and user context.
- The registered binding chooses a reviewed stored procedure. That procedure applies tenant, DataRole, Location, record visibility, soft-delete and action-entitlement rules before returning JSON.
- The React block receives a typed loading/data/error state and selects only its configured
dataKey.
A native binding response has this envelope:
{
"contractVersion": 1,
"bindingKey": "checklists-signoffs.presentation",
"data": {
"tasks": {
"presentationVersion": 1,
"kind": "task-list",
"items": []
}
}
}
A page definition is presentation, not data authority. Never replace a binding with browser SQL, a connector secret, an arbitrary stored-procedure name or a client-supplied SaaS/AppAccount/User identifier.
Live actions, revisions and exact refresh
Bindings are read contracts. A checklist toggle or Contacts favorite uses the separate Lego action broker at POST /api/runtime/lego/actions. The rendered row receives only server-issued record, field and action identifiers that are currently available to that user.
{
"contractVersion": 1,
"pageKey": "LiveChecklistContacts",
"blockId": "live-actions-checklist",
"actionId": "server-issued-action-guid",
"recordId": "server-issued-record-guid",
"expectedRevision": 7,
"values": {
"checked": true
}
}
- The browser sends the saved page key, block ID, server-issued action ID, record ID, expected revision when required, and validated input values.
- The broker reloads the saved page and catalog, confirms that the block declares the action, then calls the typed module service.
- The endpoint and stored procedure independently re-authorize the operation. A displayed action ID is user-interface intent, not proof of permission.
- A successful result names the exact binding that became stale. React reloads only that binding with
?refresh=true. - A stale checklist revision returns HTTP 409. The optimistic state is removed, the current binding is fetched again, and the user can retry against the current revision.
Use these contracts in a template
- Open the page in hosted Puck or the local Puck editor.
- Select a data-aware block such as
core.task-listorcore.presentation-grid. - Choose its
bindingKeyfrom the binding catalog. - Choose a
dataKeydeclared by that catalog entry. - Save and validate the draft, then publish it through the normal version-fenced workflow.
- Exercise loading, empty, populated, forbidden, stale-write and successful-mutation states with a real route-resolved disposable tenant.
An unknown binding name does not create an API or query. Adding a binding is a separate reviewed application contract with a stable key, approved stored procedure, mandatory server context, bounded output schema, cache policy and authorization tests.
Continue with the local Puck binding editor, interactive API presentations, and the headless application kit.
Capability review: 2026-09-14. For exact current technical availability, use the generated API Map and first-class module inventory.