Stitchflow
Netsuite logo

Netsuite User Management API Guide

API workflow

How to automate user lifecycle operations through APIs with caveats that matter in production.

UpdatedMar 6, 2026

Summary and recommendation

NetSuite exposes user lifecycle management through its SuiteTalk REST Record API at `https://<accountId>.suitetalk.api.netsuite.com/services/rest/record/v1/employee`. Authentication supports OAuth 2.0 (client credentials or authorization code) for REST and Token-Based Authentication (TBA/OAuth 1.0a) for SOAP. OAuth 2.0 client credentials flow requires the Integration record to have 'Client Credentials (Machine to Machine)' explicitly enabled - it is off by default.

NetSuite does not expose a SCIM 2.0 endpoint. There is no native webhook mechanism for employee record changes; change detection requires either SuiteScript User Event scripts (beforeSubmit/afterSubmit) triggering HTTP callouts, or polling via SuiteQL.

For teams building against an identity graph, NetSuite's employee object carries subsidiary, department, location, supervisor, and role arrays - making it a viable node in a cross-system identity graph when polled incrementally via SuiteQL filtered on lastModifiedDate.

Governance is concurrency-based, not rate-based. Standard accounts allow 10 concurrent web service requests; SuiteCloud Plus License raises this to up to 100. Errors surface as HTTP 429 or `CONCURRENCY_LIMIT_EXCEEDED` SOAP faults - not via standard rate-limit headers - so retry logic must be implemented explicitly with exponential backoff.

API quick reference

Has user APIYes
Auth methodOAuth 2.0 (client credentials or authorization code) for REST; Token-Based Authentication (TBA/OAuth 1.0a) for SOAP SuiteTalk
Base URLOfficial docs
SCIM availableNo
SCIM plan requiredEnterprise

Authentication

Auth method: OAuth 2.0 (client credentials or authorization code) for REST; Token-Based Authentication (TBA/OAuth 1.0a) for SOAP SuiteTalk

Setup steps

  1. In NetSuite, navigate to Setup > Company > Enable Features > SuiteCloud tab and enable 'REST Web Services' and 'OAuth 2.0'.
  2. Create an Integration record at Setup > Integration > Manage Integrations > New. Note the Client ID and Client Secret.
  3. For client credentials flow, assign the integration a role with appropriate permissions (e.g., Employee & Access role).
  4. Request an access token via POST to https://.suitetalk.api.netsuite.com/services/rest/auth/oauth2/v1/token with grant_type=client_credentials.
  5. Include the Bearer token in the Authorization header for all subsequent REST API calls.
  6. For TBA (SOAP), generate Consumer Key/Secret on the Integration record, then create Access Token records under Setup > Users/Roles > Access Tokens.

Required scopes

Scope Description Required for
rest_webservices Grants access to SuiteTalk REST Web Services endpoints. All REST API calls including employee/user CRUD
suite_analytics Grants access to SuiteAnalytics Connect and workbook data. Reporting and analytics queries; not required for user management

User object / data model

Field Type Description On create On update Notes
id integer Internal NetSuite record ID for the Employee. system-assigned read-only Used as path parameter in REST URLs.
firstName string Employee first name. required optional
lastName string Employee last name. required optional
email string Primary email address; used as login identifier. required optional Must be unique across the account.
isInactive boolean Marks the employee record as inactive (deprovisioned). optional (default false) optional Setting true disables login access.
giveAccess boolean Controls whether the employee can log in to NetSuite. optional optional Must be true for the user to have portal/UI access.
role array of {id, name} List of NetSuite roles assigned to the employee. optional optional Role IDs are account-specific internal IDs.
subsidiary object {id, name} Primary subsidiary the employee belongs to. required (OneWorld accounts) optional Required when OneWorld module is enabled.
department object {id, name} Department assignment. optional optional
location object {id, name} Office/location assignment. optional optional
supervisor object {id, name} Manager/supervisor record reference. optional optional
title string Job title. optional optional
phone string Primary phone number. optional optional
mobilePhone string Mobile phone number. optional optional
hireDate date (YYYY-MM-DD) Employee hire date. optional optional
employeeType enum string Employment classification (e.g., FULLTIME, PARTTIME, CONTRACTOR). optional optional
password string (write-only) Initial password for the employee login. optional optional Write-only; never returned in GET responses.
sendEmail boolean Triggers a welcome/access email to the employee on create/update. optional optional

