> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.doc-detective.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.doc-detective.com/_mcp/server.

# httpRequest

> Perform a generic HTTP request, for example to an API.

The `httpRequest` action makes arbitrary HTTP calls, allowing you to interact with and validate APIs or other web services directly within your tests.

You can specify a basic GET request using a string shorthand or use an object for more complex requests and validation:

* **String Shorthand:** Provide the full URL directly as the value for the `httpRequest` key. This performs a basic GET request to that URL.
* **Object Format:** Use an object with the following properties:
  * `url`: (Required unless using `openApi`) The target URL for the request.
  * `method`: (Optional) The HTTP method (for example, `GET`, `POST`, `PUT`, `DELETE`). Defaults to `GET`.
  * `timeout`: (Optional) Maximum duration in milliseconds to wait for the request to complete.
  * `request`: (Optional) An object defining the request details:
    * `headers`: (Optional) Request headers, as either key-value pairs or a newline-separated string block such as `Content-Type: application/json\nAuthorization: Bearer token`. In the string form, Doc Detective splits each line on its first colon. Header values can therefore contain colons themselves, including full URLs, timestamps like `12:00:00 GMT`, and `host:port` values.
    * `parameters`: (Optional) Key-value pairs for query string parameters.
    * `body`: (Optional) The request body. Can be a string or JSON object.
  * `response`: (Optional) An object defining expected response validation:
    * `headers`: (Optional) Key-value pairs for expected response headers. Values must be strings.
    * `body`: (Optional) Expected response body. Can be a string or JSON object.
    * `required`: (Optional) Array of field paths that must exist in the response body. Uses dot notation for nested fields (for example, `user.name`) and bracket notation for array indices (for example, `items[0].id`). Fields can have any value, including `null`.
  * `allowAdditionalFields`: (Optional) Whether the response may contain fields beyond those declared in `response.body`. Defaults to `true`. Set it to `false` to fail the step when the response includes any field not declared in `response.body`. The check is recursive. It descends into nested objects and names each undeclared field by its dot-path, such as `user.extra`. It applies only when the top-level `response.body` declares fields. When `response.body` is unset or empty, the response has no key constraint and the check passes. A nested empty object still constrains. A `response.body` of `{ "user": {} }` requires `user` to be empty, so any field under it fails the step.
  * `statusCodes`: (Optional) An array of acceptable HTTP status codes. If the response code is not in this list, the step fails (default: `[200, 201]`).
  * `openApi`: (Optional) Define the request based on an OpenAPI definition. Can be a string (operation ID) or an object:
    * `name`: (Optional) Name of the registered OpenAPI definition (if multiple are loaded).
    * `descriptionPath`: (Optional) Path or URL to the OpenAPI description file.
    * `operationId`: (Required) The ID of the operation to use.
    * `server`: (Optional) Base server URL for the request. If omitted, Doc Detective uses the first server defined in the OpenAPI description.
    * `validateAgainstSchema`: (Optional) Which parts of the request and response to validate against the OpenAPI schema (`request`, `response`, `both`, or `none`). The step fails on a mismatch. Defaults to `both`.
    * `mockResponse`: (Optional) When `true`, skips the HTTP request and uses the response example or schema from the OpenAPI description as the response data. Useful for testing an API that isn't fully implemented yet. If `statusCode` isn't set, Doc Detective uses the first response code defined for the operation.
    * `statusCode`: (Optional) Response code to use for validation, examples, and status-code checking. Doc Detective adds this code to the step's accepted `statusCodes`.
    * `useExample`: (Optional) Which parts of the request and response to populate with example data from the OpenAPI description (`request`, `response`, `both`, or `none`). Defaults to `none`.
    * `exampleKey`: (Optional) Key of the specific example to use if multiple exist.
    * `headers`: (Optional) Request headers to add to every request built from the definition, such as an `Authorization` header. A step-level value overrides a matching header set in your configuration.
      *Note: Properties like `request.headers`, `request.parameters`, `request.body` can override values from the OpenAPI definition or example.*
  * *Output Saving:* You can also save the response body using `path`, `directory`, `maxVariation`, and `overwrite` properties. See the [`httpRequest`](/reference/schemas/httprequest) reference for details.

