Record the flow as pairs, not requests
A request on its own says little about a stateful API. What matters is the pair: what the server sent back and what the app did with it next. Record the full flow from launch to the target call with request and response bodies, status codes, headers and timestamps, and keep the order. A proxy when it decrypts, or an interceptor that logs both directions, gives this view.
Name each call by what it changes rather than by its path, because the same path may serve several purposes with different parameters. A call that returns a session identifier, a call that returns account context, and a call that creates a resource are three different roles even when they share a base URL.
Build the dependency graph between calls
For every value in a request, ask which earlier response produced it. Most values are constants or user input, and the interesting ones appear in a response first. Search the captured responses for each request value and note the match. The result is a graph of which calls must run before which.
Confirm each edge with a negative test. Replace the suspect value with a syntactically valid placeholder and send the request. If the server refuses, the value is required and it has to come from the earlier call. If the server accepts, the value was optional or ignored, and the dependency you thought you found is not real. Negative tests are the only way to tell a required link from a coincidence.
Classify the calls before the target
Calls that precede the target fall into groups, and each group needs different handling in a client.
- Session opening calls establish identity and return tokens. They usually run once per client session, and later calls depend on the value they return.
- Context calls return configuration, entitlements or account state. The app may cache their answers for the whole session, so your client can fetch them once at the start.
- Resource creation calls make an object the target refers to by identifier. Replaying them creates duplicates, so run them once against a scratch account.
- Refresh calls replace an expiring value ahead of the expiry. They belong before the deadline rather than after a failure, because a refresh request sent after the value lapses can be refused on its own.
Grouping the prefix this way tells you which calls to replay on every run and which to replay once against a scratch account.
Test whether order is enforced
Servers enforce order to different degrees, and the tests are cheap.
- Send the target call alone with a fresh session. If it works, the prefix only supplied values you can copy and the state requirement is weak.
- Send the prefix and the target, skipping one middle call. A failure names the dependency of the target on that call. A success means the middle call served a different screen or feature.
- Send the prefix twice before the target. If the target still works, the prefix is repeatable for your purposes. If it fails, one of those calls creates state that the second run corrupts.
Handle state that no request produces
Some dependencies do not appear in the capture. An app may wait for a push message, a server-side job, or a status change that another service triggers. The symptom is a target call that fails even though the same prefix worked in the app.
Check the app log for push payloads that arrive between the prefix and the target. Check whether the target call repeats on a timer in the app, which suggests polling for a state the server sets asynchronously. When a dependency is genuinely asynchronous, the client has to poll the same status endpoint the app polls rather than assume the prefix was enough.
Encode the machine in a client
A reconstructed flow becomes a small state machine: a session step, a context step, optional creation steps, and the target. Each step validates its own response and raises a typed error on the status codes that mean the session expired. On those errors the client re-runs the prefix rather than retrying the failed call, because a token that expired once will fail again.
Stateful API workflow reconstruction is the service form of this work, and the Python client is where the machine usually ships. 401 and 403 replay diagnosis covers the failures that mean the state went stale mid-flow.
Related work
Reviewed 28 September 2026 · SReverse research desk