Some requests need work done before they leave your machine, and some need work done the moment a response lands. A payment API wants an HMAC signature computed from a timestamp and your secret. A login endpoint hands back a token that every later call needs. A checkout flow has to assert the response actually returned a 200 and the right order ID. You can do all of this by hand, but that gets old fast, and it breaks the second you share the request with a teammate.
Scripts fix that. In Apidog, you attach small pieces of JavaScript to a request that run automatically: one set before the request is sent, another after the response comes back. If you have written Postman scripts before, the muscle memory carries over, because Apidog’s engine is compatible with the same pm object API. This guide walks through both stages with a realistic example: signing a request in a pre-request script, then extracting a token in a post-response script. The behavior is documented in full on the Apidog scripting docs, but the tab names and a few behaviors differ from Postman, and those differences matter.
What pre and post scripts actually do
Apidog runs scripts in two stages, and it names them plainly.
Pre Processors run before the request is sent to the server. This is where you prepare things: generate a timestamp, compute a signature, set a random order ID, or read a variable and shape it into a header. The response does not exist yet at this point, so anything that inspects a response is off-limits here.
Post Processors run after the response is received. This is where you verify what came back with assertions, and where you pull values out of the body to reuse later. Extract an auth token, grab a newly created resource ID, check the status code, save a cursor for pagination.
Two rules follow from that split. First, pm.response (with its code, status, headers, responseTime, responseSize, text(), and json()) only works in Post Processors. There is no response to read before you send, so calling it in a Pre Processor will not help you. Second, variables are how the two stages talk to each other. A Pre Processor sets a value, the request uses it, and a Post Processor can read or overwrite it.
If you are coming from Postman, note the label change up front. Apidog’s tabs are Pre Processors and Post Processors, not “Pre-request Script” and “Tests.” The behavior maps closely, but the names on the screen are different.
Set up: open a request and find the tabs
Download Apidog to follow along. It is free and runs on macOS, Windows, and Linux, so grab it from the Download Apidog page if you do not have it yet.
Open the API request you want to script inside Apidog. Every endpoint has a Pre Processors tab and a Post Processors tab alongside the usual Params, Headers, and Body tabs. To add logic to either stage, open the tab and select add a Custom Script. That opens a code editor where you write plain JavaScript against the pm object.
Before writing anything, it helps to know where values live. Apidog resolves variables in this priority order:
Local Variables > Environment Variables > Global Variables Shared within Project > Global Variables Shared within Team.
So a local variable wins over an environment variable of the same name, and so on down the list. Keep that in mind when a value is not what you expect: something higher in the order is probably shadowing it. If you want values that persist across many requests, global parameters in Apidog sit lower in the priority order and make a good home for stable, project-wide settings.
Pre Processor example: sign a request with HMAC
Say you call a payments endpoint that authenticates each request with an HMAC-SHA256 signature. This is the same pattern many providers use for webhook verification, and Stripe’s signature docs describe it well: the server expects a timestamp and a signature computed over that timestamp plus the request body, keyed with your API secret. You need to build both fresh on every send.
Apidog ships crypto-js as a built-in library, so you do not install anything. Open the Pre Processors tab, select add a Custom Script, and write this:
// Pre Processor: sign the request before it is sent
const CryptoJS = require('crypto-js');
// current unix timestamp in seconds
const timestamp = Math.floor(Date.now() / 1000).toString();
// read the secret from an environment variable
const secret = pm.environment.get('payments_api_secret');
// build the string to sign: timestamp + newline + raw body
const body = pm.request.body ? pm.request.body.toString() : '';
const payload = timestamp + '\n' + body;
// compute the HMAC-SHA256 signature, hex encoded
const signature = CryptoJS.HmacSHA256(payload, secret).toString(CryptoJS.enc.Hex);
// stash both values as environment variables for the request to use
pm.environment.set('x_timestamp', timestamp);
pm.environment.set('x_signature', signature);
pm.console.log('Signed request at ' + timestamp);
That script computes the signature and saves both the timestamp and the signature into environment variables. Now wire them into the request. In the Headers tab, reference the stored values with the {{variableName}} syntax:
X-Timestamp: {{x_timestamp}}
X-Signature: {{x_signature}}
When you send, Apidog runs the Pre Processor first, sets the two variables, then substitutes them into the headers. The server gets a signature that was valid at send time, every time, with no manual step. Notice the require('crypto-js') call pulls in the whole module. You import the entire library, not a submodule, so require('crypto-js') works and require('crypto-js/sha256') does not.
One thing to watch: variable operations touch current values only, not the initial values you might have typed into the environment editor. That is what you want here, since the signature is meant to be ephemeral. If you need signing patterns explained for the Postman side too, the walkthrough on Postman pre-request scripts covers the same idea with Postman’s labels, and it maps cleanly onto Apidog’s Pre Processors.
Post Processor example: extract a token and assert
Now the other stage. Picture a login request that returns a token you need for every authenticated call afterward:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": { "id": 4812, "email": "dana@example.com" },
"expires_in": 3600
}
Open the Post Processors tab, select add a Custom Script, and pull the token out of the body, saving it for later requests:
// Post Processor: verify the response, then extract the token
pm.test('Status is 200', function () {
pm.response.to.have.status(200);
});
const jsonData = pm.response.json();
pm.test('Response returns a token', function () {
pm.expect(jsonData.token).to.be.a('string').and.not.empty;
});
pm.test('User id is present', function () {
pm.expect(jsonData.user.id).to.be.a('number');
});
// store the token so other requests can send it
pm.environment.set('auth_token', jsonData.token);
pm.console.log('Saved token for user ' + jsonData.user.email);
Two things happen here. The pm.test() blocks assert the response is shaped the way you expect, using Chai-style pm.expect() matchers that Apidog supports natively. And pm.environment.set('auth_token', jsonData.token) saves the token into the environment. Any later request can now send Authorization: Bearer {{auth_token}} without you copying anything by hand.
The assertion half of this deserves attention on its own. Good post-response checks are what turn a manual click-and-eyeball into a real test, and our guide to API assertions in Apidog goes deeper on the matchers and patterns worth knowing. If you also want to feed realistic fake data into these flows, Faker.js in Apidog pairs well with the variable-setting shown above.
A couple of behaviors to keep straight in Post Processors. pm.iterationData (your test data) is read-only, so you can read a data-driven value but you cannot write back to it from a script. And pm.cookies returns the cookie from the response, the one the server sent back, not the cookie that went out with your request.
Reuse logic with Public Scripts
Once you have written that HMAC signing block, you probably want it on more than one request. Copy-pasting it into ten endpoints means ten places to fix when the algorithm changes. Apidog’s answer is Public Scripts: reusable snippets you write once and attach where needed.
Create one under Settings > Public Scripts, then add it to a request’s Pre Processors or Post Processors tab. Inside a processor list, a Public Script and a Custom Script sit side by side, and the order matters: public scripts execute before custom scripts in the same list, and if you have several public scripts they run top to bottom in the listed order.
There is one gotcha that trips people up. If you want a Custom Script to call a function defined in a Public Script, that function has to be global. A normal function declaration or a const-bound function is local to its own script and invisible to the next one. Declare it by assigning without var, let, or const:
// In the Public Script: make sign() global by omitting the keyword
sign = function (payload, secret) {
const CryptoJS = require('crypto-js');
return CryptoJS.HmacSHA256(payload, secret).toString(CryptoJS.enc.Hex);
};
// In the Custom Script below it: call the global function by name
const timestamp = Math.floor(Date.now() / 1000).toString();
const secret = pm.environment.get('payments_api_secret');
pm.environment.set('x_timestamp', timestamp);
pm.environment.set('x_signature', sign(timestamp, secret));
Add the Public Script first, put the Custom Script below it, and the call resolves. Get the order wrong or add a const and you get an undefined-function error.
Libraries, external packages, and debugging
The crypto-js import above is one of a set of libraries Apidog bundles with no setup. You can require() any of them directly:
crypto-js(v3.1.9-1) for hashing and HMACjsrsasign(v10.3.0) for JWT and RSA work, which needs Apidog 1.4.5 or laterchai(v4.2.0) for the assertion matcherslodash,moment,uuid,xml2js,cheerio,postman-collection,atob,btoa,csv-parse/lib/sync,tv4,ajv- Node built-ins like
path,assert,buffer,util,url,querystring,stream, andevents
If you need something that is not on that list, pull it in at runtime with $$.liveRequire(), which fetches the package on the fly:
$$.liveRequire('nanoid', (nanoid) => {
const id = nanoid.nanoid();
pm.environment.set('request_id', id);
});
That path needs an internet connection, since Apidog downloads the package when the script runs. The bundled libraries do not.
When a script misbehaves, log your way out. Both pm.console.log() and plain console.log() print to Apidog’s console, so you can dump a computed signature or an extracted field and see exactly what the script produced before the request goes out or after it returns.
Two more limits worth naming so they do not surprise you. pm.sendRequest(), for firing an extra HTTP call inside a script, uses a callback pattern rather than Promises, so write it with a callback, not await. And Postman’s pm.nextRequest() for chaining requests is not supported here. When you need real workflow orchestration, with branching and conditional steps, Apidog uses Test Scenarios instead, where you sequence requests visually with Condition and If-Else steps. If you write scripts a lot, the built-in Apidog Script Generator can also draft one from a natural-language description to give you a starting point.
Automate the workflow with the Apidog CLI
Scripts do not only run when you click Send. When you package your requests and assertions into a saved Test Scenario, the Apidog CLI runs that whole scenario headlessly, Pre Processors and Post Processors included, which is what makes your signing and token-extraction logic part of CI instead of a manual step.
Install the CLI and authenticate, then run a scenario by id:
npm install -g apidog-cli
apidog login --with-token <YOUR_ACCESS_TOKEN>
apidog run --access-token $APIDOG_ACCESS_TOKEN -t <SCENARIO_ID> -e <ENV_ID> -r cli
The -t flag is the test scenario id, -e is the environment id, and -r picks the reporter (cli, html, or junit, comma-separated for several). Generate the access token in your Apidog account settings, export it as APIDOG_ACCESS_TOKEN, and the same scenario that ran on your desktop now runs in CI, Pre Processors and Post Processors included.
One honest caveat: a script that leans on something present only on your machine, a local file or a package you loaded once, can pass in the desktop app and then fail in the CLI, because the runner does not have that dependency. Keep scripts to the bundled libraries or $$.liveRequire() so they run the same everywhere.
FAQ
Are Apidog scripts compatible with my existing Postman scripts?
Mostly yes. Apidog’s engine uses the same pm object API, so pm.environment.set(), pm.response.json(), pm.test(), and pm.expect() all behave the way you know. The two differences to remember are the tab names, Pre Processors and Post Processors rather than Pre-request Script and Tests, and a few unsupported calls like pm.nextRequest(). Most scripts paste over and run.
Why does pm.response return undefined in my pre-request script?
Because there is no response yet. Pre Processors run before the request is sent, so nothing has come back to inspect. Any code that reads pm.response (its status, body, headers) belongs in a Post Processor. If you need a value in the pre stage, retrieve it from pm.request, a variable, or a library instead.
How do I share one script across many requests?
Use Public Scripts under Settings > Public Scripts. Write the logic once, attach it to each request’s Pre Processors or Post Processors tab, and remember that public scripts run before custom scripts in the same list. To call a Public Script function from a Custom Script, declare it global by assigning without var, let, or const.
Can I import an npm package that Apidog does not bundle?
Yes, with $$.liveRequire('package-name', (pkg) => { ... }), which downloads the package at runtime and needs an internet connection. For anything on the built-in list, like crypto-js, moment, or uuid, use a plain require() with no network needed. Note you can only require a whole module, not a submodule path.
Where do I see what my script printed?
Use pm.console.log() or console.log() and read the output in Apidog’s console after you send the request. It is the fastest way to confirm a signature was computed or a token was extracted before you trust it in a scenario.
Wrapping up
Pre Processors and Post Processors turn a static request into one that prepares itself and checks its own work. Sign before you send, extract and assert after you receive, and lift shared logic into Public Scripts so you write it once. The pm API and the bundled libraries mean most of what you know from Postman carries straight over. Open Apidog, pick any request, and add your first Custom Script to watch both stages run in a single send. It is free to start, no credit card required.