Core endpoints

List Employees

  • Method: GET
  • URL: https://<accountId>.suitetalk.api.netsuite.com/services/rest/record/v1/employee
  • Watch out for: Response items contain only summary fields and links; fetch individual records for full field data.

Request example

GET /services/rest/record/v1/employee?limit=20&offset=0
Authorization: Bearer <token>

Response example

{
  "links": [...],
  "count": 150,
  "hasMore": true,
  "items": [
    {"id": "123", "links": [...], "email": "jdoe@example.com"}
  ]
}

Get Employee

  • Method: GET
  • URL: https://<accountId>.suitetalk.api.netsuite.com/services/rest/record/v1/employee/{id}
  • Watch out for: Use the numeric internal ID, not the employee name or email, as the path parameter.

Request example

GET /services/rest/record/v1/employee/123
Authorization: Bearer <token>

Response example

{
  "id": "123",
  "firstName": "Jane",
  "lastName": "Doe",
  "email": "jdoe@example.com",
  "isInactive": false,
  "giveAccess": true,
  "subsidiary": {"id": "1", "refName": "Parent Company"}
}

Create Employee

  • Method: POST
  • URL: https://<accountId>.suitetalk.api.netsuite.com/services/rest/record/v1/employee
  • Watch out for: Successful creation returns HTTP 204 with a Location header, not a body. Parse Location to get the new record ID.

Request example

POST /services/rest/record/v1/employee
Content-Type: application/json
{
  "firstName": "Jane",
  "lastName": "Doe",
  "email": "jdoe@example.com",
  "subsidiary": {"id": "1"},
  "giveAccess": true
}

Response example

HTTP 204 No Content
Location: /services/rest/record/v1/employee/456

Update Employee (partial)

  • Method: PATCH
  • URL: https://<accountId>.suitetalk.api.netsuite.com/services/rest/record/v1/employee/{id}
  • Watch out for: PATCH only updates supplied fields. Use PUT to replace the full record (requires all mandatory fields).

Request example

PATCH /services/rest/record/v1/employee/456
Content-Type: application/json
{
  "title": "Senior Engineer",
  "department": {"id": "10"}
}

Response example

HTTP 204 No Content

Deactivate Employee

  • Method: PATCH
  • URL: https://<accountId>.suitetalk.api.netsuite.com/services/rest/record/v1/employee/{id}
  • Watch out for: Setting isInactive=true alone may not revoke login; also set giveAccess=false to ensure access is removed.

Request example

PATCH /services/rest/record/v1/employee/456
Content-Type: application/json
{
  "isInactive": true,
  "giveAccess": false
}

Response example

HTTP 204 No Content

Delete Employee

  • Method: DELETE
  • URL: https://<accountId>.suitetalk.api.netsuite.com/services/rest/record/v1/employee/{id}
  • Watch out for: NetSuite rarely allows hard deletion of Employee records due to transaction history linkage. Prefer isInactive=true instead.

Request example

DELETE /services/rest/record/v1/employee/456
Authorization: Bearer <token>

Response example

HTTP 204 No Content

Search Employees (SuiteQL)

  • Method: POST
  • URL: https://<accountId>.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql
  • Watch out for: SuiteQL field names are lowercase and differ from REST record field names (e.g., 'firstname' vs 'firstName'). Boolean fields use 'T'/'F' strings.

Request example

POST /services/rest/query/v1/suiteql
Content-Type: application/json
{
  "q": "SELECT id, email, firstname, lastname FROM employee WHERE isinactive = 'F' LIMIT 100 OFFSET 0"
}

