Summary and recommendation
The ZIA REST API uses a proprietary session-based authentication model, not OAuth 2.0.
Callers must implement a client-side API key obfuscation algorithm - combining the raw API key with a millisecond-precision Unix timestamp - before POSTing to /api/v1/authenticatedSession.
The response sets a JSESSIONID cookie that must accompany every subsequent request;
there is no bearer token alternative for the native REST API.
The base URL is cloud-instance-specific (e.g., zsapi.zscaler.net vs.
zsapi.zscalerone.net vs.
zsapi.zscalertwo.net).
Using the wrong hostname produces authentication failures with no descriptive error.
Rate limits cap at 1,000 requests/hour per admin session for most endpoints, with write endpoints limited to 400 requests/hour;
HTTP 429 responses carry no Retry-After header, so exponential backoff must be implemented manually.
API quick reference
| Has user API | Yes |
| Auth method | API Key + Session Cookie (ZIA proprietary auth). Caller POSTs credentials and API key to /authenticatedSession to obtain a JSESSIONID cookie used on subsequent requests. |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise |
Authentication
Auth method: API Key + Session Cookie (ZIA proprietary auth). Caller POSTs credentials and API key to /authenticatedSession to obtain a JSESSIONID cookie used on subsequent requests.
Setup steps
- In the ZIA Admin Portal, navigate to Administration > API Key Management and generate an API key (obfuscated key + timestamp are combined to form the auth payload).
- Construct the authentication payload: combine the API key with a Unix timestamp (milliseconds) to produce an obfuscated password per Zscaler's documented algorithm.
- POST to https://
.zscaler.net/api/v1/authenticatedSession with JSON body {"apiKey": " ", "username": " ", "password": " ", "timestamp": }. - Extract the JSESSIONID cookie from the response and include it as a Cookie header on all subsequent API requests.
- Call DELETE /authenticatedSession to log out and invalidate the session when done.
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | integer | Unique user ID assigned by ZIA. | system-assigned | required (in URL) | Read-only after creation. |
| name | string | Full display name of the user. | required | optional | Max 64 characters. |
| string | User's email address; used as login identifier. | required | optional | Must be unique within the tenant. | |
| password | string | User password (write-only). | required | optional | Never returned in GET responses. |
| groups | array[object] | List of group objects {id, name} the user belongs to. | optional | optional | Groups must exist before assignment. |
| department | object | Department object {id, name} the user is assigned to. | optional | optional | Department must exist before assignment. |
| comments | string | Free-text notes about the user. | optional | optional | |
| tempAuthEmail | string | Temporary authentication email for the user. | optional | optional | |
| authMethods | array[string] | Authentication methods enabled for the user (e.g., BASIC, DIGEST). | optional | optional | |
| type | string | User type: SUPERADMIN, ADMIN, AUDITOR, GUEST, REPORT_USER, UNAUTH. | optional | optional | Defaults to standard user if omitted. |
| adminUser | boolean | Indicates if the user has admin privileges. | optional | optional | |
| isNonEditable | boolean | If true, user cannot be edited via the API. | system-assigned | read-only | Set by system for certain built-in accounts. |
| deleted | boolean | Indicates if the user is soft-deleted. | system-assigned | read-only | Use DELETE endpoint to remove users. |
Core endpoints
List Users
- Method: GET
- URL:
/api/v1/users?page=1&pageSize=100&name=<filter> - Watch out for: Pagination is 1-indexed. Omitting pageSize defaults to 100; max is 1000. Filter by name, dept, or group using query params.
Request example
GET /api/v1/users?pageSize=100&page=1
Cookie: JSESSIONID=<session_id>
Response example
[
{
"id": 12345,
"name": "Jane Doe",
"email": "jane@example.com",
"groups": [{"id": 1, "name": "Engineering"}],
"department": {"id": 10, "name": "R&D"}
}
]
Get User by ID
- Method: GET
- URL:
/api/v1/users/{userId} - Watch out for: Returns 404 if user does not exist or has been hard-deleted.
Request example
GET /api/v1/users/12345
Cookie: JSESSIONID=<session_id>
Response example
{
"id": 12345,
"name": "Jane Doe",
"email": "jane@example.com",
"adminUser": false,
"deleted": false
}
Create User
- Method: POST
- URL:
/api/v1/users - Watch out for: Password is required on creation and must meet complexity requirements. Groups and department objects must reference existing IDs.
Request example
POST /api/v1/users
Content-Type: application/json
{"name":"John Smith","email":"john@example.com",
"password":"Str0ng!Pass","groups":[{"id":1}],
"department":{"id":10}}
Response example
{
"id": 12346,
"name": "John Smith",
"email": "john@example.com",
"groups": [{"id": 1, "name": "Engineering"}]
}
Update User
- Method: PUT
- URL:
/api/v1/users/{userId} - Watch out for: ZIA uses full PUT (not PATCH); omitting optional fields may reset them. Always include the user id in the request body matching the URL parameter.
Request example
PUT /api/v1/users/12346
Content-Type: application/json
{"id":12346,"name":"John A. Smith",
"email":"john@example.com","groups":[{"id":2}]}
Response example
{
"id": 12346,
"name": "John A. Smith",
"email": "john@example.com",
"groups": [{"id": 2, "name": "DevOps"}]
}
Delete User
- Method: DELETE
- URL:
/api/v1/users/{userId} - Watch out for: Deletion is permanent (hard delete). There is no soft-delete toggle via this endpoint.
Request example
DELETE /api/v1/users/12346
Cookie: JSESSIONID=<session_id>
Response example
HTTP 204 No Content
Bulk Delete Users
- Method: POST
- URL:
/api/v1/users/bulkDelete - Watch out for: Maximum of 500 IDs per bulk delete request. Exceeding the limit returns an error.
Request example
POST /api/v1/users/bulkDelete
Content-Type: application/json
{"ids": [12345, 12346, 12347]}
Response example
HTTP 204 No Content
Activate Configuration Changes
- Method: POST
- URL:
/api/v1/status/activate - Watch out for: CRITICAL: All create/update/delete operations in ZIA are staged and do not take effect until /status/activate is called. Forgetting this step means changes are not applied to the live policy.
Request example
POST /api/v1/status/activate
Cookie: JSESSIONID=<session_id>
Response example
{
"status": "ACTIVE"
}
Create Authenticated Session
- Method: POST
- URL:
/api/v1/authenticatedSession - Watch out for: The API key obfuscation algorithm is documented by Zscaler and must be implemented client-side. Using the raw API key directly will result in authentication failure.
Request example
POST /api/v1/authenticatedSession
Content-Type: application/json
{"apiKey":"<obfuscated_key>",
"username":"admin@example.com",
"password":"<obfuscated_pw>",
"timestamp":1700000000000}
Response example
HTTP 200 OK
Set-Cookie: JSESSIONID=abc123; Path=/; HttpOnly
{"obfuscateApiKey": true}
Rate limits, pagination, and events
- Rate limits: ZIA enforces per-hour and per-minute API rate limits. Limits vary by endpoint category. Exceeding limits returns HTTP 429.
- Rate-limit headers: No
- Retry-After header: No
- Rate-limit notes: Official docs note that bulk operations (e.g., updating many users) should include delays between requests. Exact per-endpoint limits are documented in the ZIA API rate limits help article. No standard rate-limit response headers are documented.
- Pagination method: offset
- Default page size: 100
- Max page size: 1000
- Pagination pointer: pageSize / page
| Plan | Limit | Concurrent |
|---|---|---|
| ZIA (all tiers) | 1000 requests/hour per admin session; some write endpoints limited to 400 requests/hour | 0 |
- Webhooks available: No
- Webhook notes: Zscaler ZIA does not offer outbound webhooks for user management events. Changes must be polled via the API.
- Alternative event strategy: Use SCIM provisioning from an IdP (Okta, Azure AD) to push user lifecycle events into ZIA automatically, or poll /api/v1/users on a schedule.
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise
Endpoint: https://
.zscaler.net/scim Supported operations: GET /Users, GET /Users/{id}, POST /Users, PUT /Users/{id}, PATCH /Users/{id}, DELETE /Users/{id}, GET /Groups, POST /Groups, PUT /Groups/{id}, PATCH /Groups/{id}, DELETE /Groups/{id}
Limitations:
- SCIM endpoint URL and bearer token are generated within the ZIA Admin Portal under Administration > Authentication > SCIM; the token is shown only once at generation time.
- SSO (SAML) must be configured and enabled before SCIM provisioning can be activated.
- Supported IdPs with documented integration guides: Okta, Azure AD (Entra ID), Ping Identity. Google Workspace and OneLogin are not officially documented.
- SCIM is recommended by Zscaler over SAML Just-In-Time provisioning for full lifecycle management (including deprovisioning).
- SCIM changes in ZIA also require the /status/activate call if made via the native API; however, SCIM-provisioned changes may be applied differently - consult IdP-specific integration guides.
- Available on Enterprise tier; not available on lower ZIA tiers.
Common scenarios
Provisioning a new user requires four discrete API calls: authenticate → verify department/group IDs exist → POST /api/v1/users → POST /api/v1/status/activate.
Skipping the activate call leaves the user staged but non-functional;
they cannot authenticate through ZIA until activation completes.
Deprovisioning via the API issues a hard DELETE with no soft-delete toggle and no recovery path.
For teams building an identity graph across SaaS applications, this means the ZIA user record is permanently destroyed on DELETE - any downstream reconciliation must capture the user object before deletion.
SCIM-based deprovisioning via Okta or Azure AD handles the DELETE and activation automatically, removing the need for manual API orchestration.
The update endpoint uses full PUT semantics;
partial updates are not supported.
Omitting optional fields in a PUT body may silently reset existing values.
Bulk deletion accepts a maximum of 500 user IDs per request to /api/v1/users/bulkDelete.
Provision a new employee in ZIA
- Authenticate: POST /api/v1/authenticatedSession with obfuscated API key and admin credentials to obtain JSESSIONID.
- Verify the target department and group IDs exist: GET /api/v1/departments and GET /api/v1/groups.
- Create the user: POST /api/v1/users with name, email, password, groups array, and department object.
- Activate changes: POST /api/v1/status/activate to push the new user to live policy.
- Log out: DELETE /api/v1/authenticatedSession.
Watch out for: Skipping the /status/activate call means the user is staged but not active. The user will not be able to authenticate through ZIA until activation.
Deprovision a terminated employee
- Authenticate and obtain JSESSIONID.
- Look up the user by email: GET /api/v1/users?name=
to retrieve the user ID. - Delete the user: DELETE /api/v1/users/{userId}.
- Activate changes: POST /api/v1/status/activate.
- Log out.
Watch out for: Deletion via the ZIA API is a hard delete with no recovery. If using SCIM via Okta or Azure AD, deprovisioning the user in the IdP will trigger the DELETE automatically without requiring manual API calls or activation.
Set up SCIM provisioning via Okta
- Ensure SAML SSO is configured and active in ZIA (prerequisite for SCIM).
- In ZIA Admin Portal, go to Administration > Authentication > SCIM and enable SCIM provisioning.
- Copy the generated SCIM endpoint URL and bearer token (token is shown only once - store securely).
- In Okta, open the Zscaler app integration, navigate to the Provisioning tab, and enter the SCIM base URL and bearer token.
- Enable provisioning features: Create Users, Update User Attributes, Deactivate Users.
- Assign users or groups in Okta to trigger initial provisioning to ZIA.
Watch out for: If the SCIM bearer token is lost or regenerated, the Okta integration must be updated with the new token immediately or provisioning will fail silently. SCIM requires Enterprise tier.
Why building this yourself is a trap
The activation requirement is the primary integration trap. Every write operation - create, update, delete - is staged in a pending state. A pipeline that provisions users without calling POST /api/v1/status/activate will appear to succeed (HTTP 200 responses throughout) while producing zero effect on live policy.
This is not surfaced as an error at the write endpoints.
The SCIM bearer token is a secondary trap: it is displayed exactly once at generation time in the Admin Portal. If the token is lost or a new one is regenerated, the existing IdP integration breaks silently - provisioning failures may not surface immediately depending on IdP sync frequency.
Teams integrating ZIA into an identity graph via an MCP server with 60+ deep IT/identity integrations should treat token rotation as a breaking change requiring immediate IdP reconfiguration.
Webhooks are not available in ZIA. User lifecycle events cannot be pushed outbound; all change detection requires polling /api/v1/users on a schedule or relying on SCIM push from a supported IdP.
Automate Zscaler 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.