Skip to content

API client

Scripts and tests

Pre-request and post-response scripts in the API client, with a complete reference for the Postman-compatible pm API, assertions, libraries and packages.

Scripts are JavaScript that runs around a request. A pre-request script runs before it’s sent, to compute a signature, set a variable or change the request. A post-response script runs after the response arrives, to check it with tests and pass values to the next request. Scripts use Postman’s pm API, so scripts from imported Postman collections run unchanged in most cases.

This page lists everything the sandbox implements. If a Postman feature isn’t here, Cloak Proxy doesn’t support it.

A post-response script and its test results.

  1. Pre-request and Post-response; a green dot marks a phase with code
  2. Snippets inserts common scripts
  3. The script editor
  4. Test Results shows passed/total
  5. PASS, FAIL or SKIP for each test
The Scripts tab with Post-response selected and five lines of pm.test calls, and the response's Test Results tab showing three PASS results
  1. Open a request and click Scripts.

  2. Click Post-response. The editor’s hint reads “Runs after the response arrives. Write tests with pm.test and pm.expect.”

  3. Write a test, or pick one from Snippets:

    Post-response
    pm.test("Status code is 200", () => {
    pm.response.to.have.status(200);
    });
    pm.test("Returns the pet", () => {
    const pet = pm.response.json();
    pm.expect(pet.id).to.equal(1);
    pm.expect(pet.name).to.be.a("string").and.not.be.empty;
    });
  4. Send the request. The response’s Test Results tab lists each test as PASS or FAIL with the reason, and the tab title shows the count, for example 2/2.

Collections and folders have the same Scripts tab. Their scripts run for every request inside them.

The Snippets menu inserts one of these at the end of the current script:

Snippet Inserts
Status code is 200 A test that the status is 200.
Response time is less than 200ms A test on pm.response.responseTime.
Response body: JSON value check A test comparing a JSON field with eql.
Response body: contains string A test that the body text includes a string.
Response body: is equal to a string A test that the body equals a string.
Content-Type header is present A test for a header.
Validate the response against a JSON schema A schema and a jsonSchema test.
Successful POST request A test that the status is 201 or 202.
Set an environment variable pm.environment.set(…)
Save a value from the response Saves token from the JSON body into the environment.
Set a collection variable pm.collectionVariables.set(…)
Set a global variable pm.globals.set(…)
Get a variable pm.variables.get(…)
Clear an environment variable pm.environment.unset(…)
Read a secret from the Vault await pm.vault.get(…)
Send a request await pm.sendRequest(…) and a log of the JSON.
Visualize the response as a table A pm.visualizer.set template.
Reuse a saved request await pm.execution.runRequest(…) that saves a token.
Use a shared package pm.require("helpers")
Skip this request (runner) pm.execution.skipRequest()
Run another request next (runner) pm.execution.setNextRequest(…)

Sending a request runs, in order:

  1. Pre-request scripts: the collection’s, then each folder’s from the outermost in, then the request’s.
  2. Variable resolution, path variables, auth and cookies.
  3. The network request.
  4. Post-response scripts, in the same order: collection, folders, request.

Each script sees the variables and request as the previous one left them. Empty scripts are skipped.

  • Pre-request scripts see the request as written, with {{variables}} still in place, before auth headers and cookies are added. Whatever they change is what gets resolved and sent.
  • Post-response scripts see the request as it was actually sent, with variables resolved and auth added.
  • Post-response scripts run even when the request failed to send. pm.response is then undefined, so guard tests with if (pm.response) when that matters.
  • If a pre-request script calls pm.execution.skipRequest(), the remaining pre-request scripts don’t run, nothing is sent, and the response area says “Skipped by script”.

The Timing tab shows how long the pre-request and post-response scripts took. Console entries are labeled with their phase and owner, such as “Pre-request · Petstore” or “Post-response · Get pet”.

Scripts run in JavaScriptCore, the engine in Safari, in a fresh sandbox for every script.

  • Modern JavaScript works: arrow functions, const and let, template literals, destructuring, async and await.
  • The script body is wrapped in an async function, so await works at the top level.
  • Each script has 30 seconds. After that it stops with “Script timed out after 30 seconds”.
  • There’s no file system, fetch, XMLHttpRequest or Node.js API. Use pm.sendRequest for HTTP.

