HTTP tests
An HTTP test sends a request to your endpoint on a schedule and judges the response
against its assertions. This page lists what the request carries and what each assertion
checks. You set the fields in the test editor (type HTTP endpoint), or an agent sets them
through the create_api_test MCP tool.
What the test sends
Section titled “What the test sends”| Field | What it takes |
|---|---|
URL | An http:// or https:// URL. It must be reachable from the public internet. |
| Method | GET, POST, PUT, PATCH, DELETE or HEAD. The editor defaults to GET. |
Request headers | Name and value pairs, sent as written, after request variables are filled in. |
Request body | Optional text, sent as UTF-8. |
Credential | None, or a saved credential that adds authentication. See Credentials. |
Three things every HTTP test shares with the rest of the group, each described once elsewhere:
- Fixter adds a
User-Agentand atraceparentheader of its own; your headers replace either on a name match, and a credential’s headers win over everything. See Allow tests through your firewall for the User-Agent, What a run records fortraceparent, and Credentials for the precedence. - The URL, header values and body take request variables such
as
{{uuid}}; the method, header names and a body validation sample do not. - A URL that resolves to a private or internal address is refused; see Addresses Fixter will not test.
Status pattern
Section titled “Status pattern”Status pattern is the only required assertion. It is exactly three characters, each a
digit or the wildcard x (or X), and the first is a digit from 1 to 5. A wildcard matches
any digit in its position; every other character must equal the status code’s digit.
| Pattern | Matches |
|---|---|
200 | 200 only |
2xx | Any 2xx status (the editor’s default) |
20x | 200 to 209 |
A mismatch fails the run with Expected <pattern>, got <status>, for example
Expected 2xx, got 503.
The status pattern is also how a test opts out of rate-limit handling. Normally an HTTP 429
ends the run as rate limited rather than failed. If the pattern accepts 429 (for example
429 or 4xx), a 429 is an ordinary response checked against the assertions. There is no
other per-test setting for it. See Runs, states and uptime.
Max latency
Section titled “Max latency”Max latency (in ms) is optional; left empty, speed is not checked. It fails when the
response’s total time is over the limit. Total time runs from the first request to the last
byte of the response body, so it includes every redirect hop. The failure reads
Took <actual> ms, limit <limit> ms.
Max latency is separate from the timeout (Give up after, at most 30 seconds). A response
that is slow but complete fails the latency assertion; no complete response within the
timeout ends the attempt as timed out, with
no complete response from the target within <timeout>.
Response headers
Section titled “Response headers”Response headers are optional checks, each a header name and an expected value:
- The name is case-insensitive:
content-typechecksContent-Type. - The value must equal the header’s value exactly, case-sensitive. There is no wildcard or
substring match, so an expected
application/jsonfails againstapplication/json; charset=utf-8. - When the response repeats a header, only its first value is compared.
A failure reads Header <name> was <actual>, expected <expected>, with nothing as the
actual value when the header is absent.
Body validation
Section titled “Body validation”Body validation has five tiers, from least to most strict.
| Tier | App label | Needs a sample | Passes when | Example |
|---|---|---|---|---|
NONE | None | No | Always; the body is not checked. | Any body passes, empty included. |
VALID_JSON | Valid JSON | No | The body parses as JSON. | {"ok": true} passes; an HTML error page fails. |
JSON_CONTAINS | Contains fields | Yes | Every key in the sample is present, at every depth, with the same JSON type. Extra keys are fine; values are ignored. | Sample {"id": 1} passes {"id": 99, "name": "x"} and fails {"id": "99"}. |
JSON_SHAPE | Exact fields | Yes | The same check with the exact key set: an extra key fails. Values are ignored; types are compared. | Sample {"id": 1} passes {"id": 7} and fails {"id": 7, "name": "x"}. |
EXACT_MATCH | Exact match | Yes | The body equals the sample. | Sample {"status": "ok"} passes { "status":"ok" } and fails {"status": "degraded"}. |
A tier that needs a sample refuses a blank one with
sample must not be blank for tier <TIER>.
How JSON_CONTAINS and JSON_SHAPE compare:
- A scalar must have the same JSON type as the sample’s value: number, string, boolean or
null.
{"id": "99"}against the sample{"id": 1}fails as$.id: expected number, got string. - An object must meet an object and an array an array. An object sample against an array
fails with
expected object, got array. - An array in the body passes when every element matches the first element of the sample’s array. An empty sample array accepts any array.
EXACT_MATCH compares parsed JSON when both sides are JSON, so key order and whitespace do
not matter. When either side is not JSON, it compares the two texts exactly.
A failed body check reads, by tier:
| Tier | Failure |
|---|---|
VALID_JSON | Response body is not valid JSON |
JSON_CONTAINS | Response body is missing expected fields (a type mismatch too) |
JSON_SHAPE | Response body does not match the expected shape |
EXACT_MATCH | Response body does not match the sample |
Every assertion above runs on every response, whatever the body validation tier. A status mismatch does not skip the latency, header or body checks, and the run lists every failure.
Redirects
Section titled “Redirects”An HTTP test follows redirects, so the assertions judge the final response:
- It follows 301, 302, 303, 307 and 308 responses that carry a
Location, up to five in a row. A sixth fails the run withtoo many redirects. - It never follows a redirect from https to http. A downgrading or unparseable
Locationends the chain, and that 3xx response is the one the assertions judge. To pass on it, accept3xxin the status pattern, or point the test at the final https URL. - A 303 turns any method but
HEADintoGET, and a 301 or 302 turnsPOSTintoGET. When the method changes, the body and itsContent-TypeandContent-Lengthheaders are dropped. - A redirect to another origin (scheme, host or port) drops the
Authorization,Proxy-AuthorizationandCookieheaders. - The timeout and
Max latencyboth cover the whole chain, and every hop must also be a public address.
MCP tests follow redirects too, under their own rules; see MCP tests.
Example
Section titled “Example”A test that creates an order every run and checks the response’s shape. It assumes a
Bearer token credential named Orders API token already exists.
| Field | Value |
|---|---|
Name | Create order |
Type | HTTP endpoint |
| Method | POST |
URL | https://api.example.com/v1/orders |
Credential | Orders API token |
Request headers | Content-Type = application/json; Idempotency-Key = {{uuid}} |
Status pattern | 2xx |
Max latency | 800 |
Body validation | Contains fields |
Request body:
{"sku": "PROBE-1", "quantity": 1, "reference": "{{uuid}}"}Body sample:
{"id": "", "status": "", "items": [{"sku": "", "quantity": 0}]}The two {{uuid}} values are the same within one attempt, so the idempotency key and the
order’s reference match. The sample’s values only set types: "" asks for a string, 0 for
a number.
This response passes, because the extra total key is tolerated:
{"id": "ord_123", "status": "created", "items": [{"sku": "PROBE-1", "quantity": 1}], "total": 12.5}A response with "id": 123 fails with Response body is missing expected fields, because
the sample asks for a string. A 201 that took 950 ms fails with
Took 950 ms, limit 800 ms.
Create API test runs the test once before saving it, and saves it only if that run passes.