# REST

> Send HTTP requests to REST services, validate them against the services' OpenAPI specifications, and assert on the responses.

Source: https://axx.nimbusxr.us/references/packs/rest/

Send HTTP requests to REST services, validate them against the services' OpenAPI specifications, and assert on the responses.

**Services.** Register services with `the <name> service with the following properties:` (`url`, optional `openapi`). The first service registered in a scenario is the default one; the other steps name a service with `on <name>`.

**Requests.** Each service keeps its requests in the order they are added: `a GET request to /path` adds the first (default) request and `a 2nd ordered POST request to /path` the second. Header, payload, execution and response steps address a request with `for 2nd ordered request` / `for 2nd ordered response` (default: the first). A request is executed once; its response stays available to every later step.

**Payloads.** A payload starts from an OpenAPI content example (the named one, or the first in document order; `externalValue` examples are read relative to the specification) or from an empty template `{}`, and is edited with the payload property steps (JSONPath, typed values, `null`/`undefined`). `application/x-www-form-urlencoded` payloads are sent form-encoded.

**Execution.** Requests go through one HTTP client shared by the run (connections are reused and closed at the end), honor the step timeout, and do not verify TLS certificates unless `packs.rest.tls.verify: true` is set in axx.yaml. Without a Content-Type header a payload is sent with its payload step's media type.

**Redirects.** A GET or HEAD request follows redirects (301, 302, 303, 307 and 308); a request with another method follows only `303 See Other`, with a GET, as a browser does after a form. A request that follows a redirect has the response it leads to: the response steps check that one, and the redirect responses, with their `Location` and `Set-Cookie` headers, are not kept. The other redirects of a POST, PUT, PATCH or DELETE (301, 302, 307 and 308) are its response: check the status and the `Location` header, and send the request it leads to as the next ordered request.

**Cookies.** The pack keeps no cookies: a `Set-Cookie` response header is never sent back, not even to where a redirect leads, so no request carries a cookie from another scenario. Send a cookie with the `Cookie` request header: it goes along when the request follows a redirect on the same host or to a subdomain of it.

**OpenAPI validation.** When a service has an `openapi` specification (OpenAPI 3.0 or 3.1, a URL or a file; parsed once per run), each executed request and its response are validated after sending. Every finding has a key in the style of the swagger request validator (the one the WireMock extension uses), and a level:

- `ERROR` (alias `FAIL`, the default for every key) fails the execute step, listing every error with its key;
- `WARN` and `INFO` are logged on the step;
- `IGNORE` drops the finding.

Levels come from `openapi.levels` in axx.yaml and are overridden per scenario with `the OpenAPI validation levels are:`. A key also sets every more specific key (`validation.request.body` covers `validation.request.body.schema.required`); the most specific configured key wins. A key must be one of the keys below, or a prefix of them: any other, in the step or in axx.yaml, is an error that names the closest keys.

| Key | Reported when |
| --- | --- |
| `validation.request.path.missing` | no path of the specification matches the request path |
| `validation.request.operation.notAllowed` | the path exists but not for the request method |
| `validation.request.body.missing` | the operation requires a body and the request has none |
| `validation.request.body.unexpected` | the request has a body the operation does not declare |
| `validation.request.contentType.notAllowed` | the request Content-Type is not declared for the operation |
| `validation.request.body.schema.{keyword}` | the request body violates a schema keyword (type, required, enum, format, pattern, minimum, maxLength, additionalProperties, oneOf, ...) |
| `validation.request.body.schema.invalidJson` | the request body cannot be parsed |
| `validation.request.body.schema.processingError` | the request body schema cannot be compiled or the body cannot be read |
| `validation.request.parameter.missing` | a required path parameter is missing |
| `validation.request.parameter.query.missing` | a required query parameter is missing |
| `validation.request.parameter.header.missing` | a required header parameter is missing |
| `validation.request.parameter.cookie.missing` | a required cookie parameter is missing |
| `validation.request.parameter.schema.{keyword}` | a parameter value violates its schema (type, enum, format, pattern, minimum, ...) |
| `validation.request.parameter.schema.invalidJson` | a JSON (content) parameter cannot be parsed |
| `validation.request.parameter.schema.processingError` | a parameter's schema cannot be compiled |
| `validation.request.parameter.collection.invalidFormat` | an array or object parameter is serialized in the wrong style |
| `validation.request.parameter.collection.tooManyItems` | an array parameter has more than maxItems items |
| `validation.request.parameter.collection.tooFewItems` | an array parameter has fewer than minItems items |
| `validation.request.parameter.collection.duplicateItems` | an array parameter with uniqueItems repeats an item |
| `validation.request.parameter.{in}.invalid` | any other parameter problem; {in} is `path`, `query`, `header` or `cookie` |
| `validation.request.security.missing` | the credentials a security requirement needs are absent |
| `validation.request.security.invalid` | credentials are present but do not match the security scheme |
| `validation.response.status.unknown` | the response status is not documented for the operation (and there is no default) |
| `validation.response.contentType.notAllowed` | the response Content-Type is not declared for the status |
| `validation.response.body.missing` | the response declares a body schema but has no body |
| `validation.response.body.unexpected` | a response to HEAD has a body |
| `validation.response.body.schema.{keyword}` | the response body violates a schema keyword |
| `validation.response.body.schema.invalidJson` | the response body cannot be parsed |
| `validation.response.body.schema.processingError` | the response body schema cannot be compiled or the body cannot be read |
| `validation.response.header.missing` | a required response header is missing |
| `validation.response.header.schema.{keyword}` | a response header value violates its schema |
| `validation.request.unknownError` | anything else the validator reports about the request |
| `validation.response.unknownError` | anything else the validator reports about the response |

Schema keywords use the draft-4 names: `const` is reported as `enum`, `exclusiveMinimum`/`exclusiveMaximum` as `minimum`/`maximum`, `unevaluatedProperties` as `additionalProperties`. A schema failure that names no keyword is keyed `unknownError` (`validation.request.body.schema.unknownError`).

When a scenario fails, its failure context (`rest`) shows the last request and response (headers, bodies truncated to 2 KB) and the OpenAPI findings.

Add it to a project with `axx pack add rest`.

## Settings

In `axx.yaml`, under `packs.rest`:

| Setting | Takes | Values | Default |
|---|---|---|---|
| `openapi.levels` | Validation key -> ERROR (or FAIL), WARN, INFO or IGNORE. Merged over the top-level openapi.levels. | names and values | |
| `tls.verify` | Verify TLS certificates. | `true`, `false` | `false` |

## Steps

### `rest.service`

```gherkin
Given the {word} service with the following properties:
  | property | value |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

Register a REST service: where its requests go, and the OpenAPI specification they are checked against.

- The first service registered in a scenario is the default one.
- With an `openapi` specification, every request executed and its response are validated against it, and request payloads can start from its examples.
- Values can use `${env:…}` and `${sys:…}`, which are expanded.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |

| Property | Takes | Default |
|---|---|---|
| `url` | the base URL requests go to, like `http://localhost:8400`; a request's path is added to it | _required_ |
| `openapi` | the service's OpenAPI 3.0 or 3.1 specification: a URL, or a file of the project (relative to the `resources` directories or to axx.yaml's directory) | |

