# Data shape

> Each chart takes one data object in its own shape: the values, the scale they sit on and how labels read. The shape is fixed per type and published as a JSON Schema, so your editor and your agent can check it before anything renders.

## A waterfall, for example

cash-bridge.ts:

```
const cashBridge: WaterfallData = {
  max: 4,
  ticks: ["4M", "3M", "2M", "1M", "0"],
  fmt: (v) => v.toFixed(2) + "M",
  steps: [
    { label: "OPEN", v: 1.82, total: true },
    { label: "SALES", v: 1.24 },
    { label: "REFUND", v: -0.09 },
    { label: "CLOSE", v: 1.91, total: true },
  ],
}
```

| Field | Type | Meaning |
| --- | --- | --- |
| `max` | `number` | The top of the value scale. |
| `ticks` | `string[]` | The axis tick labels. |
| `fmt` | `(v: number) => string` | Formats a value for its label. |
| `steps` | `WaterfallStep[]` | One entry per step. |
| `stagger` | `"auto" \| boolean` | Step names wider than their column: every other one on a second row (the plot's base rises one row). "auto" |

| Field | Type | Meaning |
| --- | --- | --- |
| `label` | `string` | The name shown on the chart. |
| `v` | `number` | The value. |
| `total` | `boolean` | Draws the bar up from zero, as a total. |

## Check it

Every chart's docs page links its **JSON Schema** (usechartory.com/schemas/waterfall.json for this one), and the MCP server returns the same schema with **get_chart_spec**. A function such as **fmt** can't travel as JSON: the schema names it, your code passes it.

**Note: Data never goes missing.** When labels do not fit, charts stagger or stack them. No value is dropped at any size.

## Loading, empty and error

A widget takes a **state** in place of its chart and blocks while data is on its way or isn't there.

| State | Shows |
| --- | --- |
| Loading | Skeleton blocks in surface/skeleton, at the chart's own layout |
| Empty | The frame with a title, a short line and an optional action |
| Error | The frame with a short message, a retry and an optional second action |

- [Agents](https://usechartory.com/docs/agents): Agents read the same schema through MCP.
