Blog

How Kommio's reports are counted: rollups, rebuilds and why the numbers are consistent

Why a report tile and the list of conversations behind it always agree: hourly rollups written with every change, one rule for every number, medians instead of averages, and rebuilds that run on their own after an upgrade.

Kommio team · · Updated

A support lead opens Reports on Monday, sees 1,712 resolved conversations last month, and clicks the number. If the list that opens has 1,698 rows, the report is dead: nobody will trust any tile on it again. Most of the design of Kommio’s reporting is in service of that one click, so this post explains how the numbers are made and why they stay consistent, from the rollups to the rebuild that runs after an upgrade.

Nothing is counted at request time

The Reports page never scans your conversations for a window of days. Every change in a workspace, whether a conversation is created, assigned, resolved, reopened, replied to or rated, is also recorded as an observation in hourly rollups, by the same background listener that feeds webhooks and automations, within moments of the change. A report for September reads thirty days of hourly rows and adds them up. That is why the page answers in under 200 ms at the size of our largest workspace, and in a fraction of a second at ten times that, and why it does not slow down as history grows.

There are three sets of rollup rows. One is per dimension: the whole workspace, each inbox, each team, each agent, each tag. One is per inbox, team and agent at once, so that “the Billing inbox, grouped by agent” or “Dana in the Billing inbox” is one row per hour and metric rather than a scan; the filters on the page combine because of it. And one remembers, for every counted observation, which conversation it was: the row the click-through reads.

One rule per number

Every number has one definition of what it counts and when, and the same definition is used by the tile, the chart, the breakdown, the heatmap, the scheduled email and the API. A few that matter:

  • Created counts a conversation at its creation. Resolved counts the conversations that are resolved now, once each, at their current resolution: a reopen takes the count back, and a later resolution counts it again. So Resolved is never more than the conversations that are resolved today, and the list behind it shows exactly those.
  • First response is the delay of the first reply from a person, counted at the reply, under the agent who sent it. Resolution is creation to the current resolution.
  • Assigned counts a conversation once per agent, at the latest time they were given it.
  • Everything that goes with a resolution (one-touch, replies per resolution, a missed website chat, a repeat contact, an SLA target met or breached) is counted with that resolution, in its hour and dimensions, and a reopen takes all of it back together.

Each observation is recorded under the inbox, team, agent and tags the conversation had at that moment, not the ones it has now. A conversation moved between inboxes last week was created in the old one and resolved in the new one, and the two inboxes’ reports say so.

Medians and p90, not averages

First response and resolution times are shown as a median and a p90. An average of response times is dominated by the one email that waited a week; a median tells you what a typical customer experienced, and the p90 tells you how bad the tail is without hiding it. The two come from a 40-bucket logarithmic histogram kept in the rollups, which makes them accurate to about ±20%: plenty to see a trend, and the price of never scanning the conversations.

The Wall clock / Business hours switch changes the same tiles to durations that count only the open time between two moments, in the inbox’s own hours or else the workspace’s, with holidays, in that schedule’s zone. The duration is computed when the observation is counted, so changing your hours later affects what is counted afterwards, or what a rebuild recounts.

Why the click-through matches

Every number that counts conversations opens the inbox on exactly the conversations behind it: a tile, a day on the chart, a heatmap cell, a cell of the breakdown. The list is not a second query that tries to reproduce the number’s filters. It reads the third set of rollup rows, the one that remembers the conversation behind each observation with the hour and dimensions it was counted under, so the list’s total is the number’s count by construction. The list shows a “From Reports” chip naming the number, and the same list is on the API, with a total in the response.

There is one honest exception. The counts agree as long as you may still see the conversations: one that moved out of your visibility since, or was deleted, is counted but not listed. Reports follow each person’s conversation visibility, so a manager whose role sees only their teams sees only their teams’ figures, applied to the team each observation was counted under.

Numbers that do not count conversations, such as Messages or satisfaction responses, open the conversations they were counted in; their list total is at most the number, since a conversation with three replies is listed once.

Time zones

A report covers whole local days in one zone: the one you pick, else the workspace’s, else UTC, and the page says which. Days and the heatmap’s weekday and hour are bucketed from the hourly rollups in that zone, so a manager in Lisbon and one in Tokyo each get their own days from the same rows. A workspace whose zone sits at a half or quarter hour (India, Nepal, parts of Australia) records its rollups at that offset, so its days are exact too.

Rebuilds: when the rules change

Sometimes the definition of a number changes, or a new number is added that older history can supply: combined filters needed the per-inbox-team-agent rows, the click-through needed the per-conversation rows, the quality KPIs needed one-touch and repeat-contact facts. When a release ships one of those, its migration marks every workspace as needing a rebuild, and a background job queues one rebuild per workspace at the lowest priority, at start and every five minutes, without anyone pressing anything. The page shows a notice while it runs and keeps answering from the old rows; the numbers refresh when it finishes.

A rebuild recomputes all three sets of rows from the stored conversations in one transaction: about 84 seconds at 50,000 conversations, about ten minutes at ten times that. It applies today’s rules to the whole history, and keeps the few things that cannot be recounted because they were never stored anywhere else, such as who held what at a given hour. A failed rebuild stays on the notice with a “try again”; it is never retried silently. An administrator can also start one from the page or the API, and the Chatwoot importer rebuilds the hours each run changed, so an imported history reports correctly the same day.

What this adds up to

Reports you can click on, that answer fast at any size, that show the same number in the app, in the scheduled email and on the API, and that fix themselves after an upgrade. Report data is kept for 400 days, so a report covers about thirteen months; the conversations themselves are never deleted by it. The full definitions, the filters and the API parameters are in the Reports guide.