Any other property fails the step.

**Example:**

```gherkin
Given the parcels service with the following properties:
  | url     | http://localhost:8400              |
  | openapi | http://localhost:8400/openapi.json |
```

### `rest.openapi.levels`

```gherkin
Given the OpenAPI validation levels[[ on {service}]] are:
  | validation key | level |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

Set the level of OpenAPI validation findings for this scenario, on the default or the named service.

- A key also sets the keys below it: `validation.request.body` relaxes `validation.request.body.schema.required` too. The most specific key set wins.
- The rows are merged over `openapi.levels` of axx.yaml.
- The pack's documentation lists the keys, and what each level does.

| Parameter | Takes | For example |
|---|---|---|
| `{service}` | the name of a REST service the scenario registered | `parcels` |

A row is a validation key and its level: `ERROR` (or `FAIL`), `WARN`, `INFO` or `IGNORE`. The key must be one the pack reports, or a prefix of such keys, like `validation.request.body`: any other fails the step, which names the closest keys.

**Variants**, the parts in `[[...]]` said or left out:

- `the OpenAPI validation levels are:`
- `the OpenAPI validation levels on {service} are:`

**Example:**

```gherkin
Given the OpenAPI validation levels are:
  | validation.request.body.schema.maximum | IGNORE |
  | validation.response.header.missing     | WARN   |
Given the OpenAPI validation levels on parcels are:
  | validation.request.body.schema.required | WARN |
```

### `rest.request`

```gherkin
Given a(n) {word} request to {word}[[ on {service}]]
```

Add a request to the default or the named service: its method, and its path below the service's `url`.

- The path can have a query string, like `/api/parcels?sender=kestrel-books`. A whole URL replaces the service's `url`.
- This is the service's first (default) request; the ordered form adds more.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `a(n) {word} request to {word}`
- `a(n) {word} request to {word} on {service}`

**Example:**

```gherkin
Given a GET request to /api/parcels/PX-1001
Given a DELETE request to /api/parcels/PX-1001 on parcels
```

### `rest.request.ordered`

```gherkin
Given a {ordinal} ordered {word} request to {word}[[ on {service}]]
```

Add the Nth request of a service. Requests are numbered in the order they are added: the 1st ordered request is the default request, and the Nth can only be added once N-1 exist.

| Parameter | Takes | For example |
|---|---|---|
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `a {ordinal} ordered {word} request to {word}`
- `a {ordinal} ordered {word} request to {word} on {service}`

**Example:**

```gherkin
Given a 2nd ordered GET request to /api/parcels/PX-1001
Given a 1st ordered POST request to /api/parcels on parcels
```

### `rest.request.header`

```gherkin
Given the request header {word} is {string}[[ for {ordinal} ordered request]]
```

Set a request header.

- `Content-Type` and `Accept` replace an earlier value; other headers may be added more than once, and are all sent.
- Without an ordinal it applies to the service's first (default) request; `for 2nd ordered request`, to its second.
- It uses the default service, the first one registered; `rest.request.header.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{string}` | text in double or single quotes, which the step leaves out | `"Get a quote"`, `'Express'` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

**Variants**, the parts in `[[...]]` said or left out:

- `the request header {word} is {string}`
- `the request header {word} is {string} for {ordinal} ordered request`

**Example:**

```gherkin
Given the request header Content-Type is 'application/json'
```

### `rest.request.header.on`

```gherkin
Given the request header {word} is {string} for[[ {ordinal} ordered]] request on {service}
```

`rest.request.header` on a named service: `for request on <service>` addresses the service's first (default) request, `for 2nd ordered request on <service>` its second one. Everything else works like `rest.request.header`.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{string}` | text in double or single quotes, which the step leaves out | `"Get a quote"`, `'Express'` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `the request header {word} is {string} for request on {service}`
- `the request header {word} is {string} for {ordinal} ordered request on {service}`

**Example:**

```gherkin
Given the request header Accept is 'application/json' for 1st ordered request on parcels
```

### `rest.request.headers`

```gherkin
Given the request headers[[ for {ordinal} ordered request]] are:
  | header | value |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

Set request headers, a row each.

- A name may repeat. Like the single-header step, `Content-Type` and `Accept` replace an earlier value, and other headers are all sent.
- Without an ordinal it applies to the service's first (default) request; `for 2nd ordered request`, to its second.
- It uses the default service, the first one registered; `rest.request.headers.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

A row is a header's name and its value.

**Variants**, the parts in `[[...]]` said or left out:

- `the request headers are:`
- `the request headers for {ordinal} ordered request are:`

**Example:**

```gherkin
Given the request headers are:
  | Accept          | application/json |
  | Accept-Language | de-DE            |
```

### `rest.request.headers.on`

```gherkin
Given the request headers for[[ {ordinal} ordered]] request on {service} are:
  | header | value |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

`rest.request.headers` on a named service: `for request on <service>` addresses the service's first (default) request, `for 2nd ordered request on <service>` its second one. Everything else works like `rest.request.headers`.

| Parameter | Takes | For example |
|---|---|---|
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

A row is a header's name and its value.

**Variants**, the parts in `[[...]]` said or left out:

- `the request headers for request on {service} are:`
- `the request headers for {ordinal} ordered request on {service} are:`

**Example:**

```gherkin
Given the request headers for request on parcels are:
  | Content-Type | application/json |
  | Accept       | application/json |
