Stateful, controllable sandboxes for the APIs you build on — so your end-to-end tests run against something as loyal to production as it gets, without a live key, a side effect, or a flaky call.
setup is one line · your real vendor SDK · zero live calls
test("a declined run surfaces the vendor's error", async ({ sandbox }) => { // arm the failure your app must survive — fires once, then clears await sandbox.faults.arm("task_runs", "create", { status: 402, code: "insufficient_funds", count: 1, }); const deck = sandbox.client(); // your real vendor SDK, repointed await expect(deck.taskRuns.create({ taskId: "task_rent" })) .rejects.toMatchObject({ code: "insufficient_funds" }); });
Same wire shapes, same object lifecycles, same webhooks, same clock-driven behavior — verified against the live API with contract tests. Your end-to-end suite runs against a stand-in it can't tell apart from the real thing, plus the one thing production will never give you: control.
One command boots a faithful, stateful stand-in for the real API. It's a real server — not a pile of canned responses.
$ prodbreak up deck --port 8801 prodbreak · pack=deck · world ready key minted: pbw_k3y_d3m0 $ curl -XPOST :8801/v2/task-runs -d '{"task_id":"task_rent"}' { "id": "trun_8f3k2", "status": "pending", ... } $ curl :8801/v2/task-runs/trun_8f3k2 { "id": "trun_8f3k2", "status": "completed", ... } # it remembers $ curl -XPOST :8801/__admin__/clock/advance -d '{"days":30}' { "ok": true } # 30 days of renewals & retries — instantly
Create it, list it, update it — the sandbox remembers. Multi-step flows, server-derived fields, and full object lifecycles behave like the real thing.
Script the values your code needs to see — run results, balances, webhook payloads — and arm the faults it must survive (402, 429, timeout). No more hand-written fixtures rotting in your repo: a fixture is a snapshot in time; this is a live world you direct.
Start every test from "customer with three failed payments" — not from an empty database and forty lines of setup calls.
Failure handling rots silently — a refactor breaks your retry logic and nothing notices, because no test ever exercises it. Put prodbreak in CI and every PR is checked against every failure you've rehearsed.
One job step boots the sandbox. Your whole integration suite runs against it — including the paths that only break at 3am.
Same seed + same script = same result, every run. No flaky live calls, no shared-sandbox state bleeding between PRs — every run gets its own isolated world.
No live credentials in CI, no rate limits, no per-call charges. The most dangerous thing a test can do is fail.
- name: Boot sandbox run: prodbreak up deck --port 8801 & - name: Integration tests run: npm test env: DECK_BASE_URL: http://localhost:8801 # no live keys in CI
| prod break | stripe-mock | Prism / WireMock | live API | |
|---|---|---|---|---|
| Stateful round-trip | YES | — | — | YES |
| Force vendor-shaped errors | YES | ~ magic values | — | — |
| Seed a starting state | YES | — | — | ~ by hand |
| Webhooks you control | YES | — | — | ~ wait & pray |
| Virtual clock | YES | — | — | — |
| Works when the vendor has no sandbox | YES | — | ~ schema only | — |
| Safe for CI — no keys, no cost | YES | YES | YES | — |
[ no shade — stripe-mock and Prism are great at what they do. They're just not built for this. ]
Each pack is built against the live API and verified with contract tests — fidelity is measured, not assumed.
Banking-data tasks & runs. No native sandbox exists — every real call is live. The reason prodbreak exists.
Payments, subscriptions, dunning. Test mode exists — but it can't force most failures. We can.
Email delivery, bounces, suppression lists — and the events you can't trigger on demand.
SMS & voice status callbacks, delivery failures, carrier filtering.
Bots, events, rate-limit tiers, and workspace state your tests can own.
Orders, inventory, webhooks — a full store as test fixture.
The open template pack. Runs today — boot it and curl the whole control surface.
Tell us which API keeps breaking your prod. That's our roadmap.
$ npx prodbreak up deck prodbreak · pack=deck · sandbox live → https://deck.prodbreak.dev
- DECK_BASE_URL=https://api.deck.co/v2 + DECK_BASE_URL=https://deck.prodbreak.dev
the entire migration
One command boots a sandbox for the API you integrate. It mints its own key and owns its own world.
Change one env var — the base URL. Your real vendor SDK, your real app code, untouched.
Seed state, arm faults, advance the clock, fire webhooks. The API does what your test says it does.