Skip to main content
Captures extract values from HTTP responses — body fields, headers, status codes, and timing — and store them as variables for use in later requests. This is the foundation for building multi-step test flows like authentication, CRUD operations, and workflow testing.

Capture Block Syntax

Wrap capture definitions in >>>capture and <<< markers:
Each line follows this pattern:
Capture lines are read as written, so a path may contain quotes and spaces (items.#(name=="Jane Doe").id). A trailing # comment is allowed. A line that is not <name> from <source> is a parse error, and a capture block must be closed with <<<.

Capture Sources

hitspec can capture values from these parts of the response:

Body Captures

Use dot notation and array indexing to reach into the JSON response:
Body paths use GJSON syntax, so the full GJSON path language is available — including .# for array length and queries:
Assertions and captures share one path normalizer, so the same paths work in expect body.<path> assertions: you can assert on body.items.# (array length) directly too. Bracket notation works in both (body.items[0].name, body[2].id, body.items[*].id).
Body paths need a JSON response (application/json, or any +json type such as application/problem+json, with or without ; charset=...). On a non-JSON response only the whole body can be captured.

Captures that find nothing

A capture whose path matches nothing (a typo, an error response with a different shape, a missing header) is left out and reported on stderr, once per run:
Without the warning the problem only appeared later as an unresolved {{variable}} in another request. A capture whose path contains spaces outside (...) (a note glued to the path instead of after #) is flagged by hitspec validate because it can never match.

Header Captures

Status and Duration Captures

Using Captured Values

Captured values are referenced using the {{requestName.captureName}} pattern. The requestName is the value of the @name directive on the capturing request.

How values are inserted

A captured value is inserted into the next request exactly as the response had it:
  • Strings are inserted as-is.
  • Numbers keep their exact JSON digits, so 1000000 stays 1000000 and 123456789012 is not rounded or written as 1e+06.
  • Booleans are true or false, and null is null.
  • Objects and arrays are inserted as compact JSON, for example {"id":1,"tags":["x","y"]}.
Captured values are data, not templates. If a response contains {{HOME}} or {{$env(SECRET)}}, that text is sent literally and never expanded.
Three things are required for captures to work across requests:
  1. The source request must have a # @name directive.
  2. The source request must have a >>>capture block.
  3. The consuming request must declare # @depends on the source request to guarantee execution order.

Real-World Examples

Authentication Flow

CRUD Workflow

Capturing Headers for Pagination

Captures and Assertions Together

A request can have both assertion and capture blocks. Assertions are evaluated first; if they fail, captures are still performed (allowing dependent requests to attempt execution):
Place assertion blocks before capture blocks for readability. Both blocks belong to the same request — their order does not affect behavior.
Last modified on October 19, 2026