Response example

{
  "items": [
    {"id": 123, "email": "jdoe@example.com", "firstname": "Jane", "lastname": "Doe"}
  ],
  "hasMore": false,
  "totalResults": 1
}

Assign Role to Employee

  • Method: POST
  • URL: https://<accountId>.suitetalk.api.netsuite.com/services/rest/record/v1/employee/{id}/roles
  • Watch out for: Role IDs are account-specific internal IDs; query the role record type to resolve names to IDs before assignment.

Request example

POST /services/rest/record/v1/employee/456/roles
Content-Type: application/json
{
  "role": {"id": "15"}
}

Response example

HTTP 204 No Content

Rate limits, pagination, and events

  • Rate limits: NetSuite enforces concurrency-based governance rather than per-minute rate limits. Each account has a concurrency limit on simultaneous web service requests. Exceeding the limit returns a SOAP/REST governance error. SuiteCloud Plus License increases the concurrency allowance.
  • Rate-limit headers: No
  • Retry-After header: No
  • Rate-limit notes: NetSuite does not return standard rate-limit HTTP headers. Governance errors return HTTP 429 or a SOAP fault with code CONCURRENCY_LIMIT_EXCEEDED. Implement exponential backoff. REST also enforces a per-request timeout of 15 minutes.
  • Pagination method: offset
  • Default page size: 20
  • Max page size: 1000
  • Pagination pointer: offset / limit
Plan Limit Concurrent
Standard NetSuite 10 concurrent web service requests 10
SuiteCloud Plus License Up to 100 concurrent web service requests (configurable) 100
  • Webhooks available: No
  • Webhook notes: NetSuite does not offer native outbound webhooks for record change events. There is no built-in webhook subscription mechanism for user/employee record changes.
  • Alternative event strategy: Use SuiteScript 2.x User Event scripts (beforeSubmit/afterSubmit) deployed on the Employee record to trigger HTTP callouts to external systems on create/update/delete. Alternatively, poll the SuiteQL endpoint or use the SuiteTalk SOAP ChangeLog/getSelectValue approach for change detection.

SCIM API status

  • SCIM available: No
  • SCIM version: 2.0
  • Plan required: Enterprise
  • Endpoint: Not documented

Limitations:

  • NetSuite does not natively expose a SCIM 2.0 endpoint.
  • Microsoft Entra ID SCIM provisioning connector is not officially supported by Oracle NetSuite.
  • Okta integration uses a proprietary NetSuite connector (not SCIM) leveraging SuiteTalk SOAP or REST APIs.
  • JIT (Just-In-Time) SAML provisioning is available as an alternative for SSO-based user creation.
  • Third-party middleware (e.g., Workato, Boomi) can bridge SCIM to NetSuite REST/SOAP APIs.

Common scenarios

Provisioning a new employee requires a POST to /employee with at minimum firstName, lastName, email, subsidiary (mandatory on OneWorld accounts), giveAccess=true, and at least one role ID.

The successful response is HTTP 204 with no body; the new record's internal ID must be parsed from the Location response header. Role IDs are account-specific internal integers with no global name-to-ID mapping - resolve them via the role record type before provisioning.

Deprovisioning requires a PATCH to /employee/{id} setting both isInactive=true and giveAccess=false. Setting only isInactive may not fully revoke login access. Hard DELETE of Employee records is blocked by NetSuite when transaction history exists; isInactive=true is the correct deprovisioning pattern. Any TBA tokens tied to the inactivated user are immediately invalidated - downstream integrations using those tokens will fail silently without explicit token rotation handling.

For bulk sync against an identity graph, POST to /query/v1/suiteql with a SELECT against the employee table filtered by isinactive = 'F'. SuiteQL field names are lowercase (firstname, lastname) and do not match REST record API camelCase field names (firstName, lastName) - mapping must be handled explicitly. Maximum LIMIT per SuiteQL query is 1000 rows; paginate via OFFSET and prefer filtering on lastModifiedDate for incremental syncs to avoid full-table scans on large accounts.

