FinTech
Information Architecture
Data Visualization
UX Research
From JSON Dump to Transaction Lifecycle
Giving operations teams a way to diagnose failures themselves, instead of escalating raw JSON to engineering.
The screen the case is named after: a stalled transaction read as a lifecycle, with the failure surfaced at Hold and the next action one click away.
The problem
Operations users like bankers couldn't interpret the raw transaction log on their own, so they escalated to internal teams just to understand what had happened.
What I did
I restructured the raw JSON view into a flow-based diagnostic tool, so the transaction lifecycle that was already in the data became readable at a glance, instead of decoded by hand.
The result
By the end, the people who lived in the log, the clients' operations users and the Customer Success team alike, could read a stalled transaction at a glance and diagnose it without escalating.
Role & Context
The moment
A banker gets a call: a client's expected transfer never arrived. They open the Transaction Log to find out why and hit a wall of raw JSON. They can't see where it stopped or who's holding it, so they do what everyone does with this screen: send it to Customer Success to decode.
The wall they hit: a transaction expanded into a raw JSON tree. Every field is here, including why it stalled. None of it reads as a process.
Stakes
Why a single screen mattered
This wasn't a peripheral screen. It was a core screen, used every day by the clients' operations teams and by Customer Success.
The repeating cost
Every time a transfer stalled and someone couldn't say why, the cost repeated: another escalation to Customer Success just to decode what the screen was already showing.
The deeper, strategic cost
For a platform sold to banks, a core screen that read like a developer's console wasn't a UX problem. It was a sales risk in every demo, and the kind of core-screen friction that erodes trust the moment clients rely on it.
The reframe
They didn't want more data
Initial assumption
"Users need more data and better filters to find what went wrong."
→
The actual insight
They arrived already knowing which transaction they needed, by its ID, not hunting for it. They didn't want more data. They wanted that one transaction's context: what happened, where it stalled, and what to do next.
A scan of comparable tools (Tokeny, Chainlink, Digital Asset) showed the same failure everywhere: plenty of data, no story. That confirmed the fix was not more filters, but a representation that made the process legible.
The core problem
The structure was already in the data
Every transaction's JSON held the full process: the stages, the organizations involved, and each one's role and status. Nothing was missing. The old screen simply exposed the data structure directly, as a JSON tree, and a data structure is not a process. You could read every field and still not see where it had stalled.
So I did not define a new model. I gave the existing one a representation that fit. Each transaction became a flow: stages left to right, Plan Approval, Hold, Transfer, Release, Completed, with Roll back as the exception, and participants attached to the stage they gate, each with its role and status. The whole process, and every actor in it, became visible at a glance, including the exact point where it stopped. The shift was representational, not informational: same data, shown as the process it always was.
Before · data structure
After · the process
Same transaction, two representations. The tree lists fields; the flow shows the lifecycle, and marks the exact stage where it stopped. This is where the case earns its title.
Worked example · a real transaction
A403AFD…3441X, a Loan Intent between JPMorgan Chase, Bank of America, and DTCC, stalled at Hold. The old tree showed only that the plan had failed. The flow shows why: inside Hold, Bank of America as Issuer is Rejected while JPMorgan Chase as Custodian is Approved, and the rest sit In Progress, waiting on a stage that will never clear.
All of it was in the JSON the whole time. The flow made it visible, and put the next action right where the failure is.
Key decision
Two connected views one flow · one table
Diagnosis and monitoring are different jobs, so I gave each its own view: a dedicated full screen where a single transaction's whole flow and every participant is legible, and a filterable table for scanning and monitoring many at a glance.
The monitoring view. A dense, filterable table for scanning hundreds of transactions, status color-coded so problem rows surface without reading. Selecting one leads into its full-screen flow.
The trade-off I accepted on purpose
The user leaves the table, and the list, to study one transaction, then comes back. You cannot watch hundreds and read one deeply on the same surface. I accepted that context switch on purpose, because the list adds nothing during focused diagnosis, and one view doing both jobs would do neither well.
What didn't work
Early exploration, rejected
Inline expansion, the safe choice
My first direction kept the pattern the old screen already used: expand a transaction in place, inside the table. On paper it was the safe choice, the detail opens right where you are.
It broke the moment I watched real usage. Operations users never open several transactions in parallel; they work one at a time, on the one they came for. Inline expansion is built for comparing many open rows, a need these users don't have, while cramping the single transaction they actually want to understand. So I dropped it and gave one transaction its own full screen.
The rejected pattern in its original form: detail expands in-place under the row, the same inline behaviour the old screen used.
Solution & impact
The redesign, end to end
The redesign split the Transaction Log into two connected views: a full-screen flow for diagnosing one transaction, and a table for monitoring many. The table handled the usual high-volume needs, filtering, density, responsiveness, without making any of that the story.
Read the failure, don't decode it. The flow surfaces the stall at Hold and pairs it with the instruction-level reason, Bank of America rejected as Issuer, plus a direct "Contact Issuer" action.
Every participant, legible at once. The approval panel lays out each organization's role and status, so the one rejection holding up eight in-progress actors is obvious.
Depth on demand, not by default. The underlying message trail and raw on-chain payload stay one click away for the rare case that needs them, kept out of the default read so the flow stays legible.
The screen explains itself when there's nothing to show. A considered empty state, part of treating the most-used screen as a product surface, not a data dump.
I validated it with the Customer Success team, who both used the log daily and absorbed its cost, bringing them pain-point research and testing each direction against the real cases they handled. The outcome held for everyone who used the screen, the clients' operations users and CS alike: a transaction you used to decode, you could now simply read. The work was reviewed and endorsed up to the CTO, the GM, and the CEO.
Reflection
With no analytics, the people closest to the pain were my instrument: if Customer Success could read a transaction without help, the design was working. If I did it again I would still start there, but I would also set one lightweight signal early, a simple before-and-after on how long it took to explain a failed transaction, so the improvement I could see in the room would be legible to those who weren't.