**Setting Variables:** To capture parts of the response for later steps, use the step-level `variables` object. You can assign values based on the response using expressions like `$$response.body`, `$$response.headers`, `$$response.status`, etc. You can use dot notation for nested JSON fields (for example, `$$response.body.user.id`).

Depending on which validations you configure, the action also exposes the result of each check in its `outputs` object. To capture any of them for later steps, use the step-level `variables` object with the matching expression. Each output is present only when its check applies:

* `$$requiredFieldsPresent`: whether every field in `response.required` was found (when `response.required` is set).
* `$$bodyMatches`: whether the response body matched `response.body` (when `response.body` is set).
* `$$headersMatch`: whether the expected `response.headers` were present (when `response.headers` is set).
* `$$noUnexpectedFields`: whether the response contained only expected fields (when `allowAdditionalFields` is `false`).
* `$$responseSchemaValid`: whether the response matched the OpenAPI schema (when validating the response against an OpenAPI definition).
* `$$variation`: the fractional difference between the new response and the reference file (when you save the response with `path` and a reference file already exists).

> For comprehensive options, see the [`httpRequest`](/reference/schemas/httprequest) reference.

## Examples

Here are a few ways you might use the `httpRequest` action:

### Basic GET request (string shorthand)

```json
{
  "tests": [
    {
      "steps": [
        {
          "description": "Perform a basic GET request.",
          "httpRequest": "https://reqres.in/api/users?page=2"
        }
      ]
    }
  ]
}
```

### Basic GET request (object format)

```json
{
  "tests": [
    {
      "steps": [
        {
          "description": "Perform a simple GET request using object format.",
          "httpRequest": {
            "url": "https://reqres.in/api/users?page=2"
          }
        }
      ]
    }
  ]
}
```

### POST request with JSON body

```json
{
  "tests": [
    {
      "steps": [
        {
          "description": "Create a user via POST request.",
          "httpRequest": {
            "url": "https://reqres.in/api/users",
            "method": "POST",
            "request": {
              "body": {
                "name": "morpheus",
                "job": "leader"
              }
            }
          }
        }
      ]
    }
  ]
}
```

### PUT request with headers and query parameters

```json
{
  "tests": [
    {
      "steps": [
        {
          "description": "Update a user via PUT request.",
          "httpRequest": {
            "url": "https://reqres.in/api/users/2",
            "method": "PUT",
            "request": {
              "headers": {
                "Content-Type": "application/json"
              },
              "parameters": {
                "source": "test"
              },
              "body": {
                "name": "morpheus",
                "job": "zion resident"
              }
            }
          }
        }
      ]
    }
  ]
}
```

### Send request headers as a string block

You can also provide `request.headers` as a single newline-separated string instead of an object. Doc Detective splits each line on its first colon into a name and value. Values that contain colons, such as callback URLs or timestamps, stay intact.

```json
{
  "tests": [
    {
      "steps": [
        {
          "description": "Send request headers as a newline-separated string.",
          "httpRequest": {
            "url": "https://reqres.in/api/users",
            "method": "POST",
            "request": {
              "headers": "Content-Type: application/json\nX-Callback: https://example.com/cb",
              "body": {
                "name": "morpheus",
                "job": "leader"
              }
            }
          }
        }
      ]
    }
  ]
}
```

### Validate response status code and body

```json
{
  "tests": [
    {
      "steps": [
        {
          "description": "Create user and validate response.",
          "httpRequest": {
            "url": "https://reqres.in/api/users",
            "method": "POST",
            "request": {
              "body": {
                "name": "morpheus",
                "job": "leader"
              }
            },
            "response": {
              "body": {
                "name": "morpheus",
                "job": "leader",
              }
            },
            "statusCodes": [201] // Expect HTTP 201 Created
          }
        }
      ]
    }
  ]
}
```

### Use OpenAPI definition (by operation ID)

Assumes an OpenAPI definition with operation ID `getUserById` is loaded.

```json
{
  "tests": [
    {
      "steps": [
        {
          "description": "Get user by ID using OpenAPI definition.",
          "httpRequest": {
            "openApi": "getUserById",
            "request": {
              "parameters": { // Provide required path parameter
                "id": 2
              }
            }
          }
        }
      ]
    }
  ]
}
```

### Use OpenAPI definition (detailed object)

