PostNot scripting

Inherited scripts for API workflows

PostNot scripts are JavaScript snippets that run in a short-lived frontend worker before a request is sent and after its response returns. Request-level scripts work on the current request draft, while collection and folder scripts layer on top for saved requests in a collection tree. They can set headers, query parameters, body content, authentication, and response assertions while keeping request execution in the native Rust layer.

Execution model

Where scripts run

Pre-request scripts

Run before the native request is sent. They receive a mutable request draft and active environment variables through pn.

Test scripts

Run after a response comes back. They receive response helpers and can register named assertions with pn.test.

Inherited scripts

Scripts can be saved on collections, folders, and individual requests, so shared behavior can live near the requests it applies to.

Scripts run as awaited JavaScript in a frontend worker-backed sandbox. The actual HTTP request still goes through the native Rust sender, which means script helpers can prepare requests and inspect responses without moving network execution into the browser runtime.

Authoring

Where to edit scripts

Collection scripts

Open the Collections page, choose a collection, then expand Collection scripts.

Folder scripts

Open a folder row in the Collections page, then expand Folder settings.

Request scripts

Use the Scripts request editor tab.

Inheritance

Script execution order

For a saved request inside nested folders, PostNot runs scripts from broadest scope to most specific scope:

  1. Collection script
  2. Ancestor folder scripts, from root folder to leaf folder
  3. Saved request script

If a pre-request script throws an error, PostNot stops before calling the native sender and shows the script error in the response panel. If a test script throws outside a pn.test block, later test scripts are skipped.

Reliability

What persists and what does not

Request mutations are send-only

Pre-request scripts mutate a cloned draft for the current send. They affect the request that goes to the native sender, but they do not rewrite the request editor or save back to the collection unless you save changes yourself afterward.

Environment writes do persist

await pn.variables.set(...) and await pn.variables.remove(...) update the active environment and persist those changes before execution continues. If there is no active environment selected, writes throw an error.

Inheritance only applies to saved requests

Collection and folder scripts are applied only when the current request came from a saved request inside that collection tree. An unsaved request can still use its own request-level pre-request and test scripts.

Helper requests stay out of history

pn.http.send(...) uses the same native sender, but it does not create a history entry and does not trigger inherited scripting around that helper call.

Pre-request API

Mutate the outgoing request

API What it does
pn.request.name Read or set the request display name.
pn.request.method Read or set the HTTP method.
pn.request.url Read or set the request URL.
pn.request.addHeader(key, value) Append a header row.
pn.request.upsertHeader(key, value) Create or replace a header by name.
pn.request.removeHeader(key) Remove matching headers by name.
pn.request.addQueryParam(key, value) Append a query parameter row.
pn.request.upsertQueryParam(key, value) Create or replace a query parameter by name.
pn.request.removeQueryParam(key) Remove matching query parameters by name.
pn.request.setRawBody(value) Switch to raw body mode and set body text.
pn.request.setJsonBody(value) Switch to JSON body mode and serialize objects with indentation.
pn.request.clearBody() Switch to no-body mode.
pn.request.setBearerToken(token) Switch auth to bearer token.
pn.request.setBasicAuth(username, password) Switch auth to basic auth.
pn.request.setApiKey(name, value, placement) Switch auth to API key. Placement is header or query.
pn.request.clearAuth() Clear configured request auth.
pn.request.getHeader(name) Read a request header value from the draft.
await pn.http.send(request) Send a helper HTTP request through the native layer without writing a history entry.

Header and query lookups are case-insensitive by key. Upsert helpers replace the first matching row and force it enabled; remove helpers delete every matching row.

Helper requests

Helper request shape

pn.http.send(...) expects a request object with a required url. If you omit method, it defaults to GET.

Field Accepted shape
method One of GET, POST, PUT, PATCH, DELETE, HEAD, or OPTIONS.
url Required request URL. Environment placeholders can still be used here.
queryParams / query Either an array of rows or a plain object of key/value pairs.
headers Either an array of rows or a plain object of key/value pairs.
body A string for raw mode, a plain object for JSON mode, or an explicit object with mode, raw, form, and files.
auth An object matching the normal request auth shape: none, basic, bearer, or API key.

The returned helper response uses the same shape as pn.response: status code, text helpers, headers, timing, response size, error text, and the raw response payload.

Variables

Read and write active environment variables

