OAuth 2.0

OAuth 2.0 lets your app securely access Zoom APIs for users or accounts. Zoom supports industry-standard OAuth 2.0 (rfc6749) flows to handle authorization and access tokens.

Your app can request, refresh, and revoke tokens based on its type and authorization method. Tokens expire after a set time and may need renewal or refresh.

Note

Zoom API library for Rivet handles OAuth for you. For more information, go to Zoom RivetJavascriptGet startedInitialize the client.

Request an access token

Zoom offers OAuth 2.0 flows and grant types for these four authorization use cases.

App Type: Server to Server

Get an access_token to call APIs for your own Zoom account.

To request an access token, send a POST request to https://zoom.us/oauth/token. Include a basic authorization header and provide the following required parameters either as query parameters or in the request body using the x-www-form-urlencoded format.

KeyValue
grant_typeaccount_credentials
account_idYour Zoom account ID, found on the Server to Server app build page.

The basic authorization header is your Client ID and Client Secret with a colon : in between, Base64 Encoded.

Example request

POST https://zoom.us/oauth/token?grant_type=account_credentials&account_id=Wk9PTV9BQ0NPVU5UX0lE

Request headers

{ "Authorization": "Basic Wk9PTV9DTElFTlRfSUQ6Wk9PTV9DTElFTlRfU0VDUkVU" }

If successful, the response body is a JSON representation of the access_token.

{
    "access_token": "<JWT_TOKEN>",
    "token_type": "bearer",
    "expires_in": 3600,
    "scope": "user:read:user:admin",
    "api_url": "https://api.zoom.us"
}

Access tokens expire after one hour. After your access token expires, you can request a new one. Zoom does not require a separate refresh flow.

App Type: General

Get an access_token to call APIs on behalf of a Zoom user or Zoom account.

Get a user authorization code

You can get a user authorization code by adding your app to your Zoom account. To do this, navigate to the Local Test page, and choose Add App Now.

Alternatively, construct the authorization URL using the following format:

https://zoom.us/oauth/authorize?response_type=code&client_id=ZOOM_CLIENT_ID&redirect_uri=REDIRECT_URI

See section 4.1.1 of The OAuth 2.0 Authorization Framework RFC for details.

User authorization flow

  1. When the user visits this URL, Zoom prompts them to sign in (if not already signed in) and to grant the requested permissions.
  2. After the user authorizes the app, Zoom redirects the user’s browser to the specified redirect_uri and includes an authorization code. For example, https://example.com/callback?code=AUTH_CODE.
  3. Your application should capture this authorization code from the redirect request and exchange it for an access_token using the OAuth token endpoint.
  4. If this is the user's first time authorizing your app, or if you've updated scopes or webhook settings, Zoom will prompt the user to authorize again before issuing a new authorization code.

Exchange authorization code for access token

Once you have the authorization code from the user in the redirect URI, request an access token by sending a POST request to https://zoom.us/oauth/token with a Basic Authorization header.

Include the following required parameters either as query parameters or in the request body using the x-www-form-urlencoded format.

KeyValue
grant_typeauthorization_code
codeThe code in the redirect URI after a successful user authorization.
redirect_uriYour redirect URI.
state(Optional) A string used to pass state between the request and callback. This can help prevent cross-site request forgery.

The Basic Authorization header is your Client ID and Client Secret by a colon (:) and Base64 encoded. Use PKCE to enable user authorization with a public client ID an no client secret, without the need for a backend server. See Using Proof Key for Code Exchange (PKCE) for details.

Example request

POST https://zoom.us/oauth/token?grant_type=authorization_code&code=Wk9PTV9BVVRIT1JJWkFUSU9OX0NPREU&redirect_uri=https://example.com

Request headers

{
    "Authorization": "Basic Wk9PTV9DTElFTlRfSUQ6Wk9PTV9DTElFTlRfU0VDUkVU"
}

If successful, the response body contains a JSON representation of the access_token.

{
    "access_token": "<JWT_TOKEN>",
    "token_type": "bearer",
    "refresh_token": "<JWT_TOKEN>",
    "expires_in": 3600,
    "scope": "user:read:user",
    "api_url": "https://api.zoom.us"
}

Access tokens expire after one hour. After your access token expires, you can follow the refresh flow to get a new one.

App Type: General

Get an access_token from a device flow to call APIs on behalf of a Zoom user or Zoom account.

In your app build flow settings, navigate to Features > Embed > Enable Meeting SDK > Enable Use App on Device.

