When a usage dashboard is entitled to show zero

Treating usage replies as evidence: a browser parser that refuses inconsistent numbers, and display states that separate an observed zero from unavailable, stale or partial.

November 20265 min readInterfacesPlatformsVerification

A near-empty bone surface layered with thin grey and ochre holds one short soot mark in the lower left quarter.
On this page
  1. Refusing what doesn’t add up
  2. What refusal looks like
  3. Right numbers, narrower questions
  4. What a browser cannot work out

The most misleading number a usage dashboard can show is a zero it has no right to. A zero reads as a finding: this application served no traffic, this account spent nothing. If what actually happened is that the reply arrived without some of its data, contradicted itself or left a renderer to fall back on a default, then the page is making a confident statement about something nobody observed, and the person reading it has no reason to doubt it.

An HTTP 200 does not help with this. It says that a server answered, and nothing about whether the answer is complete, whether its numbers agree with each other or even whether it concerns the application you asked about. In August 2026 I worked with agents to harden the browser dashboard for an application platform, building on imported UI foundations. I started treating usage replies as evidence to be examined rather than data to be displayed.

The dashboard was a React single-page app talking to a same-origin backend-for-frontend. Its role was to display usage evidence, while charging and request admission belonged to the platform. It had already given up the most tempting way to hide missing evidence: when a live API call failed, the failure surfaced instead of demo data taking its place.

Refusing what doesn’t add up

Generated types only get you part of the way. Most routes used TypeScript types generated from an OpenAPI document. Those types kept the client aligned with the expected response shape, but they could not validate the reply that arrived or tell the page that its error count exceeded its request count. Usage replies therefore went through a parser at the boundary.

The parser worked in layers. Each of the eight numeric fields had to be a number, a safe integer and non-negative. Then the fields had to agree: errors could not exceed requests, an average could not exceed a maximum, a report of zero requests had to be coherent with everything else in it and the observation window had to make sense.

The failure codes weren’t interchangeable. A malformed reply was marked retryable, because it can be a passing fault. An integer beyond JavaScript’s safe range wasn’t, because retrying cannot repair the browser’s representation limit. A reply that was perfectly formed but named a different application from the one requested failed with a non-retryable scope mismatch, which is the case where the page would otherwise be most confidently wrong.

The timestamps show how subtle this gets. The server sent the first and last observation times as 0 rather than omitting them. A careless client would show 1970, and a slightly less careless one would read 0 as no traffic. The parser accepted traffic with both at 0 as observed without timestamps and rejected a reply with only one of them set, and a render test asserted that the zero-traffic wording didn’t appear on the page in that case.

What refusal looks like

Each read on the page sat inside a boundary with three outcomes: a skeleton while loading, a state panel that was either recoverable or terminal, or the content. A bad usage reply therefore cost you the usage panel and nothing else. One of the tests shows the intended behaviour quite precisely. It fed the page a reply that had to be rejected, with fixture figures of 900,000,001 and 900,000,002, and asserted that “Usage unavailable” appeared, that neither figure appeared anywhere in the document, that “Try again” refetched usage and nothing else, and that a good reply then showed 12.

The assertion I care about most is that the numbers were absent from the document. It would have been easier to show the figures with a warning beside them, but a warning beside a wrong number still leaves a wrong number on the page, and people copy numbers into spreadsheets and messages and leave the warnings behind.

Right numbers, narrower questions

Some numbers are fine and still easy to misread, because the page around them implies a question they don’t answer. A row count beside a list of statements looks like the size of your history. The statements API returned no coverage, range, cursor or completeness information, so the client had no way of knowing whether it was, and the page labelled the count “Statement rows returned” with “Response size, not full-history coverage” underneath and said that the time-window panel was unavailable. Usage totals invite the same mistake, so they carried the line “These are cumulative folded totals, not a complete time series.”

Arithmetic in the browser followed the same rule of declining to guess. Charge and funding totals counted only the kinds of row the client recognised, so an adjustment landed in neither column rather than being assigned to one, a total that would overflow failed closed, and a credit value that couldn’t be formatted precisely read “Unavailable”.

A figure can also be correct and old, which is a different thing from being wrong, so the overview page kept it on screen and turned a stale flag and an observed-at time into a “Showing stale data” banner with a Refresh button. The CSV export was the hardest case, because a file leaves all the page’s wording behind. It carried raw microcredits, with RFC 4180 escaping and protection against formula cells, and it was described as not a complete export.

What a browser cannot work out

A browser can refuse a reply that contradicts itself, but it cannot detect that a perfectly coherent reply is missing three days of data. Internally consistent totals say nothing about records that never reached the aggregation step. The response has to carry evidence of how much of the requested period the total covers.

The next version I designed extended the distinction across the usage display. It depended on a matching server contract: decimal-string totals validated and held in BigInt would avoid JavaScript’s safe-integer ceiling, while coverage and watermark metadata would tell the client how much of the period the totals covered and how far processing had got. A contradictory reply would still show neither usage nor charge figures. The display states needed to express different claims:

  • Zero means the requested metric was observed across the whole period and its total was zero.
  • Unavailable means there is no figure that can be trusted, so no figure appears.
  • Stale means the figure reflects an earlier observation and has not been refreshed.
  • Partial means the figure covers only part of the period and cannot be presented as the complete total.
  • Rebuilding means the numbers are being recomputed, so neither a figure nor its absence tells you much yet.

Such a contract would let the page distinguish an observed zero from a figure it cannot justify.

Chris Chabot, November 2026