```

### `rest.request.payload.empty`

```gherkin
Given a request payload using a(n) {mimeType} empty content template[[ for {ordinal} ordered request]]
```

Start the request payload from an empty JSON object, `{}`, for the payload property steps to fill.

- It needs no OpenAPI specification.
- With `application/x-www-form-urlencoded`, the properties are sent form-encoded, and nested objects and arrays as JSON text.
- A request takes one payload step.
- Without an ordinal it applies to the service's first (default) request; `for 2nd ordered request`, to its second.
- It uses the default service, the first one registered; `rest.request.payload.empty.on` names a service.

| Parameter | Takes | Values | For example |
|---|---|---|---|
| `{mimeType}` | a content type | `application/json`, `text/json`, `application/problem+json`, `application/x-www-form-urlencoded` | `application/json` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first |  | `1st`, `2nd` |

**Variants**, the parts in `[[...]]` said or left out:

- `a request payload using a(n) {mimeType} empty content template`
- `a request payload using a(n) {mimeType} empty content template for {ordinal} ordered request`

**Example:**

```gherkin
Given a request payload using an application/json empty content template
```

### `rest.request.payload.empty.on`

```gherkin
Given a request payload using a(n) {mimeType} empty content template for[[ {ordinal} ordered]] request on {service}
```

`rest.request.payload.empty` on a named service: `for request on <service>` addresses the service's first (default) request, `for 2nd ordered request on <service>` its second one. Everything else works like `rest.request.payload.empty`.

| Parameter | Takes | Values | For example |
|---|---|---|---|
| `{mimeType}` | a content type | `application/json`, `text/json`, `application/problem+json`, `application/x-www-form-urlencoded` | `application/json` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first |  | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered |  | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `a request payload using a(n) {mimeType} empty content template for request on {service}`
- `a request payload using a(n) {mimeType} empty content template for {ordinal} ordered request on {service}`

**Example:**

```gherkin
Given a request payload using an application/json empty content template for request on parcels
```

### `rest.request.payload.example`

```gherkin
Given a request payload using a(n) {mimeType} content example[[ named {string}]][[ for {ordinal} ordered request]]
```

Start the request payload from a request body example of the service's OpenAPI specification.

- With `named '<name>'` it is the example of that name; without, the first example in document order, or the media type's single `example`.
- The example is looked up under the operation that matches the request's method and path, for the media type.
- An example with an `externalValue` is read relative to the specification.
- The service needs its `openapi` property, and the request must be added first.
- A request takes one payload step.
- Without an ordinal it applies to the service's first (default) request; `for 2nd ordered request`, to its second.
- It uses the default service, the first one registered; `rest.request.payload.example.on` names a service.

| Parameter | Takes | Values | For example |
|---|---|---|---|
| `{mimeType}` | a content type | `application/json`, `text/json`, `application/problem+json`, `application/x-www-form-urlencoded` | `application/json` |
| `{string}` | text in double or single quotes, which the step leaves out |  | `"Get a quote"`, `'Express'` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first |  | `1st`, `2nd` |

**Variants**, the parts in `[[...]]` said or left out:

- `a request payload using a(n) {mimeType} content example`
- `a request payload using a(n) {mimeType} content example named {string}`
- `a request payload using a(n) {mimeType} content example for {ordinal} ordered request`
- `a request payload using a(n) {mimeType} content example named {string} for {ordinal} ordered request`

**Example:**

```gherkin
Given a request payload using an application/json content example named 'Standard parcel'
```

### `rest.request.payload.example.on`

```gherkin
Given a request payload using a(n) {mimeType} content example[[ named {string}]] for[[ {ordinal} ordered]] request on {service}
```

`rest.request.payload.example` on a named service: `for request on <service>` addresses the service's first (default) request, `for 2nd ordered request on <service>` its second one. Everything else works like `rest.request.payload.example`.

| Parameter | Takes | Values | For example |
|---|---|---|---|
| `{mimeType}` | a content type | `application/json`, `text/json`, `application/problem+json`, `application/x-www-form-urlencoded` | `application/json` |
| `{string}` | text in double or single quotes, which the step leaves out |  | `"Get a quote"`, `'Express'` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first |  | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered |  | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `a request payload using a(n) {mimeType} content example for request on {service}`
- `a request payload using a(n) {mimeType} content example named {string} for request on {service}`
- `a request payload using a(n) {mimeType} content example for {ordinal} ordered request on {service}`
- `a request payload using a(n) {mimeType} content example named {string} for {ordinal} ordered request on {service}`

**Example:**

```gherkin
Given a request payload using an application/json content example for 1st ordered request on parcels
```

### `rest.request.payload.resource`

```gherkin
Given a request payload using a(n) {mimeType} {filepath} resource[[ for {ordinal} ordered request]]
```

Start the request payload from a file of the project, such as a fixture factory's output: it is sent with the media type.

- The file is found like other files steps name: relative to the `resources` directories, or to axx.yaml's directory.
- It is sent as it is, and a JSON payload can then be changed with the payload property steps. With `application/x-www-form-urlencoded`, the file is a JSON object whose properties are sent form-encoded.
- It needs no OpenAPI specification.
- A request takes one payload step.
- Without an ordinal it applies to the service's first (default) request; `for 2nd ordered request`, to its second.
- It uses the default service, the first one registered; `rest.request.payload.resource.on` names a service.

| Parameter | Takes | Values | For example |
|---|---|---|---|
| `{mimeType}` | a content type | `application/json`, `text/json`, `application/problem+json`, `application/x-www-form-urlencoded` | `application/json` |
| `{filepath}` | a file of the project: a path relative to the feature file's directory, the `resources` directories or axx.yaml's directory (looked for in that order), or an absolute path, quoted when it has a space |  | `seeds/parcels.yaml`, `kafka/scan-delivered.json` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first |  | `1st`, `2nd` |

**Variants**, the parts in `[[...]]` said or left out:

- `a request payload using a(n) {mimeType} {filepath} resource`
- `a request payload using a(n) {mimeType} {filepath} resource for {ordinal} ordered request`

**Example:**

```gherkin
Given a request payload using an application/json requests/order-7731.json resource
```

### `rest.request.payload.resource.on`

```gherkin
Given a request payload using a(n) {mimeType} {filepath} resource for[[ {ordinal} ordered]] request on {service}
```

`rest.request.payload.resource` on a named service: `for request on <service>` addresses the service's first (default) request, `for 2nd ordered request on <service>` its second one. Everything else works like `rest.request.payload.resource`.

| Parameter | Takes | Values | For example |
|---|---|---|---|
| `{mimeType}` | a content type | `application/json`, `text/json`, `application/problem+json`, `application/x-www-form-urlencoded` | `application/json` |
| `{filepath}` | a file of the project: a path relative to the feature file's directory, the `resources` directories or axx.yaml's directory (looked for in that order), or an absolute path, quoted when it has a space |  | `seeds/parcels.yaml`, `kafka/scan-delivered.json` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first |  | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered |  | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `a request payload using a(n) {mimeType} {filepath} resource for request on {service}`
- `a request payload using a(n) {mimeType} {filepath} resource for {ordinal} ordered request on {service}`

**Example:**

```gherkin
Given a request payload using an application/json requests/order-7732.json resource for 2nd ordered request on parcels
```

### `rest.request.property`

```gherkin
Given the request payload property {word} is {string}[[ for {ordinal} ordered request]]
```

Set a property of the request payload, by its JSONPath, like `weightGrams`, `recipient.postcode` or `$.recipient.name`.

- A value in double quotes inside the quotes, like `'"42"'`, is always a string.
- Any other value takes the type of the property's current value: a string, a boolean, an integer, a number, or an object or array parsed from JSON text.
- A property that does not exist yet, or is null, takes the type its value reads as: `true`, `42`, `1.5`, `{...}`, `[...]`, or else a string.
- A property inside an object the payload lacks creates the object: `recipient.name` in a payload without `recipient`. A path through an array (`items[2].sku`) needs the array.
- A payload step must come first.
- Without an ordinal it applies to the service's first (default) request; `for 2nd ordered request`, to its second.
- It uses the default service, the first one registered; `rest.request.property.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{string}` | text in double or single quotes, which the step leaves out | `"Get a quote"`, `'Express'` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

**Variants**, the parts in `[[...]]` said or left out:

- `the request payload property {word} is {string}`
- `the request payload property {word} is {string} for {ordinal} ordered request`

**Example:**

```gherkin
Given the request payload property sender is 'kestrel-books'
```

### `rest.request.property.on`

```gherkin
Given the request payload property {word} is {string} for[[ {ordinal} ordered]] request on {service}
```

`rest.request.property` on a named service: `for request on <service>` addresses the service's first (default) request, `for 2nd ordered request on <service>` its second one. Everything else works like `rest.request.property`.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{string}` | text in double or single quotes, which the step leaves out | `"Get a quote"`, `'Express'` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `the request payload property {word} is {string} for request on {service}`
- `the request payload property {word} is {string} for {ordinal} ordered request on {service}`

