Summary and recommendation
ServiceNow exposes user management through its Table API at `https://<instance>.service-now.com/api/now/table/sys_user`.
Authentication supports OAuth 2.0 (Bearer token via Client Credentials or Authorization Code grant) and HTTP Basic Auth;
OAuth is strongly preferred for production.
Access tokens expire after a configurable window (default 30 minutes) - implement refresh_token grant logic or your integration will fail silently on long-running jobs.
All Table API responses wrap results in a `result` key.
Single-record GETs return an object;
list GETs return an array.
The `active` field is returned as a string (`'true'`/`'false'`), not a boolean - cast explicitly in your code.
Reference fields (e.g., `manager`, `department`) return a nested object with `value` (sys_id) and `link` by default;
use `sysparm_display_value=all` to retrieve both sys_id and human-readable display values in one call.
Pagination uses offset-based parameters (`sysparm_limit` / `sysparm_offset`) with a maximum page size of 10,000.
Large unfiltered queries against sys_user can cause instance timeouts - always apply `sysparm_query` filters and restrict returned fields via `sysparm_fields`.
API quick reference
| Has user API | Yes |
| Auth method | OAuth 2.0 (Bearer token) or Basic Authentication |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise (SCIM framework requires the Identity Provider plugin and is available on Enterprise-tier instances; SSO must be configured as a prerequisite) |
Authentication
Auth method: OAuth 2.0 (Bearer token) or Basic Authentication
Setup steps
- Navigate to System OAuth > Application Registry in your ServiceNow instance.
- Create a new OAuth API endpoint for external clients (Authorization Code or Client Credentials grant).
- Note the Client ID and Client Secret generated.
- For Client Credentials: POST to https://
.service-now.com/oauth_token.do with grant_type=client_credentials, client_id, and client_secret. - Include the returned access_token as 'Authorization: Bearer
' on all API requests. - Alternatively, use HTTP Basic Auth with a service account username and password (less recommended for production).
Required scopes
| Scope | Description | Required for |
|---|---|---|
| useraccount | Grants access to perform operations on behalf of the authenticated user account. | General API access; ServiceNow OAuth scopes are defined per Application Registry entry, not as granular named scopes. |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| sys_id | string (GUID) | Unique system identifier for the user record. | auto-generated | read-only | Primary key for all cross-table references. |
| user_name | string | Login username (unique). | required | writable | Maps to SCIM userName. |
| first_name | string | User's first name. | optional | writable | |
| last_name | string | User's last name. | optional | writable | |
| string | Primary email address. | optional | writable | Used for notifications. | |
| active | boolean | Whether the user account is active. | optional (default: true) | writable | Set to false to deactivate without deleting. |
| title | string | Job title. | optional | writable | |
| department | reference (sys_user_group) | Department the user belongs to. | optional | writable | Pass sys_id of department record. |
| manager | reference (sys_user) | User's manager. | optional | writable | Pass sys_id of manager's user record. |
| phone | string | Business phone number. | optional | writable | |
| mobile_phone | string | Mobile phone number. | optional | writable | |
| time_zone | string | User's time zone (e.g., 'US/Eastern'). | optional | writable | |
| language | string | Preferred language code (e.g., 'en'). | optional | writable | |
| roles | list (via sys_user_has_role) | Roles assigned to the user. | not set directly on user record | managed via sys_user_has_role table | Must POST to sys_user_has_role to assign roles. |
| company | reference (core_company) | Company the user is associated with. | optional | writable | |
| location | reference (cmn_location) | Physical location of the user. | optional | writable | |
| password_needs_reset | boolean | Forces password reset on next login. | optional | writable | |
| locked_out | boolean | Whether the account is locked out. | optional | writable | Set to false to unlock. |
| source | string | Provisioning source identifier. | optional | writable | Useful for tracking SCIM or IdP-provisioned users. |
| sys_created_on | datetime | Record creation timestamp. | auto-generated | read-only |
Core endpoints
List users
- Method: GET
- URL:
https://<instance>.service-now.com/api/now/table/sys_user - Watch out for: Default sysparm_limit is 10,000 but large result sets can cause timeouts. Always filter with sysparm_query and request only needed fields via sysparm_fields.
Request example
GET /api/now/table/sys_user?sysparm_query=active%3Dtrue&sysparm_limit=100&sysparm_offset=0&sysparm_fields=sys_id,user_name,email,active
Authorization: Bearer <token>
Accept: application/json
Response example
{
"result": [
{"sys_id":"abc123","user_name":"jdoe","email":"jdoe@example.com","active":"true"}
]
}
Get user by sys_id
- Method: GET
- URL:
https://<instance>.service-now.com/api/now/table/sys_user/{sys_id} - Watch out for: Reference fields (e.g., manager, department) return both display_value and value (sys_id) when sysparm_display_value=all is set.
Request example
GET /api/now/table/sys_user/abc123
Authorization: Bearer <token>
Accept: application/json
Response example
{
"result": {
"sys_id":"abc123",
"user_name":"jdoe",
"email":"jdoe@example.com",
"active":"true"
}
}
Create user
- Method: POST
- URL:
https://<instance>.service-now.com/api/now/table/sys_user - Watch out for: Creating a user does not assign roles. Role assignment requires a separate POST to sys_user_has_role.
Request example
POST /api/now/table/sys_user
Authorization: Bearer <token>
Content-Type: application/json
{"user_name":"jsmith","first_name":"Jane","last_name":"Smith","email":"jsmith@example.com","active":true}
Response example
{
"result": {
"sys_id":"def456",
"user_name":"jsmith",
"email":"jsmith@example.com",
"active":"true"
}
}
Update user (partial)
- Method: PATCH
- URL:
https://<instance>.service-now.com/api/now/table/sys_user/{sys_id} - Watch out for: PATCH only updates supplied fields. Use PUT to replace the full record (omitted fields may be cleared).
Request example
PATCH /api/now/table/sys_user/def456
Authorization: Bearer <token>
Content-Type: application/json
{"title":"Senior Engineer","phone":"+1-555-0100"}
Response example
{
"result": {
"sys_id":"def456",
"title":"Senior Engineer",
"phone":"+1-555-0100"
}
}
Deactivate user
- Method: PATCH
- URL:
https://<instance>.service-now.com/api/now/table/sys_user/{sys_id} - Watch out for: ServiceNow best practice is to deactivate (active=false) rather than delete users to preserve audit history and related record integrity.
Request example
PATCH /api/now/table/sys_user/def456
Authorization: Bearer <token>
Content-Type: application/json
{"active":false}
Response example
{
"result": {
"sys_id":"def456",
"active":"false"
}
}
Delete user
- Method: DELETE
- URL:
https://<instance>.service-now.com/api/now/table/sys_user/{sys_id} - Watch out for: Deletion is irreversible and may break referential integrity on related records (incidents, changes, etc.). Deactivation is strongly preferred.
Request example
DELETE /api/now/table/sys_user/def456
Authorization: Bearer <token>
Response example
HTTP 204 No Content
Assign role to user
- Method: POST
- URL:
https://<instance>.service-now.com/api/now/table/sys_user_has_role - Watch out for: Role sys_ids must be looked up from sys_user_role table. Role names alone are not accepted; use the sys_id.
Request example
POST /api/now/table/sys_user_has_role
Authorization: Bearer <token>
Content-Type: application/json
{"user":"def456","role":"<role_sys_id>"}
Response example
{
"result": {
"sys_id":"ghi789",
"user":{"value":"def456"},
"role":{"value":"<role_sys_id>"}
}
}
Get user by username (encoded query)
- Method: GET
- URL:
https://<instance>.service-now.com/api/now/table/sys_user?sysparm_query=user_name%3Djsmith&sysparm_limit=1 - Watch out for: sysparm_query uses ServiceNow encoded query syntax. URL-encode operators (= is %3D). Returns an array even for single matches.
Request example
GET /api/now/table/sys_user?sysparm_query=user_name%3Djsmith&sysparm_limit=1
Authorization: Bearer <token>
Accept: application/json
Response example
{
"result": [
{"sys_id":"def456","user_name":"jsmith","email":"jsmith@example.com"}
]
}
Rate limits, pagination, and events
- Rate limits: ServiceNow enforces inbound REST API rate limits configurable per instance via the 'glide.rest.outbound.max_count' and related system properties. Default limits vary by instance sizing and are not publicly published as fixed tiers. Administrators can configure rate limiting via the REST API Rate Limiting plugin.
- Rate-limit headers: No
- Retry-After header: No
- Rate-limit notes: ServiceNow does not publicly document standard rate-limit response headers or Retry-After behavior in official docs. Rate limiting is enforced server-side; HTTP 429 is returned when exceeded. Admins configure limits via System Properties.
- Pagination method: offset
- Default page size: 10000
- Max page size: 10000
- Pagination pointer: sysparm_limit / sysparm_offset
| Plan | Limit | Concurrent |
|---|---|---|
| All plans (instance-configurable) | Configurable; no single published default. Typically governed by instance capacity and admin-set policies. | 0 |
- Webhooks available: Yes
- Webhook notes: ServiceNow supports outbound webhooks via Business Rules and the REST Message framework. Administrators configure Business Rules on sys_user (or other tables) to trigger HTTP calls on insert, update, or delete events. The 'Scripted REST API' and 'Flow Designer' also support outbound webhook-style notifications.
- Alternative event strategy: ServiceNow Event Management and Flow Designer can be used to trigger outbound REST calls on user lifecycle events without custom scripting.
- Webhook events: sys_user.insert, sys_user.update, sys_user.delete, Custom events via Business Rules on any table
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise (SCIM framework requires the Identity Provider plugin and is available on Enterprise-tier instances; SSO must be configured as a prerequisite)
Endpoint: https://
.service-now.com/api/now/scim/v2 Supported operations: GET /Users, GET /Users/{id}, POST /Users, PUT /Users/{id}, PATCH /Users/{id}, DELETE /Users/{id}, GET /Groups, GET /Groups/{id}, POST /Groups, PUT /Groups/{id}, PATCH /Groups/{id}, DELETE /Groups/{id}, GET /ServiceProviderConfig, GET /Schemas
Limitations:
- SSO (SAML or OIDC) must be configured before enabling SCIM provisioning.
- SAML auto-provisioning (JIT) must be disabled when using SCIM to avoid conflicts.
- For Microsoft Entra ID, the gallery application uses a SOAP-based connector, not true SCIM 2.0. Use a non-gallery 'Enterprise Application' with the SCIM endpoint for standards-compliant provisioning.
- SCIM attribute mapping may require customization for non-standard ServiceNow fields.
- Group membership sync behavior depends on IdP configuration and may require explicit mapping.
Common scenarios
Provisioning a new user is a two-step operation: POST to /api/now/table/sys_user to create the record, then POST to /api/now/table/sys_user_has_role to assign roles.
Role assignment requires the role's sys_id (not its name) - look it up first via GET on sys_user_role with an encoded query filter.
These are always separate API calls;
there is no atomic create-and-assign endpoint.
Deactivating a departing employee: resolve the user's sys_id via a GET with sysparm_query=user_name%3D<username>, then PATCH active=false.
Do not issue a DELETE - deletion is irreversible, breaks referential integrity on incidents, changes, and all related records, and cannot be undone through the UI.
Optionally DELETE records from sys_user_has_role to revoke roles, or retain them for audit history.
For IdP-driven sync, ServiceNow exposes a SCIM 2.0 endpoint at https://<instance>.service-now.com/api/now/scim/v2, available on Enterprise-tier instances with SSO configured.
When integrating with Okta, configure OAuth 2.0 Client Credentials in the Application Registry and map profile attributes to SCIM fields (userName → user_name, emails[0].value → email).
Test with a single user before enabling group-based provisioning.
For Microsoft Entra ID specifically, do not use the ServiceNow gallery application - it uses a SOAP-based connector, not SCIM 2.0.
Use a non-gallery Enterprise Application pointing to the SCIM 2.0 endpoint instead.
Provision a new employee and assign a role
- POST to /api/now/table/sys_user with user_name, first_name, last_name, email, active=true to create the user record.
- Capture the sys_id from the response.
- Look up the target role's sys_id via GET /api/now/table/sys_user_role?sysparm_query=name%3Ditil&sysparm_fields=sys_id.
- POST to /api/now/table/sys_user_has_role with {"user": "
", "role": " "} to assign the role.
Watch out for: Role lookup must use sys_id, not role name. Creating the user and assigning roles are always separate API calls.
Deactivate a departing employee
- GET /api/now/table/sys_user?sysparm_query=user_name%3D
&sysparm_fields=sys_id to resolve the user's sys_id. - PATCH /api/now/table/sys_user/
with {"active": false} to deactivate the account. - Optionally, DELETE records from sys_user_has_role for the user to revoke all roles, or leave them for audit purposes.
Watch out for: Do not DELETE the sys_user record; deactivation preserves audit trails and prevents broken references on incidents, changes, and other records.
Sync users via SCIM 2.0 from an IdP (e.g., Okta)
- Ensure SSO is configured on the ServiceNow instance and SAML JIT provisioning is disabled.
- In Okta, add ServiceNow as a SCIM 2.0 application using the base URL https://
.service-now.com/api/now/scim/v2. - Configure OAuth 2.0 Client Credentials in ServiceNow Application Registry and supply the token endpoint to Okta.
- Map Okta profile attributes to SCIM attributes (userName → user_name, emails[0].value → email, etc.).
- Enable provisioning features in Okta: Push New Users, Push Profile Updates, Deactivate Users.
- Test with a single user assignment before enabling group-based provisioning.
Watch out for: For Entra ID specifically, use a non-gallery Enterprise Application-the gallery ServiceNow app uses SOAP-based provisioning, not SCIM 2.0.
Why building this yourself is a trap
The most common API integration failure is assuming user creation also provisions access - it does not. Role assignment is a separate write to sys_user_has_role using sys_ids, and omitting it leaves users with no functional permissions. This is a silent failure: the user record exists, but the account is inert.
SCIM and SAML JIT provisioning are mutually exclusive in practice. Running both simultaneously causes the IdP to re-activate accounts deactivated in ServiceNow on the next sync cycle, undermining offboarding workflows. Disable JIT before enabling SCIM.
For teams building identity graph pipelines or connecting ServiceNow into a broader identity graph via an MCP server with 60+ deep IT/identity integrations, the split between sys_user, sys_user_has_role, and sys_user_grmember (group membership) means a complete identity picture requires joins across at least three tables
a single Table API call to sys_user will not surface effective permissions. Rate limits are instance-configurable and not publicly documented as fixed tiers; HTTP 429 is returned when exceeded, but there are no standard Retry-After headers, so implement exponential backoff defensively.
Automate ServiceNow 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.