# Netsuite M2m OAUTH

> Guides NetSuite OAuth 2.0 Client Credentials (M2M) setup — integration record, entity/role mapping, X.509 certificate, JWT assertion, token exchange, and read-only verification. Use when setting up, explaining, testing, or troubleshooting NetSuite M2M OAuth for REST Web Services, RESTlets, or SuiteAnalytics Connect.

- Skill: `unstackedapps/netsuite-m2m-oauth` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add unstackedapps/netsuite-m2m-oauth`
- Raw SKILL.md: https://api.skillmd.com/api/skills/unstackedapps/netsuite-m2m-oauth/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- License: MIT
- Author: unstackedapps (https://skillmd.com/u/unstackedapps)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/unstackedapps/netsuite-m2m-oauth

---


# NetSuite M2M OAuth

Guide a developer from zero to a verified NetSuite OAuth 2.0 Client Credentials connection while explaining what each credential and trust relationship does.

This is a **workflow skill**, not a general OAuth reference. Move through setup one checkpoint at a time and verify each checkpoint before advancing.

## Scope

Use this skill for NetSuite OAuth 2.0 **Client Credentials (Machine to Machine)** authentication for:

- REST Web Services — `rest_webservices`
- RESTlets — `restlets`
- SuiteAnalytics Connect — `suite_analytics`

Do not use this skill for:

- OAuth 2.0 Authorization Code Grant
- Token-Based Authentication (TBA)
- SOAP web services
- browser/user-login flows
- NetSuite AI Connector Service authentication

If the user is configuring the **NetSuite AI Connector Service**, stop this workflow and explain that NetSuite's current integration-record rules require the Client Credentials (Machine to Machine) Grant to be cleared for that scope. Use the appropriate AI Connector/authentication guidance instead.

## Relationship to Oracle skills

When Oracle's SuiteCloud Agent Skills are installed, treat them as authoritative platform guidance.

In particular, `netsuite-sdf-safe-guide` may provide current OAuth and security guidance. Do not duplicate or override it.

This skill owns the interactive M2M workflow:

1. explain the trust model
2. identify the required NetSuite configuration
3. create the certificate-backed mapping
4. build a JWT client assertion
5. exchange the assertion for an access token
6. make a safe authenticated request
7. troubleshoot the failing stage when verification does not succeed

## Operating style

Walk the user through the process rather than dumping every step at once.

For each checkpoint:

1. **Explain** what is being configured and why it exists.
2. **Act** by giving the exact NetSuite UI action or local development action.
3. **Verify** the smallest non-secret result that proves the checkpoint is complete.
4. Continue only when the current checkpoint is understood or verified.

Use information already available in the workspace before asking the user for it.

When generating code, inspect the existing project language, package manager, environment-variable conventions, and HTTP/JWT libraries first. Prefer existing dependencies over introducing a new stack.

## Security rules

Never ask the user to paste any of these into chat:

- private key contents
- access tokens
- client secrets
- production secrets from a secret manager

Do not print, log, commit, or echo JWT assertions or access tokens unless the user explicitly needs a redacted diagnostic representation.

Prefer:

- a local private-key file outside the repository
- a secrets manager or deployment secret store
- environment variables for identifiers and configuration
- `.gitignore` protection for any local credential material

If a private key is found inside the repository, call that out immediately and recommend removing it from version control and rotating the credential if it may have been exposed.

Do not use Administrator as the integration role merely to make authentication succeed. Use the least-privileged role that supports the integration's required operations.

## Mental model

Before setup, make sure the user understands this chain:

```text
Integration Record
    = which application is connecting
        |
        | client ID (`iss`)
        v
Application identity

Entity + Role
    = who the application acts as
      and what that identity may do

Public X.509 certificate
    = uploaded to NetSuite
      NetSuite uses it to verify signatures

Private key
    = stays with the external application
      the application uses it to sign assertions

Certificate ID
    = generated by the NetSuite M2M mapping
      used as JWT header `kid`

Signed JWT client assertion
    = short-lived proof that the client controls
      the private key for the configured mapping
        |
        | POST to token endpoint
        v
Access token
    = short-lived Bearer credential used for API calls
