Craftkitdocs

Variable catalog

Inject your data model so partner end-users see the right fields.

04 — Variable Catalog

The variable catalog is the innovation that lets partners inject their data model into the embedded builder without sharing schemas.

The catalog is also consumed by the form-fill embed (/embed/form) to drive auto-form generation: each CatalogField becomes one input, field labels and namespaces become section headings, and previewData doubles as the input's placeholder so the end-user sees the expected shape. When no catalog is attached to a session, the form embed falls back to the published version's variableManifest. See 12-form-route.md §6.

What a catalog is

A typed, namespaced directory of variables that the partner makes available to their tenants in the embed builder.

{
  "name": "partner-default",
  "version": 3,
  "allow_custom": false,
  "namespaces": [
    {
      "key": "customer",
      "label": "Customer",
      "icon": "user",
      "fields": [
        { "key": "customer.name",  "label": "Customer name",  "dataType": "text" },
        { "key": "customer.email", "label": "Customer email", "dataType": "email" }
      ]
    },
    {
      "key": "order",
      "label": "Order",
      "icon": "package",
      "fields": [
        { "key": "order.id",         "label": "Order ID",      "dataType": "text" },
        { "key": "order.placed_at",  "label": "Placed",        "dataType": "date", "format": "date:DD/MM/YYYY" },
        { "key": "order.total",      "label": "Total",         "dataType": "currency", "format": "currency:EUR" }
      ]
    }
  ],
  "loops": [
    {
      "key": "order.items",
      "label": "Items",
      "item_fields": [
        { "key": "name", "label": "Product name", "dataType": "text" },
        { "key": "qty",  "label": "Quantity",     "dataType": "number" },
        { "key": "price","label": "Unit price",   "dataType": "currency" }
      ]
    }
  ]
}

How catalogs flow

1. Partner defines a catalog in their admin (or builds it dynamically)
2. Partner POSTs catalog inline OR by name when minting a session
3. Craftkit stores the catalog under cat_… and references it in JWT
4. Embed page fetches catalog at load
5. Variable picker UI renders the catalog as a tree
6. User clicks/drags a field → variable node inserted with attrs from catalog
7. The chip's appearance includes the namespace breadcrumb

Inline vs Named catalogs

Inline (smaller catalogs, dynamic)

POST /v1/embed/sessions
{
  "tenant": {...},
  "actor": {...},
  "variable_catalog": { ... full inline ... }
}

Use when:

  • Catalog varies per tenant (e.g., custom fields)
  • Catalog is small (<50 fields)
  • Catalog is computed dynamically per session

Named (large catalogs, stable)

POST /v1/embed/sessions
{
  "tenant": {...},
  "actor": {...},
  "catalog_ref": "partner-default-v3"
}

Use when:

  • Catalog is stable across all tenants (or all in a tier)
  • Catalog is large (>50 fields)
  • Partner manages catalogs through admin UI

Catalog evolution

Catalogs are versioned, never mutated in place. Every change creates a new version (v1, v2, v3, …).

When the catalog changes, three scenarios:

Scenario Behavior
Field added Templates keep working; new field appears in picker on next session
Field renamed (label changed, key stable) Existing chips re-render with new label automatically
Field removed (key gone) Chips for missing keys render as [? customer.legacy_field] with a warning style; preview shows (missing); publishing is blocked until resolved
Field key changed Treated as remove + add. UI shows a one-click "Remap to new field" suggestion if labels match closely

The remap UX is critical for long-term data model evolution and is what separates a polished embed integration from a brittle one.

The catalog-aware variable picker

The picker has three layouts, all over the same data:

Layout A: Popover (toolbar trigger)

Popover with search + tree. Recently-used at top. Each row shows type glyph, label (middle), key (monospace, faded, right). Required fields show .

Layout B: Always-visible left rail (premium UX)

The killer move for embed mode. Catalog tree always visible on the left. Drag-to-insert + click-to-insert. Inside a loop, rail context-switches to that loop's item_fields.

Layout C: Slash menu (inline)

User types / → inline menu with both variables and formatting commands. Notion-style.

Field type catalog

Every field has a dataType. Supported types:

Type Renders as Example value
text inline text "John Doe"
longtext block text (paragraph)
number number 42
currency formatted currency "€42.00"
date formatted date "02/05/2026"
boolean "Yes" / "No" true
image (url)
url (url)
email "x@y.com"

Format strings (format: "currency:EUR", format: "date:DD/MM/YYYY") are optional helpers applied at render time.

Catalog namespacing convention

  • Top-level keys are namespaces: customer, order, booking
  • Dot-paths reference fields: customer.name, order.total
  • Loops use the same dot-path pattern: order.items
  • Inside a loop, item fields are unprefixed: name, qty, price
  • Compiled Handlebars: {{customer.name}}, {{#each order.items}}{{name}}{{/each}}

Last revised: 2026-05-02