**Example:**

```gherkin
Given the request payload property serviceLevel is 'EXPRESS' for request on parcels
```

### `rest.request.properties`

```gherkin
Given the request payload properties[[ for {ordinal} ordered request]] are:
  | JSONPath | value |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

Set properties of the request payload, a row each, in order, like the single-property step.

- `null` sets JSON null, and `undefined` removes the property, in any case.
- `"null"` and `"undefined"`, in double quotes, are the strings.
- A property inside an object the payload lacks creates the object, so a table can list `recipient.name` and `recipient.postcode` on an empty payload.
- Without an ordinal it applies to the service's first (default) request; `for 2nd ordered request`, to its second.
- It uses the default service, the first one registered; `rest.request.properties.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

A row is a property's JSONPath and its value.

**Variants**, the parts in `[[...]]` said or left out:

- `the request payload properties are:`
- `the request payload properties for {ordinal} ordered request are:`

**Example:**

```gherkin
Given the request payload properties are:
  | reference          | PX-4101 |
  | weightGrams        | 1200    |
  | recipient.postcode | "53111" |
```

### `rest.request.properties.on`

```gherkin
Given the request payload properties for[[ {ordinal} ordered]] request on {service} are:
  | JSONPath | value |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

`rest.request.properties` on a named service: `for request on <service>` addresses the service's first (default) request, `for 2nd ordered request on <service>` its second one. Everything else works like `rest.request.properties`.

| Parameter | Takes | For example |
|---|---|---|
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

A row is a property's JSONPath and its value.

**Variants**, the parts in `[[...]]` said or left out:

- `the request payload properties for request on {service} are:`
- `the request payload properties for {ordinal} ordered request on {service} are:`

**Example:**

```gherkin
Given the request payload properties for 1st ordered request on parcels are:
  | sender           | lark-ceramics |
  | recipient.street | undefined     |
```

### `rest.request.property.null`

```gherkin
Given the request payload property {word} is null[[ for {ordinal} ordered request]]
```

Set an existing property of the request payload to JSON null.

- Without an ordinal it applies to the service's first (default) request; `for 2nd ordered request`, to its second.
- It uses the default service, the first one registered; `rest.request.property.null.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

**Variants**, the parts in `[[...]]` said or left out:

- `the request payload property {word} is null`
- `the request payload property {word} is null for {ordinal} ordered request`

**Example:**

```gherkin
Given the request payload property recipient.street is null
```

### `rest.request.property.null.on`

```gherkin
Given the request payload property {word} is null for[[ {ordinal} ordered]] request on {service}
```

`rest.request.property.null` on a named service: `for request on <service>` addresses the service's first (default) request, `for 2nd ordered request on <service>` its second one. Everything else works like `rest.request.property.null`.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `the request payload property {word} is null for request on {service}`
- `the request payload property {word} is null for {ordinal} ordered request on {service}`

**Example:**

```gherkin
Given the request payload property recipient.street is null for request on parcels
```

### `rest.execute`

```gherkin
When the[[ {ordinal} ordered]] request is executed[[ on {service}]]
```

Send a request, and keep its response for the response steps.

- With an OpenAPI specification, the request and its response are validated after sending. Findings at level `ERROR` fail the step, which lists them all with their keys; `WARN` and `INFO` findings are logged.
- The payload is sent as it is, or form-encoded for `application/x-www-form-urlencoded`.
- Without a `Content-Type` header, the request has the payload's media type; without an `Accept` header, `*/*`.
- A GET or HEAD request follows redirects, and a request with another method a `303 See Other` (with a GET): the response steps then check the response it leads to. Other redirects are the response, with their `Location` header.
- No cookies are kept: a `Set-Cookie` response header is not sent back. Send one with the `Cookie` request header.
- The request honors the step timeout, and is executed once.
- Without an ordinal it sends the service's first (default) request; `the 2nd ordered request`, its second.
- Without `on {service}` it uses the default service, the first one registered.

| Parameter | Takes | For example |
|---|---|---|
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `the request is executed`
- `the request is executed on {service}`
- `the {ordinal} ordered request is executed`
- `the {ordinal} ordered request is executed on {service}`

**Example:**

```gherkin
When the request is executed
When the 2nd ordered request is executed on parcels
```

### `rest.response.status`

```gherkin
Then the[[ {ordinal} ordered]] response status code is {int}[[ on {service}]]
```

Check the HTTP status code of a response.

- Without an ordinal it checks the response to the first (default) request; `the 2nd ordered response`, the response to the second.
- Without `on {service}` it uses the default service, the first one registered.

