> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cloudcruise.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API Flow

> Replay a chain of HTTP requests as one node

The **API Flow** node sends a chain of HTTP (XHR/fetch) requests as one atomic step. Use it instead of UI interactions when a site's internal API returns the data you need, or performs the action you want, directly. It is faster and less brittle than clicking through the UI.

By default the requests run in the browser's live session, so the site's cookies attach natively. You never read, store or pass cookies yourself. With `credentials: "omit"`, the requests run cookie-free through a server-side relay instead, for endpoints that don't need the user's session.

## Example

Search for a story, then fetch the top result. The second step reads a value extracted by the first:

```json theme={null}
{
  "id": "3f2b8c1e-7a4d-4e9b-9c6f-1d2e3f4a5b6c",
  "name": "Fetch top story",
  "action": "API_FLOW",
  "parameters": {
    "target_step_id": "story",
    "steps": [
      {
        "kind": "http",
        "id": "search",
        "label": "Search stories",
        "method": "POST",
        "url": "https://api.example.com/search",
        "body": {
          "type": "json",
          "value": { "query": "{{ context.inputs.search_query }}", "page": 0 }
        },
        "replay_target": {},
        "extract": [
          { "name": "top_id", "source": "response_body", "expression": "hits[0].id", "required": true }
        ]
      },
      {
        "kind": "http",
        "id": "story",
        "label": "Fetch top story",
        "method": "GET",
        "url": "https://api.example.com/items/{{ steps.search.top_id }}",
        "replay_target": {},
        "extract": [
          { "name": "title", "source": "response_body", "expression": "title", "required": true }
        ]
      }
    ],
    "field_json_map": "{ \"top_story_title\": steps.story.title }"
  }
}
```

After the node runs, `{{context.top_story_title}}` holds the title and it appears in the run result.

## Parameters

