Webapps
A webapp is a published, multi-page application assembled from dashboard cards and content blocks. Where a dashboard is a single canvas of cards for an authenticated audience, a webapp is the customer-facing surface: a small navigable app with its own pages, navigation, and token-gated public share links so people outside the platform can view it without a login. It is Honeyframe's "ship a SaaS app from your data" surface, modeled on Dataiku's code-webapps but built no-code from the same governed blocks the rest of the platform uses.
Webapps run on the shared block architecture — every block renders through the same renderer as a dashboard card, all SQL stays server-side, and PII masking applies on every path (including anonymous public viewers). No arbitrary server code runs; the "code" in a code-card is a governed read-only SELECT, never browser-supplied SQL.
Webapps vs dashboards
| Dashboard | Webapp | |
|---|---|---|
| Shape | One canvas of cards (with pages/tabs). | A multi-page app with its own nav. |
| Building block | Cards (SQL → chart). | Page blocks — card references, HTML, and code-cards. |
| Audience | Authenticated platform users with dashboard.read. | Internal users and anonymous viewers via a public share link. |
| Sharing | Per-link public tokens on a single dashboard. | Per-link public tokens on the whole app; share links resolve to a dedicated public route. |
| Authoring | Drag-and-drop dashboard editor. | No-code webapp builder (full-width canvas). |
A webapp can embed a card from a dashboard without that dashboard being public — the webapp itself is the access grant. Only cards a webapp actually references can be executed through it; there is no arbitrary-card read.
The webapp builder
The webapp builder is a full-width canvas (it is not boxed into the centered content width that other editors use). You assemble the app from pages, give each page a set of blocks, and define the app's navigation. Each page lays its blocks out on a 24-column content grid.
Authoring affordances:
- Add a block — the Add panel carries tabs for the block kinds below (card reference, HTML, code-card).
- Per-block editors in the right rail — each block kind has its own editor (e.g. the code-card editor exposes the SQL, chart type, and display-option form).
- Live preview — code-cards preview their draft SQL before you save via a Run preview action, so you see the chart while you iterate rather than only after a save.
- Filter-aware header chips — pages can carry header signals (a meta line and an alert line) driven by SQL, and a filter bar whose options can be static or sourced from a
SELECT(a dynamic branch/segment selector). Blocks opt into receiving the page filters.
Responsive content grid
In the viewer, the page content grid collapses to a single full-width column below 767px (blocks flow in source order, so array order is reading order), matching the shell nav's breakpoint where it collapses to a drawer. The reflow is presentational only — the authored desktop layout is unchanged.
Blocks & card types
A page's content is an ordered array of blocks. Each block has a kind; a block with no explicit kind is treated as a card reference for back-compat, so older configs parse unchanged.
| Block kind | What it is |
|---|---|
page | A page in the app — a titled container holding an ordered list of blocks. |
card_ref | A reference to an existing dashboard card. Renders that card inside the app; the source dashboard need not be public. |
html_block | A custom HTML block. Two modes: sanitized (DOMPurify, no JS — the default) and sandboxed (rendered in a no-same-origin iframe, so JS can run but is fenced off from cookies, tokens, and the API). |
code_card | A governed code-card: a read-only SELECT whose result renders as a chart. The SQL is the server-side source of truth and is never sent from the browser; it runs under a single-statement read-only guard. |
nav_item | A navigation entry — a label and a path that link to a page in the app's nav. |
form | A data-capture form — typed fields validated server-side, submissions stored under a form key. |
leaderboard | A no-code scoreboard backed by the app's server-side scores table. |
agent | An embedded AI assistant drawer bound to a published agent. |
Sandboxed HTML blocks bypass PII masking by design (they render arbitrary markup), so the builder marks them with a "Custom HTML · not PII-governed" badge.
Code-card types
Code-cards render through the same DashboardCardChart renderer as dashboard cards and accept the same card_config / display_options shape. The builder exposes a registry-driven display-options form (legend, number format, sort, percentage, color scale, pie size, etc.) plus a guarded Advanced config (JSON) editor for type-specific column mappings (a malformed edit is rejected rather than blanking a valid config). The code-card type list includes histogram, choropleth, dot_strip, scorecard_heatmap, stat_funnel_plot, spc_control_chart, geo_scatter, and process_map.
process_map (v0.1.8)
process_map renders an identity / relationship / process graph from a governed SQL edge-list — a result set of transitions between nodes. It shipped first as a dashboard card type in v0.1.7 and became a no-code code-card in the webapp builder in v0.1.8, so an author can drop a relationship graph onto any app page from a governed SELECT without writing code.
The renderer is a single SVG (rounded-rect nodes with fill proportional to volume, cubic edges with arrowheads and weight labels) using a dependency-free layered layout. Its display options include show_edge_labels, edge_weight (count / percent / avg_hours), min_edge_count (a noise filter — dropped edges surface in the footer rather than vanishing silently), layout_direction (TB / LR), and show_node_counts. It accepts either a raw event log or a pre-aggregated edge-list and auto-detects which it was given.
This is the no-code path for the "interactive relationship graph" use case. For a fully custom interactive graph, a sandboxed html_block is the escape hatch — at the cost of bypassing masking, so embed only already-masked data.
Forms
A form block captures input. Fields are typed (text, number, select, date, and Indonesian NIK with format validation) and validated server-side; a submission is stored under a form_key discriminator and the submit endpoints (POST /api/(public/)webapps/{key}/forms/{form_id}/submit) are rate-limited. Forms can be authored inline or emitted by the vibe-code assistant.
Leaderboards & games
A leaderboard block renders a server-backed scoreboard (honeyframe.webapp_scores, org-isolated). Sandboxed HTML blocks that host a game submit and read scores through the window.hf runtime (hf.submitScore / hf.leaderboard) and can persist per-player state (hf.saveState / hf.loadState, parent-bridged, capped). Engine-based games (Phaser / Three.js / Pixi) need a vetted CDN — see Sandbox tiers.
Embedded assistant
An agent block mounts an AI assistant drawer bound to a published agent (chrome.assistant controls its accent color, icon, and title). The assistant is auto-grounded on the app's own dashboard cards, so it answers about the data on screen. All the agent guardrails and PII masking apply, including for anonymous public viewers.
The window.hf runtime
A sandboxed html_block talks to the app through a small in-iframe runtime, window.hf, over postMessage — never direct API access. The surface:
| Call | Purpose |
|---|---|
hf.onData(cb) | Receive the block's resolved data (hf.rows / hf.columns / hf.params). |
hf.setFilter(...) / hf.setFilters(...) | Push a page filter (opt-in via emits_filters; sanitized + parameterized). |
hf.navigate(pageKey) | Navigate to another page in the app. |
hf.onError(cb) | Surface a runtime error. |
hf.submitScore(...) / hf.leaderboard(...) | Write and read the app's scores table. |
hf.saveState(...) / hf.loadState(...) | Parent-bridged per-player state (256 KB cap, namespaced). |
GET /api/webapps/capabilities is a self-describing endpoint listing the block kinds, the sandbox contract, and this runtime surface, so tooling can introspect what an app supports.
Sandbox tiers
HTML blocks run at one of two trust levels, plus an opt-in CDN allowance:
- Sanitized (default) — DOMPurify, no JavaScript. PII-masked like any card.
- Sandboxed — a no-same-origin
iframewith an injected CSP (default-src/connect-src 'none'); JavaScript runs but is fenced from cookies, tokens, and the API. Not PII-governed. allow_cdn— opt-in loosening of the sandbox CSP to a vetted allow-list (jsDelivr, unpkg, cdnjs, esm.sh) so a game engine can load; the iframe stays opaque-origin.
Governed code-block SQL runs under an 8-second statement_timeout and a 5,000-row cap (a truncated flag surfaces when hit). Config writes are bounded (100 KB per HTML block, 2 MB per app config).
Theming
An app carries a theme (AppTheme) that skins the whole surface:
- Type —
fontFamily,fontScale, andfontUrl(Inter ships self-hosted; no CDN required). - Layout density —
sidebarWidth,borderRadius,contentPadding,assistantWidth,assistantPosition.
Edit the theme in the builder, or describe the look you want and let the assistant apply it (POST /api/webapps/{key}/ai-theme; GET /api/webapps/theme-options, PUT /api/webapps/{key}/theme). The theme registry is the safety floor — the AI can only write recognized keys, never arbitrary CSS.
Vibe-code (AI editing)
You can edit a whole app in natural language. POST /api/webapps/{key}/ai-edit returns a preview only — it never auto-saves — and is capped (≤ 50 pages, ≤ 100 blocks per page). It is multi-turn (carries a sanitized history) and data-aware (real tables, columns, and dashboard cards feed the prompt), so a request like "add a KPI row for last month's signups" produces blocks wired to real data. Version helpers cover versions, rollback, and diff_versions.
Code webapps
For a full-code app, the code webapp experience offers Standard / Shiny / Dash / Bokeh starters, a Monaco split editor, a sandboxed live preview, and Settings for backend, scalability, and security. This is the Dataiku code-webapps parity path — reach for it when the no-code blocks above aren't enough.
Any app can run chromeless / fullscreen (chrome.fullscreen, or ?fullscreen=1 / ?embed on the URL) for a wall display or an embed.
Publishing & share links
Webapps are published assets. Publishing is opt-in — an app stays a draft until you publish it. Publish and unpublish are both reversible.
Sharing works exactly like dashboard public links, scoped to the whole app:
- Open the Share modal in the builder to mint a public link. The raw token is shown once on creation — copy it then; the list view never re-displays the plaintext (tokens are hashed at rest).
- Each link carries an optional label, an optional expiry, and view tracking (count + status). Revoke is immediate, idempotent, and isolated per app.
- A share link points at a dedicated anonymous route. The public URL carries the token as a query parameter (
/public/apps/{key}?token=...); the viewer drives the PII-masked public endpoints with no platform login. - A valid share token is the access grant even when the app's global public flag is off; PII masking still applies to token viewers, and expired or revoked tokens return a uniform 404.
Page info (titles, header signals, nav) is editable from the builder, so you can adjust copy and navigation without re-publishing the underlying cards.
Building from code
The Python SDK (honeyframeapi) can publish and build webapps end to end. The quickest path publishes an existing dashboard as a webapp and mints a share link:
webapp = client.publish_webapp(...)
link = webapp.create_public_link()
print(webapp.public_url(link.token)) # shareable URL
webapp.revoke_public_link(link.token)
To assemble a multi-page app from blocks (including a process_map code-card) and create it in one call, use the page and block builders:
from honeyframeapi import page, card_ref, html_block, code_card, nav_item
webapp = client.create_webapp(
name="Ops console",
pages=[
page("Overview", blocks=[
html_block("<h1>Operations</h1>"),
card_ref(dashboard_id="...", card_id="..."),
]),
],
nav=[nav_item("Overview", "/overview")],
publish=True, # opt-in; draft by default
)
A Webapp handle also supports incremental edits — update, publish / unpublish, add_page, set_nav, and the share-link helpers above. See the SDK reference for the full signatures: SDK → Dashboards and webapps. For card authoring concepts, types, and card_config shapes that code-cards reuse, see Dashboards.