```json
{
  "tests": [
    {
      "steps": [
        {
          "description": "Get user by ID using specific OpenAPI file.",
          "httpRequest": {
            "openApi": {
              "descriptionPath": "./reqres.openapi.json",
              "operationId": "getUserById"
            },
            "request": {
              "parameters": {
                "id": 3
              }
            }
          }
        }
      ]
    }
  ]
}
```

### Set a variable from response body

```json
{
  "tests": [
    {
      "steps": [
        {
          "description": "Create user and capture the ID.",
          "httpRequest": {
            "url": "https://reqres.in/api/users",
            "method": "POST",
            "request": {
              "body": {"name": "neo", "job": "the one"}
            },
            "statusCodes": [201]
          },
          "variables": {
            "USER_ID": "$$response.body.id"
          }
        },
        {
          "description": "Use the captured ID in the next request.",
          "httpRequest": {
            "url": "https://reqres.in/api/users/$USER_ID", // Use variable in URL
            "method": "GET"
          }
        }
      ]
    }
  ]
}
```

### Save response body to a file

```json
{
  "tests": [
    {
      "steps": [
        {
          "description": "Get user data and save response to file.",
          "httpRequest": {
            "url": "https://reqres.in/api/users/2",
            "method": "GET",
            "path": "user_2_response.json", // File name
            "directory": "./output/api_responses" // Output directory
          }
        }
      ]
    }
  ]
}
```

### Validate fields with dynamic values

When testing APIs that return unpredictable values (like timestamps, UUIDs, or session tokens), you can verify that fields exist without asserting their exact values.

```json
{
  "tests": [
    {
      "steps": [
        {
          "description": "Verify user response includes required fields.",
          "httpRequest": {
            "url": "https://api.example.com/users/123",
            "method": "GET",
            "response": {
              "required": ["id", "email", "createdAt", "sessionToken"]
            }
          }
        }
      ]
    }
  ]
}
```

### Validate nested fields and arrays

Use dot notation for nested object fields and bracket notation for array elements to validate complex response structures.

```json
{
  "tests": [
    {
      "steps": [
        {
          "description": "Verify nested user profile fields exist.",
          "httpRequest": {
            "url": "https://api.example.com/users/123",
            "method": "GET",
            "response": {
              "required": [
                "user.profile.name",
                "user.profile.avatar",
                "user.settings.notifications"
              ]
            }
          }
        },
        {
          "description": "Verify array elements contain required fields.",
          "httpRequest": {
            "url": "https://api.example.com/orders",
            "method": "GET",
            "response": {
              "required": [
                "orders[0].id",
                "orders[0].total",
                "orders[0].items[0].productId"
              ]
            }
          }
        }
      ]
    }
  ]
}
```

### Combine required fields with body validation

You can use both `response.required` and `response.body` together to validate that certain fields exist while asserting specific values for others.

```json
{
  "tests": [
    {
      "steps": [
        {
          "description": "Validate both specific values and required fields.",
          "httpRequest": {
            "url": "https://api.example.com/users",
            "method": "POST",
            "request": {
              "body": {
                "username": "testuser",
                "role": "admin"
              }
            },
            "response": {
              "required": ["sessionToken", "expiresAt", "user.id"],
              "body": {
                "status": "success",
                "user": {
                  "role": "admin"
                }
              }
            },
            "statusCodes": [201]
          }
        }
      ]
    }
  ]
}
```

> **Note:** Fields with `null`, `""`, `0`, or `false` values are considered to exist and will pass validation.

### Reject unexpected response fields

Set `allowAdditionalFields` to `false` to assert that the response contains only the fields you declared in `response.body`. The step fails if the response includes any extra field.

The check descends into nested objects. It catches an extra field at any depth and names it by its dot-path. A nested empty object is still a constraint. Declaring `"user": {}` in `response.body` requires the response's `user` object to be empty, so any field it carries fails the step. Only an unset or empty top-level `response.body` lifts the constraint entirely.

```json
{
  "tests": [
    {
      "steps": [
        {
          "description": "Fail if the response returns fields beyond those declared.",
          "httpRequest": {
            "url": "https://api.example.com/users/123",
            "method": "GET",
            "response": {
              "body": {
                "id": 123,
                "name": "morpheus"
              }
            },
            "allowAdditionalFields": false
          }
        }
      ]
    }
  ]
}
```