This guide explains how to use OAuth access tokens to authenticate API requests to Zendesk. It covers the two supported OAuth flows, how to implement token refresh, and security best practices.

OAuth is the recommended authentication method because it:

  • Doesn’t require users to share their personal API token with your integration
  • Is tied to a user account, so tokens inherit that user's permissions and can't be used to access resources beyond what the authorizing user can access
  • Supports fine-grained permissions through scopes, so you can limit a token to only the access your integration actually needs
  • Uses short-lived tokens that expire automatically, reducing the risk if a token is compromised
  • Is optimal for long-term app security and maintenance

How OAuth authentication works

Instead of sending a static API token with every request, your integration obtains an OAuth access token and includes that in the request header. Access tokens are short-lived and paired with a refresh token that your integration uses to automatically get a new access token when the current one expires.

Before you begin

Before you start, make sure you have:

  • Access to the app or integration code that currently uses API token authentication.
  • Information about whether your integration uses API tokens directly or through a custom authentication flow.
  • A Zendesk account with admin access if you need to set up a new client for your integration.

Which OAuth flow should you use?

Zendesk supports two OAuth flows. Identify which OAuth flow matches your integration:

  • Authorization code: Your integration acts on behalf of a user who signs in to their Zendesk account and approves access. Use this for user-facing apps and integrations that need per-user permissions.
  • Client credentials: Your integration authenticates on behalf of the user who created the OAuth client, with no user interaction at runtime. Use this for background jobs, data pipelines, and other server-to-server setups.

If you're unsure which applies, consider whether your integration needs to act on behalf of a specific Zendesk user. If yes, use the authorization code flow. If no, use the client credentials flow.

Authorization code flow

Use this flow when your integration acts on behalf of a specific user.

How the authorization code flow works

  1. Your app redirects the user to Zendesk.
  2. The user signs in and approves access.
  3. Zendesk redirects the user back to your app with an authorization code.
  4. Your app exchanges the authorization code for an access token.
  5. Your app uses the access token to make API requests.
  6. If Zendesk returns a refresh token, your app uses it to request a new access token when the current one expires.

Step 1: Create or configure an OAuth client

Before you start the authorization flow, create or configure an OAuth client in Zendesk.

This guide assumes you are using a confidential client, one where the client secret can be stored securely on a server. If your integration runs in an environment where the secret can't be kept confidential, such as a browser-based or mobile app, you should use PKCE instead.

When you configure the client, register the redirect URI that your app uses to receive the authorization response. The redirect URI in your request must match the value configured for your OAuth client.

Step 2: Send the user to Zendesk for authorization

Redirect the user to the Zendesk authorization endpoint to start the OAuth flow. Zendesk prompts the user to sign in and approve access for your app.

Include a state parameter in the request and verify it when the user returns to your app. The state parameter helps protect against cross-site request forgery (CSRF) attacks.

Example authorization request

https://{your_subdomain}.zendesk.com/oauth/authorizations/new?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&state=RANDOM_STRING

The following query parameters are required for the authorization request. Use your app's values when constructing the URL.

ParameterRequiredDescription
response_typeYesMust be code.
client_idYesYour Zendesk OAuth client id.
redirect_uriYesThe URI Zendesk redirects the user back to after authorization. Must exactly match the value configured for your OAuth client.
stateRecommendedA random string used to help prevent CSRF attacks.

Step 3: Handle the authorization response

After the user grants or denies access, Zendesk redirects them to your redirect URI.

If the user grants access, Zendesk redirects to your app with an authorization code and the same state value you sent:

https://example.com/oauth/callback?code=AUTHORIZATION_CODE&state=xyz789

If the user denies access, Zendesk redirects to your app with an error and the same state value you sent:

https://example.com/oauth/callback?error=access_denied&state=xyz789

After your app receives the redirect, handle the response as follows:

  1. Verify that the returned state matches the value you sent.
  2. If the response includes code, continue to the token exchange step.
  3. If the response includes error, stop the flow and display an appropriate message to the user.

Step 4: Exchange the authorization code for an access token

To exchange the authorization code for an access token, send a POST request to the token endpoint. The authorization code is short-lived and can be used only once, so exchange it as soon as possible.

POST https://{your_subdomain}.zendesk.com/oauth/tokens

Include the authorization code, client id, client secret, and redirect URI in the request body.

{  "grant_type": "authorization_code",  "code": "AUTHORIZATION_CODE",  "client_id": "YOUR_CLIENT_ID",  "client_secret": "YOUR_CLIENT_SECRET",  "redirect_uri": "YOUR_REDIRECT_URI"}

If the request is successful, Zendesk returns an access token. The response will also include a refresh token.

{  "access_token": "ACCESS_TOKEN",  "token_type": "bearer",  "expires_in": 3600,  "refresh_token": "REFRESH_TOKEN"}

The following table describes the fields returned in the token response.

