Skip to main content

Engineering

Rebuilding API Debugging Around the Timeline

Two Reddit threads described the same pain. We shipped an integration debugger that answers what actually happened — on one screen.

Apr 22, 2026FetchSandbox Engineering

Two separate developers described the same pain in two separate Reddit threads in the same week.

Everything works fine when you call one endpoint. Then it breaks when you actually run the full flow. Webhooks arrive late. Retries fire twice. State doesn't match what the docs said. You sit there with logs open trying to guess what happened.

That is the shape of every API integration bug.

Postman is great at one request. Stripe's dashboard is great at one event. Kibana is great at raw log bytes. Nothing you had on hand answered the question people actually ask: what happened across the whole flow?

We spent about ten days building the thing those threads were asking for. This is a short writeup of what shipped and, more importantly, how it got small enough to ship that fast.

What the product does now

Every workflow page has three views of the same run. Pick whichever matches how you want to think about the integration.

Steps view is the default. You click Run on each step. Response appears inline. New: a small colored chip on every step's response. Green means the status matched the YAML declaration and every webhook the backend predicted for that step actually fired. Amber means status ok, webhook missed. Red means the status didn't match. Click the chip to see which specific check failed, with a detail string like expected 200, got 500 or missing payment_intent.succeeded.

Timeline view is the new one. Every request and webhook event for the sandbox, interleaved by real clock time with millisecond precision, grouped into collapsible lanes per resource (Customer, PaymentIntent, Subscription). You can literally see the webhook arrive 142 ms after the request that triggered it. Scroll through what happened yesterday. Everything persists across page reloads and across a Reset & Run Again — the whole log is on disk, not just in memory.

Requests also get a classification chip. retry for an identical request within sixty seconds. idempotent when the caller sent a matching Idempotency-Key. duplicate for same endpoint, different body — usually a caller bug. That last one is the signal that shows up red and is the one you actually want to notice.

Flows view tracks every full workflow run the developer has clicked through. Each run carries a colored status badge. Completed when the resource reached its happy-path terminal state. Running when there was recent activity. Orphaned when the flow stopped idle and never reached a terminal state — with copy that says left at requires_confirmation · 8 minutes ago. That is the exact kind of stuck integration people hunt for in production at 3am. Click any run card and the Timeline filters down to just that run.

Plus two new failure scenarios: slow_network (every response delayed 800 ms) and flaky_webhooks (30% of webhook events marked as dropped but still visible in the Timeline with a red chip, so retry handlers can be tested against actual missed events).

The shape of the problem, not the shape of one tool

The thing that made this project smaller than expected was giving up on "add another tool." A mock server doesn't help a debugger. A CLI doesn't help it. A new endpoint doesn't help it. The thing developers actually needed was one screen that answered what happened across all the pieces of the integration at once.

So we rebuilt the workflow page around three lenses on the same archived data. Same Run button. Same responses. Different ways to see what you just did.

Audit-first discipline kept it small

Here is the part that actually matters to other engineers: nearly every phase shrank when we audited the codebase before designing.

The RequestLogEntry model already had a timestamp field populated on every request. The durable timeline didn't need a new schema; the schema was already durable — we just weren't exposing it to the workflow page yet.

The ScenarioOverride model already had delay_ms and global_delay_ms fields. They were declared, returned by the public API, and never read by the engine. Adding simulated latency took three lines — wiring up fields that were already there.

The biggest reduction was proof inference. We'd planned three to five days to build a generic "derive expected-state annotations from any OpenAPI spec" pass. It turned out the inference helper already existed at workflow_enumerator.py:426 and had been running at preflight time since the original proof-engine shipped. The actual gap was that it wasn't called at workflow load time, so the frontend never saw the annotations. The fix was one new helper and one extra call site in the workflow loader — roughly two hours of code. The tests took longer than the code.

None of this is clever engineering. It is the result of a team rule: audit the codebase before designing. No one writes a proposal that says "we need a new pipeline" until someone has confirmed there isn't an existing one we can extend.

This is also why the release has so few moving pieces. No new database. No new worker process. No new polling loop anywhere — the UI refreshes lazily on tab activation and window focus, and that is it. Each phase shipped as an additive commit over an existing surface. Seven phases. Zero parallel pipelines.

Things we deliberately didn't build

Auto-resume for orphaned flows. The Flows tab shows a disabled "Resume (coming soon)" button. The shape of that feature depends on whether the orphaned step had external side effects — that is a real semantic question, not an engineering one, and shipping observability first is the right sequence.

Background orphan sweeping. Classification runs on demand when you open the Flows tab. That is right for single-user debugging and wrong at multi-tenant scale. The code path for a sweep is similar enough that adding one is a follow-up, not a rewrite.

Anomaly detection, ML-assisted anything, automatic "explanation" summaries. Human engineers drive debugging. Hiding what they need to see behind a smart layer is usually where tools lose trust. The product shows the raw events ordered by time, and it lets the engineer draw the conclusion.

Replacing logs. The Timeline complements logs; it doesn't pretend to supplant them. An engineer who wants to grep the raw log file still can. The Timeline is the view for the "what actually happened" question — not every question.

One thing we caught in stage

A late-cycle edit to the main workflow page silently failed to persist to disk during the frontend build. Every edit reported success, the tests were written against an in-memory copy that had the new code, typecheck passed, commit went through with the other Phase C.2 files. The backend endpoint shipped to stage. The frontend tab was missing.

The smoke script we'd extended caught it immediately. It clicked the Timeline tab, verified entries and lanes, then tried to click the Flows tab — and reported "Flows tab not present." One minute of investigation turned up the missing page.tsx diff. We re-applied the edits, amended the commit with the fix, redeployed stage. Smoke went green.

That is exactly why the stage-before-prod discipline exists. The bug surfaced in the right environment, against the real build, before any user saw it. "All tests green and typecheck clean" does not guarantee "all files are on disk." The only thing that guarantees the deploy is testing the deploy.

What we're watching next

Shipping observability is the first half. The second half is listening — are people actually using the Timeline view? Does anyone click the Flows tab? Which of the failure scenarios get picked up? Which workflows hit orphan state most often? Those signals pick the next build better than any roadmap does.

Same loop that started this. Two Reddit posts told us the problem. The next round of feedback will tell us which slice of it to sharpen.