E EMBAN / Docs

Widgets

Widgets are the atoms of every Emban dashboard. Each widget is a saved configuration that says what to query, how to render it, and where it lives on the 12-column grid. The builder exposes 19 chart variants across 6 top-level kinds, and most of the work of building a dashboard is picking the right kind for the question you're answering.

Kind vs. chart. A widget has a kind (which family of visualization) and a chart (the specific variant). Kind decides which query shape is allowed; chart decides how that shape is drawn. You can swap chart without re-authoring the query.

The six kinds

kpi Single-number metrics One value with optional comparison. Variants: stat (big number + delta), progress (value vs. target as a bar), gauge (arc with optional zone bands).
timeseries Trend over time Value per time bucket on an x-axis. Variants: line, area, bar, stacked_bar (needs a group-by), dual_axis (two metrics on separate y-axes), waterfall (cumulative delta).
breakdown Split by one dimension Metric sliced by a group-by. Variants: horizontal_bar, donut, list, bar, funnel, treemap, radar, scatter (two aggregates + optional size dimension).
table Tabular data Rows of values. Variants: table (ranked rows with share and change), pivot (2D matrix from two group-bys).
heatmap Activity patterns Events bucketed by hour-of-day × day-of-week. Great for "when do users use this" questions. One variant: heatmap.
text Headers and annotations Not a chart — renders markdown into the grid. Use for section dividers, context notes, or links back to your product. One variant: text.

The query model

Every widget (except text) has a query block that describes the data to fetch. The fields that matter vary by kind:

FieldUsed byMeaning
event_nameAllWhich event to aggregate (e.g. api.call). Required.
metricAllHow to aggregate: count, count_unique, count_unique_approx, sum, avg, p95, p99.
numeric_propsum / avg / p95 / p99Which numeric prop to aggregate. Required when metric isn't count.
group_bybreakdown, table, pivot, stacked_barString prop to split on. Required for breakdown kinds.
periodAllLookback window. Default 30d. Accepts 1h, 24h, 7d, 30d, etc.
granularitytimeseriesBucket size. hour, day, week, month.
comparekpi, breakdown, tableprevious_period or previous_year — attaches previous_value + delta_pct for scalar widgets.
limitbreakdown, tableCap on rows returned. Default varies by chart; typical 10–20.
second_metric, second_numeric_propdual_axisSecond aggregate drawn on the right-hand y-axis.
y_metric, y_numeric_prop, size_metric, size_numeric_propscatterSecond (y-axis) and optional third (size) aggregates. The primary metric is x.
group_by2pivotSecond group-by for the matrix columns.
dimension_filtersAllMap of {string_prop: exact_value} baked into this widget (in addition to dashboard-level filters).

The viz model

The viz block decides rendering. The same query can be shown as multiple chart variants by swapping viz.chart:

FieldMeaning
chartSpecific variant within the kind (e.g. line within timeseries).
formatNumber formatting: number, compact_number (1.2M / 42k), percent.
unitSuffix label: ms, cents, usd, percent, tokens, bytes.
colorAccent color for the primary series (hex). Defaults to the dashboard theme's accent.
targetTarget value for progress and gauge (also drawn as a reference line on timeseries).
reference_linesArray of {value, label?, color?, style?} drawn as horizontal markers on line/area/bar/stacked_bar. Style is solid, dashed (default), or dotted.
zonesArray of {min, max, color} coloring bands on a gauge's arc (e.g. 0–0.6 green, 0.6–0.85 amber, 0.85+ red). Values are fractions of target.
markdownRaw markdown body for kind=text widgets. Supports headings, bold/italic, lists, links.

Two widget-level flags affect both query and viz:

The layout grid

Dashboards use a 12-column responsive grid with 72px row height and 12px gutters. Every widget's layout is {x, y, w, h} in grid units:

{
  "id": "w_api_volume",
  "kind": "timeseries",
  "title": "API calls per day",
  "query": {"event_name": "api.call", "metric": "count", "granularity": "day", "period": "30d"},
  "viz":   {"chart": "area", "color": "#6d9edb", "reference_lines": [{"value": 50000, "label": "SLO floor"}]},
  "layout": {"x": 0, "y": 0, "w": 6, "h": 4}
}

The draft canvas lets you drag and resize interactively — the inspector panel writes these coordinates as you move things. Widgets can overlap while you arrange, but collisions are resolved at save time so the stored layout is stable.

The inspector

The right-hand pane on every widget edit screen is the inspector. It has four tabs, each editing a subset of the widget config:

Data Query shape Event name, metric, numeric prop, group-by, period, granularity, compare mode, limit. The dataset picker on the left lets you see what columns exist on your events.
Visualization Chart variant & formatting Pick chart, choose format and unit, pin a color, add reference lines or gauge zones. The preview re-renders on every change.
Target & delta Goals & direction Set a target for progress/gauge widgets. Flip invert_delta if rising-is-bad. For dual-axis, pick the second metric.
Embed Customer-facing behavior Toggle hidden or locked on customer embeds, and attach filter_bindings so the widget updates on cross-filter clicks. Requires Growth plan or above.

Design patterns

Pattern 1 KPIs at the top, trends in the middle, tables at the bottom The eye travels top-to-bottom. Lead with 3–5 KPI cards so the customer sees "what's the number" instantly. Follow with a trend or two. End with the detail table — most users never scroll to it, and that's fine.
Pattern 2 One chart, one question Dual-axis is tempting but often confusing. If you need two metrics correlated, use two adjacent widgets with the same period — your eye does the correlation and the chart stays readable.
Pattern 3 Invert delta for bad-is-up Latency, error rate, cost, failed logins: a rising value is bad news, so invert_delta=true keeps the ↑ red / ↓ green convention intuitive. The single most common source of "my dashboard looks wrong" tickets.
Pattern 4 Breakdowns need a limit Unbounded breakdown widgets are unreadable at more than 10–15 slices. Set limit explicitly and add an "Other" rollup if the long tail matters. Donut charts especially — a donut with 40 slices is a pie-chart crime.
Pattern 5 Reference lines for SLOs Put your latency SLO, revenue target, or user-count goal as a reference_line on the trend chart. The line labeled "SLO 500 ms" does more work than any KPI card alone — it puts the value in context.
Pattern 6 Text widgets for onboarding, not decoration A text widget at the top that says "This dashboard shows the last 30 days of your API usage. Click a bar to drill into a day" saves the customer a support ticket. A text widget that just says "Welcome!" wastes a row.

A full widget config

{
  "id": "w_model_breakdown",
  "kind": "breakdown",
  "title": "Tokens by model (30d)",
  "invert_delta": false,
  "query": {
    "event_name": "api.call",
    "metric": "sum",
    "numeric_prop": "tokens",
    "group_by": "model",
    "period": "30d",
    "compare": "previous_period",
    "limit": 10,
    "dimension_filters": {"status": "success"}
  },
  "viz": {
    "chart": "donut",
    "format": "compact_number",
    "unit": "tokens",
    "color": "#6d9edb"
  },
  "layout": {"x": 6, "y": 4, "w": 6, "h": 6}
}
Related: Dashboards for how widgets compose into a published surface, Events for the data model widgets query against, Permissions for hidden/locked/filter_bindings on customer-facing embeds, and Query Builder to cross-check a widget's numbers against raw SQL.