```

Reinforce this distinction when confusion appears:

**JWT client assertion != NetSuite access token.**

The client assertion proves the application's identity to the token endpoint. The access token is what NetSuite returns after successful authentication and what the client sends as `Authorization: Bearer <token>` to protected resources.

Client Credentials does not use an interactive user login. It is a machine-to-machine flow.

## Checkpoint 1 — Identify the target

Determine the resource the application needs to access.

Map it to the OAuth scope:

| Target | Scope |
|---|---|
| REST Web Services | `rest_webservices` |
| RESTlet | `restlets` |
| SuiteAnalytics Connect | `suite_analytics` |

If more than one supported target is required, use only the scopes that are actually needed.

Also establish:

- production, sandbox, or Release Preview
- the entity that the application should act as
- the role it should use
- whether this is a new integration or troubleshooting an existing one

Do not request credentials yet.

## Checkpoint 2 — Confirm account prerequisites

Guide the user through the applicable prerequisites.

### OAuth 2.0 feature

NetSuite UI:
`Setup > Company > Enable Features > SuiteCloud > Manage Authentication > OAuth 2.0`

The OAuth 2.0 feature must be enabled.

For RESTlets, NetSuite also requires Client SuiteScript and Server SuiteScript to be enabled.

For REST Web Services, confirm the REST Web Services feature and the role permissions needed for the intended REST operations.

### Integration role

The role used by the M2M mapping must have:
`Log in Using OAuth 2.0 Access Tokens`

The role must also have the record, transaction, list, setup, or analytics permissions required by the integration itself.

Explain the difference between these two permissions when relevant:

- `OAuth 2.0 Authorized Applications Management` is an administrative/setup permission.
- `Log in Using OAuth 2.0 Access Tokens` allows the mapped identity to access RESTlets, REST Web Services, or SuiteAnalytics Connect with OAuth 2.0.

### Account-specific URLs

Do **not** invent or manually normalize the account-specific domain, especially for sandbox or Release Preview accounts.

Have the user confirm it in:
`Setup > Company > Company Information > Company URLs`

Use the SuiteTalk/REST Web Services account-specific domain shown by NetSuite for the token endpoint.

Treat the domain as configuration rather than hard-coding assumptions about account-ID formatting.

Checkpoint complete when the required features, role, and account-specific domain are known.

## Checkpoint 3 — Create or verify the Integration Record

Open or create the NetSuite Integration Record for the external application.

Under OAuth 2.0:

1. enable **Client Credentials (Machine to Machine) Grant**
2. enable only the required supported scopes
3. do not enable unrelated scopes merely for troubleshooting
4. save the integration

Capture the **Client ID** securely.

Explain:

```text
Client ID = application identity
JWT `iss` = Client ID
```

The Client Credentials token exchange uses the certificate-signed client assertion. Do not design this workflow around sending the client secret to the token endpoint.

Do not ask the user to paste the Client ID or Client Secret into chat. Prefer a local environment variable or secret configuration.

Checkpoint complete when the Integration Record supports Client Credentials and the required scope.

## Checkpoint 4 — Generate the key pair and certificate

Explain the split first:

```text
public certificate -> NetSuite
private key        -> external application only
```

NetSuite's current M2M certificate requirements support X.509 public certificates with RSA or EC keys and a maximum certificate validity of two years. Prefer a shorter operational lifetime with a planned rotation process.

If the project has no existing certificate standard, an ES256-compatible certificate can be generated with OpenSSL using Oracle's documented pattern:

```bash
openssl req \
  -new \
  -x509 \
  -newkey ec \
  -pkeyopt ec_paramgen_curve:prime256v1 \
  -pkeyopt ec_param_enc:named_curve \
  -nodes \
  -days 365 \
  -out public.pem \
  -keyout private.pem