To request a device code, user code, and verification URI, make a POST request with a basic authorization header to:

https://zoom.us/oauth/devicecode?client_id=ZOOM_CLIENT_ID

The basic authorization header is your Client ID and Client Secret with a colon : in between, Base64 Encoded.

Example request

POST https://zoom.us/oauth/devicecode?client_id=Wk9PTV9DTElFTlRfSUQ

If successful, the response body is a JSON representation of the device code, user code, and verification URI.

{
    "device_code": "Wk9PTV9ERVZJQ0VfQ09ERQ",
    "user_code": "abcd1234",
    "verification_uri": "https://zoom.us/oauth_device",
    "verification_uri_complete": "https://zoom.us/oauth/device/complete/Wk9PTV9WRVJJRklDQVRJT05fVVJJX0NPTVBMRVRF",
    "expires_in": 900,
    "interval": 5
}

Ask the user to navigate to the verification URI and display the user code. Alternatively, direct the user to the complete verification URI, which contains prefilled user code. Prompt the user to sign in if the user is not signed in to Zoom.


After entering the code, the user receives a prompt to allow the app. Once allowed, the app's landing page appears. The user receives a notification that the process can continue on the device.

You can now request an access token.


To request an access token, send a POST request to https://zoom.us/oauth/token with a basic authorization header. Include the following required parameters either as query parameters or in the request body using the x-www-form-urlencoded format.

KeyValue
grant_typeurn:ietf:params:oauth:grant-type:device_code
device_codeThe device_code returned in the previous step.

The basic authorization header is your Client ID and Client Secret with a colon : in between, Base64 Encoded.

Example request

POST https://zoom.us/oauth/token?grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=Wk9PTV9ERVZJQ0VfQ09ERQ

Request headers

{ "Authorization": "Basic Wk9PTV9DTElFTlRfSUQ6Wk9PTV9DTElFTlRfU0VDUkVU" }

If successful, the response body is a JSON representation of the access_token.

{
    "access_token": "<JWT_TOKEN>",
    "token_type": "bearer",
    "refresh_token": "<JWT_TOKEN>",
    "expires_in": 3599,
    "scope": "user:read:user user:read:token",
    "api_url": "https://api.zoom.us"
}

Access tokens expire after one hour. After your access token expires, you can follow the refresh flow to get a new one.

App Type: General

Get an access_token to send, edit, and delete Chatbot messages.

To install your Chatbot to your Zoom account, navigate to the Local Test page, and choose Add App Now.

You can also construct and navigate to the authorization URL yourself following this format:

https://zoom.us/oauth/authorize?response_type=code&client_id=ZOOM_CLIENT_ID&redirect_uri=ZOOM_REDIRECT_URI

If this is the first time you are authorizing your app or if you have made webhook or scope changes, Zoom prompts you to authorize.


If authorized, you redirect to the redirect_uri with the authorization code in the code query parameter. You can use the code in the redirect URL for User Authorization OAuth to call additional API endpoints. If you only call Chatbot API endpoints, you do not need it.

https://example.com/?code=Wk9PTV9BVVRIT1JJWkFUSU9OX0NPREU

To request a chatbot token, send a POST request to https://zoom.us/oauth/token with a basic authorization header. Include the following query parameters or provide them in the request body using the x-www-form-urlencoded format.

KeyValue
grant_typeclient_credentials

The basic authorization header is your Client ID and Client Secret with a colon : in between, Base64 Encoded.

Example request

POST https://zoom.us/oauth/token?grant_type=client_credentials

Request headers

{ "Authorization": "Basic Wk9PTV9DTElFTlRfSUQ6Wk9PTV9DTElFTlRfU0VDUkVU" }

If successful, the response body is a JSON representation of the Chatbot token.

{
    "access_token": "<JWT_TOKEN>",
    "token_type": "bearer",
    "expires_in": 3600,
    "scope": "imchat:bot",
    "api_url": "https://api.zoom.us"
}

Chatbot tokens expire after one hour. After your chatbot token expires, you can request a new one. Zoom does not require a separate refresh flow.

Refresh an access token

Here are the ways to refresh access tokens for these four authorization use cases.

App Type: Server to Server

Access tokens expire after one hour. After your access token expires, you can request a new one. Zoom does not require a separate refresh flow.

App Type: General

Access tokens expire after one hour. After your access token expires, you can follow the refresh flow to get a new one.

