The synthetic user that stopped my late‑night demos — and the hour it froze our test cards

I built an hourly headless‑Chrome synthetic user on a ₹300 VPS to exercise our critical checkout. It caught bugs before users — and once triggered bank fraud controls. Here’s what I actually built and why.

Written by: Arjun Malhotra

A person typing on a laptop with a second laptop and notebooks on a wooden desk
Photo by Glenn Carstens-Peters on Unsplash

It was 9:10 AM, two hours before a product demo with a retail partner in Bengaluru. The engineer on my team pinged: “staging checkout returns 502 intermittently.” I opened the checkout and—sure enough—press Pay, wait, crash. No Sentry alert. No obvious deploy. The partner was already on the call.

That was the last straw. We had unit tests, contract tests, synthetic API pings, uptime checks — all green. But the user flow that mattered most (sign in → add to cart → pay → success page) was breaking in ways our monitors never saw. So I built a headless “synthetic user” that runs every hour from a tiny VPS and actually completes the critical flow in staging. It cost ₹300/month to run and, for months, saved us from embarrassing demos. Then one night it triggered bank fraud detection and froze our test virtual cards. Here’s how I built it, why it worked, and the hard lessons from that failure.

Why a synthetic user, not another unit test Unit tests catch logic bugs. Synthetic users catch integration failures: broken CDNs, expired certs, flaky auth tokens, payment gateways that change a response shape. Our production parity for these external integrations was never perfect — Indian payment rails, third‑party widgets, and flaky staging DNS were frequent culprits. A headless browser literally clicking through the UI surfaces those failures.

The rough stack

What it found — and why it mattered Within the first month the synthetic user found:

Each time, the synthetic run gave a HAR file and a screenshot. Developer triage time dropped from 90 minutes to 20. We started shipping with more confidence.

The failure (the honest part) One night I woke to 3 Slack alerts. The synthetic user had started failing on the payment gateway. Investigating, I found two bad things:

  1. Our “dry‑run” header wasn’t honored due to a regression in a proxy. So the synthetic run unknowingly hit the live payment sandbox endpoints.
  2. The sandbox gateway had implemented new anti‑fraud rules. Our synthetic user replayed a payment flow dozens of times during an update window. The bank’s fraud system flagged the pattern and temporarily froze the virtual cards attached to our test account. Unfreezing took three business days and a support escalation that cost effort and trust. Financial impact: a ₹1,200 hold and ~6 hours of coordinated support work across the team and the bank.

That failure taught us more than any green run ever could:

What I changed after the freeze

Limitations that haven’t gone away

A practical starter blueprint (what I’d actually recommend)

The takeaway I keep telling engineers: synthetic users are cheap insurance, but they’re also live traffic. Treat them like a person in the system — with limits, identity, and an “I am a test” header that downstream systems actually respect. Build the safety fences first. The extra hour it takes to add caps and idempotency is worth a week of frozen test cards and panicked Slack threads.