River
Y CombinatorBacked by Y Combinator
FREE TEMPLATE

Support Ticket Export Template

Four documents and four sheets that recover the ticket fields a CSV export drops, then prove which reported numbers were wrong.

Free download  ·  No account needed

Restated Figures

Redbourne, Q2 2026, CSV report pulled 14 July

One quarter of a veterinary practice management help desk, reconciled against a JSON export of the same range and the ticket API. Illustrative figures for a fictional company.

The figure that passed review

Untagged rate, from the Tags column1,54714.9%
Untagged rate, from the Root cause field1,59615.3%

Half a point apart, so it was quoted with confidence and a remediation list was generated from it. They are not the same tickets.

In both sets, genuinely untagged1,082
On the list, field already populated465
Field empty, hidden by a macro's tag514
Rows where the two disagree979

Thirty percent of the work list is wrong, in both directions, and the two errors nearly cancel in the total.

Reported figureReviewedActual
Largest root causeTrainingData import
Open tickets at quarter end381176
Platform group volume1,6112,823
Urgent inside the 4-hour target70.8%63.7%
Resolved within the hour1,3961,135
Tickets handled in Q210,52810,413

Nine figures moved. Every behaviour behind them is in the vendor's export documentation and none of it is visible in the file.

A help desk export looks like one dataset offered in a choice of formats, so everybody takes the one that opens in a spreadsheet. It is not one dataset. Zendesk's CSV report, its JSON export and its ticket API return different rows for the same date range, different fields, and different values for the same field on the same ticket. Every one of those differences is written down in Zendesk's own export documentation, and none of them is visible anywhere in the file.

This pack pulls the range every way the vendor offers, reconciles the pulls against each other row by row, and restates what you already published with the documented reason on each number that moved. Row by row is the part that matters. Two totals agreeing is not evidence they describe the same tickets, and it is the most common way a broken support figure survives review, because a plausible number is a number nobody investigates.

Redbourne sells practice management software to veterinary clinics. Its Q2 review reported 14.9% of tickets with no root cause recorded, counted from the Tags column because the multi-select field is not in a CSV. The real rate was 15.3%, so the figure was quoted with confidence, and 979 rows disagreed underneath it. Nine of Redbourne's figures moved, including which root cause was the largest in the quarter. None of them moved because anybody did anything wrong.

The two sheets that have to exist before any figure does

Field Map decides which pull each field comes from and stays true after the data changes. The bridge walks from what the file returned to the population the question was about, one named reason at a time, balancing to zero.

Field Map, extract

Four custom fields, three of them absent from the CSV

FieldTypeCSVJSONAPIAs of
Root causeMulti-selectnoyesyeshistory
Affected moduleMulti-selectnoyesyeshistory
Steps to reproduceMulti-line textnoyesyeshistory
Promised fix dateCustom datenoyesyeshistory
GroupSystemyesyesyesexport
AssigneeSystemyesyesyesexport
StatusSystemyesyesyesexport
TagsSystemyesyesyesexport
Resolution timeHours, roundedyesnonohistory
Full resolution timeMinutesyesnoyeshistory
CommentsLong textnounder 1 MByeshistory

Four fields absent, four snapshots of right now printed next to historical dates, one duration rounded away, and the ticket text only on two of the three paths.

CSV to JSON Reconciliation

Bridge from the file to the population

Rows in the CSV report10,528
Less rows created before 1 April, returned because the range filters on last update-6569,872
Add tickets created in Q2 and not updated since before it+54110,413
Add tickets over 1 MB, excluded from a CSV report entirely+3710,450
Add items held back for a timestamp within 6 minutes of the request+010,450
Add deleted tickets, absent from a CSV report by design+010,450
Tickets created 1 April to 30 June, account clock10,413

Why nobody caught it

The net error on the headline figure is 115 tickets, or 1.1%, which is well inside the range where nobody investigates. The gross error is 1,197 rows, or 11.5%. Every cut by month, group, cause or agent runs over a row set carrying those swaps, so the cuts are wrong by more than the total is.

What's in the pack

01

Field Map

One row per field: its type, whether each export path returns it, whether it is history or a snapshot of right now, which pull to take it from, and what specifically breaks if it comes from the CSV. Build it before touching the data, because it is the one artefact that stays true after the data changes.

02

Ticket Register