Globals available in every script:

Global What it is
pm The Postman API, below.
console log, info, warn, error and debug. Output goes to the Console tab. Objects are printed as indented JSON.
setTimeout(fn, ms), clearTimeout(id) Timers.
atob(text), btoa(text) Base64 decode and encode. atob also accepts Base64URL.
xml2Json(xml) Converts XML to an object: elements become keys, attributes go under $, repeated elements become arrays, and text-only elements become strings.
require(name) Loads a built-in library or workspace package. Same as pm.require.
expect, chai The assertion library, also available as pm.expect.
CryptoJS, _, uuid, tv4, Ajv The built-in libraries, also loadable with require.
postman, responseBody, responseCode, responseTime, tests Legacy globals from Postman’s older API.

Read-only information about the current run.

Property Value
pm.info.eventName "prerequest" in pre-request scripts, "test" in post-response scripts.
pm.info.iteration The runner iteration, starting at 0. Always 0 for a single send.
pm.info.iterationCount The number of iterations in the run. 1 for a single send.
pm.info.requestName The request’s name.
pm.info.requestId The request’s ID.

The request being sent. Changes in a pre-request script are sent.

Member What it is
pm.request.method The method, such as "POST". Assign to change it.
pm.request.url A Url object. Assign a string or a Url to replace it: pm.request.url = "{{baseUrl}}/v2/pets".
pm.request.headers A property list of headers. Header names match without regard to case.
pm.request.body The body as text. Assign a string, { mode: "raw", raw: "…" }, or { mode: "urlencoded", urlencoded: [{ key, value }] }.
pm.request.name The request’s name.
pm.request.addHeader({ key, value }) Adds a header.
pm.request.upsertHeader({ key, value }) Sets a header, replacing an existing one with the same name.
pm.request.removeHeader(key) Removes every header with that name.
Pre-request
// Sign the body with a shared secret. Pre-request scripts see {{variables}}
// unresolved, so resolve them first to sign what will be sent.
const secret = await pm.vault.get("webhook_secret");
const body = pm.variables.replaceIn(pm.request.body);
const signature = CryptoJS.HmacSHA256(body, secret).toString();
pm.request.upsertHeader({ key: "X-Signature", value: signature });
pm.request.url.query.upsert({ key: "ts", value: String(Date.now()) });

A header added with disabled: true isn’t sent. Body changes apply to Raw, None, GraphQL and Binary bodies. x-www-form-urlencoded and form-data bodies are rebuilt from the Body tab after scripts run, so change their fields with variables instead; pm.request.body is empty for form-data in pre-request scripts.

pm.request.url is parsed into parts. A URL that starts with a variable, like {{baseUrl}}/users, treats the first segment as the host.

Member What it is
protocol The scheme, such as "https", or "".
auth The user:password part before @, or "".
host The host split on dots, as an array: ["api", "example", "com"].
port The port as a string, or "".
path The path segments, as an array.
query A property list of query parameters.
hash The fragment after #, or "".
getHost() The host as a string.
getPath() The path, starting with /.
getQueryString() The encoded query string of enabled parameters.
getPathWithQuery() The path and query.
getRemote() The host and port.
addQueryParams(params) Adds one { key, value } or an array of them.
removeQueryParams(keys) Removes parameters by name, or by an array of names.
update(text) Parses a new URL into the same object.
toString() The whole URL.

When a script doesn’t change the URL, the original text is kept exactly, including its {{variables}}.

Headers, query parameters, response headers and cookies are property lists: ordered { key, value, disabled } items with these methods.

Method What it does
get(key) The value of the last enabled item with that key, or undefined.
one(key) The first item with that key.
has(key, value?) Whether an enabled item has that key (and value, when given).
add(item) or add(key, value), append(…) Adds an item.
upsert(item) or upsert(key, value) Updates the first item with that key and enables it, or adds one.
remove(key) or remove(predicate) Removes matching items.
clear() Removes everything.
each(fn), map(fn), filter(fn), find(fn) Iterate over the items.
all() A copy of the items as an array.
count() How many items there are.
idx(n) The item at a position.
toObject() Enabled items as { key: value }.
toString() Enabled items as Key: value lines.
members The underlying array.

The response, in post-response scripts. undefined in pre-request scripts and when the request failed.