To refresh an access token, send a POST request to https://zoom.us/oauth/token with a basic authorization header. Include the following query parameters or provide them in the request body using the x-www-form-urlencodedformat.

KeyValue
grant_typerefresh_token
refresh_tokenYour refresh_token.

The basic authorization header is your Client ID and Client Secret with a colon : in between, Base64 Encoded.

Example request

POST https://zoom.us/oauth/token?grant_type=refresh_token&refresh_token=<JWT_TOKEN>

Request headers

{ "Authorization": "Basic Wk9PTV9DTElFTlRfSUQ6Wk9PTV9DTElFTlRfU0VDUkVU" }

If successful, the response body is a JSON representation of the refreshed access_token.

{
    "access_token": "<JWT_TOKEN>",
    "token_type": "bearer",
    "refresh_token": "<JWT_TOKEN>",
    "expires_in": 3600,
    "scope": "user:read:user",
    "api_url": "https://api.zoom.us"
}

Refresh tokens expire after 90 days. You should always use the latest refresh token for the next refresh request. If your refresh token expires, you can direct the user to the authorize URL to restart the User OAuth flow.

App Type: General

Access tokens expire after one hour. After your access token expires, you can follow the refresh flow to get a new one.

To refresh an access token, send a POST request to https://zoom.us/oauth/token with a basic authorization header. Include the required parameters as query strings or in the request body using the x-www-form-urlencoded format.

KeyValue
grant_typerefresh_token
refresh_tokenYour refresh_token.

The basic authorization header is your Client ID and Client Secret with a colon : in between, Base64 Encoded.

Example request

POST https://zoom.us/oauth/token?grant_type=refresh_token&refresh_token=<JWT_TOKEN>

Request headers

{ "Authorization": "Basic Wk9PTV9DTElFTlRfSUQ6Wk9PTV9DTElFTlRfU0VDUkVU" }

If successful, the response body is a JSON representation of the refreshed access_token.

{
    "access_token": "<JWT_TOKEN>",
    "token_type": "bearer",
    "refresh_token": "<JWT_TOKEN>",
    "expires_in": 3600,
    "scope": "user:read:user user:read:token",
    "api_url": "https://api.zoom.us"
}

Refresh tokens expire after 90 days. You should always use the latest refresh token for the next refresh request. If the refresh token expires, request a new device code, user code, and verification URI, then direct the user to the verification URI to restart the Device OAuth flow.

App Type: General

Chatbot tokens expire after one hour. After your chatbot token expires, you can request a new one. Zoom does not require a separate refresh flow.

Revoke an access token

App Type: All

To revoke an access token, send a POST request to https://zoom.us/oauth/revoke with a basic authorization header. Include the required parameters as query strings or in the request body using the x-www-form-urlencoded format.

KeyValue
tokenYour access_token.

The basic authorization header is your Client ID and Client Secret with a colon : in between, Base64 Encoded.

Example request

POST https://zoom.us/oauth/revoke?token=<JWT_TOKEN>

Request headers

{ "Authorization": "Basic Wk9PTV9DTElFTlRfSUQ6Wk9PTV9DTElFTlRfU0VDUkVU" }

If successful, the response body is a JSON representation of the revoked token.

{
    "status": "success"
}

Use an access token

To call a Zoom API endpoint, make a request with your access_token bearer token.

Example request

GET https://api.zoom.us/v2/users/me

Request header

{ "Authorization": "Bearer <JWT_TOKEN>" }

If successful, the response body is a JSON representation of your user.

