Summary and recommendation
Zuora's user management API lives under the `/v1` base path (`https://rest.zuora.com/v1`).
Authentication uses OAuth 2.0 client credentials - there are no named OAuth scopes;
access is governed entirely by the Zuora role assigned to the OAuth client's associated user, which must be Administrator-level.
Tokens expire after 3600 seconds by default and must be re-requested using the same client credentials.
Note that the token endpoint (`/oauth/token`) sits outside the `/v1` base path, and sandbox environments use a separate base URL (`rest.apisandbox.zuora.com`).
Zuora also exposes a SCIM 2.0 interface for identity graph synchronization, but it is mediated entirely through an IdP (Okta or Azure AD) and is not directly callable outside that context.
API quick reference
| Has user API | Yes |
| Auth method | OAuth 2.0 (client credentials grant); legacy basic auth (deprecated) |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise (requires Zuora OneID and SSO enablement) |
Authentication
Auth method: OAuth 2.0 (client credentials grant); legacy basic auth (deprecated)
Setup steps
- Log in to Zuora as an administrator and navigate to Settings > Security > Manage OAuth Clients.
- Create an OAuth client to obtain a client_id and client_secret.
- POST to https://rest.zuora.com/oauth/token with grant_type=client_credentials, client_id, and client_secret to receive a bearer access token.
- Include the token in all API requests as Authorization: Bearer
. - Tokens expire; re-request as needed using the same client credentials.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| N/A – Zuora OAuth 2.0 client credentials do not use named scopes | Access is governed by the Zuora user role assigned to the OAuth client, not by OAuth scope strings. | All API operations |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | string | Unique Zuora internal user ID | system-generated | read-only | Used as path parameter for user-specific operations |
| username | string | Login username (typically email) | required | optional | Must be unique within the tenant |
| string | User's email address | required | optional | Used for notifications and login | |
| firstName | string | User's first name | required | optional | |
| lastName | string | User's last name | required | optional | |
| status | string | Account status: Active or Inactive | defaults to Active | optional | Set to Inactive to deactivate without deletion |
| profileId | string | ID of the user profile (role) assigned to the user | required | optional | Controls permissions within Zuora |
| userType | string | Type of user: Regular, Administrator, etc. | optional | optional | |
| tenantId | string | Tenant the user belongs to | system-assigned | read-only | |
| phoneNumber | string | User's phone number | optional | optional | |
| title | string | User's job title | optional | optional | |
| department | string | User's department | optional | optional | |
| locale | string | User's locale preference (e.g., en_US) | optional | optional | |
| timezone | string | User's timezone (e.g., America/Los_Angeles) | optional | optional | |
| createdDate | datetime | Timestamp when the user was created | system-generated | read-only | ISO 8601 format |
| updatedDate | datetime | Timestamp of last update | system-generated | system-generated | ISO 8601 format |
Core endpoints
List Users
- Method: GET
- URL:
https://rest.zuora.com/v1/users - Watch out for: Returns only users within the authenticated tenant. pageSize max is 40.
Request example
GET /v1/users?pageSize=20&page=1
Authorization: Bearer <token>
Response example
{
"users": [
{"id":"u-001","username":"jane@example.com","status":"Active"}
],
"nextPage": "https://rest.zuora.com/v1/users?page=2"
}
Get User by ID
- Method: GET
- URL:
https://rest.zuora.com/v1/users/{userId} - Watch out for: Returns 404 if the userId does not exist in the current tenant.
Request example
GET /v1/users/u-001
Authorization: Bearer <token>
Response example
{
"id": "u-001",
"username": "jane@example.com",
"firstName": "Jane",
"lastName": "Doe",
"status": "Active",
"profileId": "p-admin"
}
Create User
- Method: POST
- URL:
https://rest.zuora.com/v1/users - Watch out for: profileId must reference a valid existing profile; otherwise returns 400.
Request example
POST /v1/users
Content-Type: application/json
{
"username":"john@example.com",
"firstName":"John",
"lastName":"Smith",
"email":"john@example.com",
"profileId":"p-readonly"
}
Response example
{
"id": "u-002",
"username": "john@example.com",
"status": "Active"
}
Update User
- Method: PUT
- URL:
https://rest.zuora.com/v1/users/{userId} - Watch out for: Zuora uses PUT (full or partial update accepted) rather than PATCH for this endpoint.
Request example
PUT /v1/users/u-002
Content-Type: application/json
{
"firstName":"Jonathan",
"status":"Inactive"
}
Response example
{
"id": "u-002",
"firstName": "Jonathan",
"status": "Inactive"
}
Delete User
- Method: DELETE
- URL:
https://rest.zuora.com/v1/users/{userId} - Watch out for: Deleting a user is irreversible. Consider setting status=Inactive instead to preserve audit history.
Request example
DELETE /v1/users/u-002
Authorization: Bearer <token>
Response example
{
"success": true
}
Send Password Reset Email
- Method: POST
- URL:
https://rest.zuora.com/v1/users/{userId}/password - Watch out for: Only triggers email-based reset; does not return or set a password directly via API.
Request example
POST /v1/users/u-001/password
Content-Type: application/json
{
"sendResetEmail": true
}
Response example
{
"success": true
}
Get User Profiles (Roles)
- Method: GET
- URL:
https://rest.zuora.com/v1/user-profiles - Watch out for: Profile IDs are required when creating or updating users; retrieve them from this endpoint first.
Request example
GET /v1/user-profiles
Authorization: Bearer <token>
Response example
{
"profiles": [
{"id":"p-admin","name":"Administrator"},
{"id":"p-readonly","name":"Read Only"}
]
}
Get OAuth Token
- Method: POST
- URL:
https://rest.zuora.com/oauth/token - Watch out for: Token endpoint is outside the /v1 base path. Tokens expire in 3600 seconds by default.
Request example
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=<id>&client_secret=<secret>
Response example
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 3600
}
Rate limits, pagination, and events
- Rate limits: Zuora enforces concurrent request limits and per-minute request limits that vary by tenant configuration and plan. Specific numeric limits are not publicly documented in a single canonical table.
- Rate-limit headers: No
- Retry-After header: No
- Rate-limit notes: Zuora returns HTTP 429 when limits are exceeded. Official docs recommend exponential back-off. Exact per-plan limits are not published in public documentation.
- Pagination method: offset
- Default page size: 20
- Max page size: 40
- Pagination pointer: pageSize / page
| Plan | Limit | Concurrent |
|---|---|---|
| Enterprise (general) | Not publicly specified; governed by tenant contract | 0 |
- Webhooks available: Yes
- Webhook notes: Zuora supports event-based notifications via Callout (webhook) notifications configured in the Zuora UI or via the Notifications API. These fire on billing and subscription events, not specifically on user lifecycle events.
- Alternative event strategy: For user provisioning/deprovisioning events, use SCIM 2.0 push provisioning via Okta or Azure AD rather than Zuora-native webhooks.
- Webhook events: Invoice Posted, Payment Processed, Subscription Created, Subscription Amended, Account Created
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise (requires Zuora OneID and SSO enablement)
Endpoint: Tenant-specific; provisioned via IdP connector (Okta or Azure AD). No single canonical public base URL - the SCIM endpoint is configured within the IdP application settings for Zuora.
Supported operations: Create User (POST /Users), Read User (GET /Users/{id}), Update User (PUT /Users/{id}), Deactivate User (PATCH /Users/{id} with active=false), List Users (GET /Users), Push Groups (GET/POST /Groups)
Limitations:
- SCIM provisioning requires Zuora OneID (SSO) to be enabled first; SSO is an Enterprise-tier feature.
- Officially documented IdP integrations are Okta and Azure AD only; other IdPs are not officially supported.
- The SCIM base URL is not a single public endpoint - it is generated per-tenant within the IdP app configuration.
- Group push support may be limited depending on IdP connector version.
- Password sync is not supported via SCIM; authentication is handled by the IdP.
Common scenarios
Three integration patterns cover the majority of lifecycle automation needs.
First, provisioning a new user: POST to /oauth/token for a bearer token, GET /v1/user-profiles to retrieve valid profileId values (do not hardcode these
they are tenant-specific), then POST /v1/users with username, email, firstName, lastName, and profileId.
A missing or invalid profileId returns a 400 with no user created.
Second, deactivating a departed employee: paginate GET /v1/users (max pageSize of 40) to locate the user by email or username, then PUT /v1/users/{userId} with {"status": "Inactive"}.
Prefer this over DELETE - the DELETE endpoint is irreversible and eliminates audit history.
Third, for identity graph synchronization at scale, SCIM 2.0 via Okta or Azure AD supports Create, Read, Update, Deactivate, List Users, and Group Push.
The SCIM base URL and bearer token are tenant-specific, generated within Zuora OneID settings, and are entirely separate from the REST API OAuth credentials
SSO must be fully configured before SCIM provisioning will function.
Provision a new internal user with a specific role
- POST /oauth/token to obtain a bearer token using client credentials.
- GET /v1/user-profiles to retrieve available profile IDs and identify the correct role.
- POST /v1/users with username, email, firstName, lastName, and the target profileId.
- Confirm the response returns status=Active and store the returned user id.
Watch out for: If profileId is omitted or invalid, the API returns 400. Always pre-fetch profile IDs rather than hardcoding them.
Deactivate a departed employee
- GET /v1/users?pageSize=40 (paginate as needed) to find the user by username or email.
- Note the user's id from the response.
- PUT /v1/users/{userId} with body {"status": "Inactive"} to deactivate the account.
- Verify the response reflects status=Inactive.
Watch out for: Prefer deactivation over DELETE to retain audit history. Reactivation is possible by setting status back to Active.
Set up automated provisioning via Okta SCIM
- Ensure the Zuora tenant is on the Enterprise plan with Zuora OneID (SSO) enabled.
- In Zuora admin settings, enable SAML 2.0 SSO and configure Okta as the IdP.
- In Okta, add the Zuora application from the Okta Integration Network and enable SCIM provisioning.
- Copy the SCIM base URL and bearer token from the Zuora OneID SCIM settings into the Okta provisioning configuration.
- Enable Create Users, Update User Attributes, and Deactivate Users in Okta's provisioning tab.
- Assign users or groups in Okta to trigger SCIM push to Zuora.
Watch out for: The SCIM endpoint and token are tenant-specific and generated within Zuora OneID settings - they are not the same as the REST API OAuth token. SSO must be fully configured before SCIM provisioning will function.
Why building this yourself is a trap
Several non-obvious constraints will surface in production integrations. Rate limits are real but not publicly documented in a canonical table; Zuora returns HTTP 429 on breach and recommends exponential back-off, but exact per-tenant limits require a support inquiry.
The /v1/users endpoint may return 404 on legacy tenants - confirm availability before building against it. OAuth clients are created per-tenant in the Zuora UI with no self-service developer portal, which complicates credential rotation in automated pipelines.
When a user is deactivated via API or UI, any OAuth clients owned by that user are not automatically invalidated - orphaned credentials remain active and can silently sustain integrations or create a security exposure.
For teams building an identity graph across systems, SCIM 2.0 is the more reliable synchronization path, but it is gated behind the Enterprise plan, Zuora OneID enablement, and a supported IdP; direct SCIM calls outside an IdP context are not documented and should not be assumed to work.
Automate Zuora 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.