A leadership dashboard for two brands’ support: ticket volume and refunds, chargebacks and Visa’s VAMP rate, Trustpilot outcomes, team speed. Computed offline from Zendesk, Solidgate and Trustpilot, published as static JSON — and honest about which numbers are still moving.
RemoteJobsFinder + GlobalWork · Jul ’25 – Sep ’26
RemoteJobsFinder and GlobalWork are two job-search products with one support team on Zendesk, two payment providers, and a company-level target: keep Visa’s dispute-monitoring rate under 1%. The support numbers that fed that conversation were produced once a month by running a pipeline and pasting the output into Slack and a Google Doc. Nobody could compare periods, split by brand, or check a figure without asking.
I built Support Stats so leadership has one URL with the whole history: tickets and refunds, chargebacks and VAMP, Trustpilot, team speed — by brand and by provider, monthly or weekly.
The charts were the easy part. The work was making every number state what it counts, by which date, and whether it is final — and building the checks that keep it that way when nobody is looking.
A month’s numbers take minutes of API calls to compute and would burn the rate limit on every page view. So the page never computes anything. It reads finished JSON.
The Zendesk sets refresh themselves. The Solidgate and Trustpilot sets come from files with no API behind them, so they refresh when a new export is dropped in — and every card built on them says which export it is reading.
It started as seven tabs. A restructure folded them into four, one domain per tab, and moved every definition behind an (i) so the page shows numbers and the popover shows what they count.
RemoteJobsFinder + GlobalWork · Jul ’25 – Sep ’26
RemoteJobsFinder + GlobalWork · Jul ’25 – Sep ’26
over the 1% target · latest closed month · matches the hub
646 fraud alerts (TC40) + 38 chargebacks past RDR (TC15)
settled Visa orders · the denominator · hub figure
RemoteJobsFinder + GlobalWork · Jul ’25 – Sep ’26
RemoteJobsFinder + GlobalWork · Jul ’25 – Sep ’26
real customer wait
speed on follow-ups
handled tickets · created → solved
RemoteJobsFinder + GlobalWork · Jul ’25 – Sep ’26
A Zendesk ticket has a creation date, a last-updated date, and — buried in its audit log — the exact moment each tag was added. Which one you count by decides whether a closed month stays closed.
For eleven weeks the pipeline counted confirmed refunds by updated_at while every document called it the event date. I had asked for exact tag-add dating and approved it twice; the substitution was made by the AI assistant I was building with, both times, when the approved approach hit an API limit — and every later session repeated the label. Roughly one refund in seven landed in the wrong week, and closed months moved on recount.
The fix is mechanical, not a promise. Refund and policy tags are dated by the tag-add moment from each ticket’s audit log, the builders refuse any other basis for them, and CI recounts the last finished week from a second Zendesk API, ticket for ticket. One ticket’s difference fails the run, and a failed run publishes nothing.
Intake tags stay on created_at: they are applied at creation, and a creation date never changes.
Visa’s dispute-monitoring rate is the number the company is judged on. It is computed from the provider’s exports and cross-checked against the provider’s own figure — exact, every month.
Day 0 after month-end: September reads 1.41%, drawn dashed with a hollow point, and kept out of the headline KPI.
Almost all of the numerator is fraud alerts: the cardholder told their bank they did not recognise the charge. Those arrive in a couple of days and are then banked for good. The chargebacks are the small, moving part — RDR refunds most of them before they count, and it takes about two weeks to finish. So a fresh month always reads worse than it closes, and the dashboard draws it as provisional rather than pretending it is final or hiding it.
The consequence turns the intuition around. Preventing disputes barely moves the rate; RDR was already keeping them out. What moves it is not giving the customer a reason to call their bank in the first place: a renewal warning, a clear descriptor, a price they recognise. The refund policy, it turned out, is a cost and experience lever, not a VAMP lever.
Two populations that used to sit on one page with no denominators, now each with its own: disputers traced back to support, and refused requesters traced forward to the bank. A few percent of refusals come back as chargebacks; the mechanism behind them is the same every time.
Every disputer in the provider’s export, matched against Zendesk: a manual ticket in the 60 days before the dispute counts as prior contact.
The honest ceiling: more than four in five disputers never wrote to support at all. The refund policy is a real lever on the ones who did, and only on them.
The money-back policy gates on whether the product was used. The people who go to their bank are not disputing usage. They are disputing a renewal they did not see coming.
The median time from ticket creation to solved sat at exactly 48 hours for months, then read minutes. Nothing had sped up. The mix of tickets had changed.
The distribution has two humps. Provider notifications and spam close on the spot; everything else runs into a 48-hour auto-solve rule. The median is just whichever hump holds more than half the tickets, so when the auto-closed share crossed 50% it jumped from two days to minutes while the upper quartiles stayed put.
The dashboard now measures close time on handled tickets — solved with at least one agent reply — and keeps the all-tickets line as a faint reference with a caption saying what it actually measures. The same audit found the opposite case too: next-reply time really had fallen sharply, and a probe of the ticket histories showed why — follow-ups land while the agent who just replied is still online.
Which stretch you look at and how finely you slice it are two different controls. The period picker stays; one switch re-cuts the same months into ISO weeks.
Six points. Flip to Weeks to see the same six months as twenty-six.
The mean and the anomaly flags are computed in the builder and shipped in the JSON, never on the page. The Zendesk tabs get the switch; the Solidgate and Trustpilot tabs stay monthly because their inputs are monthly exports, and their cards carry a small “monthly” badge to say so.
Most tags the dashboard counts are set by automation, not by people. Reconstructed from the Zendesk trigger API and the Make scenario, because the numbers cannot be read without it.
Make only tags. Zendesk sends the reply and sets the status, through a trigger keyed on that one tag. So every automated reply is a non-human comment, and the team metrics exclude it by construction rather than by guessing.
It also settles what “refund requests” means. That tag is applied by a trigger only to the payment provider’s own notification emails — so the dashboard counts refunds requested through the provider’s flow, not customers writing in. A number that looked obvious turned out to need a sentence next to it.
Built in about three months alongside the support job, with an independent review of the analysis before any number went to leadership.