Member What it is
pm.response.code The status code, such as 200.
pm.response.status The reason phrase, such as "OK".
pm.response.reason() The reason phrase.
pm.response.headers A property list of headers. pm.response.headers.get("content-type") matches without regard to case.
pm.response.text() The body as text.
pm.response.json() The body parsed as JSON. Throws if it isn’t JSON.
pm.response.stream The body text.
pm.response.responseTime The total time in milliseconds.
pm.response.responseSize The body size in bytes.
pm.response.to Starts an assertion on the response, like pm.expect(pm.response). See Response assertions.

A property list of the cookies in the client’s cookie jar that match the request’s URL, as { key, value }.

const session = pm.cookies.get("session_id");
pm.test("Has a session", () => pm.expect(pm.cookies.has("session_id")).to.be.true);

Reads across every scope with the normal priority: local, data, environment, collection, global.

Method What it does
get(name) The value from the highest-priority scope that has it, or undefined.
has(name) Whether any scope has it.
set(name, value) Sets a local variable for the rest of this send. Objects are stored as JSON; other values as strings.
unset(name) Removes a local variable.
toObject() Every variable merged into one object, with higher-priority scopes winning.
replaceIn(text) Resolves {{name}}, {{$dynamic}} and {{vault:name}} in a string.
const url = pm.variables.replaceIn("{{baseUrl}}/users/{{$randomInt}}");

pm.environment, pm.collectionVariables, pm.globals, pm.iterationData

Section titled “pm.environment, pm.collectionVariables, pm.globals, pm.iterationData”

One object per scope, all with the same methods. pm.environment is the active environment, pm.collectionVariables the request’s collection, pm.globals the globals, and pm.iterationData the current runner row (empty outside the runner).

Method What it does
get(name) The value in this scope, or undefined.
set(name, value) Sets it in this scope. Objects are stored as JSON; other values as strings.
unset(name) Removes it from this scope.
has(name) Whether this scope has it.
clear() Removes every variable in this scope.
toObject() This scope’s variables as an object.
replaceIn(text) Resolves variables in a string across all scopes, like pm.variables.replaceIn.

Changes to the environment, collection variables and globals are saved after a single send. See Change variables from scripts.

Values are always strings. Parse numbers and objects you stored: JSON.parse(pm.environment.get("user")).

Secrets in the macOS Keychain. Every method returns a promise.

Method What it does
await pm.vault.get(name) The secret’s value, or undefined.
await pm.vault.set(name, value) Saves a secret.
await pm.vault.unset(name) Deletes a secret.

set and unset are applied to the Keychain when a request you sent from its editor finishes; in the collection runner and cloakctl they have no effect. See Keep secrets in the Vault.

pm.test(name, fn)
pm.test.skip(name)

pm.test runs fn and records a test called name. It passes unless fn throws, which every failed assertion does. Tests don’t stop the script: a failing test is recorded and the next line runs.

  • If fn returns a promise, a rejection fails the test.
  • If fn takes an argument, it’s called with a callback; calling it with an error fails the test.
  • pm.test.skip(name) records a skipped test (SKIP) without running anything.

Tests can be in pre-request scripts too; they appear in the same Test Results list.

pm.expect(value) starts a chai-style assertion. The sandbox implements the subset below; anything else throws “is not a function” or isn’t defined.

pm.expect(user.age).to.be.a("number").and.be.at.least(18);
pm.expect(tags).to.include("admin");
pm.expect(order).to.have.nested.property("customer.email", "ada@example.com");
pm.expect(() => JSON.parse("{")).to.throw();

These words read naturally and do nothing: to, be, been, is, that, which, and, has, have, with, at, of, same, but, does, still, also, own.

Flag Effect
.not Negates the assertion that follows.
.deep Makes equal and property compare by value. (include and eql always do.)
.nested Lets property take a dotted path, such as "items[0].id".
.any With keys: at least one key must be present.
.all With keys: the keys must be exactly these.
.length Makes the next above, below, least, most or within compare the length (or size, or number of keys).

Properties, used without parentheses:

Assertion Passes when the value
.true, .false Is exactly true or false.
.null, .undefined Is null or undefined.
.NaN Is NaN.
.exist Is neither null nor undefined.
.empty Is an empty string, array or object.
.ok Is truthy. On a response, means status 200.

Methods:

Assertion Passes when
.equal(v), .equals(v), .eq(v) The value is === to v (deeply equal with .deep).
.eql(v), .eqls(v) The value deeply equals v.
.above(n), .gt(n), .greaterThan(n) The value is greater than n.
.below(n), .lt(n), .lessThan(n) The value is less than n.
.least(n), .gte(n) The value is at least n.
.most(n), .lte(n) The value is at most n.
.within(lo, hi) The value is between lo and hi, inclusive.
.closeTo(n, delta), .approximately(n, delta) The value is within delta of n.
.a(type), .an(type) The type is "string", "number", "boolean", "object", "array", "null", "undefined", "function", "regexp" or "date".
.instanceOf(C), .instanceof(C) The value is an instance of C.
.include(v), .includes(v), .contain(v), .contains(v) A string contains v; an array has an item deeply equal to v; an object has every key and value of object v, or has key v.
.members(list) An array has exactly the same items as list, in any order.
.oneOf(list) The value deeply equals an item of list.
.match(re), .matches(re) The value, as a string, matches the regular expression.
.string(s) The value, as a string, contains s.
.lengthOf(n) The value’s length (or number of keys) is n.
.property(name) The value has the property. Continues the chain on the property’s value.
.property(name, value) The property equals value (deeply with .deep).
.keys(…), .key(…) The object has these keys (names as arguments, an array, or an object’s keys).
.satisfy(fn), .satisfies(fn) fn(value) returns something truthy.
.throw(expected?), .throws(expected?) The function throws; optionally an error whose message matches a regular expression or contains a string, or that is an instance of a class.
.jsonSchema(schema) The value (or a response’s JSON body) matches the JSON Schema.

expect.fail(message) fails immediately.

Start from pm.response.to (or pm.expect(pm.response).to).

Assertion Passes when
.have.status(code) The status code equals code.
.have.status(reason) The reason phrase equals the string, such as "Created".
.have.header(name) The header exists (any case).
.have.header(name, value) The header has exactly that value.
.have.body() The body isn’t empty.
.have.body(text) The body equals the string.
.have.body(re) The body matches the regular expression.
.have.body(object) The JSON body deeply equals the object.
.have.jsonBody() The body is JSON.
.have.jsonBody(path) The JSON body has a value at the dotted path, such as "data.items[0].id".
.have.jsonBody(path, value) The value at the path deeply equals value.
.have.jsonBody(object) The JSON body deeply equals the object.
.have.jsonSchema(schema) The JSON body matches the schema.
.have.responseTime.below(ms) The response time is under ms. Any number assertion works after responseTime.
.be.ok Status 200.
.be.success Status 2xx.
.be.info Status 1xx.
.be.redirection Status 3xx.
.be.error Status 4xx or 5xx.
.be.clientError Status 4xx.
.be.serverError Status 5xx.
.be.accepted Status 202.
.be.badRequest Status 400.
.be.unauthorized Status 401.
.be.forbidden Status 403.
.be.notFound Status 404.
.be.rateLimited Status 429.
.be.json Content-Type contains json and the body parses.
.be.html Content-Type contains html.
.be.xml Content-Type contains xml.
.be.withBody The body isn’t empty.
Post-response
pm.test("Created", () => {
pm.response.to.have.status(201);
pm.response.to.have.header("Location");
pm.response.to.be.json;
pm.response.to.have.jsonBody("id");
pm.response.to.have.responseTime.below(800);
});

jsonSchema (and tv4 and Ajv, below) validate against a draft-07 subset. Supported keywords:

Applies to Keywords
Any value type (including "integer" and arrays of types), enum, const, allOf, anyOf, oneOf, not
Numbers minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf
Strings minLength, maxLength, pattern, format (email, uuid, date-time)
Arrays items (one schema, or an array for tuples), minItems, maxItems, uniqueItems
Objects properties, required, additionalProperties (false or a schema)

$ref isn’t resolved. A failure message lists up to five problems, such as id should be integer but is string.

const schema = {
type: "object",
required: ["id", "name"],
properties: {
id: { type: "integer" },
name: { type: "string", minLength: 1 },
email: { type: "string", format: "email" },
},
};
pm.test("Matches the schema", () => pm.response.to.have.jsonSchema(schema));

