← All posts Insights 3 min read

NetSuite OAuth 2.0 M2M Authentication: Complete Setup Guide (with Postman)

Step-by-step OAuth 2.0 client credentials setup for NetSuite, validated end-to-end in Postman. Covers JWT signing, the three places teams get stuck, and what to build after auth works.

If you’ve ever watched NetSuite return INVALID_LOGIN_ATTEMPT for the fifth time in a row, you know the drill. The good news: OAuth 2.0 client credentials (M2M) is now the cleanest auth path, and you can validate it end-to-end in Postman in about twenty minutes.

What you’ll have at the end: a working access token, a verified REST call returning real data, and a checklist of the three traps that catch most teams.

Step 1 — Enable the features

Setup → Company → Enable Features → SuiteCloud. Turn on:

  • REST Web Services
  • OAuth 2.0
  • Token-Based Authentication (yes, even if you’re only using OAuth 2.0 — they share plumbing)

Save. NetSuite logs you out. That’s normal.

Step 2 — Create the Integration record

Setup → Integration → Manage Integrations → New.

  1. Name it something you’ll recognise in six months. Not “Test”.
  2. Untick Token-Based Authentication.
  3. Tick Client Credentials (Machine to Machine) Grant.
  4. Leave User Credentials off.
  5. Save.

⚠ Trap #1: NetSuite shows the Client ID and Client Secret exactly once. Copy both into a password manager right now. Close the page without copying and you have to reset the integration and start over.

Step 3 — Generate the key pair

NetSuite’s client credentials flow requires a signed JWT. You keep the private key. NetSuite gets the public certificate.

openssl genrsa -out netsuite_private.pem 4096
openssl req -new -x509 -key netsuite_private.pem \
  -out netsuite_public.pem -days 730 \
  -subj "/C=US/ST=NA/L=NA/O=YourCompany/CN=netsuite-integration"

Then in NetSuite: Setup → Integration → OAuth 2.0 Client Credentials (M2M) Setup → Create New.

  • Application — the integration record you just made
  • Entity — a dedicated integration user (not your CFO’s account)
  • Role — a custom Integration Role (see Step 6)
  • Certificate — upload netsuite_public.pem

NetSuite returns a Certificate ID. Save it next to the client secret.

Step 4 — Build the JWT (in Postman)

Create a new request. Drop this into the Pre-request Script tab. Replace the four placeholders:

const ACCOUNT_ID  = "1234567";                          // your NetSuite account id
const CLIENT_ID   = pm.environment.get("client_id");
const CERT_ID     = pm.environment.get("cert_id");
const PRIVATE_KEY = pm.environment.get("private_key");  // full PEM with newlines

const header  = { alg: "RS256", typ: "JWT", kid: CERT_ID };
const now     = Math.floor(Date.now() / 1000);
const payload = {
    iss: CLIENT_ID,
    scope: "rest_webservices",
    aud: `https://${ACCOUNT_ID.toLowerCase()}.suitetalk.api.netsuite.com/services/rest/auth/oauth2/v1/token`,
    iat: now,
    exp: now + 3600
};

const { KJUR } = require("jsrsasign");
const jwt = KJUR.jws.JWS.sign("RS256", header, payload, PRIVATE_KEY);
pm.environment.set("jwt_assertion", jwt);

⚠ Trap #2: The aud claim must match your account’s token endpoint exactly, lowercased, with the right region prefix. If your URL is 1234567-sb1.app.netsuite.com, use 1234567-sb1, not 1234567. Get this wrong → invalid_client with no useful detail.

Step 5 — Exchange the JWT for a token

  • Method: POST
  • URL: same as your aud claim
  • Body type: x-www-form-urlencoded

Form fields:

  • grant_typeclient_credentials
  • client_assertion_typeurn:ietf:params:oauth:client-assertion-type:jwt-bearer
  • client_assertion{{jwt_assertion}}

Send. You get back access_token and expires_in: 3600. Store it in the test script:

pm.environment.set("access_token", pm.response.json().access_token);

Step 6 — Make your first authenticated call

  • Method: GET
  • URL: https://1234567.suitetalk.api.netsuite.com/services/rest/record/v1/customer
  • Header: Authorization: Bearer {{access_token}}

If you get a JSON list of customer record links, you’re done — auth is correct end to end.

⚠ Trap #3: Permissions. If you get 200 OK on the token endpoint but 403 on the record call, the Entity’s Role doesn’t have REST Web Services enabled. Build a dedicated Integration Role with only the record-level permissions you need — Administrator is too dangerous, Customer Center is usually too much.

What to build next

  • Token refresh on 401 — not on a timer
  • Exponential backoff on 429 — respect Retry-After
  • Idempotency keys on every write
  • Environment guards so sandbox creds can’t run against prod

Each of those is a post in its own right.

Skip the build entirely?

NetSuite Integration Basic and Pro ship with the OAuth 2.0 plumbing already wired in. You bring credentials. We handle JWT signing, refresh, and 429 retries.


Ship it

Need this in your stack?

We build, integrate, and ship — no calls, just delivery.

Start a project →