Skip to content

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.

FieldWhat it takes
URLAn http:// or https:// URL. It must be reachable from the public internet.
MethodGET, POST, PUT, PATCH, DELETE or HEAD. The editor defaults to GET.
Request headersName and value pairs, sent as written, after request variables are filled in.
Request bodyOptional text, sent as UTF-8.
CredentialNone, 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:

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.

PatternMatches
200200 only
2xxAny 2xx status (the editor’s default)
20x200 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 (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 are optional checks, each a header name and an expected value:

  • The name is case-insensitive: content-type checks Content-Type.
  • The value must equal the header’s value exactly, case-sensitive. There is no wildcard or substring match, so an expected application/json fails against application/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 has five tiers, from least to most strict.

TierApp labelNeeds a samplePasses whenExample
NONENoneNoAlways; the body is not checked.Any body passes, empty included.
VALID_JSONValid JSONNoThe body parses as JSON.{"ok": true} passes; an HTML error page fails.
JSON_CONTAINSContains fieldsYesEvery 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_SHAPEExact fieldsYesThe 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_MATCHExact matchYesThe 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:

TierFailure
VALID_JSONResponse body is not valid JSON
JSON_CONTAINSResponse body is missing expected fields (a type mismatch too)
JSON_SHAPEResponse body does not match the expected shape
EXACT_MATCHResponse 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.

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 with too many redirects.
  • It never follows a redirect from https to http. A downgrading or unparseable Location ends the chain, and that 3xx response is the one the assertions judge. To pass on it, accept 3xx in the status pattern, or point the test at the final https URL.
  • A 303 turns any method but HEAD into GET, and a 301 or 302 turns POST into GET. When the method changes, the body and its Content-Type and Content-Length headers are dropped.
  • A redirect to another origin (scheme, host or port) drops the Authorization, Proxy-Authorization and Cookie headers.
  • The timeout and Max latency both 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.

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.

FieldValue
NameCreate order
TypeHTTP endpoint
MethodPOST
URLhttps://api.example.com/v1/orders
CredentialOrders API token
Request headersContent-Type = application/json; Idempotency-Key = {{uuid}}
Status pattern2xx
Max latency800
Body validationContains 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.