Admin UI & class catalogue
Stable .ck-* class names you can target for theming.
07 — Embed Admin UI
The partner-facing surface for managing their embed integration.
Lives at app.craftkit.dev/dashboard/{projectSlug}/embed/* once the project
is in embed-partner mode.
Information architecture
Project › Embed
├─ Overview (status + setup checklist + activity)
├─ Configuration
│ ├─ Keys (publishable, secret, signing)
│ ├─ Allowed Origins (CSP + CORS)
│ ├─ Catalogs (named, versioned, diffable)
│ ├─ Permission Presets (admin/editor/viewer roles)
│ └─ Embed Webhooks (separate from regular webhooks)
├─ Sessions (live console + session inspector)
├─ Usage & Billing
│ ├─ This period
│ ├─ Tenants (per-tenant breakdown)
│ ├─ Bill-back attribution
│ ├─ Plans & limits
│ └─ Invoices
└─ Test Embed (sandbox to play with the integration)Enablement (first visit)
Embed section is disabled by default. First visit shows:
┌──────────────────────────────────────────────────────────────────┐
│ Embed Craftkit in your product │
│ │
│ Let your customers build templates inside your own UI, with │
│ variables that map to your data model. │
│ │
│ ✦ Drop-in iframe builder │
│ ✦ Inject your variable catalog │
│ ✦ Webhook callbacks on publish │
│ ✦ Per-tenant branding & permissions │
│ │
│ Pricing: included on Scale and above. Counts toward your render │
│ quota. View pricing → │
│ │
│ [Enable Embed for this project] [Read the integration docs] │
└──────────────────────────────────────────────────────────────────┘Overview screen
- Setup checklist (5 items: keys, origins, catalog, webhook, first session)
- Live activity sparkline (sessions per hour, last 24h)
- Recent events feed (last 10)
- Quick-start code snippet (one curl)
- "Test embed" button (opens sandbox modal)
Configuration → Keys
Three keypairs:
| Type | Visibility | Use |
|---|---|---|
Publishable (ck_pk_*) |
Always shown | Browser-safe identifier |
Secret (ck_live_*) |
Shown ONCE on creation | Server-side only |
| Signing keys (Ed25519 keypair) | Public key always shown | Optional: partners verify JWTs themselves |
All keys rotatable. Secret keys: support multiple active (Production + Staging), each with name + last-used + revoke. Signing keys: 24h overlap on rotation.
Configuration → Allowed Origins
┌──────────────────────────────────────────────────────────────────┐
│ Allowed origins │
│ │
│ Origin Environment Added │
│ ────────────────────────────────────────────────────────────── │
│ https://app.kleesto.com Production Apr 3 ✕ │
│ https://staging.kleesto.com Staging Apr 3 ✕ │
│ http://localhost:3000 Development Apr 3 ✕ │
│ │
│ [+ Add origin] Wildcards: https://*.partner.com │
│ │
│ CSP snippet: │
│ frame-src https://embed.craftkit.dev; │
│ connect-src https://api.craftkit.dev; │
│ │
│ ⚠ Recent denials (last 24h) │
│ ◾ 2 attempts from https://evil.example.com — blocked │
│ [Add to allowlist] [Mark as expected] [Ignore] │
└──────────────────────────────────────────────────────────────────┘Configuration → Catalogs
Named, versioned catalogs. Tree editor with diff/remap tooling.
┌──────────────────────────────────────────────────────────────────┐
│ ▾ partner-default v3 · current │
│ 62 fields · 4 namespaces · 2 loops │
│ Used in 1,205 sessions this month │
│ [Edit] [View JSON] [Diff vs v2] [Test in sandbox] │
│ │
│ ▸ partner-eu v1 · current │
│ ▸ partner-default v2 · archived │
└──────────────────────────────────────────────────────────────────┘Edit view: tree of namespaces → fields, plus diff vs previous version showing add/remove/rename, with remap suggestions when keys change.
Configuration → Permission Presets
Named role bundles (admin, editor, viewer) with permission flags. Backend
sends permissions_preset: "editor" instead of 8 individual flags.
Sessions
Live console listing currently-active sessions + recent (last 7d) sessions.
Each row: session id, tenant, actor, template, duration, outcome icon.
Click a session → session inspector with full lifecycle:
- Created at + IP
- Token issued + exp
- Iframe loaded
- Editor ready
- Each variable inserted (with timestamp + source)
- Token refreshes
- Save/publish events
- Webhook deliveries (with response status)
- Errors
Plus [Revoke] button — invalidates all tokens immediately.
Test Embed sandbox
┌──────────────────────────────────────────────────────────────────┐
│ Test embed [✕] │
│ │
│ Mock a session and play with the embedded builder right here. │
│ │
│ Catalog: [ partner-default v3 ▾ ] │
│ Permissions: [ editor ▾ ] │
│ Tenant: [ Mock Tenant Ltd ▾ ] [+ custom] │
│ Branding: [ ⬛ #B7541C ] [Logo: …] [ Locale: en-GB ▾ ] │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ [ Embedded Craftkit builder mounted here, fully working ] │ │
│ │ [ Same iframe, same JWT flow, same catalog injection ] │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ Event log │
│ 14:32:11 craftkit.ready │
│ 14:32:24 craftkit.variable.inserted customer.name │
│ 14:32:38 craftkit.template.saved v1 │
│ 14:32:55 craftkit.template.published v1 │
│ │
│ [Open in new tab] [Copy session URL] [Reset] │
└──────────────────────────────────────────────────────────────────┘This single button removes 90% of integration friction.
Embed Webhooks (separate from regular webhooks)
Embed lifecycle events:
embed.session.created/refreshed/expired/revokedembed.template.saved/publishedembed.variable.inserted(high-volume, opt-in)embed.security.origin_blocked/permission_deniedembed.feedback.submittedembed.quota.exhausted
Retry: exponential backoff 1s → 1024s, max 8 attempts. Idempotency via
x-craftkit-event-id header (24h dedupe window).
Stable .ck-* class catalogue (styling contract)
Every class below is a versioned API surface. Partners may target it
via appearance.rules or via a stylesheetUrl that loads inside the
iframe. Adding a class is non-breaking; renaming, removing, or
restructuring its DOM nesting is a breaking change requiring a major
version bump of the embed.
Root + chrome
| Class | DOM context |
|---|---|
.ck-embed-root |
Outermost wrapper. Holds data-theme, data-density, lang. CSS variables live here. |
.ck-embed-shell |
Two-pane shell: catalog rail + canvas. |
.ck-embed-canvas-wrap |
Scrollable canvas pane. |
.ck-embed-banner |
Inline notification banner above the canvas. |
.ck-embed-banner-warn |
Warning variant of banner. |
.ck-embed-error |
Full-iframe error state. |
.ck-embed-error-card |
The card inside the error state. |
.ck-embed-error-icon |
Icon glyph in the error card. |
.ck-embed-error-ref |
Reference id displayed under the error message. |
Catalog rail (variable picker)
| Class | DOM context |
|---|---|
.ck-catalog-rail |
Left rail container. |
.ck-catalog-rail-title |
Section heading inside the rail. |
.ck-catalog-search |
Search input wrapper. |
.ck-catalog-namespace |
A grouping (e.g. "Customer", "Order"). |
.ck-catalog-namespace-header |
Header row of a namespace. |
.ck-catalog-namespace-count |
Badge with the field count. |
.ck-catalog-field |
Draggable field row. |
.ck-catalog-field-type |
Type-glyph badge (T/N/D/…). |
.ck-catalog-field-label |
Field label text. |
.ck-catalog-field-key |
Monospace key path next to the label. |
.ck-catalog-field-req |
Required marker. |
Toolbar / actions
| Class | DOM context |
|---|---|
.ck-toolbar |
Toolbar container. |
.ck-toolbar-section |
Group inside the toolbar. |
.ck-publish-button |
The publish CTA — shown unless layout.showPublishButton: false. |
.ck-save-hint |
"Saved …" hint next to publish. |
Block palette + canvas
| Class | DOM context |
|---|---|
.ck-block-palette |
Right-side block insert panel. |
.ck-block-palette-button |
A single insert button (Heading / Text / …). |
.ck-canvas |
Page canvas root. |
.ck-page |
A single page. |
.ck-section |
A page section. |
.ck-row |
A row inside a section. |
.ck-block |
Generic block wrapper. |
.ck-block-text, .ck-block-heading, .ck-block-paragraph, .ck-block-variable, .ck-block-image, .ck-block-signature |
Block-type variants. |
Inspector + form primitives
| Class | DOM context |
|---|---|
.ck-inspector |
Right rail when a block is selected. |
.ck-inspector-field |
A single form row in the inspector. |
.ck-input |
Text input / textarea base. |
.ck-input-invalid |
Invalid input state. |
.ck-button |
Button base — modifiers below. |
.ck-button-primary |
Filled brand button. |
.ck-button-secondary |
Outlined neutral button. |
.ck-button-ghost |
Hover-only button. |
.ck-prose |
Rich-text rendering target inside Tiptap blocks. |
State suffixes accepted by rules
:hover, :focus, :focus-visible, :active, :disabled,
::placeholder, --selected, --invalid. Anything else dropped.
See 11-styling.md for the full styling contract.
Last revised: 2026-05-03