{
    "id": "Wk9PTV9VU0VSX0lE",
    "first_name": "Jane",
    "last_name": "Dev",
    "display_name": "Jane Dev",
    "email": "jane.dev@example.com",
    "type": 2,
    "role_name": "Owner",
    "pmi": 1234567890,
    "use_pmi": false,
    "personal_meeting_url": "https://janedev.zoom.us/j/1234567890?pwd=Wk9PTV9QTUlfUEFTU0NPREU",
    "timezone": "America/Denver",
    "verified": 1,
    "dept": "",
    "created_at": "2019-04-05T15:24:32Z",
    "last_login_time": "2025-03-19T22:42:47Z",
    "last_client_version": "6.4.0.51205(mac)",
    "pic_url": "https://janedev.zoom.us/p/v2/Wk9PTV9QSUNfVVJM/Wk9PTV9QSUNfVVJM",
    "cms_user_id": "",
    "jid": "Wk9PTV9VU0VSX0lE@xmpp.zoom.us",
    "group_ids": ["Wk9PTV9HUk9VUF9JRA"],
    "im_group_ids": ["Wk9PTV9HUk9VUF9JRA"],
    "account_id": "Wk9PTV9BQ0NPVU5UX0lE",
    "language": "en-US",
    "phone_country": "US",
    "phone_number": "+1 1234567890",
    "status": "active",
    "job_title": "",
    "cost_center": "",
    "company": "Zoom",
    "location": "",
    "custom_attributes": [
        {
            "key": "Wk9PTV9DVVNUT01fQVRUUklCVVRFX0tFWQ",
            "name": "Test",
            "value": "set from API"
        }
    ],
    "login_types": [1, 100],
    "role_id": "0",
    "account_number": 12345678,
    "cluster": "aw1",
    "phone_numbers": [
        {
            "country": "US",
            "code": "+1",
            "number": "1234567890",
            "verified": false,
            "label": ""
        }
    ],
    "user_created_at": "2019-04-05T15:23:55Z"
}

Me context

You can replace the userID path parameter with me to target the user associated with the token. This approach lets you use user-specific endpoints without the need to look up or store the user ID for each token. For example:

URLMethods
/v2/users/meGET, PATCH
/v2/users/me/tokenGET
/v2/users/me/meetingsGET, POST

Deauthorization

App Type: General

When a user deauthorizes or removes a production app, your app receives an HTTP POST request. The request goes to the deauthorization notification endpoint URL you specified on the App Listing page.

After receiving a deauthorization webhook event, apps must delete all associated user data.

An unsecured deauthorization notification endpoint URL leaves your server vulnerable to denial of service attacks. We recommend you verify the requests sent to your deauthorization notification endpoint URL with our supported webhook verification methods.

Below is an example deauthorization webhook event:

{
    "event": "app_deauthorized",
    "event_ts": 1740439732278,
    "payload": {
        "account_id": "Wk9PTV9BQ0NPVU5UX0lE",
        "user_id": "Wk9PTV9VU0VSX0lE",
        "signature": "Wk9PTV9TSUdOQVRVUkU",
        "deauthorization_time": "2019-06-17T13:52:28.632Z",
        "client_id": "XO8c5xFdQVqGAgGB3utlRA"
    }
}

Using Proof Key for Code Exchange (PKCE)

The User Authorization use case also supports Proof Key for Code Exchange (PKCE). Use PKCE when you don't have a backend server for user authorization, such as on mobile devices. Zoom offers a separate public client ID in the authorization request that doesn't require an associated client secret.

Get your Public Client ID

  1. Open your app and navigate to App Credentials under Basic Information.
  2. Toggle Use Public Client OAuth to on.
  3. Copy the public client ID and send it, Base64 encoded, for user authorization. It does not require a client secret.

Using with client ID and secret

When you turn on public client OAuth, you can still use the client ID:client secret base64 encoded format with confidential client types.

Detailed justification for PKCE

If you make your app public, you may need to add an explanation why your application requires a public client ID in production. This helps our application review team test your PKCE integration and confirm your intended OAuth flow. Indicate whether you plan to use:

  • The confidential client OAuth flow (for example, a web app with a backend),
  • The public client OAuth PKCE flow (for example, a mobile or desktop app), or
  • Both.

This information ensures our security team can validate your configuration according to your intended use.

Authorization URL query parameters

PKCE also offers the following optional parameters. See the Proof Key for Code Exchange by OAuth Public Clients RFC for details on how to use these.

KeyValue
code_verifierA cryptographically random string used to correlate the authorization flow to the access token request, generated locally by the client. Not sent in the authorization request, but sent in the access token request so that Zoom can validate the previously received code_challenge.
code_challengeA challenge derived from the code_verifier and sent in the authorization request to verify against the code_verifier later.
code_challenge_methodThe S256 or plain method used to derive the code challenge. Sent in the authorization request Defaults to "plain" if not present in the request.

Note

The OAuth consent page must always be displayed for public clients that do not use a client secret (RFC 6819, Section 5.2.3.2 – Public Clients). This prevents silent or automatic authorization that could expose user credentials or tokens to untrusted applications. This applies to the authorization code exchange step (/oauth/token?grant_type=authorization_code). It's not applicable to the refresh token flow.

Here is an example of using PKCE with the Meeting SDK for Android.

See Introducing support for public PKCE OAuth for native and client-side apps on the developer blog for details.