Provision a new employee with login access

  1. POST to /employee with firstName, lastName, email, subsidiary, giveAccess=true, and desired role IDs.
  2. Parse the Location header from the 204 response to extract the new employee internal ID.
  3. Optionally PATCH the record with additional fields (department, title, supervisor).
  4. If sendEmail=true was included, the user receives a welcome email with login instructions.

Watch out for: If the subsidiary field is omitted on a OneWorld account, the POST returns a 400 validation error. Always resolve subsidiary ID before provisioning.

Deprovision (offboard) an employee

  1. PATCH /employee/{id} with isInactive=true and giveAccess=false to revoke login and mark inactive.
  2. Optionally remove role assignments by DELETE to /employee/{id}/roles/{roleId} for each assigned role.
  3. Verify via GET /employee/{id} that isInactive=true and giveAccess=false are reflected.

Watch out for: Hard DELETE of Employee records is typically blocked by NetSuite when transaction history exists. isInactive=true is the standard deprovisioning pattern.

Bulk-list active employees for sync

  1. POST to /query/v1/suiteql with query: SELECT id, email, firstname, lastname, isinactive FROM employee WHERE isinactive = 'F' LIMIT 1000 OFFSET 0.
  2. Check hasMore in the response; if true, increment OFFSET by 1000 and repeat.
  3. Map SuiteQL lowercase field names to your system's schema (note: firstname ≠ firstName in REST record API).

Watch out for: SuiteQL has a maximum LIMIT of 1000 per query. For accounts with >1000 employees, pagination via OFFSET is required. Large offsets can be slow; consider filtering by lastModifiedDate for incremental syncs.

Why building this yourself is a trap

The most consequential API caveat is the dual-field deprovisioning requirement: isInactive=true alone does not guarantee login revocation. Systems that set only one flag and assume access is removed will leave accounts partially active. This is not documented prominently and is a common integration defect.

The REST and SuiteQL field name mismatch (firstName vs firstname, boolean true/false vs string 'T'/'F') creates silent data mapping errors in pipelines that treat the two interfaces as interchangeable. Validate field names against the specific API surface being used - they are not equivalent.

Concurrency governance errors do not surface via standard HTTP headers, so off-the-shelf retry libraries that inspect Retry-After or X-RateLimit-* headers will not function correctly. Implement retry logic that catches HTTP 429 and SOAP fault code CONCURRENCY_LIMIT_EXCEEDED explicitly.

For high-volume provisioning pipelines, the SuiteCloud Plus License is a hard dependency - without it, 10 concurrent requests is the ceiling, and bulk operations will hit governance limits quickly.

Automate Netsuite workflows without one-off scripts

Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.

Every app coverage, including apps without APIs
60+ app integrations plus browser automation for apps without APIs
IT graph reconciliation across apps and your IdP
Less than a week to launch, maintained as APIs and admin consoles change
SOC 2 Type II. ~2 hours of your team's time

UpdatedMar 6, 2026

* Details sourced from official product documentation and admin references.

Keep exploring

Related apps

Abnormal Security logo

Abnormal Security

API Only
AutomationAPI only
Last updatedMar 2026

Abnormal Security is an enterprise email security platform focused on detecting and investigating threats such as phishing, account takeover (ATO), and vendor email compromise. It does not support SCIM provisioning, which means every app in your stack

ActiveCampaign logo

ActiveCampaign

API Only
AutomationAPI only
Last updatedFeb 2026

ActiveCampaign uses a group-based permission model: every user belongs to exactly one group, and all feature-area access (Contacts, Campaigns, Automations, Deals, Reports, Templates) is configured at the group level, not per individual. The default Adm

ADP logo

ADP

API Only
AutomationAPI only
Last updatedFeb 2026

ADP Workforce Now is a mid-market to enterprise HCM platform that serves as the HR source of record for employee data — payroll, benefits, time, and talent. User access is governed by a hybrid permission model: predefined security roles (Security Maste