Mapping Stateful API Workflows to Integration Test Phases Before Writing a Single Request
Map your API's state dependencies and test phases before writing requests to avoid chained failures.

Integration testing against stateful APIs starts by mapping the state each call produces and the later calls that consume it. That map determines the test phases, their assertions, and whether each dependency needs a stateless stub, a stateful simulator, or sandbox validation. Before writing a single request, engineers must make those decisions so tests validate complete workflows without relying on production.
Why writing requests before mapping workflows produces the wrong tests
A suite passes when requests run alone, then fails when those same requests are chained. A capture call references a payment ID that the preceding create call never produced, or a downstream operation lacks the token it needs. Each endpoint appears covered. The sequence is not, because the tests were assembled request by request without tracing their dependencies.
The structural mistake is choosing requests and substitutes before deciding what state the workflow requires. Without that map, engineers cannot distinguish an assertion about response parsing from one about a resource created earlier. They also cannot decide which dependencies need memory or how many test phases the workflow demands. The graph must therefore precede mock definitions and request authoring rather than become documentation added after the suite exists.
What a workflow map actually captures — state tokens, resource handles, and the dependencies between steps
A workflow map is a directed graph of state changes. Each node represents an API call, and each edge identifies an artifact that one call produces and another consumes. In an e-commerce checkout, order creation produces the order identifier that connects payment and fulfillment to the same purchase. Payment creation produces a separate payment ID. Stripe needs an existing PaymentIntent ID for capture, so the later capture request must use the same handle that creation returned.
The map also includes state that arrives outside the synchronous request chain. A payment_intent.succeeded event can provide the transition that permits order fulfillment to proceed. That edge belongs in the graph itself. If a webhook handler and a reconciliation sweep can both act on the pending payment, their paths must show the shared resource and resulting concurrency risk.
Other workflows have different artifacts but the same dependency structure. A GitHub installation token is scoped and expires, while a Slack channel ID or PayPal order ID connects operations across a resource lifecycle. A handle alone is not sufficient. The map must show when the handle can still be used, including retry boundaries and transitions that commit effects.
How the workflow map partitions into distinct test phases, each with a defined assertion scope
Phase boundaries follow what each part of the graph produces and consumes. A phase covers a subgraph with known inputs or outputs supplied by an earlier phase, and its assertions concern the changes that subgraph owns. The tool does not define the boundary. For a commercial workflow, this divides the tests into four phases.
Phase 1 covers domain logic before the network. A function-level fake for the payment client supports assertions about pricing, discounts, routing, and the resulting request shape. No simulator is needed at this boundary. Phase 2 then exercises protocol and serialization through real HTTP served by a local stateless stub, asserting URL construction, header encoding, authentication token handling, and response parsing without chaining calls.
Phase 3 covers the stateful sequence, including creation, capture, confirmation, and fulfillment as the mapped workflow requires. Its simulator must remember the payment ID produced earlier and validate its use downstream. Phase 4 tests the provider boundary with a small sandbox suite that uses safe credentials and cleans up after itself. It checks one or two critical request shapes for assumptions beyond the simulator’s reach, with these scopes separating request correctness from sequence correctness and provider compatibility.
How do you test APIs where order of operations matters without stateless stubs hiding errors?
A stateless stub returns a matched response regardless of the calls that preceded it. That is sufficient for Phase 2, where response parsing need not depend on a previous creation. Phase 3 requires a different mechanism. A canned PaymentIntent response cannot establish that the ID in a capture request came from the create call, because the stub retains no record of that creation.
A stateful simulator must therefore keep resource records from one call to the next within a session. Creating a PaymentIntent must produce an ID that subsequent requests can retrieve and capture. State transitions must constrain what happens next. An intent that has been captured cannot be captured again, and one that has been cancelled cannot allow fulfillment to go ahead.
The simulator must retain idempotency keys so repeated creation does not produce duplicates, and state transitions must trigger webhook payloads in a controlled sequence. Pre-seeded realistic data is also necessary for pagination and filtering assertions that an empty registry would conceal. Fidelity follows the assertions being made. A simulator that remembers IDs but ignores transitions supports only part of the stateful phase.
Fault injection as a first-class assertion in stateful phases, not an afterthought
Failure scenarios belong on the same graph as successful transitions. A failure can leave a payment ID or committed effect in an uncertain state at a dependency edge. The fault belongs at that dependency edge. A 503 with Retry-After followed by success must exercise a retry that preserves the payment identity without creating another resource.
Other paths require different assertions. A 202 accepted response followed by two pending polling responses must keep fulfillment waiting until the work is complete and let polling stop correctly. A commit-then-drop scenario must commit the payment effect but drop the HTTP response, forcing the client to retry safely. Concurrent duplicate payment_intent.succeeded deliveries must complete exactly one order transition. Each case tests a sequence.
The resilience matrix must also cover 429, 500, and 503 responses with dynamic headers, together with ReadTimeout, ConnectTimeout, and sudden TCP disconnects. Circuit breaker testing must also have its own tests. These scenarios belong in Phase 3 wherever their assertions depend on accumulated state. Mapping them first identifies the token or effect at risk rather than treating every failed endpoint as interchangeable.
Mapping idempotency and webhook legs — the two workflow elements most often missing from phase design
Idempotency adds a dependency between a request and its later repetition. Stripe and Resend enforce it through the provider's Idempotency-Key header, so the simulator must honor that header. When the request is sent again with the same key, it must return the same response without creating another record. This requires memory of the earlier request. Phase 2 can test header encoding, but only Phase 3 can show what the repeated call does to stored state.
Webhook delivery adds an asynchronous edge between a provider transition and the application's response to it. The map must expose this edge, including the point where the synchronous path depends on its result. Phase 3 must then assert signature validation, ordering, retry after delivery failure, and deduplication. A simulator that cannot inject the webhook at a specified point leaves the asynchronous leg untested or pushes the test toward a live endpoint, defeating the phase's purpose.
Where each test phase lives in the CI pipeline and what gates each transition
Phase 1's pre-network assertions need no provider access or simulator, while Phase 2 runs after unit tests and acts as a pull-request gate before merge. Its failures identify protocol or serialization mismatches against the agreed interface. Stateful sequence checks need a separate boundary. Phase 3 must run in ephemeral, per-PR environments because shared resource records allow one test's activity to contaminate another's assertions.
Phase 4 belongs in pre-release or staging, where the minimal live suite uses safe credentials, unique test references, and cleanup. Its failure signal is assumption drift rather than the code-regression signal sought in earlier phases. Provider access has a deliberate pipeline position. It is not a fallback for state that the integration environment failed to model.
Secrets must enter from the CI vault at the phase boundary where they are needed. Phases 1 and 2 require none, even though Phase 2 checks how authentication tokens are encoded. Reports must preserve the same separation. Publishing JUnit XML for each phase lets a failure stop the right gate and keeps a stateful sequence failure from being reported as protocol-test noise.
Applying the phase map to AI agents that call external APIs
An agent can write output that makes sense while building an invalid API request. Its workflow map therefore needs parameter extraction and endpoint selection as explicit assertion targets, as well as the resource dependencies any client has. Each tool call still produces or consumes state. A multi-turn create, read, and update sequence must show how the agent passes the resource ID from one turn into the next.
Function-level tool fakes serve Phase 1 by testing tool selection and parameter extraction. HTTP interception supports the protocol boundary, where malformed JSON, wrong field names, missing required keys, and amounts expressed in dollars instead of minor units become concrete request failures. Stateful consequences then belong in Phase 3. An in-process stateful fake must check that the agent passes the correct handle through the sequence and does not accept unrelated canned responses.
Record/replay lets CI check application logic, but recorded responses cannot show how well the model works now. Live model evaluation belongs at the Phase 4 boundary before release. The distinction keeps the assertion scope explicit. The agent's language does not substitute for evidence that its generated requests obey the API contract and preserve workflow state.
Simulator drift as the failure mode that undoes correct phase design
Even a correctly partitioned suite can validate an outdated simulation. A provider may add a required field, change a response shape, or alter a state transition while the simulator continues to accept the previous contract. Phase 3 then passes against the wrong behavior. A simulator built for API v1 but used for v2 creates the same mismatch through version confusion.
Automated schema diff jobs compare its registered schema with the upstream OpenAPI or AsyncAPI contract and flag field additions, removals, or type changes to the owning squad. Consumer-driven contract tests that run in upstream service CI block breaking changes before merge. Those checks address contracts before release. A thin Phase 4 suite then tests authentication, one or two critical request shapes, response parsing, and a webhook against the real boundary.
Schema comparison exposes contract differences, consumer tests check compatibility, and live validation detects assumptions the simulator cannot report about itself. Verification must run before every release. Automated comparison must be tied to the represented API version.
Turning the workflow map into a reusable test architecture decision record
The map becomes a test architecture decision record when it records why the phases were designed that way. Each step must name the state token it produces, the downstream consumer that uses it, and the assertion scope assigned to it. The rationale must connect that assignment to dependency cost and state requirements. Each dependency also needs a recorded substitute type, whether a stateless stub, stateful simulator, or sandbox validation, and the API version against which the simulator is verified.
The record must connect fault scenarios to the edges where failures endanger state. It must also record how drift will be detected for each simulated dependency, whether through schema comparison, consumer contract testing, or live validation. Ownership and versioning belong with those decisions. A centralized registry can make the record a shared source of truth for mock assets, preventing duplicated configurations and exposing cross-squad drift.
When the workflow changes, the record must change with it so simulator requirements and CI gates remain tied to the same graph. That makes the map an input to reusable test tooling. Request authoring follows an explicit architectural decision. The tests can then establish not merely that each call works, but that every dependent call uses the state the workflow actually produced.


