Overview
RestForge includes a powerful JavaScript scripting engine that lets you automate workflows, test API responses, and manipulate request/response data dynamically. Scripts enable advanced use cases that go far beyond simple HTTP requests.
With scripting, you can:
- Generate dynamic data: Timestamps, UUIDs, random values, hashes, and signatures computed at runtime
- Chain requests: Extract tokens from one response and inject them into the next request automatically
- Automate testing: Write assertions to validate status codes, response times, JSON schemas, and business logic
- Manage variables: Read and write environment, global, and collection variables programmatically
- Conditional logic: Skip requests, branch workflows, or retry failed operations based on runtime conditions
- Transform data: Parse, encode, decode, hash, and manipulate request/response payloads on the fly
RestForge supports two types of scripts that run at different stages of the request lifecycle:
Script Execution Order1. Pre-Request Script (runs BEFORE the HTTP request is sent)
2. HTTP Request (sent to server)
3. HTTP Response (received from server)
4. Post-Response Script (runs AFTER the HTTP response is received)
Scripts execute locally on your iPhone or iPad using a high-performance JavaScript engine. All pm.* APIs run in a secure sandbox with no access to device resources beyond the app's own data.
Postman Compatibility
RestForge scripting is fully compatible with Postman's scripting syntax. You can copy/paste scripts between Postman and RestForge without modification in most cases. If you encounter compatibility issues, please report them via the Feedback tab.
Pre-Request Scripts
Pre-request scripts run immediately before the HTTP request is sent to the server. They're ideal for preparing dynamic data, setting authentication tokens, generating timestamps, or modifying request parameters based on runtime conditions.
Where to Write Pre-Request Scripts
Navigate to the Scripts tab in your request, then select the Pre-request sub-tab. The editor provides syntax highlighting, autocomplete for pm.* APIs, and 80+ built-in snippets.
When Pre-Request Scripts Run
Pre-request scripts execute in this order when you tap Send:
- Collection-level pre-request script (if request is in a collection)
- Folder-level pre-request script (if request is in a folder)
- Request-level pre-request script
- HTTP request is sent with modified data
Common Use Cases
- Set timestamp headers for APIs that require time-based signatures
- Generate request IDs or correlation IDs for distributed tracing
- Compute HMAC signatures for AWS, Azure, or custom authentication schemes
- Load test data from variables and inject it into request body or query params
- Conditionally skip requests based on environment or previous response state
Example: Generate Timestamp and Set Custom Header// Generate ISO 8601 timestamp
const timestamp = new Date().toISOString();
// Set environment variable for reuse in other requests
pm.environment.set('current_timestamp', timestamp);
// Add custom header to this request
pm.request.headers.add({
key: 'X-Request-Time',
value: timestamp
});
console.log('Request timestamp:', timestamp);
Example: Generate UUID for Request ID// Generate a random UUID v4
function uuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
const requestId = uuid();
pm.environment.set('request_id', requestId);
pm.request.headers.add({key: 'X-Request-ID', value: requestId});
Post-Response Scripts
Post-response scripts run immediately after the HTTP response is received from the server. They're essential for extracting data from responses, writing automated tests, updating variables for chained requests, and validating API contracts.
Where to Write Post-Response Scripts
Navigate to the Scripts tab in your request, then select the Post-response sub-tab (also called "Tests" tab in the UI, since most post-response scripts are used for testing).
When Post-Response Scripts Run
Post-response scripts execute in this order after receiving the HTTP response:
- HTTP response is received and parsed
- Collection-level post-response script (if applicable)
- Folder-level post-response script (if applicable)
- Request-level post-response script
- Test results are displayed in the Tests tab with pass/fail indicators
Common Use Cases
- Extract authentication tokens from login responses and save to environment variables
- Validate response status codes, headers, and JSON structure
- Parse pagination cursors and save them for subsequent requests
- Assert business logic (e.g., order total matches sum of line items)
- Save response data to variables for use in downstream requests
- Calculate and verify response time SLAs
Example: Extract Token from Login Response// Parse JSON response
const response = pm.response.json();
// Extract access token from response
if (response.access_token) {
// Save to environment variable
pm.environment.set('auth_token', response.access_token);
// Also save refresh token if present
if (response.refresh_token) {
pm.environment.set('refresh_token', response.refresh_token);
}
console.log('Auth token saved to environment');
} else {
console.error('No access_token found in response');
}
Example: Extract Pagination Cursor// Get response data
const data = pm.response.json();
// Check if there's a next page cursor
if (data.pagination && data.pagination.next_cursor) {
pm.environment.set('next_cursor', data.pagination.next_cursor);
console.log('Next cursor:', data.pagination.next_cursor);
} else {
pm.environment.unset('next_cursor');
console.log('No more pages');
}
pm.request API
The pm.request object provides access to the current HTTP request. You can read and modify the request's method, URL, headers, body, and authentication settings from your pre-request script.
Available Properties
| Property |
Type |
Description |
| pm.request.method |
String |
HTTP method (GET, POST, PUT, PATCH, DELETE, etc.) |
| pm.request.url |
Object |
URL object with protocol, host, path, query, hash properties |
| pm.request.headers |
Object |
HeaderList with add(), remove(), upsert() methods |
| pm.request.body |
Object |
Request body with mode, raw, urlencoded, formdata properties |
| pm.request.auth |
Object |
Authentication configuration (type, credentials) |
Example: Modify Request URL Dynamically// Get base URL from environment
const baseUrl = pm.environment.get('api_base_url');
// Build dynamic path based on user ID
const userId = pm.environment.get('user_id');
const newUrl = `${baseUrl}/users/${userId}/profile`;
// Update request URL
pm.request.url = newUrl;
console.log('Request URL updated to:', newUrl);
Example: Add Authorization Header// Get token from environment
const token = pm.environment.get('auth_token');
// Add Bearer token to request headers
if (token) {
pm.request.headers.add({
key: 'Authorization',
value: `Bearer ${token}`
});
}
pm.response API
The pm.response object provides access to the HTTP response received from the server. It's only available in post-response scripts and offers methods to parse JSON, read headers, check status codes, and measure response time.
Available Properties & Methods
| Property/Method |
Type |
Description |
| pm.response.code |
Number |
HTTP status code (200, 404, 500, etc.) |
| pm.response.status |
String |
HTTP status message ("OK", "Not Found", etc.) |
| pm.response.headers |
Object |
Response headers as key-value pairs |
| pm.response.body |
String |
Raw response body as string |
| pm.response.responseTime |
Number |
Response time in milliseconds |
| pm.response.json() |
Function |
Parse response body as JSON object |
| pm.response.text() |
Function |
Get response body as text (same as .body) |
Example: Check Response Status and Parse JSON// Check if response is successful
if (pm.response.code === 200) {
// Parse JSON response
const data = pm.response.json();
// Extract specific fields
const userId = data.id;
const userName = data.name;
// Save to environment for next request
pm.environment.set('user_id', userId);
pm.environment.set('user_name', userName);
console.log(`User ${userName} (ID: ${userId}) loaded successfully`);
} else {
console.error('Request failed with status:', pm.response.status);
}
Example: Read Response Headers// Get specific header
const contentType = pm.response.headers.get('Content-Type');
const rateLimit = pm.response.headers.get('X-RateLimit-Remaining');
console.log('Content-Type:', contentType);
console.log('Rate limit remaining:', rateLimit);
// Check response time
if (pm.response.responseTime > 1000) {
console.warn('Slow response:', pm.response.responseTime, 'ms');
}
pm.environment API
The pm.environment object allows you to read, write, and delete environment variables from your scripts. Environment variables are scoped to the currently active environment and are perfect for configuration that changes between dev, staging, and production.
Available Methods
- pm.environment.get(variableName) – Get the value of an environment variable
- pm.environment.set(variableName, value) – Set an environment variable (creates if doesn't exist, updates if exists)
- pm.environment.unset(variableName) – Delete an environment variable
Example: Save Access Token to Environment// Parse login response
const response = pm.response.json();
// Extract token and expiry
const accessToken = response.access_token;
const expiresIn = response.expires_in; // seconds until expiration
// Calculate expiry timestamp
const expiryTime = Date.now() + (expiresIn * 1000);
// Save to environment
pm.environment.set('access_token', accessToken);
pm.environment.set('token_expiry', expiryTime);
console.log('Token saved, expires at:', new Date(expiryTime).toISOString());
Example: Use Environment Variable in Request// Get API base URL from environment
const baseUrl = pm.environment.get('api_base_url') || 'https://api.example.com';
// Get user ID from previous response
const userId = pm.environment.get('current_user_id');
// Build request URL
pm.request.url = `${baseUrl}/users/${userId}/orders`;
console.log('Fetching orders for user:', userId);
pm.globals API
The pm.globals object provides access to global variables that persist across all environments, collections, and requests. Globals are useful for truly universal values like API keys, user preferences, or feature flags.
Interface
The pm.globals API is identical to pm.environment:
- pm.globals.get(variableName) – Get global variable value
- pm.globals.set(variableName, value) – Set global variable
- pm.globals.unset(variableName) – Delete global variable
Collection Variables
RestForge also supports collection-scoped variables via pm.collectionVariables.get(name). These are read-only in scripts (they can only be set via the Collection Settings UI).
Example: Global vs Environment vs Collection Variables// Global: available everywhere (e.g., user preference)
const theme = pm.globals.get('ui_theme') || 'light';
// Environment: changes per environment (e.g., dev vs prod API)
const apiUrl = pm.environment.get('api_base_url');
// Collection: scoped to this collection only (read-only)
const collectionVersion = pm.collectionVariables.get('version');
console.log('Theme:', theme);
console.log('API URL:', apiUrl);
console.log('Collection version:', collectionVersion);
Variable Precedence
When multiple variable scopes define the same variable name, RestForge resolves them in this order (highest to lowest priority):
- Environment variables
- Collection variables
- Global variables
pm.test & pm.expect
RestForge includes a full-featured testing framework based on Chai assertions. You can write test cases that validate response status codes, headers, JSON structure, response times, and complex business logic. Test results appear in the Tests tab with green checkmarks for passes and red X marks for failures.
Writing Tests with pm.test()
The pm.test(name, function) method defines a named test case. Inside the test function, use pm.expect() to make assertions.
Basic Test Structurepm.test("Test name", function() {
pm.expect(actualValue).to.equal(expectedValue);
});
Common Assertions
| Assertion |
Description |
| .to.equal(value) |
Strict equality (===) |
| .to.eql(value) |
Deep equality (for objects and arrays) |
| .to.be.a(type) |
Type check ('string', 'number', 'object', 'array') |
| .to.include(value) |
Array/string contains value |
| .to.have.property(key) |
Object has property |
| .to.be.above(number) |
Greater than (>) |
| .to.be.below(number) |
Less than (<) |
| .to.be.true / .to.be.false |
Boolean checks |
| .to.have.lengthOf(n) |
Array/string length equals n |
| .to.match(regex) |
String matches regular expression |
Example: Comprehensive Test Suite for User API// Test 1: Status code is 200
pm.test("Status code is 200", function() {
pm.expect(pm.response.code).to.equal(200);
});
// Test 2: Response time is acceptable
pm.test("Response time is below 500ms", function() {
pm.expect(pm.response.responseTime).to.be.below(500);
});
// Test 3: Content-Type is JSON
pm.test("Content-Type is application/json", function() {
pm.expect(pm.response.headers.get('Content-Type')).to.include('application/json');
});
// Test 4: Response has required fields
pm.test("Response has user data", function() {
const data = pm.response.json();
pm.expect(data).to.have.property('id');
pm.expect(data).to.have.property('name');
pm.expect(data).to.have.property('email');
});
// Test 5: Field types are correct
pm.test("Field types are valid", function() {
const data = pm.response.json();
pm.expect(data.id).to.be.a('number');
pm.expect(data.name).to.be.a('string');
pm.expect(data.email).to.be.a('string');
});
// Test 6: Email format is valid
pm.test("Email format is valid", function() {
const data = pm.response.json();
pm.expect(data.email).to.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
});
// Test 7: User is active
pm.test("User account is active", function() {
const data = pm.response.json();
pm.expect(data.status).to.equal('active');
});
Test Results Display
After running your request, switch to the Tests tab to see results. Passing tests show a green checkmark, failing tests show a red X with the assertion error message. You'll also see the total pass/fail count and execution time.
pm.sendRequest
The pm.sendRequest() API allows you to make additional HTTP requests from within your scripts. This is powerful for implementing complex workflows like token refresh, dependency fetching, webhooks, or multi-step authentication flows.
Basic Usage: Simple GET Request
Simple URL String + Callbackpm.sendRequest('https://api.example.com/status', function(err, response) {
if (err) {
console.error('Request failed:', err);
return;
}
console.log('Status:', response.code);
console.log('Body:', response.json());
});
Advanced Usage: Full Request Specification
For POST, PUT, PATCH, DELETE requests or when you need to set headers and body, pass a request specification object:
Request Spec Objectconst requestSpec = {
url: 'https://api.example.com/data',
method: 'POST',
header: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + pm.environment.get('token')
},
body: {
mode: 'raw',
raw: JSON.stringify({
key: 'value',
timestamp: Date.now()
})
}
};
pm.sendRequest(requestSpec, function(err, response) {
if (err) {
console.error(err);
return;
}
const data = response.json();
console.log('Response:', data);
});
Response Object Properties
| Property |
Type |
Description |
| response.code |
Number |
HTTP status code |
| response.status |
String |
HTTP status message |
| response.headers |
Object |
Response headers (use .get() method) |
| response.json() |
Function |
Parse response as JSON |
| response.text() |
Function |
Get response as text |
| response.responseTime |
Number |
Response time in milliseconds |
| response.responseSize |
Number |
Response size in bytes |
Real-World Example: Token Refresh Pattern
Automatic Token Refresh in Pre-Request Script// Check if token is expired
const tokenExpiry = pm.environment.get('token_expiry');
const now = Date.now();
if (!tokenExpiry || now >= tokenExpiry) {
console.log('Token expired or missing, refreshing...');
// Refresh token request
const refreshToken = pm.environment.get('refresh_token');
pm.sendRequest({
url: pm.environment.get('auth_url') + '/refresh',
method: 'POST',
header: {
'Content-Type': 'application/json'
},
body: {
mode: 'raw',
raw: JSON.stringify({
refresh_token: refreshToken
})
}
}, function(err, response) {
if (err || response.code !== 200) {
console.error('Token refresh failed');
return;
}
const data = response.json();
// Save new token
pm.environment.set('access_token', data.access_token);
pm.environment.set('token_expiry', Date.now() + (data.expires_in * 1000));
console.log('Token refreshed successfully');
});
} else {
console.log('Token still valid');
}
Safety Limits
To prevent infinite loops and resource exhaustion, pm.sendRequest() has these safety limits:
- Maximum 3 requests per script execution
- 30 second timeout per individual request
- 60 second total timeout for all requests in a single script
Postman Compatibility
RestForge's pm.sendRequest() API is fully compatible with Postman's implementation. Scripts using this API can be copied between Postman and RestForge without modification.
pm.execution
The pm.execution object provides control over script execution flow. Currently, it supports skipping the HTTP request conditionally via pm.execution.skipRequest().
pm.execution.skipRequest()
Call this method from a pre-request script to prevent the HTTP request from being sent. This is useful for:
- Conditional execution based on environment or global variables
- Skipping requests when cached data is still valid
- Implementing feature flags to disable certain requests
- Dry-run mode for testing scripts without hitting real APIs
Example: Skip Request Based on Feature Flag// Check if feature is enabled
const featureEnabled = pm.environment.get('feature_xyz_enabled');
if (featureEnabled !== 'true') {
console.log('Feature XYZ is disabled, skipping request');
pm.execution.skipRequest();
}
Example: Skip Request if Cache is Valid// Check cache expiry
const cacheExpiry = pm.globals.get('user_data_cache_expiry');
const now = Date.now();
if (cacheExpiry && now < cacheExpiry) {
console.log('Cache is still valid, skipping API request');
pm.execution.skipRequest();
// Use cached data instead
const cachedData = JSON.parse(pm.globals.get('user_data_cache'));
console.log('Using cached data:', cachedData);
} else {
console.log('Cache expired, fetching fresh data');
}
Console & Logging
RestForge supports standard JavaScript console methods for debugging and logging. All console output appears in the Console tab at the bottom of the request screen.
Available Methods
- console.log(message, ...args) – General logging (appears in black text)
- console.warn(message, ...args) – Warnings (appears in yellow/orange)
- console.error(message, ...args) – Errors (appears in red)
Example: Debug Logging// Log variable values
const userId = pm.environment.get('user_id');
console.log('Processing user ID:', userId);
// Log object inspection
const response = pm.response.json();
console.log('Full response:', response);
// Warning for slow responses
if (pm.response.responseTime > 1000) {
console.warn('Slow response detected:', pm.response.responseTime, 'ms');
}
// Error logging
if (pm.response.code >= 400) {
console.error('Request failed with status:', pm.response.code, pm.response.status);
}
Viewing Console Output
After running your request, tap the Console tab at the bottom of the screen. You'll see timestamped log entries with color-coded severity levels. The console automatically clears when you run a new request.
Built-in Snippets
RestForge includes 80+ pre-written code snippets that you can insert into your scripts with a single tap. Snippets are organized into categories and cover the most common scripting patterns.
Snippet Categories
| Category |
Example Snippets |
| Variables |
Set environment variable, Get environment variable, Clear variable, Set global variable |
| Assertions |
Status code is 200, Response time < 200ms, Body contains string, JSON value check, Header exists |
| Data Extraction |
Extract JSON field, Get response header, Parse cookies, Extract array item, Get nested property |
| Generation |
Generate UUID, Random integer, Random string, Current timestamp, ISO 8601 date |
| Encoding |
Base64 encode/decode, URL encode/decode, MD5 hash, SHA256 hash, HMAC signature |
How to Insert Snippets
- In the Scripts tab, tap the Snippets button in the toolbar
- Browse or search for the snippet you need
- Tap the snippet to insert it at your cursor position
- Customize the inserted code with your variable names and values
Pro Tip: Learn by Example
Snippets are an excellent way to learn the scripting API. Insert a snippet, read the code, and modify it to fit your use case. Most snippets include helpful comments explaining what each line does.
Examples & Recipes
Here are complete, real-world examples demonstrating common scripting patterns. You can copy these recipes and adapt them to your API workflows.
Recipe 1: OAuth Token Chain
Automatically fetch an OAuth token before every authenticated request.
Collection-Level Pre-Request Script// Check if we have a valid token
const token = pm.environment.get('oauth_token');
const tokenExpiry = pm.environment.get('oauth_token_expiry');
const now = Date.now();
// If no token or token expired, fetch new one
if (!token || !tokenExpiry || now >= tokenExpiry) {
console.log('Fetching new OAuth token...');
const clientId = pm.environment.get('oauth_client_id');
const clientSecret = pm.environment.get('oauth_client_secret');
const tokenUrl = pm.environment.get('oauth_token_url');
pm.sendRequest({
url: tokenUrl,
method: 'POST',
header: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: {
mode: 'urlencoded',
urlencoded: [
{key: 'grant_type', value: 'client_credentials'},
{key: 'client_id', value: clientId},
{key: 'client_secret', value: clientSecret}
]
}
}, function(err, response) {
if (err || response.code !== 200) {
console.error('Token fetch failed:', err || response.code);
return;
}
const data = response.json();
const expiresIn = data.expires_in || 3600; // Default 1 hour
pm.environment.set('oauth_token', data.access_token);
pm.environment.set('oauth_token_expiry', now + (expiresIn * 1000));
console.log('OAuth token refreshed, expires in', expiresIn, 'seconds');
});
}
Request-Level Pre-Request Script// Add token to Authorization header
const token = pm.environment.get('oauth_token');
if (token) {
pm.request.headers.add({
key: 'Authorization',
value: `Bearer ${token}`
});
}
Recipe 2: Dynamic Request Body with Timestamp
Generate request bodies with timestamps, signatures, or other dynamic fields.
Pre-Request Script for Signed API Request// Generate timestamp
const timestamp = Date.now();
// Get API secret from environment
const apiSecret = pm.environment.get('api_secret');
const apiKey = pm.environment.get('api_key');
// Build payload
const payload = {
api_key: apiKey,
timestamp: timestamp,
data: {
userId: pm.environment.get('user_id'),
action: 'update_profile'
}
};
// Generate signature (simple example, use HMAC in production)
const signatureString = JSON.stringify(payload) + apiSecret;
const signature = require('crypto-js').SHA256(signatureString).toString();
payload.signature = signature;
// Set request body
pm.request.body = {
mode: 'raw',
raw: JSON.stringify(payload)
};
console.log('Request signed with timestamp:', timestamp);
Recipe 3: Complete Response Validation Suite
Validate response structure, data types, business logic, and performance in one script.
Post-Response Script for E-Commerce Order API// Test 1: Response is successful
pm.test("Order created successfully", function() {
pm.expect(pm.response.code).to.equal(201);
});
// Test 2: Response time is acceptable
pm.test("Response time is below 1 second", function() {
pm.expect(pm.response.responseTime).to.be.below(1000);
});
// Parse response
const order = pm.response.json();
// Test 3: Response structure
pm.test("Response has required fields", function() {
pm.expect(order).to.have.property('orderId');
pm.expect(order).to.have.property('items');
pm.expect(order).to.have.property('total');
pm.expect(order).to.have.property('status');
});
// Test 4: Data types
pm.test("Field types are correct", function() {
pm.expect(order.orderId).to.be.a('string');
pm.expect(order.items).to.be.an('array');
pm.expect(order.total).to.be.a('number');
pm.expect(order.status).to.be.a('string');
});
// Test 5: Business logic - total matches sum of items
pm.test("Order total matches sum of line items", function() {
const calculatedTotal = order.items.reduce((sum, item) => {
return sum + (item.price * item.quantity);
}, 0);
pm.expect(order.total).to.equal(calculatedTotal);
});
// Test 6: Order has at least one item
pm.test("Order contains items", function() {
pm.expect(order.items.length).to.be.above(0);
});
// Test 7: Status is valid
pm.test("Order status is valid", function() {
const validStatuses = ['pending', 'confirmed', 'processing', 'shipped', 'delivered'];
pm.expect(validStatuses).to.include(order.status);
});
// Save order ID for subsequent requests
pm.environment.set('last_order_id', order.orderId);
console.log('Order created:', order.orderId);
Recipe 4: Pagination Cursor Pattern
Automatically extract pagination cursors and prepare for the next page request.
Post-Response Script for Paginated API// Parse paginated response
const response = pm.response.json();
// Validate response
pm.test("Status code is 200", function() {
pm.expect(pm.response.code).to.equal(200);
});
pm.test("Response has data array", function() {
pm.expect(response.data).to.be.an('array');
});
// Check pagination info
if (response.pagination) {
const pagination = response.pagination;
// Log current page info
console.log('Page size:', pagination.page_size);
console.log('Total items:', pagination.total);
console.log('Items in this page:', response.data.length);
// Save next cursor if available
if (pagination.next_cursor) {
pm.environment.set('next_cursor', pagination.next_cursor);
console.log('Next cursor saved:', pagination.next_cursor);
console.log('To fetch next page, use cursor:', pagination.next_cursor);
} else {
pm.environment.unset('next_cursor');
console.log('No more pages available');
}
// Save previous cursor for backward navigation
if (pagination.previous_cursor) {
pm.environment.set('previous_cursor', pagination.previous_cursor);
}
// Test: Pagination metadata is valid
pm.test("Pagination metadata is valid", function() {
pm.expect(pagination.page_size).to.be.a('number');
pm.expect(pagination.page_size).to.be.above(0);
pm.expect(pagination.total).to.be.a('number');
});
// Test: Page size matches actual data length (unless last page)
if (pagination.next_cursor) {
pm.test("Page is full", function() {
pm.expect(response.data.length).to.equal(pagination.page_size);
});
}
} else {
console.log('No pagination info in response');
}
Pre-Request Script for Next Page (set query param)// Get next cursor from environment
const nextCursor = pm.environment.get('next_cursor');
if (nextCursor) {
// Add cursor to query params
const url = pm.request.url;
url.query.add({key: 'cursor', value: nextCursor});
console.log('Using cursor for next page:', nextCursor);
} else {
console.log('No cursor set, fetching first page');
}