Scripting API
Postguy runs pre-request and post-response scripts in an embedded QuickJS engine. The scripting surface is implemented as Rust closures in crates/script. This page lists every global and member.
- For when scripts run, their order, and how failures are shown, see Scripts and tests.
- For the
{{$name}}generators you can use in request text, see Dynamic variables.
Settings › Disable scripts turns every script off.Phases
| Phase | pm.info.eventName | pm.response / res | Request edits |
|---|---|---|---|
| Pre-request | prerequest | not defined | allowed |
| Post-response | test | defined | refused: pm.request is read-only in post-response scripts |
The request's Tests tab runs in the post-response phase, after the post-response scripts. It runs the enabled assertions in order first, then the Tests script.
- Each script runs inside its own function.
constandletnames never collide between the collection, folder and request scripts. Share data through variables, not JS scope. - After a script finishes, queued promise callbacks and
setTimeoutcallbacks are drained before the next script starts. - A thrown error is recorded as a failure with the script's origin label and line number.
- A failing pre-request script stops the remaining pre-request scripts of that run.
- A failing post-response script does not stop the later ones, unless a limit (time, cancel) tripped.
Globals
| Global | Available | What it is |
|---|---|---|
pm | always | The Postman-style API, described below. pm.response exists in post-response scripts only. |
postman | always | Only postman.setNextRequest(name). |
pg | always | Every pm member plus Bruno-style variable and request methods, flow control, pg.assets and pg.report. |
bru | always | Bruno-style variables, sendRequest, setNextRequest, runner. |
req | always | Bruno-style request object. |
res | post-response | Bruno-style callable response. |
test | always | The same function as pm.test. |
expect | always | The same function as pm.expect. |
console | always | log, info, warn, error, debug. |
atob, btoa | always | Base64 helpers (Latin-1 text). |
setTimeout, clearTimeout | always | Queued timers, run after the script body. |
Buffer | always | Minimal Node-style Buffer. |
require | always | Built-in fs, path, buffer, crypto shims and modules from the collection's Assets folder. |
__dirname, __filename | always | / and /script.js. |
console, atob, btoa, timers
console.log/info/warn/error/debug(...args)joins arguments with a space. Objects are rendered as JSON. A line is cut at 16 KiB with… (truncated). Lines past the line limit are dropped.btoa(text)throwsInvalidCharacterError: btoa only encodes Latin-1 textfor characters above U+00FF.atob(text)ignores whitespace and optional padding and throwsInvalidCharacterError: the string to be decoded is not correctly encodedon bad input. The result is a Latin-1 string.setTimeout(fn, delay?, ...args)returns a numeric id. The delay is not waited: callbacks run after the script body, ordered by delay and then by creation. Timers scheduled from timers run in later rounds.clearTimeout(id)cancels one.
pm
Variable scopes
pm.globals, pm.collectionVariables, pm.environment, pm.variables and pm.iterationData share the same methods. Values are stored as text: strings as-is, everything else through JSON.stringify. The limit is 1 MiB.
| Member | Notes |
|---|---|
get(key) | Value, or undefined when missing. |
has(key) | true when get would return a value. |
toObject() | All entries of the scope as an object. |
set(key, value) | Not on iterationData. |
unset(key) | Not on iterationData. |
clear() | Not on iterationData. |
replaceIn(text) | pm.variables only. Replaces {{var}} over every scope using the precedence below. |
name | pm.environment only. Name of the active environment, or null. |
| Scope | Writable | Notes |
|---|---|---|
pm.globals | yes | Workspace globals. |
pm.collectionVariables | yes | Collection variables. |
pm.environment | yes, when an environment is selected | Otherwise set/unset/clear throw no environment is selected; choose one before setting environment variables. |
pm.variables | yes | set stores a local value for the run; local values are never persisted. get reads the merged chain. toObject() returns the merged view. |
pm.iterationData | no | Current data row of a collection run. Throws pm.iterationData is read-only on write attempts (no write methods exist). Values keep their JSON type. |
pm.variables.get looks in this order, and the first match wins: local values, runtime variables (pg.setVar), iteration data, environment, folder variables, collection variables, globals.
Request
pm.request is read-only after the response:
| Member | Description |
|---|---|
name, id | Request name and id. |
method | Getter/setter. The setter upper-cases the value. |
url | Getter returns an object; setter accepts a string or an object with toString. Text is unresolved ({{vars}} still in place). |
url.toString(), url.getHost(), url.getPath(), url.getQueryString() | URL parts. |
url.query.get(key), has(key), toObject() | Read query rows. |
url.query.add({key, value}), upsert({key, value}), remove(key) | Edit query rows (pre-request only). |
headers.get(name), has(name), toObject() | Enabled headers; names are case-insensitive. |
headers.add({key, value}) | Append a header. |
headers.upsert({key, value}) | Replace the header of that name and enable it, or append. |
headers.remove(name) | Remove every header of that name. |
body.mode | none, raw, urlencoded, formdata or other (binary, GraphQL and any mode scripts cannot edit). |
body.raw | Getter/setter for raw text. Getter returns null unless the mode is raw. Setting switches the body to raw. |
body.urlencoded, body.formdata | Arrays of {key, value}; empty unless the mode matches. |
body.update(value) | Same as setting body.raw; non-strings are stored as JSON. |
A key/value object without a key throws expected an object with a key and a value.
Response
pm.response exists only in post-response scripts. It is a plain snapshot:
| Member | Description |
|---|---|
code | Numeric status. |
status | Reason text, for example OK. |
reason() | The same text as status. |
responseTime | Milliseconds. |
responseSize | Body size in bytes. |
headers.get(name), has(name), toObject() | Case-insensitive header lookup. |
text() | Body as text (invalid UTF-8 replaced). |
json() | Parsed body; throws JSONError: <detail> when the body is not valid JSON. |
to | Response assertions, listed below. |
Cookies
| Member | Description |
|---|---|
pm.cookies.get(name), has(name), toObject() | Name/value pairs from the response's Set-Cookie headers plus the cookies the jar would send for the request URL. A response cookie wins over a jar cookie of the same name. |
pm.cookies.jar() | Throws pm.cookies.jar() is not supported yet. |
Tests and requests
| Member | Description |
|---|---|
pm.test(name, fn?) | Records a passing test when fn returns, a failing one with the error message when it throws. Without fn it records a pass. If fn returns a promise, the test is the test did not finish until the promise settles; a rejection fails it with the reason. |
pm.test.skip(name, fn?) | Records a skipped test without running fn. |
pm.expect(value) | Chai-style assertion, see Assertions. |
pm.sendRequest(urlOrOptions, callback?) | Sends a request through the engine's pipeline (cookie jar and settings apply). Returns a promise, or calls callback(error, response). |
pm.info | eventName, iteration, iterationCount, requestName, requestId. |
pm.sendRequest options:
- A URL string, or
{ url, method, header, body }. urlis a string or an object withtoString.methoddefaults toGET.headeris an array of{key, value}or a plain object.bodyis{ mode: "raw" | "urlencoded" | "formdata", raw | urlencoded | formdata }.
The response has the same shape as pm.response, but without to. A failed send passes an Error to the callback, or rejects the promise.
It throws these errors:
pm.sendRequest was called more than 10 times, after the limit.the script was cancelled, when cancelled.pm.sendRequest expects a URL or an options object, for any other argument.
Flow control
| Call | Effect |
|---|---|
pm.execution.setNextRequest(name) | Runner only: the next request to run, by name. |
pm.execution.setNextRequest(null) | Any non-string argument (or none) stops the run after this request. |
pm.execution.skipRequest() | Pre-request only: skips sending this request. In a post-response script it throws pm.execution.skipRequest() can only be called in a pre-request script. |
postman.setNextRequest(name) | Alias of pm.execution.setNextRequest. |
See Collection runner for how the runner applies them.
// Pre-request: sign the request and skip it when a flag is off
const stamp = Date.now().toString();
pm.variables.set("stamp", stamp);
pm.request.headers.upsert({ key: "X-Stamp", value: stamp });
if (pm.environment.get("dryRun") === "true") pm.execution.skipRequest();
// Post-response: assert and chain
pm.test("created", () => pm.response.to.have.status(201));
pm.test("has id", () => pm.expect(pm.response.json()).to.have.property("id"));
pm.collectionVariables.set("userId", String(pm.response.json().id));
pm.execution.setNextRequest("Fetch user");
Assertions
pm.expect(value) (also expect(value)) returns an assertion. Failures throw an AssertionError whose message is expected <value> to <description> (or to not <description> after not). Values that are cyclic or too large fail with value is too deeply nested or too large.
Chain words
These words return the assertion unchanged: to, be, been, is, that, which, and, has, have, with, at, of, same, but, does, still, own, all, any.
Flags: not negates the next check. deep makes equal, include, property and oneOf compare structurally.
Terminals
These properties run the check when read, for example pm.expect(x).to.be.true.
| Terminal | Passes when |
|---|---|
ok | The value is truthy. |
true / false | The value is exactly true / false. |
null / undefined | The value is null / undefined. |
NaN | The value is a number that is NaN. |
exist | The value is neither null nor undefined. |
empty | An empty string, array or object. Any other type fails. |
Methods
| Method (aliases) | Checks |
|---|---|
equal(x) (equals, eq) | Strict equality. Arrays and objects compare by identity unless deep is set. |
eql(x) (eqls) | Deep equality. |
above(n) (gt, greaterThan) | Number greater than n. |
below(n) (lt, lessThan) | Number less than n. |
least(n) (gte) | Number at least n. |
most(n) (lte) | Number at most n. |
within(lo, hi) | Number between lo and hi, inclusive. |
a(type), an(type) | Type name, compared lower-case: undefined, null, boolean, number, string, array, object, function, symbol. |
include(x) (includes, contain, contains) | Substring of a string; element of an array; or an object containing every key/value of the given object (deep compare). |
property(name, value?) | The object has the property; with value, it also equals it. A passing property makes the property's value the new subject of the chain. |
lengthOf(n) (length) | String length (UTF-16 units) or array length. Other types fail with expected <value> to have a length. |
keys(...names) (key) | The object has exactly these keys (array or arguments). |
members(array) | Same elements in any order (deep compare). |
oneOf(array) | The value is in the array. |
match(regex) (matches) | A string matching the regular expression. |
string(text) | A string containing text. |
Number comparisons on a non-number throw expected <value> to be a number. A missing argument throws the assertion needs an argument.
pm.expect(pm.response.json().items).to.be.an("array").and.have.lengthOf(2);
pm.expect({ a: 1, b: [2] }).to.eql({ a: 1, b: [2] });
pm.expect("abc").to.match(/b/);
pm.expect(5).to.be.within(1, 10);
pm.response.to
Response assertions use the same chain words, not and deep. They throw there is no response in a pre-request script when used without a response.
Terminals (status class and content type):
| Terminal | Passes when |
|---|---|
ok | Status is 2xx. |
success | Status is 2xx. |
error | Status is 400 or above. |
clientError | Status is 4xx. |
serverError | Status is 5xx. |
notFound | Status is 404. |
json | A content-type header contains json. |
Methods:
| Method | Checks |
|---|---|
status(code | reason) | A number compares the status code; any other value compares the reason text case-insensitively. |
header(name) | The header is present. |
header(name, value) | The header has exactly this value. Name lookup is case-insensitive. |
body(text) | The body text equals text. |
jsonBody() | The body is valid JSON. |
jsonBody(path) | The dot path exists (object keys and array indices, for example data.items.0.id). |
jsonBody(path, value) | The value at the path equals value (JSON equality). |
pm.test("json ok", () => {
pm.response.to.be.ok;
pm.response.to.be.json;
pm.response.to.have.header("content-type", "application/json");
pm.response.to.have.jsonBody("data.items.0.id", 1);
});
pg
pg contains every member of pm (the same values) and these additions. It does not add postman.
Variables (also on bru)
Bruno-style methods. They call the same scopes as pm:
| Method | Scope |
|---|---|
getVar(key), setVar(key, value), hasVar(key), deleteVar(key) | Runtime variables: kept by the engine for the session and across requests, never written to a file. |
getEnvVar(key), setEnvVar(key, value), hasEnvVar(key), deleteEnvVar(key) | Active environment. setEnvVar and deleteEnvVar throw the no-environment error when none is selected. |
getGlobalVar(key), setGlobalVar(key, value) | Globals. |
getGlobalEnvVar(key), setGlobalEnvVar(key, value) | Globals (alias names). |
getCollectionVar(key), setCollectionVar(key, value) | Collection variables. |
getFolderVar(key) | Merged folder variables, read-only. |
getRequestVar(key) | Local values set with pm.variables.set. |
getEnvName() | Name of the active environment, or null. |
getProcessEnv(key) | Walks the normal variable chain. Operating-system environment variables are never exposed. |
interpolate(text) | Replaces {{var}} using the same precedence as pm.variables. |
set* methods accept a third options argument and ignore it.
Request methods (on pg.request and req)
| Method | Notes |
|---|---|
getUrl(), getMethod(), getName() | Reads. |
setUrl(url), setMethod(method) | Pre-request only. The method is upper-cased. |
getHeader(name), getHeaders() | Enabled headers; getHeaders() returns a snapshot object. |
setHeader(name, value), setHeaders(object), deleteHeader(name) | Pre-request only. |
getBody() | Raw text parsed as JSON when it parses, otherwise the string. Form modes return an array of {key, value}. undefined for no body or other. |
setBody(value) | A string as-is, anything else as compact JSON. undefined throws <label>.setBody expects a string or a JSON value. |
pg.request also keeps the pm.request members. After the response, setters throw <label> is read-only in post-response scripts, where the label is req, pg.request or pm.request.
Flow control
| Call | Effect |
|---|---|
pg.setNextRequest(name), pg.skipRequest() | Same as the pm.execution calls. |
pg.stopExecution() | Stops the run after this request. Valid in both phases. |
pg.assets
Read-only access to the collection's Assets folder (see Collections). Paths use / and are relative to the folder root. A path that climbs above the root throws path <p> leaves the collection assets.
| Method | Returns |
|---|---|
base64(path) | File contents as a base64 string. |
text(path) | File contents as UTF-8 text; throws asset <path> is not UTF-8 text; use pg.assets.base64. |
exists(path) | true for a file or folder that exists. |
list(dir?) | Names of the direct children of a folder (root when omitted). |
Requests that are not saved in a collection throw assets are only available for requests saved in a collection.
pg.report
Adds custom columns to run reports (see Collection runner).
| Method | Notes |
|---|---|
set(key, value) | Primitives are stored as JSON; objects as their JSON.stringify text; strings longer than 32 KiB are cut with …. undefined, functions, symbols and BigInts remove the key. |
get(key) | The stored value, or undefined. |
toObject() | All fields in first-set order. |
Limits: keys up to 128 characters (pg.report keys may be at most 128 characters), at most 100 fields (pg.report holds at most 100 fields).
pg.report.set("tenant", pg.getEnvVar("tenant"));
pg.report.set("latencyMs", pm.response.responseTime);
Bruno compatibility
Postguy accepts the Bruno script globals, so scripts from imported Bruno folders run. There is no Bruno export.
bru members:
| Member | Notes |
|---|---|
| Variable methods | All the variable methods above. |
bru.sendRequest(...) | Same as pm.sendRequest. |
bru.setNextRequest(name) | Same as pm.execution.setNextRequest. |
bru.runner.setNextRequest(name), skipRequest(), stopExecution() | Flow control. |
Unsupported bru calls throw <name> is not supported in Postguy scripts:
| Call | Error text |
|---|---|
bru.sleep(ms) | bru.sleep is not supported in Postguy scripts |
bru.cwd() | bru.cwd is not supported in Postguy scripts |
bru.runRequest(...) | bru.runRequest is not supported in Postguy scripts |
req
The request methods plus accessors that read and write the same values: req.url (string), req.method, req.headers (snapshot object; assigning merges each entry into the headers) and req.body (parsed JSON or string; assigning calls setBody).
res
res (post-response only) is both a function and an object.
| Member | Description |
|---|---|
res(path) | Queries a JSON body. Supports a.b, a[0], a["key"] and ..key (every key at any depth, returned as an array, depth limit 64). Returns undefined when nothing matches or the body is not JSON. |
res.status, res.statusText, res.responseTime | Status code, reason text, milliseconds. |
res.headers | Object with lower-cased names; repeated headers are joined with , . |
res.body | Parsed JSON, or the text when the body is not JSON. |
res.getStatus(), getStatusText(), getHeader(name), getHeaders(), getBody(), getResponseTime() | Accessor forms. getHeader is case-insensitive. |
res.getSize() | { body, headers, total } in bytes. |
test("token present", () => {
expect(res.status).to.equal(200);
bru.setVar("token", res("data.token"));
});
Node-style shims
Postguy does not embed Node. The shims are Rust closures over the collection's Assets folder.
require
| Specifier | Result |
|---|---|
fs, path, buffer, crypto (optionally node: prefixed) | Built-in shims below. |
./x, ../x, /x | A CommonJS module from the Assets folder, resolved relative to the requiring module. Tried in order: the path itself, .js, .json, then /index.js. .json files are parsed. |
| anything else | Throws require('<name>') is not available in Postguy scripts; npm packages are not supported. For axios the hint ; use pg.sendRequest is appended. |
Modules receive exports, require, module, __filename and __dirname.
- Modules are cached per run, so dependency cycles see the partial
module.exports. - A module that throws is not cached.
- A missing module throws
Cannot find module '<spec>' in the collection assets: <detail>. - At most 64 modules load per run.
- Each source is limited to 8 MiB and must be UTF-8.
fs (read-only virtual file system)
The root / is the collection's Assets folder.
| Function | Notes |
|---|---|
fs.readFileSync(path, options?) | Returns a Buffer, or a string when an encoding is given ("utf8" or { encoding }). |
fs.existsSync(path) | true when the file or folder exists. |
fs.readdirSync(path?) | Names of the direct children. |
Every writing function (writeFileSync, appendFileSync, mkdirSync, unlinkSync, rmSync, rmdirSync, renameSync, copyFileSync, truncateSync, chmodSync, symlinkSync, openSync, writeSync, createWriteStream, mkdtempSync and the callback forms writeFile, appendFile, mkdir, unlink, rm, rmdir, rename, copyFile, truncate, chmod, symlink, open, write) exists and throws fs.<name> is not available in Postguy scripts (scripts can only read collection assets).
path
POSIX only: join, resolve (with / as the working directory), normalize, dirname, basename(path, ext?), extname, isAbsolute, sep (/).
crypto
Only crypto.randomUUID().
Buffer
| Member | Notes |
|---|---|
Buffer.from(value, encoding?) | value is a string, an array of bytes, or another Buffer. Anything else throws Buffer.from expects a string, an array of bytes or a Buffer. |
Buffer.isBuffer(value) | Type check. |
buffer.length | Byte length. |
buffer.toString(encoding?) | Encode the bytes as text. |
Encodings: utf8 (utf-8), base64, base64url, hex, latin1 (binary). Others throw Buffer encoding '<name>' is not supported in Postguy scripts. Base64 decoding is lenient (both alphabets, optional padding).
Limits
These are the defaults of Limits in crates/script/src/lib.rs. The wall-clock budget covers all scripts of one run.
| Limit | Default | When exceeded |
|---|---|---|
| Wall-clock time | 5,000 ms | the script ran longer than 5000 ms |
| Engine heap | 64 MiB | the script used more than 64 MiB of memory |
| Native stack | 1 MiB | The script fails with an engine error (for example runaway recursion). |
pm.sendRequest calls | 10 per run | pm.sendRequest was called more than 10 times |
| Console lines kept | 1,000 | Further lines are dropped. |
| Console line length | 16 KiB | Cut with … (truncated). |
pm.test results kept (assertions count too) | 1,000 | more than 1000 tests; the rest were ignored (once). |
| Variable value size | 1 MiB | a variable value may not be larger than 1 MiB |
| Pending timers | 10,000 | too many pending timers |
| Timer rounds | 100 | setTimeout callbacks kept scheduling more callbacks |
| Promise jobs per drain round | 1,000,000 | too many queued promise jobs |
Modules loaded by require | 64 per run | scripts may load at most 64 modules per run |
| Module source size | 8 MiB | module <path> is larger than 8 MiB |
pg.report | 100 fields, 128-char keys, 32 KiB values | See pg.report. |
A user cancel stops a running script with the script was cancelled. Scripts have no file system, process or network access, except pm.sendRequest and read-only Assets. An engine error or panic becomes a recorded failure, never a crash.