| Parameter | Type | Required | Description |
| - | - | - | - |
| `steps` | array | Yes | Requests to send. See [Steps](#steps) |
| `target_step_id` | string | Yes | ID of the step whose result the node publishes |
| `field_json_map` | string | No | JSONata expression evaluated over `{ steps, context }`. Its top-level keys merge into the workflow context, so `{ "total": ... }` lands at `context.total` and appears in the run result. Nest keys under `runtime` to keep a value available to later nodes but out of the result |
| `credentials` | string | No | `include` (default), `same-origin`, or `omit`. `omit` runs the requests cookie-free through a server-side relay |
| `redirect_policy` | string | No | `follow` (default) or `manual`. `manual` captures a redirect's `Location` instead of following it, for example to read an OAuth `code`. A step can override it with its own `redirect_policy` |
| `ordering` | array | No | `{ from_step_id, to_step_id, reason }` entries that document a dependency with no value flowing between the steps, such as a request that only sets a cookie. `reason` is `session_state`, `side_effect`, or `explicit_order`. The `steps` array must already list the steps in that order |
| `run_if` | object | No | Run the node only if the condition matches; otherwise skip it and continue on the `to` edge |

## Steps

| Field | Type | Required | Description |
| - | - | - | - |
| `id` | string | Yes | Unique within the node. Lowercase letter first, then lowercase letters, digits or `_`, up to 64 characters |
| `label` | string | Yes | Human-readable name |
| `method` | string | Yes | `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, or `OPTIONS` |
| `url` | string | Yes | Full URL. May contain `{{ }}` references |
| `replay_target` | object | Yes | Where the request is sent from. Use `{}` (top frame) unless the request must come from inside an iframe |
| `kind` | string | No | `http` |
| `query` | object | No | Query parameters. An array value repeats the parameter |
| `headers` | object | No | Request headers. Don't set `Cookie` or `Authorization` by hand; they come from the session or from an extracted value |
| `body` | object | No | Request body. See [Body](#body). Omit for no body |
| `extract` | array | No | Values to read from the response. See [Extract](#extract) |
| `repeat` | object | No | Turns the step into pagination. See [Pagination](#pagination) |
| `expected_status` | object | No | `{ "range": "2xx" }` (default) or `{ "values": [200, 201] }` |
| `retry` | object | No | `{ max_attempts, retry_on_status?, retry_on_network_error? }`, up to 5 attempts. Not allowed on `POST` or `PATCH` steps |
| `timeout_ms` | number | No | Request timeout |
| `on_error` | object | No | `{ "message": "..." }` replaces the default failure message when the step fails |
| `save_response_as_file` | object | No | `{ "file_name": "report.pdf" }` stores the response as a file. Target step only, PDF responses only, and not combined with `extract`, `repeat`, `credentials: "omit"` or `redirect_policy: "manual"` |
| `protocol_hint` | string | No | `rest`, `graphql`, or `other`. Informational only |
| `graphql_error_policy` | string | No | `fail_on_any`: fail the step if a GraphQL response contains errors |

### Value Flow

Steps run in array order. Later steps read earlier results with inline references: `{{ steps.<step_id>.<extract_name> }}` in `url`, `query`, `headers`, or a `body` value. List every step after the steps it references. Workflow variables such as `{{ context.inputs.order_id }}` work in the same fields.

### Extract

Each entry has a `name` and one of three sources:

| Source | Shape | Reads |
| - | - | - |
| `response_body` | `{ name, source: "response_body", expression }` | JSONata path into the parsed body, e.g. `hits[0].id` |
| `response_header` | `{ name, source: "response_header", header }` | A response header |
| `response_text` | `{ name, source: "response_text", pattern, flags?, group? }` | Regex match over the raw response text. `group` picks a capture group |

Set `required: true` to fail the step when no value is found. Without it, a missing value silently becomes empty in later references. A `response_text` extraction returns one match; to collect many, use `$match` in `field_json_map`.

### Body

| `type` | Shape |
| - | - |
| `json` | `{ "type": "json", "value": <any JSON> }`. Put `{{ }}` references directly in as values |
| `form_urlencoded` | `{ "type": "form_urlencoded", "value": { "field": "value" } }` |
| `text` | `{ "type": "text", "value": "raw string" }` |
| `multipart` | `{ "type": "multipart", "parts": [...] }`. Each part is `{ name, kind: "text", value }` or `{ name, kind: "file", filename?, content_type?, source }` |
| `binary` | `{ "type": "binary", "source": ..., "content_type"?: ... }` |

A file `source` is `{ "type": "signed_url", "url" }`, `{ "type": "workflow_input", "path" }`, or `{ "type": "prior_step", "step_id", "extraction_name" }`.

### Pagination

`repeat` sends the step repeatedly and collects the pages:

| Field | Description |
| - | - |
| `cursor.from` | Reads the next cursor from each response. Same shapes as an `extract` entry, without `required` |
| `cursor.into` | `{ location: "url" \| "query" \| "header" \| "body", path }`: where the cursor goes in the next request |
| `until` | `{ "type": "cursor_absent" }`, `{ "type": "empty_collection", "path" }`, or `{ "type": "response_predicate", "expression" }` |
| `accumulate` | JSONata path collected from every page into one array, available as `steps.<id>.accumulated` |
| `max_iterations` | Required. Hard cap on pages, at most 100 |

## Limits

Saving a workflow fails if an API Flow node breaks these limits:

* At most 20 steps per node
* Step IDs must be unique and match the ID format above, and `target_step_id` must name one of them
* Extraction names must be unique within a step
* At most 40 headers and 40 query parameters per step
* `retry.max_attempts` at most 5
* `repeat.max_iterations` is required and at most 100

## Edges

API Flow uses a single `to` edge to the next node.

## Notes

* With the default `credentials: "include"`, requests are sent from the page the browser is on. If that page has a different origin than the API (a different subdomain counts), the request fails. Make sure the [Start](/concepts/workflow-dsl/start) URL or an earlier [Navigate](/concepts/workflow-dsl/navigate) puts the browser on the right origin
* `POST`, `PUT`, `PATCH` and `DELETE` steps change data on the target system on every run. Test them carefully
* If an API Flow node replaces UI steps that did the same thing, remove those steps, or the action runs twice
* The Builder Agent can build API Flow nodes from network traffic it captures while you navigate the site


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.