Pre-request scripts
Run before the native request is sent. They receive a mutable request draft and active environment variables through pn.
PostNot scripting
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
Run before the native request is sent. They receive a mutable request draft and active environment variables through pn.
Run after a response comes back. They receive response helpers and can register named assertions with pn.test.
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
Open the Collections page, choose a collection, then expand Collection scripts.
Open a folder row in the Collections page, then expand Folder settings.
Use the Scripts request editor tab.
Inheritance
For a saved request inside nested folders, PostNot runs scripts from broadest scope to most specific scope:
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
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.
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.
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.
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
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
pn.http.send(...) expects a request object with a required url. If you omit method, it defaults to GET.
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
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.
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
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.
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
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.
A failed assertion inside pn.test(...) marks that test as failed and records its error text, but later registered tests still run.
Throwing outside pn.test(...), or triggering a runtime error such as invalid JSON parsing, stops the remaining inherited test scripts after queued tests finish.
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
const token = pn.variables.get('api_token');
if (!token) {
throw new Error('Missing api_token environment variable');
}
pn.request.setBearerToken(token);
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);
pn.request.upsertHeader('Accept', 'application/json');
pn.request.upsertHeader('X-Client', 'PostNot');
pn.request.setJsonBody({
source: 'postnot',
requestId: pn.variables.get('request_id')
});
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
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
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.