Widget Types
The widget's type is the contract. Every push sends a full data payload, validated on ingest
against the type's schema before it's stored. There are eight types. Any field not listed is
rejected implicitly — send only what's below.
Three types share one severity vocabulary: ok, warn, crit, info. A status card's
level, a worklist item's severity and a kpi's optional level all use it, and each is drawn
with a shape as well as a colour.
line / bar / area
The same shape: named series of (x, y) points. x is a string or number; y is a number.
{
"series": [
{ "name": "revenue", "points": [
{ "x": "2026-01", "y": 120 },
{ "x": "2026-02", "y": 145 }
]}
]
}
| Field | Type | Rule |
|---|---|---|
series | array | required |
series[].name | string | required |
series[].points | array | required |
points[].x | string | number | required |
points[].y | number | required |
kpi
A single number, optionally labeled, with an optional note under it and an optional severity.
{ "value": 145000, "label": "MRR", "unit": "$", "delta": 12000, "note": "Up from August", "level": "ok" }
| Field | Type | Rule |
|---|---|---|
value | number | required |
label | string | optional |
unit | string | optional |
delta | number | optional |
note | string | optional; at most 120 characters |
level | string | optional; ok, warn, crit or info |
table
Columns plus rows. Every row must have exactly as many cells as there are columns.
{
"columns": ["Month", "Revenue"],
"rows": [["Jan", 120], ["Feb", 145]]
}
| Field | Type | Rule |
|---|---|---|
columns | string[] | required |
rows | array[] | required; each row's length must equal columns.length |
pie
Labeled slices with numeric values.
{
"slices": [
{ "label": "Enterprise", "value": 60 },
{ "label": "SMB", "value": 40 }
]
}
| Field | Type | Rule |
|---|---|---|
slices | array | required |
slices[].label | string | required |
slices[].value | number | required |
status
A verdict card: a question, a one-line answer, a severity, and a few facts behind it. The pusher decides the level; Snapshots only draws it.
{
"level": "crit",
"question": "Are the books complete?",
"headline": "No. 80% of bills are still drafts.",
"facts": [
{ "label": "Bills in the books", "value": "9 of 45" },
{ "label": "Draft value", "value": 8269494, "format": "currency" },
{ "label": "Oldest draft", "value": 188, "unit": "days" }
],
"note": "Drafts count for nothing in the books until opened."
}
| Field | Type | Rule |
|---|---|---|
level | string | required; ok, warn, crit or info |
headline | string | required; non-empty, at most 140 characters |
question | string | optional |
facts | array | optional; at most 8 |
facts[].label | string | required |
facts[].value | string | number | required |
facts[].format | string | optional; plain, currency or percent (overrides the widget's number format) |
facts[].unit | string | optional |
note | string | optional |
worklist
An ordered queue of things to do. Items render in the order you send them, each with a severity
stripe, a title, an optional detail line, the action to take and a figure. An empty items array
is valid and renders as good news ("Nothing needs attention."), not as missing data.
{
"items": [
{ "severity": "crit", "title": "TDS deducted, not deposited",
"detail": "Booked on 4 bills; every due date has passed.",
"action": "Deposit and record the challan payment",
"value": 93900, "format": "currency", "meta": "oldest due 30 Apr" }
],
"empty_text": "Nothing needs attention."
}
| Field | Type | Rule |
|---|---|---|
items | array | required; at most 50 |
items[].severity | string | required; ok, warn, crit or info |
items[].title | string | required; non-empty |
items[].detail | string | optional |
items[].action | string | optional; shown as text, not a link |
items[].value | string | number | optional |
items[].format | string | optional; plain, currency or percent |
items[].unit | string | optional |
items[].meta | string | optional; small text under the figure |
empty_text | string | optional |
Presentation config
A widget's config changes only how snapshots are drawn, never what's stored. Number-related fields:
| Field | Applies to | Effect |
|---|---|---|
number_format | all numeric types | plain, currency or percent |
unit | all numeric types | suffix for plain numbers; the currency prefix when nothing more specific is set |
currency_symbol | table, status, worklist | prefix for currency-formatted values only, so counts beside them stay bare |
locale | all numeric types | digit grouping, e.g. en-IN for 82,69,494. Blank uses the viewer's browser locale |
column_formats | table | per-column format, e.g. { "Value": "currency" }; other columns use number_format |
stale_after_days | all | when the freshness stamp turns stale |
A payload that violates any rule is rejected with 422 VALIDATION_ERROR, and every violation is
listed in the response's details array — see Error Codes.