The deliverable. One row per ticket, carrying the multi-select, multi-line text and custom date fields recovered from the JSON export. Durations arrive in native minutes instead of rounded hours, timestamps carry both clocks, and each snapshot field sits next to its value when the work was done.

03

CSV to JSON Reconciliation

An arithmetic bridge from what the file returned to the population the question was about, one line per named reason, balancing to zero. Nothing downstream is quotable until it balances. It also records the clock and pull moment for each path, since two pulls an hour apart are two datasets.

04

Restated Figures

The sheet anyone who read the original report needs. Per published number: the corrected value, the documented export behaviour that moved it, and whether the row sets agree. Ranked by consequence rather than by size of move, so the figure that changed a decision comes first.

05

Export Procedure

The three pulls, in order, with what each one is for and what to record about it. Widen the range by a month either side, take the cursor form of the incremental endpoint rather than the time-based one, and side-load the metric records in the same pass.

06

Field Completeness Note

Which fields each path omits and what a report loses per omission, including the four separate reasons a Tags column is not the multi-select field behind it. Pairs with the naming convention pack when the fix is the tag namespace rather than the report, and with the support ticket categorization template when the recovered field turns out to hold categories nobody can apply.

07

Measurement Definitions

Every reported figure with the timestamp that selects its rows, the clock it is computed on, the unit it comes from, and whether its field is history or a snapshot. The metric definitions pack does the same job across the whole business, and the SLA reporting template applies the same discipline to a breach rate specifically.

08

Reconciliation Findings

The worked example end to end: nine reported figures, what each one actually was, and the documented behaviour behind each move. Written so it reads as an explanation rather than an accusation against whoever built the original report.

How it works

  1. 1

    Start from the report, then map the fields

    Send the export you reported off and the deck that circulated. River reads your custom field list against what each export path carries and names what is missing from the file. On the worked example three of four custom fields were absent, and two were what the review was about.

  2. 2

    Pull the range three ways, widened

    The tabular report as the artefact under investigation, the structured export for the omitted fields and UTC timestamps, and the API with metric records side-loaded for durations in minutes. A month either side, because the range does not filter on creation.

  3. 3

    Balance the row set before any figure

    A bridge from the file to the population your question meant, one named reason per line, to zero. River reports the gross error ahead of the net one, since the net error is what let the report through and every breakdown inherits the swaps.

  4. 4

    Compare sets by row, then restate

    For each figure, the intersection and both differences rather than just the two numbers. Then each published number with its corrected value and the vendor's documented behaviour in one clause, ranked by which one changed a decision.

Frequently asked questions

Which fields does a Zendesk CSV export actually leave out?

Multi-line text, multi-select and custom date fields are excluded from CSV reports and available in the JSON and XML ones. Ticket comments, descriptions and deleted tickets are also absent by design. Read that list against your own custom field list, because it usually describes the fields your report is about.

Can I just count the Tags column instead of the multi-select field?

No, and it fails in four ways at once. Macros and triggers write into the same namespace without setting the field, a retired option leaves its tags behind, a renamed tag string splits one value in two, and tags are a snapshot at export time rather than history.

Why does my export return a different number of tickets than I expected?

The date range filters on a system-generated timestamp, normally the most recent update rather than the creation date, so a quarter-shaped range returns tickets touched in the quarter. Widen the pull and select on creation afterwards. Filtering the file you already have cannot recover rows it never returned.

Does this work with Intercom, or only Zendesk?

Both, and the differences matter. Intercom's dataset export carries reporting metadata without message content, filters on conversation created rather than last update, and captures user attributes as at the start of the conversation. That last convention is the inverse of Zendesk's, so a joined column holds two meanings.

Why are my resolution times different in every report?

A CSV report rounds Resolution time to the nearest whole hour and carries two more duration columns measuring different events. Take minutes from the incremental export API with metric records side-loaded. Where the target is written in business hours, compute it in the hours the policy defines, as the sales and marketing SLA pack does.

My totals reconcile. Is that enough?

No. Two totals agreeing is not evidence they describe the same tickets, and errors in opposite directions cancel. On the worked example an untagged rate was half a point out and 979 rows disagreed, so a third of the remediation list was wrong. Compare ticket ids, not counts.

Can I report group or assignee volume for a past quarter?

Not from the field. Group, assignee, status, priority, type and tags are the value at the time of export, so a reorg inside the period rewrites the table retrospectively. Take historical values from the audit or metric event history, or group by something historical instead.

Get the ticket data out completely, then find out which numbers moved

Edit with AI