Summary and recommendation
Zoom's REST API is versioned at https://api.zoom.us/v2 and covers the full user lifecycle: create, read, update, deactivate, and delete. JWT authentication was deprecated in June 2023 and is no longer supported; all integrations must use OAuth 2.0, specifically a Server-to-Server OAuth app for backend automation.
The token exchange is a POST to https://zoom.us/oauth/token with grant_type=account_credentials and a Basic auth header carrying base64-encoded clientId:clientSecret - the returned Bearer token is used on all subsequent calls.
Core user management scopes are user:read:admin (list and read) and user:write:admin (create, update, delete, status changes). Email updates and status changes are separate PUT endpoints - PATCH /users/{userId} cannot modify email or status, a common source of silent failures in provisioning pipelines.
User IDs are UUIDs or email strings in most endpoints; UUIDs are preferred to avoid breakage after email changes, which is relevant when building an identity graph that maps Zoom identities to canonical user records across systems.
API quick reference
| Has user API | Yes |
| Auth method | OAuth 2.0 – Server-to-Server OAuth app (for backend/automated use) or Account-level OAuth app. JWT authentication was deprecated June 2023 and is no longer supported. |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Business or Enterprise (SSO must be configured) |
Authentication
Auth method: OAuth 2.0 – Server-to-Server OAuth app (for backend/automated use) or Account-level OAuth app. JWT authentication was deprecated June 2023 and is no longer supported.
Setup steps
- Log in to the Zoom App Marketplace (marketplace.zoom.us).
- Create a 'Server-to-Server OAuth' app for automated/backend use.
- Add required user-management scopes (e.g., user:read:admin, user:write:admin).
- Activate the app and note the Account ID, Client ID, and Client Secret.
- Exchange credentials for an access token: POST https://zoom.us/oauth/token?grant_type=account_credentials&account_id={accountId} with Basic auth header (base64-encoded clientId:clientSecret).
- Use the returned access_token as a Bearer token in the Authorization header for all API calls.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| user:read:admin | Read user details and list users across the account. | List Users, Get User, Get User Settings |
| user:write:admin | Create, update, and delete users on the account. | Create User, Update User, Delete User, Update User Status, Update User Email |
| user:master | Manage users on sub-accounts (Master Account only). | Cross-account user management |
| user_zak:read | Retrieve a user's ZAK (Zoom Access Key) token. | Get User ZAK |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | string | Unique Zoom user ID (UUID). | auto-generated | immutable | Use 'me' as alias for the authenticated user. |
| string | User's email address. | required | via PUT /users/{userId}/email only | Must match a verified domain on paid plans. Cannot be changed via PATCH /users/{userId}. | |
| first_name | string | User's first name. | optional | supported | Max 64 chars. |
| last_name | string | User's last name. | optional | supported | Max 64 chars. |
| display_name | string | Display name shown in meetings. | optional | supported | Defaults to first_name + last_name if not set. |
| type | integer | License type: 1=Basic, 2=Licensed, 3=On-prem. | required | supported | Changing type consumes or releases a license seat. |
| role_id | string | Role assigned to the user. | optional | supported | Use GET /roles to list available role IDs. |
| status | string | Account status: active, inactive, pending. | auto (pending until invite accepted, or active for autoCreate) | via PUT /users/{userId}/status only | Cannot be set directly in the create or update body. |
| pmi | integer | Personal Meeting ID (10-digit). | auto-generated | supported | Must be unique across the account. |
| timezone | string | User's timezone in IANA format. | optional | supported | e.g., 'America/New_York'. |
| dept | string | Department the user belongs to. | optional | supported | Free-form string. |
| job_title | string | User's job title. | optional | supported | Max 128 chars. |
| location | string | User's location. | optional | supported | Free-form string. |
| phone_number | string | User's phone number. | optional | supported | Full functionality requires Zoom Phone add-on. |
| group_ids | array | Groups the user belongs to. | not directly settable | via POST /groups/{groupId}/members | Must be managed through the Groups API, not the user create/update body. |
| vanity_name | string | Personal meeting room name. | optional | supported | Must be unique across the account. |
| host_key | string | 6-digit host key for claiming host in meetings. | auto-generated | supported | Only returned with user:read:admin scope. |
| use_pmi | boolean | Whether to use PMI for instant meetings. | optional | supported | Default false. |
| created_at | string (ISO 8601) | Timestamp when the user was created. | auto-generated | immutable | Read-only. |
| last_login_time | string (ISO 8601) | Timestamp of the user's last login. | N/A | immutable | Read-only. |
Core endpoints
List Users
- Method: GET
- URL:
https://api.zoom.us/v2/users - Watch out for: next_page_token expires after 15 minutes. Max page_size is 300. Rate limit category is 'Medium' (20 req/s), not 'Light'.
Request example
GET /v2/users?status=active&page_size=300&next_page_token=abc123
Authorization: Bearer {token}
Response example
{
"total_records": 450,
"next_page_token": "xyz789",
"users": [
{"id":"abc","email":"user@example.com","type":2,"status":"active"}
]
}
Get User
- Method: GET
- URL:
https://api.zoom.us/v2/users/{userId} - Watch out for: Use 'me' as userId to get the token owner's profile. Fields like host_key only appear with admin scope.
Request example
GET /v2/users/abc123
Authorization: Bearer {token}
Response example
{
"id": "abc123",
"email": "user@example.com",
"first_name": "Jane",
"last_name": "Doe",
"type": 2,
"status": "active"
}
Create User
- Method: POST
- URL:
https://api.zoom.us/v2/users - Watch out for: action controls flow: 'create' sends invite email (user stays pending); 'autoCreate' skips email but requires SSO; 'custCreate' is API-managed with no email; 'ssoCreate' requires SSO. Wrong action is a common integration error.
Request example
POST /v2/users
{
"action": "autoCreate",
"user_info": {
"email": "new@example.com",
"type": 2,
"first_name": "Jane",
"last_name": "Doe"
}
}
Response example
{
"id": "newUserId",
"email": "new@example.com",
"type": 2
}
Update User
- Method: PATCH
- URL:
https://api.zoom.us/v2/users/{userId} - Watch out for: Returns 204 with no body on success. Email cannot be changed here; use PUT /users/{userId}/email. Status cannot be changed here; use PUT /users/{userId}/status.
Request example
PATCH /v2/users/abc123
{
"first_name": "Janet",
"dept": "Engineering",
"timezone": "America/Chicago"
}
Response example
HTTP 204 No Content
Delete User
- Method: DELETE
- URL:
https://api.zoom.us/v2/users/{userId} - Watch out for: Default action is 'disassociate' (removes from account, Zoom account persists). Use action=delete for permanent removal. transfer_email reassigns meetings/webinars before deletion.
Request example
DELETE /v2/users/abc123?action=delete&transfer_email=manager@example.com
Authorization: Bearer {token}
Response example
HTTP 204 No Content
Update User Status (Activate/Deactivate)
- Method: PUT
- URL:
https://api.zoom.us/v2/users/{userId}/status - Watch out for: action must be 'activate' or 'deactivate'. Deactivated users cannot sign in but retain data and still consume a license seat.
Request example
PUT /v2/users/abc123/status
{
"action": "deactivate"
}
Response example
HTTP 204 No Content
Update User Email
- Method: PUT
- URL:
https://api.zoom.us/v2/users/{userId}/email - Watch out for: New email must not already exist in Zoom. User receives a confirmation email. SSO users may need a separate IdP update.
Request example
PUT /v2/users/abc123/email
{
"email": "newemail@example.com"
}
Response example
HTTP 204 No Content
Get/Update User Settings
- Method: GET
- URL:
https://api.zoom.us/v2/users/{userId}/settings - Watch out for: Settings are nested by category. Use PATCH /users/{userId}/settings to update. Some settings require specific add-ons or plan levels to take effect.
Request example
GET /v2/users/abc123/settings
Authorization: Bearer {token}
Response example
{
"schedule_meeting": {"host_video": true},
"recording": {"local_recording": false},
"feature": {"meeting_capacity": 100}
}
Rate limits, pagination, and events
- Rate limits: Zoom enforces per-second rate limits per API category. User endpoints fall under 'Light' or 'Medium' categories. Daily caps also apply and vary by plan.
- Rate-limit headers: Yes
- Retry-After header: Yes
- Rate-limit notes: Response headers include X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Category, and Retry-After (on HTTP 429). List Users is categorized as 'Medium' (20 req/s). Exceeding limits returns HTTP 429.
- Pagination method: token
- Default page size: 30
- Max page size: 300
- Pagination pointer: next_page_token
| Plan | Limit | Concurrent |
|---|---|---|
| Free / Pro | Light endpoints: 30 req/s per account; Medium endpoints (e.g., List Users): 20 req/s per account | 0 |
| Business / Enterprise | Light endpoints: 30 req/s per account; Medium endpoints: 20 req/s per account; higher daily caps | 0 |
- Webhooks available: Yes
- Webhook notes: Zoom supports webhooks via a Webhook-only app or OAuth app configured in the Marketplace. Subscribe to user-related events to receive real-time HTTP POST notifications to a configured endpoint URL. Endpoint must respond with HTTP 200 within a timeout window.
- Alternative event strategy: Poll GET /v2/users with status filters if webhooks are not feasible. Zoom Dashboard API provides activity data for audit purposes.
- Webhook events: user.created, user.updated, user.deleted, user.deactivated, user.activated, user.invitation_accepted, user.signed_in, user.signed_out, user.presence_status_updated
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Business or Enterprise (SSO must be configured)
Endpoint: https://api.zoom.us/scim2
Supported operations: GET /Users (list users), GET /Users/{id} (get user), POST /Users (create user), PUT /Users/{id} (replace user), PATCH /Users/{id} (update user), DELETE /Users/{id} (delete/deprovision user), GET /Groups (list groups), POST /Groups (create group), PATCH /Groups/{id} (update group members), DELETE /Groups/{id} (delete group)
Limitations:
- Bearer token authentication no longer supported; OAuth 2.0 Authorization Code Grant is required.
- Requires Business plan or higher with SSO enabled.
- Email domain must be associated with the Zoom account.
- Users must have SSO login type configured for SSO-based provisioning.
- Officially supported IdPs: Okta, Azure AD (Entra ID), OneLogin. Google Workspace is not natively supported.
- AD Sync Tool available for on-premises Active Directory scenarios.
- SCIM endpoint path is /scim2 (not /scim); older documentation may reference the deprecated /scim path.
Common scenarios
Three scenarios cover the majority of production integration patterns.
For SSO-based provisioning with no invite email, POST /v2/users with action='autoCreate' and type=2. This requires SSO to be configured and the email domain verified on the account; without those prerequisites, the call fails silently or falls back to pending status. Use action='create' if SSO is not in place - it sends an invite email and leaves the user in pending status until accepted.
For offboarding, the correct sequence is: deactivate immediately via PUT /v2/users/{userId}/status with action='deactivate', then downgrade the license type to 1 (Basic) via PATCH /v2/users/{userId} to free the seat, then permanently delete via DELETE /v2/users/{userId}?action=delete&transfer_email={manager} to reassign meetings and recordings before removal. Skipping the type downgrade before deletion still frees the seat, but the transfer_email param on DELETE is the only mechanism to preserve scheduled meetings and cloud recordings - omitting it results in unrecoverable data loss.
For bulk directory sync, GET /v2/users?status=active&page_size=300 with next_page_token pagination. The token expires after 15 minutes; for large accounts, complete pagination in a single run or implement retry-from-page-one logic. Pair the initial full load with webhooks (user.created, user.updated, user.deleted) to keep the identity graph current incrementally without repeated full scans.
Provision a new licensed user via SSO (no invite email)
- Obtain an OAuth 2.0 access token: POST https://zoom.us/oauth/token?grant_type=account_credentials&account_id={accountId} with Basic auth (base64 clientId:clientSecret).
- POST /v2/users with action='autoCreate', user_info.type=2, and the user's SSO-linked email.
- Verify the user is active: GET /v2/users/{userId} and confirm status='active'.
- Optionally assign to a group: POST /v2/groups/{groupId}/members with the new user's ID.
- Optionally configure user settings: PATCH /v2/users/{userId}/settings.
Watch out for: autoCreate requires SSO to be configured on the account and the email domain to be verified. Without SSO, use action='create', which sends an invite email and leaves the user in 'pending' status until accepted.
Offboard a user when they leave the organization
- Look up the user by email: GET /v2/users/{email} (email can be used as userId).
- Deactivate the user immediately: PUT /v2/users/{userId}/status with body {"action": "deactivate"}.
- Downgrade license to Basic to free the seat: PATCH /v2/users/{userId} with {"type": 1}.
- Transfer meetings and recordings: DELETE /v2/users/{userId}?action=delete&transfer_email=manager@example.com for permanent removal with data transfer.
Watch out for: Deactivation alone does not free a license seat. The transfer_email query param on DELETE reassigns meetings and webinars to another user before permanent deletion. Deleted users cannot be recovered.
Bulk-list all active users and sync to an external directory
- GET /v2/users?status=active&page_size=300 to fetch the first page.
- Check response for next_page_token; if present, repeat GET with &next_page_token={token}.
- Continue until next_page_token is absent or empty; use total_records for progress tracking.
- Map Zoom user fields (id, email, first_name, last_name, type, dept, role_id) to the external directory schema.
- Subscribe to user.created, user.updated, and user.deleted webhooks to keep the sync incremental after the initial load.
Watch out for: next_page_token expires in 15 minutes. For large accounts, complete pagination quickly or implement retry-from-start logic. List Users is rate-limited at 20 req/s (Medium category).
Why building this yourself is a trap
The most consequential API trap is the action parameter on POST /v2/users. Four values exist - create, autoCreate, custCreate, ssoCreate - each with different email, SSO, and activation behaviors.
Choosing the wrong action produces a user in an unexpected state (pending instead of active, or active without SSO linkage) that is not immediately visible in the API response, only in a subsequent GET. This is the leading cause of provisioning pipeline failures in Zoom integrations.
The DELETE endpoint defaults to disassociate, not permanent deletion. Without action=delete, the user is removed from the account but their Zoom identity persists, which creates orphaned records in any identity graph that relies on Zoom user IDs as a join key.
Pass action=delete explicitly for permanent removal, and always supply transfer_email to avoid silent recording loss.
Rate limits require attention at scale: List Users is categorized as Medium (20 req/s), not Light (30 req/s). The response headers X-RateLimit-Remaining and Retry-After are present on 429 responses and should drive backoff logic.
SCIM is available at https://api.zoom.us/scim2 (note: scim2, not scim) on Business plan or higher with SSO enabled; Bearer token auth to the SCIM endpoint is no longer supported and must be replaced with OAuth 2.0 Authorization Code Grant.
Automate Zoom 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.