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 }
    ]}
  ]
}
FieldTypeRule
seriesarrayrequired
series[].namestringrequired
series[].pointsarrayrequired
points[].xstring | numberrequired
points[].ynumberrequired

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" }
FieldTypeRule
valuenumberrequired
labelstringoptional
unitstringoptional
deltanumberoptional
notestringoptional; at most 120 characters
levelstringoptional; 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]]
}
FieldTypeRule
columnsstring[]required
rowsarray[]required; each row's length must equal columns.length

pie

Labeled slices with numeric values.

{
  "slices": [
    { "label": "Enterprise", "value": 60 },
    { "label": "SMB", "value": 40 }
  ]
}
FieldTypeRule
slicesarrayrequired
slices[].labelstringrequired
slices[].valuenumberrequired

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."
}
FieldTypeRule
levelstringrequired; ok, warn, crit or info
headlinestringrequired; non-empty, at most 140 characters
questionstringoptional
factsarrayoptional; at most 8
facts[].labelstringrequired
facts[].valuestring | numberrequired
facts[].formatstringoptional; plain, currency or percent (overrides the widget's number format)
facts[].unitstringoptional
notestringoptional

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."
}
FieldTypeRule
itemsarrayrequired; at most 50
items[].severitystringrequired; ok, warn, crit or info
items[].titlestringrequired; non-empty
items[].detailstringoptional
items[].actionstringoptional; shown as text, not a link
items[].valuestring | numberoptional
items[].formatstringoptional; plain, currency or percent
items[].unitstringoptional
items[].metastringoptional; small text under the figure
empty_textstringoptional

Presentation config

A widget's config changes only how snapshots are drawn, never what's stored. Number-related fields:

FieldApplies toEffect
number_formatall numeric typesplain, currency or percent
unitall numeric typessuffix for plain numbers; the currency prefix when nothing more specific is set
currency_symboltable, status, worklistprefix for currency-formatted values only, so counts beside them stay bare
localeall numeric typesdigit grouping, e.g. en-IN for 82,69,494. Blank uses the viewer's browser locale
column_formatstableper-column format, e.g. { "Value": "currency" }; other columns use number_format
stale_after_daysallwhen 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.