| Parameter | Takes | For example |
|---|---|---|
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{int}` | a whole number | `200`, `3` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `the response status code is {int}`
- `the response status code is {int} on {service}`
- `the {ordinal} ordered response status code is {int}`
- `the {ordinal} ordered response status code is {int} on {service}`

**Example:**

```gherkin
Then the response status code is 200
Then the 2nd ordered response status code is 201 on parcels
```

### `rest.response.body.contains`

```gherkin
Then the response body contains {string}[[ for {ordinal} ordered response]]
```

Check that the response body contains the text.

- Without an ordinal it checks the response to the first (default) request; `for 2nd ordered response`, the response to the second.
- It uses the default service, the first one registered; `rest.response.body.contains.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{string}` | text in double or single quotes, which the step leaves out | `"Get a quote"`, `'Express'` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

**Variants**, the parts in `[[...]]` said or left out:

- `the response body contains {string}`
- `the response body contains {string} for {ordinal} ordered response`

**Example:**

```gherkin
Then the response body contains 'already registered'
```

### `rest.response.body.contains.on`

```gherkin
Then the response body contains {string} for[[ {ordinal} ordered]] response on {service}
```

`rest.response.body.contains` on a named service: `for response on <service>` addresses the service's first (default) response, `for 2nd ordered response on <service>` its second one. Everything else works like `rest.response.body.contains`.

| Parameter | Takes | For example |
|---|---|---|
| `{string}` | text in double or single quotes, which the step leaves out | `"Get a quote"`, `'Express'` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `the response body contains {string} for response on {service}`
- `the response body contains {string} for {ordinal} ordered response on {service}`

**Example:**

```gherkin
Then the response body contains 'already registered' for 2nd ordered response on parcels
```

### `rest.response.header.is`

```gherkin
Then the response header {word} is {string}[[ for {ordinal} ordered response]]
```

Check that a response header has the value.

- The header's name is matched in any case.
- With the header repeated, one of its values must be the value.
- Without an ordinal it checks the response to the first (default) request; `for 2nd ordered response`, the response to the second.
- It uses the default service, the first one registered; `rest.response.header.is.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{string}` | text in double or single quotes, which the step leaves out | `"Get a quote"`, `'Express'` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

**Variants**, the parts in `[[...]]` said or left out:

- `the response header {word} is {string}`
- `the response header {word} is {string} for {ordinal} ordered response`

**Example:**

```gherkin
Then the response header Content-Type is 'application/json'
```

### `rest.response.header.is.on`

```gherkin
Then the response header {word} is {string} for[[ {ordinal} ordered]] response on {service}
```

`rest.response.header.is` on a named service: `for response on <service>` addresses the service's first (default) response, `for 2nd ordered response on <service>` its second one. Everything else works like `rest.response.header.is`.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{string}` | text in double or single quotes, which the step leaves out | `"Get a quote"`, `'Express'` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `the response header {word} is {string} for response on {service}`
- `the response header {word} is {string} for {ordinal} ordered response on {service}`

**Example:**

```gherkin
Then the response header Content-Type is 'application/json' for response on parcels
```

### `rest.response.header.matches`

```gherkin
Then the response header {word} matches {pattern}[[ for {ordinal} ordered response]]
```

Check that a response header matches a regular expression.

- The regular expression is in Java syntax, and must match the whole value.
- With the header repeated, one of its values must match.
- Without an ordinal it checks the response to the first (default) request; `for 2nd ordered response`, the response to the second.
- It uses the default service, the first one registered; `rest.response.header.matches.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{pattern}` | a regular expression (Java syntax) with no spaces, which matches the whole value | `PX-\d{4}`, `[A-Z]{2}-\d+` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

**Variants**, the parts in `[[...]]` said or left out:

- `the response header {word} matches {pattern}`
- `the response header {word} matches {pattern} for {ordinal} ordered response`

**Example:**

```gherkin
Then the response header Content-Type matches ^application/json.*$
```

### `rest.response.header.matches.on`

```gherkin
Then the response header {word} matches {pattern} for[[ {ordinal} ordered]] response on {service}
```

`rest.response.header.matches` on a named service: `for response on <service>` addresses the service's first (default) response, `for 2nd ordered response on <service>` its second one. Everything else works like `rest.response.header.matches`.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{pattern}` | a regular expression (Java syntax) with no spaces, which matches the whole value | `PX-\d{4}`, `[A-Z]{2}-\d+` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `the response header {word} matches {pattern} for response on {service}`
- `the response header {word} matches {pattern} for {ordinal} ordered response on {service}`

**Example:**

```gherkin
Then the response header Content-Type matches ^application/json$ for 1st ordered response on parcels
```

### `rest.response.header.missing`

```gherkin
Then the response header {word} is missing[[ for {ordinal} ordered response]]
```

Check that the response has no header of that name.

- Without an ordinal it checks the response to the first (default) request; `for 2nd ordered response`, the response to the second.
- It uses the default service, the first one registered; `rest.response.header.missing.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

**Variants**, the parts in `[[...]]` said or left out:

- `the response header {word} is missing`
- `the response header {word} is missing for {ordinal} ordered response`

**Example:**

```gherkin
Then the response header Content-Length is missing
```

### `rest.response.header.missing.on`

```gherkin
Then the response header {word} is missing for[[ {ordinal} ordered]] response on {service}
```

`rest.response.header.missing` on a named service: `for response on <service>` addresses the service's first (default) response, `for 2nd ordered response on <service>` its second one. Everything else works like `rest.response.header.missing`.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `the response header {word} is missing for response on {service}`
- `the response header {word} is missing for {ordinal} ordered response on {service}`

**Example:**

```gherkin
Then the response header Retry-After is missing for response on parcels
```

### `rest.response.headers.are`

```gherkin
Then the response headers[[ for {ordinal} ordered response]] are:
  | header | value |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

Check response headers, a row each, like the single-header step.

- A name may repeat.
- Every row is checked, and every mismatch reported.
- Without an ordinal it checks the response to the first (default) request; `for 2nd ordered response`, the response to the second.
- It uses the default service, the first one registered; `rest.response.headers.are.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

A row is a header's name and the value it must have.

**Variants**, the parts in `[[...]]` said or left out:

- `the response headers are:`
- `the response headers for {ordinal} ordered response are:`

**Example:**

```gherkin
Then the response headers are:
  | Content-Type | application/json     |
  | Location     | /api/parcels/PX-4101 |
```

### `rest.response.headers.are.on`

```gherkin
Then the response headers for[[ {ordinal} ordered]] response on {service} are:
  | header | value |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

`rest.response.headers.are` on a named service: `for response on <service>` addresses the service's first (default) response, `for 2nd ordered response on <service>` its second one. Everything else works like `rest.response.headers.are`.

| Parameter | Takes | For example |
|---|---|---|
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

A row is a header's name and the value it must have.

**Variants**, the parts in `[[...]]` said or left out:

- `the response headers for response on {service} are:`
- `the response headers for {ordinal} ordered response on {service} are:`

**Example:**

```gherkin
Then the response headers for 1st ordered response on parcels are:
  | Content-Type | application/problem+json |
  | Retry-After  | 5                        |
```

### `rest.response.headers.match`

```gherkin
Then the response headers[[ for {ordinal} ordered response]] match:
  | header | regular expression |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

Check that response headers match regular expressions, a row each.

- Each regular expression is in Java syntax, and must match the whole value.
- Every row is checked, and every mismatch reported.
- Without an ordinal it checks the response to the first (default) request; `for 2nd ordered response`, the response to the second.
- It uses the default service, the first one registered; `rest.response.headers.match.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

A row is a header's name and a regular expression its value must match.

**Variants**, the parts in `[[...]]` said or left out:

- `the response headers match:`
- `the response headers for {ordinal} ordered response match:`

**Example:**

```gherkin
Then the response headers match:
  | Content-Type | application/json.*       |
  | Location     | /api/parcels/PX-[0-9]{4} |
```

### `rest.response.headers.match.on`

```gherkin
Then the response headers for[[ {ordinal} ordered]] response on {service} match:
  | header | regular expression |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

`rest.response.headers.match` on a named service: `for response on <service>` addresses the service's first (default) response, `for 2nd ordered response on <service>` its second one. Everything else works like `rest.response.headers.match`.

| Parameter | Takes | For example |
|---|---|---|
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

A row is a header's name and a regular expression its value must match.

**Variants**, the parts in `[[...]]` said or left out:

- `the response headers for response on {service} match:`
- `the response headers for {ordinal} ordered response on {service} match:`

**Example:**

```gherkin
Then the response headers for response on parcels match:
  | Retry-After | [0-9]+ |
