Where a card dispute spends its time
I mapped a fictional Canadian card issuer's dispute process, measured 1,200 synthetic cases in SQL, and tested five Lean changes against the same cases. Most of a dispute's life is spent waiting, and most rework starts at intake.
Fernbrook Card Services is fictional and every case comes from a seeded Python model. The process is an outside-in sketch of a typical dispute workflow and does not describe any real bank. Future-state results are estimates from stated assumptions.
The problem
A disputed charge passes through six internal teams before the customer gets an answer: the contact centre, triage, disputes operations, the analysts, the chargeback team and customer resolution. In this model no team owns the whole path, so each queue is managed on its own.
Canada's Financial Consumer Agency (FCAC) requires banks to deal with complaints within 56 days and does not allow the clock to be paused. Its 2026 review found banks missing that limit, including one whose alert fired on day 55. I used 56 days as the outer limit for the whole dispute journey. Whether a particular dispute legally counts as a complaint is outside this study.
Questions
- Where does a dispute's time go: hands-on work, internal queues, the customer, or the merchant and card network?
- Which of the eight Lean wastes show up, and how large is each?
- What causes most rework, and does it differ by channel?
- Is the process stable week to week, and would a control chart catch a problem in time?
- Which changes cut the controllable lead time most, and what do they cost?
Scope
| Suppliers | Inputs | Process | Outputs | Customers |
|---|---|---|---|---|
| Cardholders, merchants, card network, contact centre | Dispute details, transaction record, receipts, merchant response | Intake, triage, documents, investigation, provisional credit, chargeback, merchant response, final decision | Decision, final credit or rebill, notice to the customer, chargeback record | The cardholder first, then finance, compliance and the merchant |
The study covers the issuer's steps from intake to final notice. Fraud detection before a dispute exists, card replacement and the network's arbitration rules are left out.
The current process
Seven working steps sit between the customer's first contact and the final notice, with two waits the issuer can't control. Queue times are first-pass medians and work times are averages.
Four loops send work backwards. Missing documents go back to the customer. A wrong category found during investigation goes back to triage and then to investigation again. A rejected chargeback is re-keyed, and an unreachable customer gets a second notice.
Data and model
| Table | Rows | Key | One row is |
|---|---|---|---|
disputes | 1,200 | case_id | A customer dispute with its channel, type, amount, tenure, outcome, and open and close times |
process_steps | step_event_id; unique (case_id, step_seq) | One step on one case, with team, wait type, wait hours, work minutes and any rework reason | |
chargebacks | case_id | The filing outcome and merchant response time for a case sent to the network | |
teams | 8 | team_id | Six internal teams and two outside parties |
The model in python/dispute_model.py draws every random value for a case up front with seed 2027, so a rerun produces the same files. The future-state scenarios reuse those draws and change only the process rules, which keeps luck out of the comparison.
Week 14 (6 to 12 April 2026) carries a planted analyst shortage that slows the investigation queue 2.2 times, so the control chart in section 05 can be checked against a known answer. All waits are calendar hours; there is no business-hours calendar.
SQL checks before analysis
The CSV files load into SQLite through sql/schema.sql, which sets primary keys, foreign keys and check constraints. Eleven validation queries run first, and the script stops if any of them finds a failure.
| Check | Failures | Result |
|---|
Medians and P90s use window functions instead of engine-specific percentile functions, so the queries move to PostgreSQL with small changes.
WITH ranked AS (
SELECT internal_days,
ROW_NUMBER() OVER (ORDER BY internal_days) AS r,
COUNT(*) OVER () AS n
FROM case_time
)
SELECT MAX(CASE WHEN r = CAST(0.5 * n AS INTEGER) THEN internal_days END) AS median_internal_days,
MAX(CASE WHEN r = CAST(0.9 * n AS INTEGER) THEN internal_days END) AS p90_internal_days
FROM ranked;
All 18 analysis queries are in sql/analysis_queries.sql, and their output is in docs/query_results.md.
Where the time goes
The average dispute stays open days. Someone is working on it for hours of that.
One average case, start to finish
Each block is the average hours per case for one step, in process order. Work is drawn at a minimum width so it stays visible.
The part the issuer controls, enlarged
The same case with the customer and merchant waits removed.
Show as table
Inside the part the issuer controls (median days, P90 ), hands-on work is % of the time. The rest is cases sitting in queues between teams.
The longest single wait is outside the issuer: merchants take days on average to answer a chargeback. The issuer can't shorten that, but it can tell the customer what is happening and avoid stacking its own queues on top. Of all cases, % pass day 45 and % pass day 56.
Median queue wait before each step
Hours, first pass. P90 is in the table.
Show as table
Cases by age at closure
Share of all 1,200 cases.
Show as table
Why it happens
% of cases repeat at least one step. A wrong dispute category and incomplete documents cause % of those cases, and both start at intake.
Cases with rework, by cause
Labels give each cause's case count, its share and the running total.
Show as table
Wrong category rate by intake channel
Phone and branch are wrong about twice as often as app and web.
Show as table
Cases with any rework, by channel
Phone produces the most rework, and app the least.
Show as table
Is the investigation queue stable?
An individuals (XmR) chart of the average investigation wait for cases opened each week. The limits are the mean plus or minus 2.66 times the average moving range.
Average investigation queue wait by opening week
Show as table
The chart flags week ( hours against an upper limit of ) and no other week. That is the planted shortage. The other 25 weeks vary inside the limits, so the way to lower the average wait is to change the process itself. With week 14 removed the upper limit tightens to hours, which is the limit to monitor from here.
The eight wastes
| Waste | Where it shows up | Evidence |
|---|---|---|
| Defects | Wrong dispute category; chargebacks rejected for typing errors | wrong-category cases and re-filed chargebacks |
| Overproduction | Not found; work starts only when a customer raises a dispute | None |
| Waiting | Queues before investigation, chargeback filing, triage and the final notice | % of internal lead time |
| Non-utilized talent | Specialists investigating small disputes | % of specialist hours go to disputes under $50 |
| Transportation | Cases handed between teams | handoffs per case, up to |
| Inventory | Work in progress parked in queues | The investigation queue holds waiting hours over 26 weeks |
| Motion | Typing case details into the chargeback form by hand | Chargeback Team hours |
| Extra processing | Full investigation and chargeback for small disputes | disputes under $50, averaging $ |
Five whys on the wrong category
- Investigation is repeated because the analyst finds the case was filed under the wrong dispute type and sends it back.
- The type was wrong because triage picks the category from the intake notes.
- The notes are unreliable because phone and branch intake capture a free-text story with no structured fields, and those channels are wrong about twice as often.
- Intake is free text because the call script is designed to keep calls short and never asks for what triage needs.
- The likely root cause is that no standard intake maps to the dispute categories. On real data I would confirm this with the process owners and a sample of case notes before changing anything.
Five changes, tested on the same cases
Each change targets one waste. I ran each one alone, then all five together at full and at half of the assumed effect.
| Change | Waste | Assumed effect at full strength |
|---|---|---|
| Guided digital intake: required fields and document upload in the app and on the web, and a matching checklist for phone and branch agents | Defects, waiting | Incomplete intake ×0.40 (app, web) and ×0.65 (phone, branch); second document requests halved; phone intake takes 1.5 minutes longer |
| Rule-assisted triage that suggests a category from the structured intake fields | Defects, transportation | Wrong category ×0.40 |
| Low-value fast track: non-fraud disputes under $50 from customers with at least a year of tenure are credited at triage and closed with no chargeback | Extra processing, non-utilized talent | The issuer absorbs the disputed amount |
| A daily queue board showing work in progress and case age, with alerts at day 30 and day 45 | Waiting, inventory | Internal queue waits ×0.75; a surge like week 14 is caught sooner (the 2.2× slowdown becomes 1.4×) |
| Chargeback form pre-filled from the case record | Motion, defects | Typing errors ×0.25; four minutes less per filing |
Median internal lead time by scenario
Days. Every bar runs the same 1,200 cases.
Cases past the deadline bands
Current state against all five changes at full effect.
The fast track has to pay for itself
Over a year, the fast track frees about hours of work and writes off about $ that the issuer would otherwise recover through chargebacks. That only pays if an hour of dispute work costs more than $ fully loaded. At the $ I assumed, the margin is thin, so I would pilot it with a lower dollar limit, track write-offs monthly, and keep it only if faster answers also cut repeat calls.
Recommendations, in order
It had the largest single effect in the simulation (median internal lead time days against ), it can start on a whiteboard or a shared spreadsheet, and it answers FCAC's day-55 finding directly. Waiting is % of internal lead time.
- Fix intake and triage together; they cover % of rework cases.
- Pre-fill the chargeback form to remove the re-filings and the second round of typing.
- Pilot the fast track behind the break-even test above.
- Send customers a status update during the merchant wait, the longest wait in the process and the one the issuer can't remove.
Keeping the gains
Each measure has an owner, a review rhythm and a rule for what to do when it moves.
| Measure | View | Rhythm | Owner | When it moves |
|---|---|---|---|---|
| Investigation queue wait | XmR chart with limits from stable weeks | Weekly | Disputes operations lead | A point above the upper limit means checking staffing and work in progress that day |
| Open cases by age | Daily queue board | Morning stand-up | Team leads | Past day 30 a case gets a named owner; past day 45 it goes to the manager |
| Rework rate by cause | p-chart and monthly Pareto | Weekly, monthly | Triage lead | A new top cause gets a five-whys within two weeks |
| Fast track write-offs | Run chart against budget | Monthly | Finance partner | Above break-even, lower the dollar limit |
| Cases past day 56 | Count on the board | Daily | Disputes manager | The target is zero, and each one is reviewed |
What the board would have shown
On 18 March the investigation queue held cases. On 15 April, the week after the shortage, it held . A daily board puts that number in front of the team at the morning stand-up.
Limits of this study
- The data is synthetic. Rates and timings are assumptions built from public descriptions of card disputes, and no real bank's data or process is represented.
- Future-state numbers apply assumed effects to the same cases. A real project would pilot each change and measure it.
- The 56-day limit comes from FCAC's complaint-handling rules. Whether a dispute counts as a complaint is a legal question I haven't answered, and card network timelines, which vary by reason code, are simplified.
- Time is calendar time with no business hours, holidays or staffing model, and queue effects are multipliers instead of a capacity simulation.
- The $ hourly labour cost is an assumption used only for the break-even.
- I designed the process, the assumptions and the recommendations, and checked every figure against the query output. I used Claude Code to help write the code.
Sources
- FCAC, Supervisory Highlight: Thematic Review on Complaint Handling: the 56-day maximum, no pausing, and the alert on day 55.
- FCAC, Revised Guideline on Complaint-Handling Procedures for Banks, June 23, 2026.
- ASQ glossary, Eight wastes (DOWNTIME).
- Process cycle efficiency is value-added time divided by lead time, the common Lean definition.
Files
python/dispute_model.py, the process model and data rulessql/schema.sql,sql/validation_queries.sqlandsql/analysis_queries.sqlpython/simulate_future_state.py, the scenarios and break-evendata/data_dictionary.csvand the four data filesdocs/assumptions.md, every parameter and the reason for it