> For the complete documentation index, see [llms.txt](https://docs.does.qa/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.does.qa/guides/test-apis-in-a-flow.md).

# Test APIs in a Flow

API calls belong in the same Flow as your browser steps. Use them to seed data, fetch a token, assert backend state, or clean up after a UI journey.

```mermaid
flowchart TD
  open["Open app"] --> post["POST login API"]
  post --> check["Check JSON Value"]
  check --> next["UI or more APIs"]
```

Use the **Flow Builder** for this walkthrough.

## What you need

* A fully qualified API URL (or a [Value](/getting-started/terminology.md) that expands to one)
* Optional credentials or tokens as [Values](/values/value-store.md)
* A Flow open in the Flow Builder

## Build a login-token Flow

This example calls a login API, stores the JSON response, then asserts it. The same pattern works for any GET, POST, PUT, PATCH, or DELETE.

{% stepper %}
{% step %}

## Open the app

Start with [**Open**](/test-steps/starter/open.md). Point it at your application URL so the Test Case has a browser context, even when the next steps are API calls.
{% endstep %}

{% step %}

## Add the API call

Add [**POST**](/test-steps/integration/post.md) from the Integration family.

1. Set **URL** to the login endpoint. Use `$BASE_URL` plus a path when the host changes per environment.
2. Set **Body Content-Type** to **JSON**.
3. Enter a JSON body that uses Values for credentials, for example username and password from Assets.
4. Set **Response Storage Name** to something clear, such as `authResponse`.
   {% endstep %}

{% step %}

## Expect the right status

Open advanced options on the POST step. **Expected Response Status Codes** defaults to **2xx**. Change it when the API should return a specific code, such as **201**.

Statuses in the **5xx** range fail the step as errors.
{% endstep %}

{% step %}

## Send auth on later calls

For APIs that need a Bearer token or API key, open **Headers** on the Integration step and pass JSON such as:

```json
{
  "Authorization": "Bearer $API_TOKEN"
}
```

Keep `$API_TOKEN` (or similar) in [Assets → Values](/values/creating-values.md), or overwrite it for a Run with a [Run Recipe](/configuration/run-recipes.md). Integration steps read Headers you set on the step. They do not reuse browser cookies from Open.
{% endstep %}

{% step %}

## Assert the JSON response

Add [**Check JSON Value**](/test-steps/check/check-json-value.md).

1. Set **Input Item** to the same name you stored, such as `authResponse`.
2. Choose **Match Mode** **Contains** or **Equals**.
3. Set the expected JSON with **Custom**, or compare against another Value Store item.

Use [**Check JSON Schema**](/test-steps/check/check-json-schema.md) when you want a schema assert, or [**Check Value Store**](/test-steps/check/check-value-store.md) for a single stored field.
{% endstep %}

{% step %}

## Continue with UI or more APIs

Add further Integration steps that reuse `$BASE_URL` and Headers, or continue with Action and Check steps on the page. The stored response stays available for the rest of the Test Case under the **Response Storage Name**.
{% endstep %}
{% endstepper %}

{% hint style="info" %}
**Pro tip:** Store API hosts and secrets as Assets Values or Recipe overwrites. Keep response storage names specific (`authResponse`, `order`) so later Checks stay readable.
{% endhint %}

A finished login-token path looks like this:

<figure><img src="/files/5KoOE7nWUfciQXfy2tdJ" alt="Open, POST, and Check JSON Value"><figcaption><p>Example API login-token Flow</p></figcaption></figure>

## Choose GET, POST, PUT, PATCH, or DELETE

| Test Step                                   | Typical use                                       |
| ------------------------------------------- | ------------------------------------------------- |
| [GET](/test-steps/integration/get.md)       | Read a resource                                   |
| [POST](/test-steps/integration/post.md)     | Create a resource or submit a form-style API body |
| [PUT](/test-steps/integration/put.md)       | Replace a resource                                |
| [PATCH](/test-steps/integration/patch.md)   | Update part of a resource                         |
| [DELETE](/test-steps/integration/delete.md) | Remove a resource                                 |

Switch **Method** on an Integration API step to change between these without rebuilding the rest of the step.

## What gets stored

| Response Object    | Stored under the Response Storage Name |
| ------------------ | -------------------------------------- |
| **Body** (default) | Response body                          |
| **Headers**        | Response headers                       |
| **Complete**       | Body and headers together              |

The storage name must start with a letter and use only letters, numbers, and underscores.

## Environments

Use the same Flow across staging and production by keeping the host in a Value:

* [Share data with Values](/better-coverage/share-data-with-values.md) for Assets Values and `$` insertion
* [Run across environments](/better-coverage/run-across-environments.md) for tags and overwrites
* [CI / CD](/configuration/ci-cd.md) to pass Value overrides from the pipeline

## Browser timing is a different check

[**Check API Response Time**](/test-steps/check/check-api-response-time.md) measures how long a **browser** network request takes during the Test Case. It does not time Integration API steps. Use it when you care about page network performance, not when you are asserting an Integration response.

## Related

* [Integration](/test-steps/integration.md)
* [POST](/test-steps/integration/post.md)
* [Check JSON Value](/test-steps/check/check-json-value.md)
* [Value Store](/values/value-store.md)
* [Share data with Values](/better-coverage/share-data-with-values.md)