```

### `rest.response.headers.missing`

```gherkin
Then the response headers[[ for {ordinal} ordered response]] are missing:
  | header |
```

Check that the response has none of the headers the table names.

- Without an ordinal it checks the response to the first (default) request; `for 2nd ordered response`, the response to the second.
- It uses the default service, the first one registered; `rest.response.headers.missing.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

A row names a header, in its first cell.

**Variants**, the parts in `[[...]]` said or left out:

- `the response headers are missing:`
- `the response headers for {ordinal} ordered response are missing:`

**Example:**

```gherkin
Then the response headers are missing:
  | Retry-After |
  | Set-Cookie  |
```

### `rest.response.headers.missing.on`

```gherkin
Then the response headers for[[ {ordinal} ordered]] response on {service} are missing:
  | header |
```

`rest.response.headers.missing` on a named service: `for response on <service>` addresses the service's first (default) response, `for 2nd ordered response on <service>` its second one. Everything else works like `rest.response.headers.missing`.

| Parameter | Takes | For example |
|---|---|---|
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

A row names a header, in its first cell.

**Variants**, the parts in `[[...]]` said or left out:

- `the response headers for response on {service} are missing:`
- `the response headers for {ordinal} ordered response on {service} are missing:`

**Example:**

```gherkin
Then the response headers for response on parcels are missing:
  | Retry-After |
```

### `rest.response.property.is`

```gherkin
Then the response payload property {word} is {string}[[ for {ordinal} ordered response]]
```

Check a property of a JSON response, by its JSONPath, like `status`, `recipient.postcode` or `[?(@.sender=='kestrel-books')].reference`.

- An indefinite path, like a filter, reads as a list.
- The response must be JSON: `application/json`, `text/json` or any `+json` type, whatever its charset.
- A value is compared with its JSON type. Text, like `REGISTERED`, is a string, and so is a value in double quotes, like `"42"`.
- `42` is an integer, `42L` a long and `1.5` a number. An integer never equals a decimal: `5` is not `5.0`.
- `true` and `false` are booleans. `{...}` and `[...]` are a JSON object and a JSON array; an object's members may come in any order.
- Without an ordinal it checks the response to the first (default) request; `for 2nd ordered response`, the response to the second.
- It uses the default service, the first one registered; `rest.response.property.is.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{string}` | text in double or single quotes, which the step leaves out | `"Get a quote"`, `'Express'` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

**Variants**, the parts in `[[...]]` said or left out:

- `the response payload property {word} is {string}`
- `the response payload property {word} is {string} for {ordinal} ordered response`

**Example:**

```gherkin
Then the response payload property status is 'REGISTERED'
```

### `rest.response.property.is.on`

```gherkin
Then the response payload property {word} is {string} for[[ {ordinal} ordered]] response on {service}
```

`rest.response.property.is` on a named service: `for response on <service>` addresses the service's first (default) response, `for 2nd ordered response on <service>` its second one. Everything else works like `rest.response.property.is`.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{string}` | text in double or single quotes, which the step leaves out | `"Get a quote"`, `'Express'` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `the response payload property {word} is {string} for response on {service}`
- `the response payload property {word} is {string} for {ordinal} ordered response on {service}`

**Example:**

```gherkin
Then the response payload property status is 'REGISTERED' for 1st ordered response on parcels
```

### `rest.response.property.null`

```gherkin
Then the response payload property {word} is null[[ for {ordinal} ordered response]]
```

Check that a property of the response payload exists, and is JSON null.

- Without an ordinal it checks the response to the first (default) request; `for 2nd ordered response`, the response to the second.
- It uses the default service, the first one registered; `rest.response.property.null.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

**Variants**, the parts in `[[...]]` said or left out:

- `the response payload property {word} is null`
- `the response payload property {word} is null for {ordinal} ordered response`

**Example:**

```gherkin
Then the response payload property lastLocation is null
```

### `rest.response.property.null.on`

```gherkin
Then the response payload property {word} is null for[[ {ordinal} ordered]] response on {service}
```

`rest.response.property.null` on a named service: `for response on <service>` addresses the service's first (default) response, `for 2nd ordered response on <service>` its second one. Everything else works like `rest.response.property.null`.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `the response payload property {word} is null for response on {service}`
- `the response payload property {word} is null for {ordinal} ordered response on {service}`

**Example:**

```gherkin
Then the response payload property lastLocation is null for response on parcels
```

### `rest.response.property.undefined`

```gherkin
Then the response payload property {word} is undefined[[ for {ordinal} ordered response]]
```

Check that a property of the response payload does not exist.

- An indefinite path always exists: it reads as a list, which may be empty.
- Without an ordinal it checks the response to the first (default) request; `for 2nd ordered response`, the response to the second.
- It uses the default service, the first one registered; `rest.response.property.undefined.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

**Variants**, the parts in `[[...]]` said or left out:

- `the response payload property {word} is undefined`
- `the response payload property {word} is undefined for {ordinal} ordered response`

**Example:**

```gherkin
Then the response payload property recipient.street is undefined
```

### `rest.response.property.undefined.on`

```gherkin
Then the response payload property {word} is undefined for[[ {ordinal} ordered]] response on {service}
```

`rest.response.property.undefined` on a named service: `for response on <service>` addresses the service's first (default) response, `for 2nd ordered response on <service>` its second one. Everything else works like `rest.response.property.undefined`.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `the response payload property {word} is undefined for response on {service}`
- `the response payload property {word} is undefined for {ordinal} ordered response on {service}`

**Example:**

```gherkin
Then the response payload property recipient.street is undefined for 1st ordered response on parcels
```

### `rest.response.property.matches`

```gherkin
Then the response payload property {word} matches {pattern}[[ for {ordinal} ordered response]]
```

Check that a property of the response payload is a string that matches a regular expression.

- The regular expression is in Java syntax, and must match the whole value.
- Without an ordinal it checks the response to the first (default) request; `for 2nd ordered response`, the response to the second.
- It uses the default service, the first one registered; `rest.response.property.matches.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{pattern}` | a regular expression (Java syntax) with no spaces, which matches the whole value | `PX-\d{4}`, `[A-Z]{2}-\d+` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

**Variants**, the parts in `[[...]]` said or left out:

- `the response payload property {word} matches {pattern}`
- `the response payload property {word} matches {pattern} for {ordinal} ordered response`

**Example:**

```gherkin
Then the response payload property barcode matches ^PX[0-9]{11}$
```

### `rest.response.property.matches.on`

```gherkin
Then the response payload property {word} matches {pattern} for[[ {ordinal} ordered]] response on {service}
```

