Every developer has been there. You're testing an API that requires HMAC-signed requests. So you open your terminal, hash the payload manually, paste the signature into the header field, hit send — and it expired 30 seconds ago. You do it again.
Authentication is the most repeated, most error-prone part of API testing. And it doesn't have to be.
This post walks through how pre-processors can eliminate the manual auth dance entirely — so your requests just work, every time you hit send.
What Is a Pre-Processor?
A pre-processor is a script (or built-in rule) that runs before your HTTP request is sent. It can read environment variables, compute values, and inject headers or query parameters dynamically.
Think of it as a "prepare before send" hook. The request you see in the editor stays clean and readable. The auth logic lives separately, runs automatically, and you never think about it again.
Common things pre-processors handle:
- HMAC signing — compute a signature from the request body + timestamp + secret key, inject it as a header
-
Bearer token from environment — grab
{{ACCESS_TOKEN}}from your env and set theAuthorizationheader -
Timestamp nonces — add a fresh
X-Request-Timestampso replay protection doesn't reject your test -
Base64 encoding — for APIs that expect
Basic Author encoded payloads - Custom logic — anything you can write in JavaScript
The Classic Problem: HMAC Signing
Let's say you're working with a payment API or a webhook endpoint that validates requests with HMAC-SHA256. The signature is usually something like:
HMAC-SHA256(secret, method + "\n" + path + "\n" + timestamp + "\n" + body)
Without pre-processors, your workflow is:
- Copy the request body
- Open a terminal or online tool
- Compute the hash
- Paste it into the
X-Signatureheader - Also paste in a fresh timestamp
- Send — and pray it's still valid
With a pre-processor, you write the logic once:
const crypto = require('crypto');
const secret = env.get('API_SECRET');
const timestamp = Date.now().toString();
const body = request.body || '';
const path = request.url.pathname;
const payload = `${request.method}\n${path}\n${timestamp}\n${body}`;
const sig = crypto.createHmac('sha256', secret).update(payload).digest('hex');
request.headers.set('X-Request-Timestamp', timestamp);
request.headers.set('X-Signature', `sha256=${sig}`);
Save it. Done. Every time you send that request — or any request in the collection — the signature is computed fresh and injected automatically.
Bearer Tokens from Environment
This one's even simpler. Most APIs use OAuth2 or JWT, and your access token lives in an environment variable.
Instead of remembering to paste the token into the Authorization header every session, a pre-processor handles it:
const token = env.get('ACCESS_TOKEN');
if (token) {
request.headers.set('Authorization', `Bearer ${token}`);
}
Or with the built-in bearer-from-env processor, you don't even need to write JavaScript — just select it from the dropdown, point it at your variable, and you're done.
The token rotates? Update the variable in your environment, not in every request header across your whole collection.
Chaining with Post-Processors
Pre-processors become even more powerful when paired with post-processors — scripts that run after the response comes back.
A common pattern: your first request logs in and returns a token. A post-processor extracts it and stores it back into the environment. Every subsequent request's pre-processor picks it up automatically.
// Post-processor on /auth/token
const token = response.json('$.access_token');
env.set('ACCESS_TOKEN', token);
Now your entire test flow is automated: log in once, all downstream requests authenticate themselves. No copy-paste, no manual token refresh.
Why This Matters for Teams
When auth logic lives in a pre-processor attached to a collection, it's:
- Reviewable — anyone can read the script and understand how auth works
- Shared — teammates get the same behavior when they pull the collection
- Documented — the logic is co-located with the requests, not buried in someone's shell history
- Consistent — no more "it works on my machine" because Dave forgot to paste the header
This is especially valuable for public-facing APIs. When you publish docs, your readers need to understand auth. Having the logic explicit in your workspace means you can document it alongside the requests — and even expose the code sample in the generated docs.
Beyond Auth: Other Pre-Processor Use Cases
Auth is the most common use case, but pre-processors handle anything that needs to happen before send:
Idempotency keys — generate a UUID and set it as Idempotency-Key so POST requests don't accidentally duplicate on retry.
request.headers.set('Idempotency-Key', crypto.randomUUID());
Request tracing — attach a correlation ID for debugging distributed systems:
request.headers.set('X-Correlation-ID', crypto.randomUUID());
Environment-specific base URLs — swap the host based on the active environment so the same request works against local, staging, and production.
Conditional headers — attach API version headers only when targeting v2 endpoints, skip them for v1.
Getting Started
If you haven't tried pre-processors yet, the quickest way is to open a request, go to the Pre-processors tab, and add a built-in rule first — bearer-from-env or HMAC are good starting points. Once you're comfortable with what runs before send, writing a custom script for your specific auth scheme becomes straightforward.
The goal isn't to write less code overall. It's to write it once, in the right place, and let it run reliably every time.
Your teammates will thank you. Future-you will thank you more.
APIKumo is a free API workspace for the full lifecycle — build requests, automate auth with pre/post processors, and publish typed docs with AI chat and MCP built in. Try it at apikumo.com
Top comments (0)