Skip to main content
hitspec tests live in plain text files with .http or .hitspec extensions. Each file contains one or more HTTP requests along with optional variables, assertions, and captures.

Minimal Example

The smallest valid test is a request with one assertion:

File Structure

A typical .http file follows this structure:

Request Separator

Use ### to separate multiple requests within a single file. Everything before the first ### (or before the first request line if no separator is used) is treated as the file-level scope for variable definitions.
The ### separator is required when a file contains more than one request. Text after ### on the same line is treated as a human-readable title and is ignored by the parser.

Variables

Define variables at the top of a file using the @variable = value syntax:
Reference them anywhere in the file with double curly braces:
See Variables for the full variable system including built-in functions and captures.

Request Line

Every request starts with an HTTP method followed by a URL:
The method is the first word and the URL is everything after it. An optional final HTTP/1.1-style token is read as the protocol version and is not part of the URL. Spaces inside {{ ... }} never split the line, so built-in function arguments and padded variable names work in a URL:

Headers

Headers follow the request line, one per line, using standard Name: Value format:
Everything after the first : is the header value, trimmed of surrounding whitespace and otherwise sent exactly as written: quotes, # and apostrophes are part of the value.
Comment lines (# ... or // ...) between the request line and the blank line are skipped, and headers after them are still headers. A # @name x line in this position is only a comment; put directives before the request line.
A blank line separates headers from the request body. Any lines after the blank line (until the next block marker or separator) are treated as the body.

Request Body

JSON

Form URL-Encoded

Use the & prefix syntax for readable form data:

Multipart Form Data

Use the >>>multipart block for file uploads and mixed form data:
file <name> @<path> sends the file as form field <name>; file @<path> uses the field name file. A file that cannot be read fails the request with an error naming the path.

GraphQL

Use >>>graphql and >>>variables blocks:

XML

File-Based Body

Reference an external file as the request body using < ./path:
The file path is resolved relative to the .http file’s directory. A file that cannot be read fails the request with an error naming the path; hitspec does not send the path text instead. Content-Type is auto-detected from the file extension (.json, .xml, .yaml, .html, .csv, .txt) when no explicit Content-Type header is set. Variable interpolation works in the file path:

Query Parameters

Inline with the URL:
Or use the explicit ? prefix syntax for readability:
Query parameters written as ? name = value lines are appended to the URL in the order you wrote them, repeated names are all sent, and names and values are percent-encoded (a space is %20). Characters that cannot appear in a URL (spaces, quotes, non-ASCII) in the URL itself are percent-encoded too; %XX escapes you wrote are kept. In a form block (& name = value lines) values keep their spaces and each name and value is percent-encoded when sent. A body written inline as a=b&c=d is sent exactly as written.

Assertion Blocks

Wrap assertions in >>> and <<< markers:
Every line in the block is an expect line, a comment (# or //) or blank. A block must be closed with <<<, must follow a request line, and may appear more than once per request. Unknown operators, operators without a value and misspelt block types (>>>captur) are parse errors (exit code 2), not silently ignored. See Assertions for all 26 operators.

Capture Blocks

Capture response values for use in later requests:
See Captures for the full capture system.

Metadata Directives

Metadata directives are comments that start with # @ and control request behavior:

All Directives

Directives not in this table are reported by hitspec validate as warnings (# @tag is probably # @tags), except custom ones starting with x- (# @x-owner team-a) and @contract.*. A request may start with its # @directives and omit the ### line.

@waitFor — Service Readiness Polling

The @waitFor directive polls a URL before executing the request. This is useful when your test depends on a service that may not be immediately available (e.g., a container that was just started).
Syntax:
If the URL does not return the expected status within the timeout, the request fails without executing. When the target cannot be reached at all (connection refused, DNS failure), the run exits with code 4. The poll uses the same HTTP client settings as the run (default headers, proxy, CA bundle, --insecure).

@if / @unless — Conditional Execution

Skip requests based on variable values:
A bare value is truthy when it is defined and not empty, "0", "false", "no" or "null". An undefined variable is falsy. Conditions can compare with == and !=, negate with !, and combine with && and || (&& binds tighter):
Each side of a comparison is resolved on its own and compared as text (as numbers when both sides are numbers); quotes around either side are ignored. An undefined variable counts as empty, so {{missing}} == "prod" is false and {{missing}} != "prod" is true. Without a comparison, # @if {{env}} is true for any non-empty value, so use == to test for a specific one.

Stress Test Annotations

Fine-tune how individual requests behave during stress testing:

Comments

Lines starting with # that do not have an @ directive are treated as regular comments and ignored:

Authentication

Use @auth to attach credentials to a request. hitspec supports 8 authentication methods:

File Extensions

hitspec recognizes two file extensions:
  • .http — the standard extension, compatible with REST Client and other HTTP file tools
  • .hitspec — an alternative extension for files that use hitspec-specific features
Both are functionally identical.
Last modified on October 19, 2026