Scripts can read and update active environment values directly. Environment placeholders and built-in dynamic variables such as {{$guid}} are still resolved by the native sender after pre-request scripts finish, so scripts may also write placeholders into headers, query parameters, URLs, body text, or auth fields.

Variable reads only include enabled environment variables with non-empty keys. If you update a variable in one script, later scripts in the same send see the updated value.

API What it does
pn.variables.get(name) Return one active environment variable value, or null.
pn.variables.has(name) Check whether an active variable exists.
pn.variables.all() Return active variables as a plain object.
await pn.variables.set(name, value, options) Create or update an active environment variable and persist it before the request continues. Use {"{ secret: true }"} to store it in the OS credential store.
await pn.variables.remove(name) Delete an active environment variable and persist the change.

Test API

Inspect responses and assert behavior

API What it does
pn.response.code HTTP status code, or null when unavailable.
pn.response.status HTTP status text.
pn.response.durationMs Elapsed request duration in milliseconds.
pn.response.sizeBytes Response body size in bytes.
pn.response.errorText Request error text, if the native sender reported one.
pn.response.executedAt ISO timestamp for when the response finished.
pn.response.text() Return the raw response body text.
pn.response.json() Parse the response body as JSON. This throws if the body is not valid JSON.
pn.response.header(name) Read one response header value by name.
pn.response.headers Array of response headers with key and value.
pn.test(name, fn) Register a named sync or async assertion block. Failures are shown in the response panel.
pn.expect(value) Start an assertion chain.
pn.response.raw Underlying raw response payload for cases where you need the full object.
await pn.http.send(request) Send a helper HTTP request during test evaluation without writing a history entry.

Available expectations

toBe, toEqual, toInclude, toMatch, toBeTruthy, toBeFalsy, toBeGreaterThan, and toBeLessThan.

Tests are recorded in the order you register them. Async assertions are supported, so pn.test(...) may itself await helper requests or other async work.

Failure modes

Failure behavior

Pre-request failures abort the send

Throwing from a pre-request script stops execution before the main request is sent. The error is surfaced in the response panel as a script failure instead of a network failure.

Failed tests do not stop later tests

A failed assertion inside pn.test(...) marks that test as failed and records its error text, but later registered tests still run.

Thrown test-script errors stop later scripts

Throwing outside pn.test(...), or triggering a runtime error such as invalid JSON parsing, stops the remaining inherited test scripts after queued tests finish.

Environment write errors surface explicitly

If PostNot cannot persist an active-environment update, execution ends with an Active environment update error so you can distinguish it from request or assertion failures.

Examples

Common scripts

Apply a bearer token from the active environment

const token = pn.variables.get('api_token');

if (!token) {
  throw new Error('Missing api_token environment variable');
}

pn.request.setBearerToken(token);

Fetch a token before send

const auth = await pn.http.send({
  method: 'POST',
  url: '{{base_url}}/auth/token',
  body: {
    clientId: pn.variables.get('client_id'),
    clientSecret: pn.variables.get('client_secret')
  }
});

const token = auth.json().access_token;
await pn.variables.set('api_token', token, { secret: true });
pn.request.setBearerToken(token);

Add shared collection headers

pn.request.upsertHeader('Accept', 'application/json');
pn.request.upsertHeader('X-Client', 'PostNot');

Build a JSON body before send

pn.request.setJsonBody({
  source: 'postnot',
  requestId: pn.variables.get('request_id')
});

Assert a JSON response

pn.test('status is ok', () => {
  pn.expect(pn.response.code).toBe(200);
});

pn.test('response has an id', () => {
  const body = pn.response.json();
  pn.expect(body.id).toBeTruthy();
});

Current limits

Current async-helper limits

Scripts can now await helper HTTP requests, but PostNot still allows only one native request in flight at a time. Await helper calls sequentially instead of firing parallel Promise.all(...) request batches, and note that helper requests reuse the native sender without creating separate history entries.

The scripting runtime is intentionally small and worker-backed right now. The stable contract is the pn surface documented here; browser globals or Postman pm helpers should not be treated as supported compatibility APIs.

Portability

Postman event import and export

PostNot imports and exports collection, folder, and request scripts through Postman collection event blocks. Imported scripts are kept as JavaScript source, but PostNot exposes the pn API rather than Postman's pm API, so complex Postman scripts may need manual adaptation after import.