`rest.response.property.matches` on a named service: `for response on <service>` addresses the service's first (default) response, `for 2nd ordered response on <service>` its second one. Everything else works like `rest.response.property.matches`.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{pattern}` | a regular expression (Java syntax) with no spaces, which matches the whole value | `PX-\d{4}`, `[A-Z]{2}-\d+` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `the response payload property {word} matches {pattern} for response on {service}`
- `the response payload property {word} matches {pattern} for {ordinal} ordered response on {service}`

**Example:**

```gherkin
Then the response payload property barcode matches ^PX[0-9]{11}$ for response on parcels
```

### `rest.response.properties.are`

```gherkin
Then the response payload properties[[ for {ordinal} ordered response]] are:
  | JSONPath | value |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

Check properties of a JSON response, a row each, like the single-property step.

- `null` and `undefined`, in any case, check for JSON null and for no such property; `"null"`, in double quotes, is the string.
- Every row is checked, and every mismatch reported.
- A value is compared with its JSON type. Text, like `REGISTERED`, is a string, and so is a value in double quotes, like `"42"`.
- `42` is an integer, `42L` a long and `1.5` a number. An integer never equals a decimal: `5` is not `5.0`.
- `true` and `false` are booleans. `{...}` and `[...]` are a JSON object and a JSON array; an object's members may come in any order.
- Without an ordinal it checks the response to the first (default) request; `for 2nd ordered response`, the response to the second.
- It uses the default service, the first one registered; `rest.response.properties.are.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

A row is a property's JSONPath and the value it must have.

**Variants**, the parts in `[[...]]` said or left out:

- `the response payload properties are:`
- `the response payload properties for {ordinal} ordered response are:`

**Example:**

```gherkin
Then the response payload properties are:
  | reference | PX-4101    |
  | status    | REGISTERED |
  | zone      | DE-1       |
```

### `rest.response.properties.are.on`

```gherkin
Then the response payload properties for[[ {ordinal} ordered]] response on {service} are:
  | JSONPath | value |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

`rest.response.properties.are` on a named service: `for response on <service>` addresses the service's first (default) response, `for 2nd ordered response on <service>` its second one. Everything else works like `rest.response.properties.are`.

| Parameter | Takes | For example |
|---|---|---|
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

A row is a property's JSONPath and the value it must have.

**Variants**, the parts in `[[...]]` said or left out:

- `the response payload properties for response on {service} are:`
- `the response payload properties for {ordinal} ordered response on {service} are:`

**Example:**

```gherkin
Then the response payload properties for 1st ordered response on parcels are:
  | serviceLevel     | STANDARD     |
  | recipient.name   | Ada Lovelace |
  | recipient.street | undefined    |
```

### `rest.response.properties.match`

```gherkin
Then the response payload properties[[ for {ordinal} ordered response]] match:
  | JSONPath | regular expression |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

Check that properties of the response payload are strings that match regular expressions, a row each.

- Each regular expression is in Java syntax, and must match the whole value.
- Every row is checked, and every mismatch reported.
- Without an ordinal it checks the response to the first (default) request; `for 2nd ordered response`, the response to the second.
- It uses the default service, the first one registered; `rest.response.properties.match.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

A row is a property's JSONPath and a regular expression its value must match.

**Variants**, the parts in `[[...]]` said or left out:

- `the response payload properties match:`
- `the response payload properties for {ordinal} ordered response match:`

**Example:**

```gherkin
Then the response payload properties match:
  | barcode   | ^PX[0-9]{11}$  |
  | signature | ^[0-9a-f]{64}$ |
```

### `rest.response.properties.match.on`

```gherkin
Then the response payload properties for[[ {ordinal} ordered]] response on {service} match:
  | JSONPath | regular expression |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

`rest.response.properties.match` on a named service: `for response on <service>` addresses the service's first (default) response, `for 2nd ordered response on <service>` its second one. Everything else works like `rest.response.properties.match`.

| Parameter | Takes | For example |
|---|---|---|
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

A row is a property's JSONPath and a regular expression its value must match.

**Variants**, the parts in `[[...]]` said or left out:

- `the response payload properties for response on {service} match:`
- `the response payload properties for {ordinal} ordered response on {service} match:`

**Example:**

```gherkin
Then the response payload properties for 2nd ordered response on parcels match:
  | [0].reference | PX-[0-9]{4} |
```

### `rest.token`

```gherkin
Given the {word} token with the following properties:
  | property | value |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

Register a bearer token under a name: a JSON Web Token Axx signs, or an OAuth 2.0 client credentials token Axx gets from a token endpoint.

- A request uses it with `the request is authorized with the {word} token`; the steps of other packs that take header rows, like the websocket and sse packs, with `${token:<name>}`.
- A JSON Web Token is signed each time it is used, with `iat` and `exp` from then; the claims of the table add to them or replace them.
- A client credentials token is got once for the run, and again once it expires.
- Values expand `${env:..}` and `${sys:..}`; the token and its secrets are masked in logs and failures.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |

| Property | Takes | Values | Default |
|---|---|---|---|
| `algorithm` | how Axx signs a JSON Web Token | `HS256`, `HS384`, `HS512`, `RS256`, `ES256` | `HS256` |
| `key` | the secret an HS token is signed with, like `${env:SHOP_TOKEN_KEY}`, or the PEM private key (a file of the project) of an RS or ES token |  | |
| `key id` | the `kid` in the token's header |  | |
| `claim.<name>` | a claim of the token: a JSON value (`true`, `42`, `["parcels:read"]`) or text |  | |
| `expires in` | how long the token lasts from when it is used, like `5m` |  | `5m` |
| `token url` | an OAuth 2.0 token endpoint Axx gets a client credentials token from, instead of signing one |  | |
| `client id` | the client's ID, for the token endpoint |  | |
| `client secret` | the client's secret, like `${env:SHOP_CLIENT_SECRET}` |  | |
| `scope` | the scopes asked for, separated by spaces |  | |
| `audience` | the API the token is for, for token endpoints that ask for one |  | |

Give a `key` for a token Axx signs, or a `token url`, `client id` and `client secret` for one it gets, not both.

**Example:**

```gherkin
Given the shop token with the following properties:
  | key        | ${env:SHOP_TOKEN_KEY} |
  | claim.shop | maple-crafts          |
Given the shop-system token with the following properties:
  | token url     | http://localhost:8400/oauth/token |
  | client id     | maple-crafts                      |
  | client secret | ${env:SHOP_CLIENT_SECRET}         |
  | scope         | parcels:read                      |
```

_Since 0.1.5._

### `rest.request.token`

```gherkin
Given the request is authorized with the {word} token[[ for {ordinal} ordered request]]
```

Send the request with the token of that name: `Authorization: Bearer <token>`.

- The token is had when the request is executed: a JSON Web Token is signed then.
- Without an ordinal it applies to the service's first (default) request; `for 2nd ordered request`, to its second.
- It uses the default service, the first one registered; `rest.request.token.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

