Postguy
Guide

Scripts and tests

Pre-request and post-response scripts, the Tests tab with 28 assertion operators, execution order and sandbox limits.

Postguy runs JavaScript before a request is sent and after the response arrives. It also checks a table of assertions against the response.

Scripts run in an embedded QuickJS sandbox with the pm API, the pg object and Bruno-style globals. This page covers where scripts live, when they run and which limits apply. The full API is in the Scripting API reference.

This page covers HTTP requests. gRPC requests run their own request-level Pre-invoke, On message and Post-response scripts and Test tab; see gRPC. WebSocket script phases are not available. pm.cookies.jar() and pm.visualizer are not implemented.

Where scripts live

PlaceTabPhases
CollectionScriptsPre-request, Post-response
FolderScriptsPre-request, Post-response
RequestScriptsPre-request, Post-response
RequestTestsassertion table and Test script

A tab shows a dot when it has content. The script editors have a Snippets menu with ready-made code.

Execution order

For one send:

  1. Pre-request: the collection script, then each folder script from the outermost to the innermost folder, then the request script. Every script runs in its own function scope, so const and let names never collide between levels.
  2. Postguy builds auth, URL and body from the (possibly edited) request and sends it. See HTTP requests.
  3. Post-response: the collection script, the folder scripts (outer to inner), then the request script.
  4. Tests tab: the enabled assertion rows in table order, then the Test script.

All scripts of one phase share one sandbox run and one time budget. They see each other's variable changes. The Tests assertions and script are part of the post-response run.

A request with only a Tests tab and no scripts still runs the post-response phase.

Failure behaviour

WhereWhat happens
Pre-request script throws, times out or runs out of memoryNothing is sent. The send fails with pre-request script in <origin> failed at line <n>: <message>, and the error is written to the Console. Later pre-request scripts do not run.
pm.execution.skipRequest() in a pre-request scriptThe request is skipped. This is not a failure.
Post-response script throwsThe response is kept. The failure is listed as a script failure and a warning. The next scripts and the Tests tab still run. A timeout or memory limit stops the rest.
An assertion failsOne failed result with a message such as expected res.status equals 201, got 200. The remaining rows still run.

<origin> names the level: collection <name>, folder <name> or request <name>. The Tests tab is request <name> tests.

Test results

The response viewer has a Test Results tab. It is labelled Test Results (passed/total) once something ran.

  • Filters: All, Passed, Skipped and Failed.
  • Rows are marked PASS, FAIL and SKIP.
  • Each failure shows its message. Script failures show in a banner.
  • Results from assertion rows are tagged as assertions.
  • Secret variable values are masked in failure messages.

In the runner, test results count towards the run's pass and fail totals.

Examples

A pm-style post-response script:

pm.test("status is 200", () => {
  pm.response.to.have.status(200);
});

pm.test("has an id", () => {
  const body = pm.response.json();
  pm.expect(body.id).to.be.a("number");
  pm.environment.set("lastId", String(body.id));
});

The same with pg. It has every pm member and adds a runtime variable scope. That scope lives for the app session and is never written to a file:

pg.test("status is 200", () => pg.response.to.have.status(200));

pg.setVar("token", pg.response.json().token);
pg.report.set("traceId", pg.response.headers.get("x-trace-id"));

A Bruno-style script uses the bru, req, res, test and expect globals. res exists in the post-response phase only:

test("created", () => {
  expect(res.status).to.equal(201);
});
bru.setVar("orderId", res.body.id);

A pre-request script can change the request before it is sent, and can stop it:

pm.variables.set("requestTime", new Date().toISOString());
if (!pm.environment.get("baseUrl")) {
  pm.execution.skipRequest();
}

Tests tab

The Tests tab has two parts: an assertion table and a Test script.

Each assertion row has an enable checkbox, an expression, an operator and a value:

  • Expression: JavaScript evaluated after the post-response scripts, with res available. Examples: res.status, res.body.id, res.headers['content-type']. An expression that throws fails the row with could not evaluate <expr>: <message>.
  • Value: supports {{variables}} and is trimmed. Postguy parses it as a JSON literal when possible: undefined, true, false, null, numbers, quoted strings and JSON arrays or objects. Anything else is plain text.
  • Unary operators ignore the value. The value field is hidden for them.

The result name is <expr> <operator token> <value>.

Operators

There are 28 operators. Each has a label in the table and a token in the collection file.

  • Postguy also reads equals and notEquals as eq and neq.
  • An unknown token is kept in the file and evaluated as eq.
TokenLabelPasses when
eqequalsdeep-equal to the parsed value, or a string equal to the raw text
neqnot equalsnot eq
gtgreater thannumber comparison; fails with expected a number otherwise
gtegreater or equalnumber comparison
ltless thannumber comparison
lteless or equalnumber comparison
ininequals one item of a JSON array or a comma-separated list
notInnot inequals none of those items
containscontainsa string contains the text, or an array has an equal item
notContainsnot containsthe opposite
startsWithstarts witha string starts with the text
endsWithends witha string ends with the text
matchesmatchesa JavaScript RegExp built from the value finds a match in the string form of the result
notMatchesnot matchesno match
betweenbetweena number inside an inclusive range written 1, 10 or [1, 10]
lengthlengtha string (characters), array or object (keys) has exactly the given length
isEmptyis emptyunary
isNotEmptyis not emptyunary; null and undefined fail
isNullis nullunary
isUndefinedis undefinedunary
isDefinedis definedunary
isTruthyis truthyunary
isFalsyis falsyunary
isNumberis numberunary
isStringis stringunary
isBooleanis booleanunary
isArrayis arrayunary
isJsonis JSONunary; an array or an object

Type errors:

  • startsWith and endsWith fail with expected a string on other types.
  • contains and notContains fail with expected a string or an array.

Tests and assertions travel with Postman exports as postguyTests. Bruno assertions map to these operators on import. See Import and export.

Sandbox limits

Scripts get no filesystem, process or network access. The only way out is pm.sendRequest and the cookie lookup. The app runs both through its own request pipeline. A script error or panic becomes a reported failure, never a crash.

LimitValue
Wall-clock time for all scripts of one phase5,000 ms
JavaScript heap64 MiB
Native stack1 MiB
pm.sendRequest calls per run10
Kept console lines per run1,000
Kept test results per run1,000; more are ignored and reported as a script failure
Modules loaded with require per run64
Size of one module8 MiB, UTF-8 text

A run that hits the time or memory limit stops with a failure message that names the limit. A pre-request run that does so sends nothing.

require and assets

require loads CommonJS modules from the collection's Assets folder. Paths are relative to the folder root, for example require('./lib/helpers.js').

  • The virtual fs, path, buffer and crypto shims read the same folder. crypto offers randomUUID. Every write function throws an explanatory error.
  • pg.assets offers text, base64, exists and list.
  • npm packages cannot be loaded.
  • bru.sleep, bru.cwd and bru.runRequest are refused with an error.

See Collections for managing assets, and the Scripting API for the exact surface.

Turn scripts off

Settings › General › Disable scripts skips pre-request and post-response scripts, and the Tests tab, for every request and collection run.

The send then carries a warning: scripts are disabled in Settings; pre-request and post-response scripts were skipped. See Settings.

Headless

  • request_send runs the whole pipeline, scripts and tests included, and returns the script report with the result.
  • collection_run runs scripts and tests across a collection.
  • runtime_variables reads (and optionally clears) the pg.setVar and bru.setVar values of a collection.
  • request_update edits a request's scripts and tests through its definition.
See Automation tools and the CLI.
Copyright © 2026