chai.assert(condition, message) and its methods assert.ok, assert.equal (==), assert.strictEqual, assert.deepEqual and assert.isTrue throw an AssertionError when they fail.

Sends another HTTP request from a script and returns a promise of its response.

const res = await pm.sendRequest(url)
const res = await pm.sendRequest(options)
await pm.sendRequest(options, (err, res) => { … })
Option What it’s for
url The URL. {{variables}} are resolved. A plain string argument is a GET to that URL.
method Defaults to GET.
header or headers An array of { key, value }, or an object of name to value.
body { mode: "raw", raw: text or object } (objects are sent as JSON), { mode: "urlencoded", urlencoded: [{ key, value }] } (adds Content-Type: application/x-www-form-urlencoded if missing), or a string.

The response has the same members as pm.response, including .to assertions. On a network error the promise rejects and the callback gets the error.

Pre-request
const res = await pm.sendRequest({
url: "{{baseUrl}}/oauth/token",
method: "POST",
header: { Accept: "application/json" },
body: {
mode: "urlencoded",
urlencoded: [
{ key: "grant_type", value: "client_credentials" },
{ key: "client_id", value: pm.environment.get("clientId") },
{ key: "client_secret", value: await pm.vault.get("client_secret") },
],
},
});
pm.environment.set("token", res.json().access_token);

pm.sendRequest sends only what you give it: no auth, scripts or cookie jar. It goes through Cloak Proxy when the proxy is running, follows redirects, verifies TLS, and times out after 30 seconds. Several calls can run at once with Promise.all.

Member What it does
pm.execution.setNextRequest(name) In the collection runner, runs the request with that name (or ID) next instead of the following one.
pm.execution.setNextRequest(null) In the runner, stops the run after this request.
pm.execution.skipRequest() In a pre-request script, cancels sending this request.
pm.execution.runRequest(ref, options?) Runs another saved request and returns a promise of its response.
pm.execution.location The path of the running request: collection, folders and request names, as an array.
pm.execution.location.current "prerequest" or "test".

pm.execution.runRequest runs a saved request with its own scripts and auth, so a login request can be the single place your token logic lives.

Pre-request
const login = await pm.execution.runRequest("Auth/Login", {
variables: { user: "ada@example.com" },
});
pm.variables.set("token", login.json().token);
  • ref is the request’s ID, its name, or a path ending in its name: "Login", "Auth/Login" or "Petstore/Auth/Login". When several match, one in the current collection wins.
  • options.variables become local variables for that run only.
  • The other request’s variable changes don’t flow back to yours.
  • Calls can nest up to five levels deep, so a request that runs itself fails instead of looping.

Renders HTML from a template and data into the response’s Visualize tab.

Method What it does
pm.visualizer.set(template, data) Renders template with data and shows the result.
pm.visualizer.clear() Removes it.

Templates use a Handlebars subset:

Syntax Meaning
{{name}}, {{user.email}} A value, HTML-escaped. Objects are shown as JSON.
{{{html}}} A value, not escaped.
{{#each items}} … {{/each}} Repeats for each array item or object property. Inside: {{this}}, {{@index}}, {{@first}}, {{@last}}, {{@key}}.
{{#if value}} … {{else}} … {{/if}} Shows a block when the value is truthy and not an empty array.
{{#unless value}} … {{/unless}} The opposite of if.
{{#with object}} … {{/with}} Changes the context to object.
{{../name}} A value from the outer context.
{{! comment }} Ignored.
Post-response
const template = `
<table>
<tr><th>#</th><th>Name</th><th>Tag</th></tr>
{{#each pets}}<tr><td>{{@index}}</td><td>{{name}}</td><td>{{tag}}</td></tr>{{/each}}
</table>`;
pm.visualizer.set(template, { pets: pm.response.json() });

pm.require(name) loads a module and returns its exports. It’s the same function as the global require.

Name Loads
"crypto-js", "lodash", "uuid", "chai", "tv4", "ajv", "xml2js", "atob", "btoa" A built-in library.
"helpers" or "@team/helpers" A workspace package named helpers. The @scope/ part is ignored.
"npm:dayjs@1.11.13" A downloaded npm package at that version.
"npm:dayjs" A downloaded npm package, whichever version you have.

A module loads once per script, however many times you require it. Unknown names throw “No module ‘…’. Add it to the workspace’s Packages, or use one of: crypto-js, lodash, uuid, chai, tv4, ajv, xml2js”.

Older Postman scripts keep working:

Legacy Equivalent
postman.setNextRequest(name) pm.execution.setNextRequest(name)
postman.getEnvironmentVariable(name), postman.setEnvironmentVariable(name, value), postman.clearEnvironmentVariable(name) pm.environment.get, set, unset
postman.getGlobalVariable(name), postman.setGlobalVariable(name, value) pm.globals.get, set
responseBody pm.response.text()
responseCode.code, responseCode.name pm.response.code, pm.response.status
responseTime pm.response.responseTime
tests["name"] = condition A test that passes when condition is truthy.

These need no download. Each is also a global.

Library require name What’s included
CryptoJS crypto-js MD5, SHA1, SHA256, SHA384, SHA512, HmacMD5, HmacSHA1, HmacSHA256, HmacSHA512, and enc.Hex, enc.Base64, enc.Utf8 with parse and stringify. Results print as hex with toString(), or Base64 with toString(CryptoJS.enc.Base64).
lodash (_) lodash get, set, has, isEqual, cloneDeep, keys, values, pick, omit, map, filter, find, sortBy, uniq, random, sample, times, merge, isEmpty, isString, isNumber, isArray, isObject, isFunction, includes, groupBy, flatten, chunk, first, last, size, capitalize, camelCase, forEach, each.
uuid uuid uuid.v4()
chai chai expect, assert, AssertionError.
tv4 tv4 tv4.validate(data, schema) returns true or false and sets tv4.error; tv4.validateResult(data, schema) returns { valid, error }.
Ajv ajv new Ajv().compile(schema) returns a validator function with .errors; ajv.validate(schema, data).
xml2js xml2js parseString(xml, callback), using the same conversion as xml2Json.
atob, btoa atob, btoa Base64 decode and encode.

The lodash, CryptoJS and schema libraries are small implementations of the listed functions, not the full packages.

const CryptoJS = require("crypto-js");
const digest = CryptoJS.SHA256("hello").toString(CryptoJS.enc.Base64);
const ids = _.uniq(pm.response.json().map((item) => item.id));

Packages are shared modules every script in the workspace can load. They’re saved as packages/<name>.js in the workspace, so they travel with Git.

  1. Click Packages in the rail, then + › New Package. A package named helpers opens with example code.

  2. Rename it and write a CommonJS module that assigns module.exports:

    helpers
    module.exports = {
    expectOk(response) {
    pm.expect(response.code, "status").to.be.within(200, 299);
    },
    bearer(token) {
    return { key: "Authorization", value: `Bearer ${token}` };
    },
    };
  3. Click Copy Import to copy a line like const helpers = pm.require("helpers");, and paste it into any script.

Inside a package, module, exports, require, pm and console are available, so packages can require each other and use the pm API of the script that loaded them.

  1. Click Packages in the rail, then + › Add from npm….

  2. Type a package name with an optional version or range, such as dayjs@1.11.13, and click Download.

  3. The client asks jsDelivr for the exact version, downloads the package’s main file, and saves it as a package named npm:dayjs@1.11.13.

  4. Load it in a script:

    const dayjs = pm.require("npm:dayjs@1.11.13");
    pm.environment.set("tomorrow", dayjs().add(1, "day").format("YYYY-MM-DD"));

Downloading is the only step that needs the internet; after that, scripts load the package from the workspace offline. Downloading the same name and version again replaces it.

Limits:

  • Self-contained builds work, such as dayjs, lodash and validator. Packages that require other packages don’t.
  • The main file must be JavaScript and under 3 MB.
  • Scoped names work: @scope/name@1.2.3.
  • A script that throws stops at that line. The Console tab lists the error in red, prefixed with where it came from, such as “Post-response script (Get pet):” followed by the error message.
  • Failed assertions inside pm.test don’t stop the script; they show in Test Results.
  • console.log output appears in Console with its level and source. The tab title counts entries and errors.
  • Post-response scripts still run after a network error, so check pm.response before reading it.

In the collection runner and cloakctl collection run, gRPC, MCP, AI, SQL, WebSocket, Socket.IO, MQTT and TCP requests run their scripts too. Each answers with an HTTP-style response, so the same pm.response tests apply. See API client protocols for what each protocol’s response contains.