# Replayable flows

> Turn captured requests, such as log in, create an order and pay, into an API client flow that reads fresh IDs, tokens and cookies from each response.

Source: https://docs.subcloak.com/cloak-proxy/api-client/run/replayable-flows/

Added in Cloak Proxy 0.2.1.

**Make Replayable Flow** turns a sequence of captured requests into a [flow](https://docs.subcloak.com/cloak-proxy/api-client/run/flows/) in the API client that sends them again in order. Replaying captured requests as they are usually fails: the order ID in step 3 belongs to an order the server created during the capture, and the access token from the login has expired. A replayable flow fixes that by reading every value a later request reuses from the fresh response of an earlier one, so each run works with new IDs and tokens. Load-testing tools call this correlation.

Use it to reproduce a bug that needs several calls, to keep a smoke test of a real user journey, or to start a test from what your app actually does.

## Make a flow from captured requests

1. In the [traffic list](https://docs.subcloak.com/cloak-proxy/inspect/traffic-list/), select the requests of the journey, for example the login, the order and the payment. Order doesn't matter: Cloak Proxy sorts them by when they started.

   To pick every request that carried one value, [trace it](https://docs.subcloak.com/cloak-proxy/inspect/trace-value/) and click **Make Replayable Flow** in the Trace Value window instead.

2. Right-click one of the selected requests and choose **Make Replayable Flow from N Requests…** (or **Make Replayable Flow…** for one request).

   The sheet lists the steps and the values it will chain between them.

3. Check **Values chained**. Each entry says which response provides the value and which later requests use it.

4. Change the **Flow name** if you like, and choose whether to check each step's status.

5. Click **Create Flow** (or press <kbd>Return</kbd>).

   The API client opens on **Flows** with the new flow selected.

6. Click **Run** (<kbd>⌘</kbd> <kbd>↩</kbd>) to replay it.

**Make Replayable Flow…** is unavailable when you right-click an encrypted tunnel, and tunnels in a selection are left out.

*[Screenshot: The Make Replayable Flow sheet for four ShopApp requests: log in, create an order, get the order and pay, with three chained values, accessToken from step 1's body $.access_token used in the Authorization header of steps 2 and 3, userId from step 1 used in the X-User-Id header of step 4, and orderId from step 2's body $.data.order.id used in the path of step 3 and the order_id query parameter of step 4. Three values chained across four steps: a token, a user ID and an order ID.]*

### The Make Replayable Flow sheet

| Part | What it shows or does |
|---|---|
| **Flow name** | The name of the flow and of its collection. It starts as **Replay:** followed by the first request's method and path and how many more there are, such as **Replay: POST /v1/login and 3 more**. |
| **Steps (N)** | One line per request: method, host and path, in time order. |
| **Values chained (N)** | Every value carried from a response to later requests: the variable, such as `{{orderId}}`, the captured value (long ones are shortened, such as `eyJhbGciOiJI…iJ9.c2ln`), **From step N's response,** and where in it, and **Used in** with each step and where. When there are none, it says **No later request reuses a value from an earlier response, so the steps replay as captured.** |
| **Check that each step succeeds, like it did when captured** | On by default. Adds a **Check** block after each step whose captured status was below 400. |
| **Cancel** | Closes the sheet (<kbd>Esc</kbd>). |
| **Create Flow** | Creates the collection and the flow, and opens the flow in the API client. |

On Linux, **Make Replayable Flow…** opens in a small window of its own rather than a sheet, with the same parts.

## What Create Flow makes

Two things go into your [workspace](https://docs.subcloak.com/cloak-proxy/api-client/workspace-and-git/):

- **A collection** named like the flow, with one request per step, named by number and request, such as `1. POST /v1/login` and `2. POST /v1/orders`. Each one is a copy of the captured request, made the same way as [Save to API Client](https://docs.subcloak.com/cloak-proxy/api-client/capture-to-client/): the method, URL, headers and decompressed body, with the captured response saved as an example. Chained values are replaced with `{{variables}}`.
- **A flow** in **Flows** with the same name. It runs the steps in order.

For each step, the flow has these blocks:

| Block | Example | What it does |
|---|---|---|
| **Send Request** | `step2` | Sends the step's request and saves the result as `step1`, `step2` and so on. |
| **Check** | `step2.status < 400`, message **Step 2 (POST api.shopapp.example/v1/orders) should succeed, like it did when captured (201)** | Fails the run if the step didn't succeed. Only when the status check is on and the step succeeded when captured. |
| **Set Variable** | `orderId` = `step2.body.data.order.id` | Reads a value from this step's fresh response for later steps. One per value the step provides. |

For the screenshot above, the flow looks like this:

```text title="Replay: POST /v1/login and 3 more"
Send Request   1. POST /v1/login          → step1
Check          step1.status < 400
Set Variable   accessToken = step1.body.access_token
Set Variable   userId = step1.body.user.id
Send Request   2. POST /v1/orders         → step2   (Authorization: Bearer {{accessToken}})
Check          step2.status < 400
Set Variable   orderId = step2.body.data.order.id
Send Request   3. GET /v1/orders/{{orderId}}  → step3   (Authorization: Bearer {{accessToken}})
Check          step3.status < 400
Send Request   4. POST /v1/payments?order_id={{orderId}}  → step4   (X-User-Id: {{userId}})
Check          step4.status < 400
```

It's an ordinary flow: you can edit, reorder or delete blocks, add **Output** blocks, set [inputs](https://docs.subcloak.com/cloak-proxy/api-client/run/flows/#inputs), or [automate](https://docs.subcloak.com/cloak-proxy/api-client/run/flows/#automate-a-flow) it. See [Flows](https://docs.subcloak.com/cloak-proxy/api-client/run/flows/) for every block.

## How values are chained

Cloak Proxy goes through the steps in order. For each response, it collects the values that look like identifiers, then checks whether a later request uses them.

1. **Find candidate values in a response.** It looks at every string and number in a JSON response body (up to the first 200 items of each array) and at the response headers, except standard ones such as `Date`, `Content-Type`, `Set-Cookie`, `ETag`, `Cache-Control`, `Server` and the security headers.

2. **Keep only values that look like IDs or tokens.** A value qualifies when it:

   - is at least 4 characters long, with no spaces, and
   - contains a digit, or is at least 20 characters long, and
   - isn't a Unix timestamp in seconds or milliseconds, a date such as `2026-10-14`, a version number such as `1.2.3`, or a number with fewer than 5 digits.

   So `fc68188e`, `usr_42ab` and a JWT qualify; `true`, `admin`, `2026-10-14T10:00:00Z` and `1760436000` don't.

3. **Skip values the app made itself.** If an earlier request (or the same one) already sent the value, the server didn't create it, so it isn't chained.

4. **Find where later requests use it.** It searches each later request's path, query parameters, headers and body (form fields or raw text). A value must match as a whole token: `48213` isn't replaced inside `948213`, but is in `/orders/48213` or `"id": "48213"`.

5. **Replace it with a variable.** Every use in a later request becomes `{{variable}}`, and a **Set Variable** block after the providing step reads the fresh value. A value is chained from the first response that has it, once.

Values that no later request uses aren't chained. Values the app got before the first selected request, such as a token from a login you didn't select, stay exactly as captured.

### Variable names

The variable is named after where the value was in the response:

| Found at | Variable | Read with |
|---|---|---|
| `body $.access_token` | `accessToken` | `step1.body.access_token` |
| `body $.user.id` | `userId` | `step1.body.user.id` |
| `body $.data.order.id` | `orderId` | `step2.body.data.order.id` |
| `body $.items[0].sku_code` | `skuCode` | `step3.body.items[0].sku_code` |
| `header X-Request-Id` | `requestId` | `step1.headers["x-request-id"]` |

The name comes from the last key, in camelCase. When that key is a generic word such as `id`, `uuid`, `token`, `key`, `code`, `value` or `number`, the key before it is added in the singular: `orders.id` gives `orderId`. Header names lose an `X-` prefix. If two values would get the same name, the second gets a number, such as `orderId2`. Names that clash with flow variables, such as `response`, `item`, `index`, `trigger`, `vars` and `step1`, are avoided the same way.

### Cookies

Cookies come from the run, not the capture. When an earlier step's response set a cookie with `Set-Cookie`, that cookie is removed from the captured `Cookie` header of later requests. When the flow runs, the API client's cookie jar sends the fresh cookie the server set during the run.

A request's own `Cookie` header and the cookie jar are merged: the request sends its own cookies plus every cookie from the jar it doesn't already have. When both have a cookie with the same name, the request's own value wins.

> **Note:**
> Merging is new in 0.2.1. Before, a request with its own `Cookie` header got no cookies from the jar. This applies to every request the API client sends, not only replayable flows.

## Run the flow

In the API client, open the flow under **Flows** and click **Run** (<kbd>⌘</kbd> <kbd>↩</kbd>). The **Run** pane shows each step with its status and time, the chained values being set, and whether each check passed. See [Read the run results](https://docs.subcloak.com/cloak-proxy/api-client/run/flows/#read-the-run-results).

From the command line, run it with [`cloakctl flow run`](https://docs.subcloak.com/cloak-proxy/automation/cloakctl/#run-a-flow), for example from CI or cron. The command exits with `1` when a check fails:

```bash title="Terminal"
cloakctl flow run "Replay: POST /v1/login and 3 more"
```

The requests use the API client's settings, the active environment and your [variables](https://docs.subcloak.com/cloak-proxy/api-client/variables-and-environments/). To run the same flow against staging, replace the host in each request with a variable such as `{{baseUrl}}` and set it per [environment](https://docs.subcloak.com/cloak-proxy/api-client/variables-and-environments/#environments).

> **A replay sends real requests:**
> When the flow runs, its requests go to the real servers, exactly like the app's did. Replaying a checkout creates a new order and may charge a card. Replay against a test or staging server with test accounts, and only against servers you own or have written permission to test.

> **The collection holds captured credentials:**
> The requests are copies of what your app sent, so the collection can contain a password in a login body, API keys and tokens in headers, and cookies. They're saved in plain text in the workspace folder. Remove or replace them with [secret variables](https://docs.subcloak.com/cloak-proxy/api-client/variables-and-environments/) before you commit the workspace to Git or share it.

## When a replay fails

| Problem | What to do |
|---|---|
| The first request fails with `401` | Its credentials or token came from before the capture and have expired. Select the login request too, or update the token in the first request. |
| A later step fails with `404` or `400` | A value wasn't chained: it may be computed by the app, sent in a shape Cloak Proxy doesn't recognize, or too short to qualify. Find it with [Trace Value](https://docs.subcloak.com/cloak-proxy/inspect/trace-value/), then add a **Set Variable** block and put `{{name}}` in the request yourself. |
| A step fails because a value is a one-time code | Codes sent by email or SMS, CAPTCHA answers and signed nonces can't be replayed. Replace that step, or start the flow after it. |
| A **Check** fails | The step returned a status of 400 or higher. Read the step's response in the **Run** pane. A status that differs from the capture but is below 400, such as `200` instead of `201`, passes. |

## Make a flow from an AI assistant

The [MCP server](https://docs.subcloak.com/cloak-proxy/automation/mcp-server/) has a `build_replay_flow` tool. It takes `ids` (the request numbers, in any order), and optionally `name` (default **Replay of N requests**) and `check_status` (default `true`). It creates the collection and the flow, opens them in the API client, and returns the steps and every chained value with where it comes from and where it's used. Run the flow with `run_flow`. See the [MCP tools reference](https://docs.subcloak.com/cloak-proxy/automation/mcp-tools-reference/#build_replay_flow).

## Related

  - [Flows](https://docs.subcloak.com/cloak-proxy/api-client/run/flows/): Every block, inputs, automation and run results.
  - [Trace Value](https://docs.subcloak.com/cloak-proxy/inspect/trace-value/): Follow an ID from the response it came from to every request that sent it.
  - [Make tests from traffic](https://docs.subcloak.com/cloak-proxy/testing/make-tests/): The same chaining, as a collection with inferred tests.
  - [Headless and CI](https://docs.subcloak.com/cloak-proxy/automation/headless-and-ci/): Run flows from the command line and in pipelines.
