The N/https module is the primary tool in SuiteScript 2.1 for making outbound HTTP and HTTPS requests to external APIs, webhooks, and third-party services. Whether you are syncing data with Shopify, posting to a Slack channel, calling a REST API, or fetching a rate from a payment processor, N/https is how you do it from inside NetSuite.
This guide covers every method in the N/https module with real, production-ready code examples you can drop directly into your SuiteScript projects.
What is the N/https Module?
The N/https module provides functions for sending HTTP requests from a SuiteScript execution context to an external URL. It supports all standard HTTP methods β GET, POST, PUT, DELETE, HEAD β and lets you set custom headers, send JSON or form-encoded bodies, and read the response status code, headers, and body.
N/https replaces the older N/http module and adds built-in support for HTTPS (TLS) connections, OAuth 1.0 signatures, and SecretKey-based credential management. It is available in Scheduled Scripts, Map/Reduce Scripts, Suitelets, RESTlets, and User Event afterSubmit contexts.
How to Load the N/https Module
/**
* @NApiVersion 2.1
* @NScriptType ScheduledScript
*/
define(['N/https'], (https) => {
const execute = (context) => {
// https is now available
};
return { execute };
});
https.get() β Making a GET Request
Use https.get() to fetch data from an external API endpoint. The function accepts a URL and optional headers, and returns a ClientResponse object with the response code, headers, and body.
/**
* @NApiVersion 2.1
* @NScriptType ScheduledScript
*/
define(['N/https', 'N/log'], (https, log) => {
const execute = (context) => {
const response = https.get({
url: 'https://api.example.com/products?limit=50',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});
log.debug('Response Code', response.code);
if (response.code === 200) {
const data = JSON.parse(response.body);
log.debug('Products Retrieved', data.length);
} else {
log.error('API Error', 'Status: ' + response.code + ' | Body: ' + response.body);
}
};
return { execute };
});
The response.code is the HTTP status code (200, 201, 400, 404, 500, etc.). The response.body is the raw response string β use JSON.parse() to convert it to an object when the API returns JSON. The response.headers object contains the response headers returned by the server.
https.post() β Making a POST Request
Use https.post() to send data to an external API. You can send JSON, form-encoded data, or any other body format by setting the appropriate Content-Type header. The body parameter must be a string.
/**
* @NApiVersion 2.1
* @NScriptType UserEventScript
*/
define(['N/https', 'N/log'], (https, log) => {
const afterSubmit = (context) => {
if (context.type !== context.UserEventType.CREATE) return;
const rec = context.newRecord;
// Build the payload to send to external system
const payload = {
orderId: rec.id,
customerName: rec.getValue({ fieldId: 'entity' }),
amount: rec.getValue({ fieldId: 'total' }),
currency: rec.getValue({ fieldId: 'currency' }),
createdDate: rec.getValue({ fieldId: 'trandate' })
};
const response = https.post({
url: 'https://api.example.com/orders',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
if (response.code === 201 || response.code === 200) {
log.audit('Order Synced', 'External ID: ' + JSON.parse(response.body).id);
} else {
log.error('Sync Failed', response.code + ' | ' + response.body);
}
};
return { afterSubmit };
});
https.put() β Making a PUT Request
Use https.put() to update an existing resource in an external system. PUT replaces the entire resource, so you need to send the full object β not just the fields you are changing.
/**
* @NApiVersion 2.1
* @NScriptType ScheduledScript
*/
define(['N/https', 'N/log'], (https, log) => {
const execute = (context) => {
const externalId = '98765';
const updatedData = {
status: 'fulfilled',
trackingNumber: '1Z999AA10123456784',
shippedDate: '2026-05-16'
};
const response = https.put({
url: 'https://api.example.com/orders/' + externalId,
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(updatedData)
});
log.debug('PUT Response', response.code + ' | ' + response.body);
};
return { execute };
});
https.delete() β Making a DELETE Request
Use https.delete() to remove a resource from an external system. Always validate that you have the correct resource ID before calling DELETE β this action is typically irreversible in the target system.
/**
* @NApiVersion 2.1
* @NScriptType ScheduledScript
*/
define(['N/https', 'N/log'], (https, log) => {
const execute = (context) => {
const resourceId = '12345';
const response = https.delete({
url: 'https://api.example.com/products/' + resourceId,
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
});
if (response.code === 200 || response.code === 204) {
log.audit('Resource Deleted', 'ID: ' + resourceId);
} else {
log.error('Delete Failed', response.code + ' | ' + response.body);
}
};
return { execute };
});
https.request() β Generic HTTP Method
Use https.request() when you need to specify the HTTP method explicitly, or when using less common methods like PATCH. This is the most flexible function in the module and supports all methods that the other helper functions do, plus anything else you need.
/**
* @NApiVersion 2.1
* @NScriptType ScheduledScript
*/
define(['N/https'], (https) => {
const execute = (context) => {
// PATCH β partial update (only send changed fields)
const response = https.request({
method: https.Method.PATCH,
url: 'https://api.example.com/customers/555',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: 'newemail@example.com'
})
});
log.debug('PATCH Response', response.code);
};
return { execute };
});
https.Method Enum β All Supported HTTP Methods
The N/https module exposes an enum of all supported HTTP methods through https.Method. These are the values you pass to the method parameter of https.request():
| https.Method Value | HTTP Method | Typical Use |
|---|---|---|
| https.Method.GET | GET | Fetch data from an API |
| https.Method.POST | POST | Create a new resource |
| https.Method.PUT | PUT | Replace an existing resource |
| https.Method.DELETE | DELETE | Remove a resource |
| https.Method.HEAD | HEAD | Check existence without body |
| https.Method.PATCH | PATCH | Partially update a resource |
Practical Example: Calling a Webhook on Record Save
A very common integration pattern is triggering a webhook whenever a record is created or updated in NetSuite. Here is a complete User Event Script that posts a JSON payload to a webhook URL whenever a Sales Order is submitted:
/**
* @NApiVersion 2.1
* @NScriptType UserEventScript
*/
define(['N/https', 'N/log', 'N/runtime'], (https, log, runtime) => {
const afterSubmit = (context) => {
// Only fire on Create and Edit
if (context.type === context.UserEventType.DELETE) return;
const rec = context.newRecord;
const webhookPayload = {
event: context.type === context.UserEventType.CREATE ? 'order.created' : 'order.updated',
recordType: 'salesorder',
internalId: rec.id,
tranId: rec.getValue({ fieldId: 'tranid' }),
entity: rec.getText({ fieldId: 'entity' }),
status: rec.getText({ fieldId: 'status' }),
total: rec.getValue({ fieldId: 'total' }),
currency: rec.getText({ fieldId: 'currency' }),
executedBy: runtime.getCurrentUser().name
};
try {
const response = https.post({
url: 'https://hooks.example.com/netsuite/sales-orders',
headers: {
'Content-Type': 'application/json',
'X-Webhook-Secret': 'YOUR_WEBHOOK_SECRET'
},
body: JSON.stringify(webhookPayload)
});
if (response.code >= 200 && response.code < 300) {
log.audit('Webhook Sent', 'SO ' + rec.getValue({ fieldId: 'tranid' }) + ' | HTTP ' + response.code);
} else {
log.error('Webhook Failed', 'HTTP ' + response.code + ' | ' + response.body);
}
} catch (e) {
log.error('Webhook Exception', e.message);
}
};
return { afterSubmit };
});
Practical Example: Paginated API with GET in Map/Reduce
When syncing large datasets from an external API that returns paginated results, Map/Reduce is the right script type. Here is how to handle pagination using N/https.get() inside a Map/Reduce getInputData function:
/**
* @NApiVersion 2.1
* @NScriptType MapReduceScript
*/
define(['N/https', 'N/log'], (https, log) => {
const getInputData = () => {
const allItems = [];
let page = 1;
let hasMore = true;
while (hasMore) {
const response = https.get({
url: 'https://api.example.com/items?page=' + page + '&limit=100',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Accept': 'application/json'
}
});
if (response.code !== 200) {
log.error('API Error', 'Page ' + page + ' | ' + response.code);
break;
}
const result = JSON.parse(response.body);
allItems.push(...result.data);
hasMore = result.hasNextPage;
page++;
}
log.audit('Total Items Fetched', allItems.length);
return allItems;
};
const map = (context) => {
const item = JSON.parse(context.value);
// Process each item and write to NetSuite
context.write({ key: item.sku, value: JSON.stringify(item) });
};
const reduce = (context) => {
// Upsert into NetSuite
log.debug('Processing SKU', context.key);
};
const summarize = (summary) => {
log.audit('Sync Complete', 'Input errors: ' + summary.inputSummary.error);
};
return { getInputData, map, reduce, summarize };
});
Handling Authentication Methods
Different external APIs use different authentication schemes. Here is how to handle the three most common authentication patterns with N/https in SuiteScript 2.1:
Bearer Token (most common): Pass the token in the Authorization header as shown in all examples above. Store the token value in a Script Parameter rather than hard-coding it, so it can be updated without modifying the script.
// Bearer Token β store token in Script Parameter "custscript_api_token"
const token = runtime.getCurrentScript().getParameter({ name: 'custscript_api_token' });
const response = https.get({
url: 'https://api.example.com/data',
headers: {
'Authorization': 'Bearer ' + token
}
});
Basic Authentication: Encode the username and password in Base64 and pass them as the Authorization header with the Basic scheme. Use N/encode to perform the Base64 encoding safely inside SuiteScript.
/**
* @NApiVersion 2.1
* @NScriptType ScheduledScript
*/
define(['N/https', 'N/encode'], (https, encode) => {
const execute = (context) => {
const username = 'myuser';
const password = 'mypassword';
const credentials = encode.convert({
string: username + ':' + password,
inputEncoding: encode.Encoding.UTF_8,
outputEncoding: encode.Encoding.BASE_64
});
const response = https.get({
url: 'https://api.example.com/secure-endpoint',
headers: {
'Authorization': 'Basic ' + credentials,
'Accept': 'application/json'
}
});
log.debug('Response', response.code);
};
return { execute };
});
API Key in Header: Some APIs accept a dedicated API key header like X-Api-Key or api-key. Pass it directly as a custom header key-value pair alongside your other headers.
const response = https.post({
url: 'https://api.example.com/endpoint',
headers: {
'X-Api-Key': 'YOUR_API_KEY_HERE',
'Content-Type': 'application/json'
},
body: JSON.stringify({ key: 'value' })
});
Error Handling and Retry Logic
External APIs can fail for many reasons β network timeouts, rate limits, temporary server errors. A production-ready integration should always include error handling and, where appropriate, retry logic. Here is a reusable helper function that retries a failed request up to three times with exponential backoff:
/**
* @NApiVersion 2.1
* @NScriptType ScheduledScript
*/
define(['N/https', 'N/log'], (https, log) => {
/**
* Makes an HTTPS GET request with automatic retry on 5xx errors.
* @param {string} url
* @param {Object} headers
* @param {number} maxRetries
* @returns {Object} ClientResponse
*/
const getWithRetry = (url, headers, maxRetries = 3) => {
let attempt = 0;
while (attempt < maxRetries) {
const response = https.get({ url, headers });
if (response.code >= 200 && response.code < 300) {
return response; // Success
}
if (response.code >= 500) {
attempt++;
log.error('Retry ' + attempt, 'HTTP ' + response.code + ' for ' + url);
if (attempt < maxRetries) {
// Simple delay β use a loop as SuiteScript has no setTimeout
const waitUntil = Date.now() + (attempt * 2000);
while (Date.now() < waitUntil) { /* wait */ }
}
} else {
// 4xx errors are not retryable
log.error('Client Error', 'HTTP ' + response.code + ' | ' + response.body);
return response;
}
}
throw new Error('Max retries exceeded for: ' + url);
};
const execute = (context) => {
try {
const response = getWithRetry(
'https://api.example.com/products',
{ 'Authorization': 'Bearer YOUR_TOKEN' }
);
log.debug('Data', response.body);
} catch (e) {
log.error('Fatal Error', e.message);
}
};
return { execute };
});
Governance Costs for N/https
Every https.get(), https.post(), https.put(), https.delete(), and https.request() call costs 10 governance units regardless of the HTTP method. This applies per request, not per byte transferred. In a Scheduled Script with 10,000 governance units, you can make up to 1,000 outbound HTTP calls per execution. In a Map/Reduce Script, governance is tracked per stage, giving you much more headroom for large sync jobs. Always design your integration to batch as much data as possible per request to stay within governance limits.
Common Errors and How to Fix Them
The most frequent N/https errors are SSS_HTTPS_DISALLOWED_DOMAIN which means the target URL's domain is not allowlisted in your NetSuite company preferences under Setup β Company β Enable Features, UNEXPECTED_ERROR on the request which typically means the external server returned a non-standard response or a connection timeout, and JSON parse errors on the response body which happen when you call JSON.parse() on a non-JSON response such as an HTML error page from a CDN or load balancer.
To allow an external domain in NetSuite, navigate to Setup β Company β Enable Features β SuiteCloud tab and add your domain to the list of allowed outbound domains. This is required for every external host you want to call from SuiteScript.
Next Steps
Now that you understand how to make outbound HTTP requests with N/https, explore how to combine it with the N/runtime module to read script parameters for credentials, the N/encode module for Base64 encoding and decoding payloads, and the N/crypto module for generating HMAC signatures when calling APIs that require request signing. These three modules together with N/https cover the full lifecycle of a production-grade API integration in SuiteScript 2.1.
Discover more from The NetSuite Pro
Subscribe to get the latest posts sent to your email.
Leave a Reply