```

Before running a command that writes credential material into the current workspace, choose a safe destination and confirm it will not be committed.

After generation, verify without exposing secret material:

```bash
openssl x509 -in public.pem -noout -subject -issuer -dates
```

If RSA-PSS is required by the user's platform, use a NetSuite-supported RSA size and a matching `PS*` JWT algorithm rather than changing algorithms independently.

Checkpoint complete when:

- a public X.509 certificate exists
- the corresponding private key is stored safely
- the selected JWT algorithm matches the key type

## Checkpoint 5 — Create the NetSuite M2M mapping

NetSuite UI:
`Setup > Integration > Manage Authentication > OAuth 2.0 Client Credentials (M2M) Setup`

Create a new mapping and select:

1. **Entity**
2. **Role**
3. **Application** — the Integration Record
4. **Certificate** — upload the public certificate only

Save the mapping.

Explain the trust relationship:

```text
Application + Entity + Role + Certificate = M2M identity
```

Record the resulting **Certificate ID** securely as configuration.

Explain:

```text
Certificate ID = JWT header `kid`
```

Never upload the private key to NetSuite.

If the integration record does not appear in the Application list, first verify that **Client Credentials (Machine to Machine) Grant** is enabled on that Integration Record.

Checkpoint complete when the mapping exists and the Certificate ID is known.

## Checkpoint 6 — Build the local configuration

Prefer environment variables or the project's existing secret/configuration mechanism.

Use names appropriate to the project. A clear generic model is:

```text
NETSUITE_TOKEN_URL
NETSUITE_CLIENT_ID
NETSUITE_CERTIFICATE_ID
NETSUITE_PRIVATE_KEY_PATH
NETSUITE_SCOPE
NETSUITE_RESOURCE_BASE_URL
```

Construct `NETSUITE_TOKEN_URL` from the **confirmed account-specific SuiteTalk domain**, using the current NetSuite token path:

```text
/services/rest/auth/oauth2/v1/token
```

Do not commit real values to `.env.example`. Examples must use placeholders.

For RESTlets, remember that the protected RESTlet resource URL may use the account-specific RESTlets domain even though the M2M token endpoint is documented on the account-specific SuiteTalk domain.

Checkpoint complete when the application can load configuration without printing secrets.

## Checkpoint 7 — Create the JWT client assertion

Use a maintained JWT library supported by the project's language/runtime. Do not hand-roll Base64url encoding or cryptographic signing.

### JWT header

Use:

```json
{
  "typ": "JWT",
  "alg": "ES256",
  "kid": "<CERTIFICATE_ID>"
}
```

`alg` is an example. It must match the certificate/key type. NetSuite currently supports `PS256`, `PS384`, `PS512`, `ES256`, `ES384`, and `ES512` for the client assertion.

### JWT payload

Use the required NetSuite claims:

```json
{
  "iss": "<CLIENT_ID>",
  "scope": ["rest_webservices"],
  "aud": "<EXACT_TOKEN_URL>",
  "iat": 0,
  "exp": 0
}
```

Populate:

- `iss` with the Integration Record Client ID
- `scope` with the required NetSuite M2M scope or scopes
- `aud` with the exact token endpoint being called
- `iat` with current Unix time in seconds
- `exp` with a short future Unix time

NetSuite requires `exp` to be less than 60 minutes after `iat`. Prefer a short assertion lifetime, such as about five minutes, unless the project's requirements justify something else.

A unique `jti` may be added if the implementation/library uses one, but do not invent additional required claims that NetSuite does not require.

Before sending the assertion, decode its header and payload locally **without printing the signature or secret key** and verify:

- `kid` equals the Certificate ID from the mapping
- `iss` equals the Integration Record Client ID
- `alg` matches the private key
- `scope` matches the Integration Record scope
- `aud` exactly matches the token endpoint
- `iat` and `exp` are Unix seconds, not milliseconds
- local system time is accurate

Checkpoint complete when the assertion can be generated and its non-secret claims are correct.

## Checkpoint 8 — Exchange the assertion for an access token

POST to the confirmed token endpoint with:

```http
Content-Type: application/x-www-form-urlencoded
```

Form fields:

```text
grant_type=client_credentials
client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
client_assertion=<SIGNED_JWT>
```

Use the project's existing HTTP client when possible.

Do not log the request body because it contains the signed client assertion.

A successful response contains:

```json
{
  "access_token": "<REDACTED>",
  "expires_in": 3600,
  "token_type": "Bearer"
}
```

Explain that NetSuite M2M access tokens are currently valid for 60 minutes. There is no interactive refresh-token workflow here; when the access token expires, create a fresh client assertion and run the Client Credentials token exchange again.

Store/cache the access token only as long as needed and never commit it.

Checkpoint complete when NetSuite returns an access token.

## Checkpoint 9 — Verify with a safe authenticated operation

Do not declare success merely because the token endpoint returned `200`.

Make one non-destructive authenticated request appropriate to the selected target.

For HTTP APIs, send:

```http
Authorization: Bearer <ACCESS_TOKEN>
```

Prefer a read-only operation such as metadata lookup, a permitted record GET, or a RESTlet GET that is known not to mutate data.

Do not create, update, delete, post, transform, or trigger business transactions merely to test authentication.

For SuiteAnalytics Connect, verify by opening the intended connection and running the smallest harmless query supported by the user's environment.

The workflow is complete only when:

- the token exchange succeeds
- the access token is accepted by the intended NetSuite service
- the read-only verification succeeds with the mapped role

## Completion summary

At the end, summarize the resulting configuration without exposing secrets:

```text
Target:              REST Web Services | RESTlet | SuiteAnalytics Connect
Environment:         production | sandbox | Release Preview
Scope:               <scope>
Integration Record:  configured
Entity/Role:         configured
Certificate mapping: configured
Private key:         stored locally/secret manager, not shared
JWT assertion:       generated successfully
Token exchange:      successful
Read-only API test:  successful
Status:              M2M authentication verified
```

If anything remains unverified, say exactly which checkpoint is incomplete. Do not report the integration as working until the final authenticated read-only verification succeeds.

## Additional resources

- Troubleshooting and common mistakes: [references/troubleshooting.md](references/troubleshooting.md)
- Oracle docs and version notes: [references/oracle-docs.md](references/oracle-docs.md)