**Variants**, the parts in `[[...]]` said or left out:

- `the request is authorized with the {word} token`
- `the request is authorized with the {word} token for {ordinal} ordered request`

**Example:**

```gherkin
Given the request is authorized with the shop token
```

_Since 0.1.5._

### `rest.request.token.on`

```gherkin
Given the request is authorized with the {word} token for[[ {ordinal} ordered]] request on {service}
```

`rest.request.token` on a named service: `for request on <service>` addresses the service's first (default) request, `for 2nd ordered request on <service>` its second one. Everything else works like `rest.request.token`.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `the request is authorized with the {word} token for request on {service}`
- `the request is authorized with the {word} token for {ordinal} ordered request on {service}`

**Example:**

```gherkin
Given the request is authorized with the shop token for 2nd ordered request on parcels
```

_Since 0.1.5._

### `rest.request.signed`

```gherkin
Given the request is signed in the {word} header[[ for {ordinal} ordered request]] with the following properties:
  | property | value |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

Sign the request as webhooks are signed: an HMAC of its body, or of what the `signs` template gives, in the header.

- The signature is made when the request is executed, over the body as it is sent.
- `sha256={signature}` is GitHub's form; `t={timestamp},v1={signature}` with `{timestamp}.{body}` signed, Stripe's.
- The key expands `${env:..}`, and is masked.
- Without an ordinal it applies to the service's first (default) request; `for 2nd ordered request`, to its second.
- It uses the default service, the first one registered; `rest.request.signed.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

| Property | Takes | Values | Default |
|---|---|---|---|
| `key` | the secret the signature is made with, like `${env:COURIER_WEBHOOK_KEY}` |  | _required_ |
| `algorithm` | the HMAC | `hmac-sha256`, `hmac-sha1`, `hmac-sha512` | `hmac-sha256` |
| `signs` | what is signed: a template of `{body}`, `{timestamp}` (Unix seconds), `{method}` and `{path}` (with the query), like `{timestamp}.{body}` |  | ``{body}`` |
| `encoding` | how the signature is written | `hex`, `base64` | `hex` |
| `value` | the header's value: a template of `{signature}` and `{timestamp}`, like `sha256={signature}` or `t={timestamp},v1={signature}` |  | ``{signature}`` |
| `timestamp header` | a header that also carries `{timestamp}`, as Slack's `X-Slack-Request-Timestamp` does |  | |

**Variants**, the parts in `[[...]]` said or left out:

- `the request is signed in the {word} header with the following properties:`
- `the request is signed in the {word} header for {ordinal} ordered request with the following properties:`

**Example:**

```gherkin
Given the request is signed in the X-Courier-Signature header with the following properties:
  | key   | ${env:COURIER_WEBHOOK_KEY} |
  | value | sha256={signature}         |
```

_Since 0.1.5._

### `rest.request.signed.on`

```gherkin
Given the request is signed in the {word} header for[[ {ordinal} ordered]] request on {service} with the following properties:
  | property | value |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

`rest.request.signed` on a named service: `for request on <service>` addresses the service's first (default) request, `for 2nd ordered request on <service>` its second one. Everything else works like `rest.request.signed`.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

| Property | Takes | Values | Default |
|---|---|---|---|
| `key` | the secret the signature is made with, like `${env:COURIER_WEBHOOK_KEY}` |  | _required_ |
| `algorithm` | the HMAC | `hmac-sha256`, `hmac-sha1`, `hmac-sha512` | `hmac-sha256` |
| `signs` | what is signed: a template of `{body}`, `{timestamp}` (Unix seconds), `{method}` and `{path}` (with the query), like `{timestamp}.{body}` |  | ``{body}`` |
| `encoding` | how the signature is written | `hex`, `base64` | `hex` |
| `value` | the header's value: a template of `{signature}` and `{timestamp}`, like `sha256={signature}` or `t={timestamp},v1={signature}` |  | ``{signature}`` |
| `timestamp header` | a header that also carries `{timestamp}`, as Slack's `X-Slack-Request-Timestamp` does |  | |

**Variants**, the parts in `[[...]]` said or left out:

- `the request is signed in the {word} header for request on {service} with the following properties:`
- `the request is signed in the {word} header for {ordinal} ordered request on {service} with the following properties:`

**Example:**

```gherkin
Given the request is signed in the Stripe-Signature header for request on parcels with the following properties:
  | key   | ${env:PAYMENTS_WEBHOOK_KEY}  |
  | signs | {timestamp}.{body}           |
  | value | t={timestamp},v1={signature} |
```

_Since 0.1.5._

### `rest.request.webhook`

```gherkin
Given the request is signed as a standard webhook with the key {string}[[ for {ordinal} ordered request]]
```

Sign the request as a Standard Webhook (standardwebhooks.com): its `webhook-id`, `webhook-timestamp` and `webhook-signature` headers.

- The key is `whsec_` and the key in base64, as Standard Webhooks give it; it expands `${env:..}`, and is masked.
- The signature is made when the request is executed, over the body as it is sent.
- Without an ordinal it applies to the service's first (default) request; `for 2nd ordered request`, to its second.
- It uses the default service, the first one registered; `rest.request.webhook.on` names a service.

| Parameter | Takes | For example |
|---|---|---|
| `{string}` | text in double or single quotes, which the step leaves out | `"Get a quote"`, `'Express'` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |

**Variants**, the parts in `[[...]]` said or left out:

- `the request is signed as a standard webhook with the key {string}`
- `the request is signed as a standard webhook with the key {string} for {ordinal} ordered request`

**Example:**

```gherkin
Given the request is signed as a standard webhook with the key '${env:SHOP_WEBHOOK_KEY}'
```

_Since 0.1.5._

### `rest.request.webhook.on`

```gherkin
Given the request is signed as a standard webhook with the key {string} for[[ {ordinal} ordered]] request on {service}
```

`rest.request.webhook` on a named service: `for request on <service>` addresses the service's first (default) request, `for 2nd ordered request on <service>` its second one. Everything else works like `rest.request.webhook`.

| Parameter | Takes | For example |
|---|---|---|
| `{string}` | text in double or single quotes, which the step leaves out | `"Get a quote"`, `'Express'` |
| `{ordinal}` | a position, counting from 1; an optional ordinal left out is the first | `1st`, `2nd` |
| `{service}` | the name of a REST service the scenario registered | `parcels` |

**Variants**, the parts in `[[...]]` said or left out:

- `the request is signed as a standard webhook with the key {string} for request on {service}`
- `the request is signed as a standard webhook with the key {string} for {ordinal} ordered request on {service}`

**Example:**

```gherkin
Given the request is signed as a standard webhook with the key '${env:SHOP_WEBHOOK_KEY}' for request on shops
```

_Since 0.1.5._

## Parameter types

| Parameter | Takes | Values | For example |
|---|---|---|---|
| `{service}` | the name of a REST service the scenario registered |  | `parcels` |