FieldDescription
access_tokenThe token your app uses to make API requests.
token_typeThe token type. Zendesk returns bearer.
expires_inThe number of seconds before the access token expires.
refresh_tokenA token your app can use to request a new access token, if returned.

Store both the access and refresh tokens securely.

Step 5: Use the access token to make API requests

Include the access token in the Authorization header when you make requests to Zendesk APIs.

Example request

GET https://{your_subdomain}.zendesk.com/api/v2/tickets.jsonAuthorization: Bearer ACCESS_TOKEN

Example using curl

curl https://example.zendesk.com/api/v2/tickets.json \  -H "Authorization: Bearer ACCESS_TOKEN"

Step 6: Refresh the access token when needed

If Zendesk returns a refresh token, use it to request a new access token when the current one expires.

POST https://{your_subdomain}.zendesk.com/oauth/tokens
{  "grant_type": "refresh_token",  "refresh_token": "REFRESH_TOKEN",  "client_id": "YOUR_CLIENT_ID",  "client_secret": "YOUR_CLIENT_SECRET"}

If the request is successful, Zendesk returns a new access token. The response may also include a new refresh token.

{  "access_token": "NEW_ACCESS_TOKEN",  "token_type": "bearer",  "expires_in": 3600,  "refresh_token": "NEW_REFRESH_TOKEN"}

If Zendesk returns a new refresh token, replace the old value with the new one.

Client credentials flow

Use this flow when your integration runs without user interaction, such as background jobs, data pipelines, or other server-to-server setups.

This flow requires a confidential client, one where the client secret can be kept secure on the server. Use client credentials only in trusted, server-side environments where the client secret can be stored securely and never exposed to end users. The access token inherits the permissions of the user associated with the OAuth client, so make sure that user has only the permissions your integration need.

Unlike the authorization code flow, no user redirect is required and Zendesk does not return a refresh token. When the access token expires, your app requests a new one using the same client credentials.

How the client credentials flow works

  1. Your app sends the client id and client secret directly to Zendesk.
  2. Zendesk returns an access token.
  3. Your app uses the access token to make API requests.
  4. When the access token expires, your app requests a new one.

Step 1: Create or configure an OAuth client

Create or configure an OAuth client in Zendesk as a confidential client. Only use this flow in server-side applications or other trusted environments where the client secret can be protected. No redirect URI is required for this flow.

Step 2: Request an access token

Send a POST request to the token endpoint with your client id, client secret, and the required scope.

POST https://{your_subdomain}.zendesk.com/oauth/tokens
{  "grant_type": "client_credentials",  "client_id": "YOUR_CLIENT_ID",  "client_secret": "YOUR_CLIENT_SECRET",  "scope": "YOUR_SCOPE"}

If the request is successful, Zendesk returns an access token. No refresh token is returned for this flow.

{  "access_token": "ACCESS_TOKEN",  "token_type": "bearer",  "expires_in": 3600,  "scope": "read"}

Store the access token securely.

Step 3: Use the access token to make API requests

Include the access token in the Authorization header when you make requests to Zendesk APIs.

Example request

GET https://{your_subdomain}.zendesk.com/api/v2/tickets.jsonAuthorization: Bearer ACCESS_TOKEN

Example using curl

curl https://example.zendesk.com/api/v2/tickets.json \  -H "Authorization: Bearer ACCESS_TOKEN"

Step 4: Request a new access token when needed

Because the client credentials flow does not return a refresh token, request a new access token when the current one expires. Use the same request as Step 2.

Security considerations

  • Keep the client_secret confidential. This is especially critical for the client credentials flow, where the secret is used directly to obtain tokens.
  • Never expose access tokens or refresh tokens in client-side code, logs, or URLs.
  • Validate the state value returned by Zendesk before continuing. (Authorization code flow only.)
  • Make sure the redirect_uri exactly matches the value configured for your OAuth client. (Authorization code flow only.)
  • Store tokens securely and restrict access to them.
  • Handle expired, revoked, or already used authorization codes gracefully. (Authorization code flow only.)
  • For the client credentials flow, make sure the OAuth client's associated user has only the permissions your integration needs.
  • Only use the client credentials flow with confidential clients where the secret can be protected server-side.

Common errors

access_denied

(Authorization code flow only) The user denied access to your app. Prompt the user to retry the authorization flow if they still want to connect their account.

invalid_grant

(Authorization code flow only) The authorization code is invalid, expired, or already used. Start the authorization flow again and exchange the new code immediately.

redirect_uri_mismatch

(Authorization code flow only) The redirect URI in your request does not match the redirect URI configured for your OAuth client. Make sure the request uses the exact redirect URI registered for the client.

invalid_client

The client id or client secret is invalid. Verify that your OAuth client credentials are correct.

Next steps

After you complete the migration:

  • Test the full flow in a development environment.
  • Confirm that your app handles expired tokens correctly.
  • If you used the authorization code flow, verify token refresh behavior and confirm that your app handles denied access correctly.