Skip to main content
Lots of API workflows branch: a payment either captures or declines, an identity check either verifies or fails, a transcription either completes or errors. Each branch produces a different downstream sequence — different webhooks, different fields, a different final state. ProdBreak lets you pick which arm runs, on demand, so you can test the failure path as easily as the happy one — the path that’s a nightmare to trigger against the real vendor.

You pick the arm; you never invent one

The branches a workflow can take are part of the pack — they’re the same forks the real API has. You choose which lawful arm runs; you can’t add an arm or reshape the sequence. That’s the same causes-not-effects rule applied to forks: you steer among real outcomes, you don’t puppeteer impossible ones. A branch is chosen through the same surface you use for values you control — there’s no separate “branch” verb to learn. You pin the fork’s outcome, and ProdBreak runs the rest of that arm faithfully.
// take the FAILURE arm of an identity-verification workflow
await sandbox.exogenous.on("identity.verification", "branch", "failed");

// the arm's data is yours to define too — here, why it failed
await sandbox.exogenous.on("identity.verification", "output.reason", "document_expired");

const run = await idv.verifications.create({ userId: "usr_42" });
await sandbox.clock.advance("0s");            // drain the workflow inline (see Deterministic CI)
// → the "verification.failed" webhook fires, status lands on `failed`, output.reason is set
Swap "failed" for "verified" and the success arm runs instead — its webhook, its fields, its final state. Same workflow, both paths, your choice.

Success arm vs failure arm

The two arms usually fill different fields, exactly like production:
Success armFailure arm
Final statuscompleted / verified / capturedfailed / declined
Webhook…​.completed…​.failed
Payloadthe success output (a transcript, a balance) bindsthe error data (reason, code) is set; the success output never binds
You define the out-of-control values on whichever arm you pick — the success payload, or the failure reason — and ProdBreak keeps every channel (the webhook, a later GET, the list) agreeing on it, just like any other pinned value.

Default branch

If you don’t pick, the pack’s declared default arm runs (usually success) — so a suite that only cares about the happy path needs zero branch setup. Pin a branch only for the tests that exercise the other arm.
“Succeed, then fail on the retry.” Making the same call branch differently across repeated attempts (a flaky third-party that fails once then succeeds) needs an ordered, sequenced choice. That primitive is post-MVP — for now, a branch choice is fixed per world (pin it, run the test, reset). Most failure-path tests only need one arm at a time.