> ## Documentation Index
> Fetch the complete documentation index at: https://developers.hubspot.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

---
id: b67dd3db-1395-435c-b4fe-0059690f23b9
---

# Manage OAuth access tokens using the v3 APIs

> Learn how to use the new v3 OAuth endpoints to manage access and refresh tokens to securely perform CRUD actions with HubSpot APIs.

export const BlogPostCTA = ({title, href}) => {
  return <div className="card-condensed-cta">
      <Card title={title} href={href} horizontal={true} cta="Read more on the HubSpot Developer Blog" icon="bullhorn" />
    </div>;
};

export const PostmanCard = ({title, collectionId}) => {
  return <div className="card-condensed-cta">
      <Card title={title ? title : "Postman collection"} href={`https://app.getpostman.com/run-collection/${collectionId}`} horizontal={true} cta="Run in Postman" icon={<svg xmlns="http://www.w3.org/2000/svg" width={25} height={25} preserveAspectRatio="xMidYMid" viewBox="0 0 256 256">
              <path fill="#FF6C37" d="M254.953 144.253c8.959-70.131-40.569-134.248-110.572-143.206C74.378-7.912 10.005 41.616 1.047 111.619c-8.959 70.003 40.569 134.248 110.572 143.334 70.131 8.959 134.248-40.569 143.334-110.7Z" />
              <path fill="#FFF" d="m174.2 82.184-54.007 54.007-15.229-15.23c53.11-53.11 58.358-48.503 69.236-38.777Z" />
              <path fill="#FF6C37" d="M120.193 137.47c-.384 0-.64-.128-.895-.384l-15.358-15.229a1.237 1.237 0 0 1 0-1.792c54.007-54.006 59.638-48.887 71.028-38.649.255.256.383.512.383.896s-.128.64-.383.896l-54.007 53.878c-.128.256-.512.384-.768.384Zm-13.437-16.509 13.437 13.438 52.087-52.087c-9.47-8.446-15.87-11.006-65.524 38.65Z" />
              <path fill="#FFF" d="m135.679 151.676-14.718-14.718 54.007-54.006c14.46 14.59-7.167 38.265-39.29 68.724Z" />
              <path fill="#FF6C37" d="M135.679 152.956c-.384 0-.64-.128-.896-.384l-14.718-14.718c-.256-.256-.256-.512-.256-.896s.128-.64.384-.895L174.2 82.056a1.237 1.237 0 0 1 1.791 0 15.58 15.58 0 0 1 4.991 11.902c-.256 14.206-16.38 32.25-44.28 58.614-.383.256-.767.384-1.023.384Zm-12.926-15.998c8.19 8.319 11.646 11.646 12.926 12.926 21.5-20.476 42.36-41.464 42.488-55.926.128-3.327-1.152-6.655-3.327-9.214l-52.087 52.214Z" />
              <path fill="#FFF" d="m105.22 121.345 10.878 10.878c.256.256.256.512 0 .768-.128.128-.128.128-.256.128l-22.524 4.863c-1.152.128-2.175-.64-2.431-1.791-.128-.64.128-1.28.512-1.664l13.053-13.054c.256-.256.64-.384.768-.128Z" />
              <path fill="#FF6C37" d="M92.934 139.262c-1.92 0-3.327-1.536-3.327-3.455 0-.896.384-1.792 1.024-2.432l13.053-13.054c.768-.64 1.792-.64 2.56 0l10.878 10.878c.768.64.768 1.792 0 2.56-.256.256-.512.384-.896.512l-22.524 4.863c-.256 0-.512.128-.768.128Zm11.902-16.51-12.542 12.543c-.256.256-.383.64-.128 1.024.128.383.512.511.896.383l21.116-4.607-9.342-9.342Z" />
              <path fill="#FFF" d="M202.739 52.238c-8.191-7.935-21.373-7.679-29.307.64-7.935 8.318-7.679 21.372.64 29.306A20.678 20.678 0 0 0 199.155 85l-14.59-14.59 18.174-18.172Z" />
              <path fill="#FF6C37" d="M188.405 89.223c-12.158 0-22.012-9.854-22.012-22.012 0-12.158 9.854-22.012 22.012-22.012 5.631 0 11.134 2.176 15.23 6.143.255.256.383.512.383.896s-.128.64-.384.895L186.357 70.41l13.566 13.566c.512.512.512 1.28 0 1.792l-.256.256c-3.327 2.047-7.295 3.199-11.262 3.199Zm0-41.337c-10.75 0-19.452 8.703-19.324 19.453 0 10.75 8.702 19.452 19.452 19.324 2.944 0 5.887-.64 8.575-2.047l-13.438-13.31c-.256-.256-.384-.512-.384-.896s.128-.64.384-.895l17.149-17.15c-3.456-2.943-7.807-4.479-12.414-4.479Z" />
              <path fill="#FFF" d="m203.122 52.622-.255-.256-18.301 18.044 14.461 14.462c1.408-.896 2.816-1.92 3.967-3.072a20.51 20.51 0 0 0 .128-29.178Z" />
              <path fill="#FF6C37" d="M199.155 86.28c-.384 0-.64-.128-.896-.384l-14.589-14.59c-.256-.256-.384-.512-.384-.896s.128-.64.384-.895l18.173-18.173a1.237 1.237 0 0 1 1.791 0l.384.256c8.575 8.574 8.575 22.396.128 31.098-1.28 1.28-2.687 2.432-4.223 3.328-.384.128-.64.256-.768.256Zm-12.798-15.87 12.926 12.926c1.024-.64 2.048-1.536 2.816-2.304 7.294-7.294 7.678-19.196.64-26.875L186.357 70.41Z" />
              <path fill="#FFF" d="M176.375 84.488a7.879 7.879 0 0 0-11.134 0l-48.247 48.247 8.063 8.063 51.062-44.792c3.328-2.816 3.584-7.807.768-11.134-.256-.128-.384-.256-.512-.384Z" />
              <path fill="#FF6C37" d="M124.929 142.077c-.384 0-.64-.128-.896-.383l-8.063-8.063a1.237 1.237 0 0 1 0-1.792l48.247-48.247a9.115 9.115 0 0 1 12.926 0 9.115 9.115 0 0 1 0 12.926l-.384.384-51.063 44.792c-.128.255-.384.383-.767.383Zm-6.143-9.342 6.27 6.271 50.167-44.024c2.816-2.304 3.072-6.527.768-9.342-2.303-2.816-6.526-3.072-9.342-.768-.128.128-.256.256-.512.384l-47.351 47.48Z" />
              <path fill="#FFF" d="M80.009 187.637c-.512.256-.768.768-.64 1.28l2.175 9.214c.512 1.28-.256 2.816-1.663 3.2-1.024.384-2.176 0-2.816-.768l-14.077-13.95 45.943-45.943 15.87.256 10.75 10.75c-2.56 2.175-18.045 17.149-55.542 35.961Z" />
              <path fill="#FF6C37" d="M78.985 202.61c-1.024 0-2.048-.383-2.688-1.151l-13.95-13.95c-.255-.256-.383-.512-.383-.896 0-.383.128-.64.384-.895l45.944-45.944c.256-.256.64-.384.895-.384l15.87.256c.383 0 .64.128.895.384l10.75 10.75c.256.256.384.64.384 1.024s-.128.64-.512.896l-.895.767c-13.566 11.902-31.995 23.804-54.902 35.194l2.175 9.086c.384 1.664-.384 3.456-1.92 4.352-.767.384-1.407.512-2.047.512Zm-14.078-15.997 13.182 13.054c.384.64 1.152.896 1.792.512.64-.384.896-1.152.512-1.792l-2.176-9.214c-.256-1.152.256-2.176 1.28-2.688 22.652-11.39 40.952-23.163 54.39-34.81l-9.47-9.47-14.718-.256-44.792 44.664Z" />
              <path fill="#FFF" d="m52.11 197.62 11.006-11.007 16.38 16.381-26.107-1.791c-1.151-.128-1.92-1.152-1.791-2.304 0-.512.128-1.024.512-1.28Z" />
              <path fill="#FF6C37" d="m79.497 204.146-26.236-1.791c-1.92-.128-3.199-1.792-3.071-3.712.128-.768.384-1.535 1.024-2.047L62.22 185.59a1.237 1.237 0 0 1 1.792 0l16.38 16.38c.385.385.512.897.257 1.408-.256.512-.64.768-1.152.768Zm-16.381-15.74-10.11 10.11c-.384.255-.384.895 0 1.151.127.128.255.256.511.256l22.652 1.536-13.053-13.054ZM104.452 146.557c-.768 0-1.28-.64-1.28-1.28 0-.384.128-.64.384-.896l12.414-12.414a1.237 1.237 0 0 1 1.792 0l8.062 8.063c.384.384.512.768.384 1.28-.128.384-.512.767-1.023.895l-20.477 4.352h-.256Zm12.414-11.902-8.446 8.446 13.821-2.943-5.375-5.503Z" />
              <path fill="#FFF" d="m124.8 140.926-14.077 3.071c-1.024.256-2.048-.384-2.303-1.408-.128-.64 0-1.28.511-1.791l7.807-7.807 8.063 7.935Z" />
              <path fill="#FF6C37" d="M110.467 145.277a3.168 3.168 0 0 1-3.2-3.2c0-.895.385-1.663.897-2.303l7.806-7.807a1.237 1.237 0 0 1 1.792 0l8.062 8.063c.384.384.512.768.384 1.28-.128.384-.512.767-1.023.895l-14.078 3.072h-.64Zm6.399-10.622-6.91 6.91c-.257.257-.257.512-.129.768s.384.384.768.384l11.774-2.56-5.503-5.502ZM203.25 64.907c-.256-.767-1.151-1.151-1.92-.895-.767.255-1.151 1.151-.895 1.92 0 .127.128.255.128.383.768 1.536.512 3.455-.512 4.863-.512.64-.384 1.536.128 2.048.64.512 1.536.384 2.048-.256 1.92-2.432 2.303-5.503 1.023-8.063Z" />
            </svg>} />
    </div>;
};

<PostmanCard collectionId="26126890-299a6876-6b19-47e6-8f39-15030bc50e7a" />

<style>
  {`
    code {
      text-wrap:nowrap!important;
    }
    td code {
      text-wrap: wrap!important;
    }
    `}
</style>

The new v3 OAuth endpoints provide security improvements over the previous [v1 endpoints](/api-reference/legacy/authentication/oauth-tokens/v1/guide), while still maintaining the same functionality to leverage OAuth 2.1 to manage access and refresh tokens.

Each of the v3 endpoints require that parameters are included in the request body, which differs from how the corresponding values were included as query parameters in the v1 endpoints. This new approach ensures that sensitive data such as your app's client ID and secret don't inadvertently appear in server logs or other persistent logging in your app's backend services.

Before you can use these endpoints, you'll have to [create an app](/apps/developer-platform/build-apps/create-an-app). A user will then need to install it into their account to initiate OAuth access. Review the sections below on how to use the new v3 endpoints to initiate OAuth access, manage access tokens, and retrieve token metadata.

## Token types

The OAuth authorization flow involves three distinct token types:

* **Authorization code:** a temporary, single-use code that's provided in the redirect URL as a query parameter when the user approves the installation of your app. Your app then has a short window to exchange it for an access token and a refresh token, after which the authorization code cannot be reused.
* **Access token:** your app's authentication credential used for every API request made on behalf of the user and the account where they installed your app. This token is provided as a *Bearer* token in the request, and expires after 30 minutes.
* **Refresh token:** your app's long-term authentication credential that you can use to generate a new access token after the previous one expires.

Check out the blog post linked below for guidance on how to cache and auto-refresh access tokens:

<BlogPostCTA title="Blog Post: Production-Ready OAuth Token Management" href="https://developers.hubspot.com/blog/oauth-token-management-hubspot-integrations#token-caching-auto-refresh" />

## Initiate OAuth access

After you create your app, a user can install it into their HubSpot account using the install URL located in your [app's settings](/apps/developer-platform/build-apps/manage-apps-in-hubspot), which will include the `client_id`, `redirect_uri`, and `scopes` as query parameters. You may also include `optional_scopes` and `state`, if needed.

For example, the Node.js code block below demonstrates how to construct this authorization URL:

```js theme={null}
const authorizationUrl =
  'https://app.hubspot.com/oauth/authorize' +
  `?client_id=${encodeURIComponent(CLIENT_ID)}` + // app's client ID
  `&scope=${encodeURIComponent(SCOPES)}` + // scopes being requested by the app
  `&redirect_uri=${encodeURIComponent(REDIRECT_URI)}`; // where to send the user after the consent page
```

After a user authorizes your app and installs it into their account, the redirect URL will be appended with a `code` query parameter, which you can use to [generate an access token and a refresh token](#generate-initial-access-and-refresh-tokens). The access token will be used to authenticate requests that your app makes, while the refresh token will be used to get a new access token when the current one expires.

## Generate initial access and refresh tokens

After a user authorizes your app, the response will include a `code` value as a query parameter. Using this code, you'll generate the initial access token and refresh token. Access tokens are short-lived (30 minutes), and you can check the `expires_in` parameter when generating an access token to determine its lifetime (in seconds).

To get OAuth access and refresh tokens, make a URL-form encoded `POST` request to `/oauth/v3/token`. In the request body, you'll specify various auth parameters, such as `client_id` and `client_secret`, along with the `code` passed back through the redirect URL.

For example, your request may look similar to the following:

```shell theme={null}
curl --request POST \
  --url https://api.hubspot.com/oauth/v3/token \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data client_id=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee \
  --data client_secret=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee \
  --data code=na1-aaaa-bbbb-cccc-dddd-eeeeeeeeeeee \
  --data grant_type=authorization_code \
  --data redirect_uri=http://localhost:3000/oauth-callback
```

| Parameter       | Type   | Description                                                                                 |
| --------------- | ------ | ------------------------------------------------------------------------------------------- |
| `grant_type`    | String | Must be `authorization_code` for the request to generate initial access and refresh tokens. |
| `code`          | String | The `code` returned in the redirect URL after the user installs the app.                    |
| `redirect_uri`  | String | The app's set redirect URL.                                                                 |
| `client_id`     | String | The app's client ID.                                                                        |
| `client_secret` | String | The app's client secret.                                                                    |

In the response, you'll receive the access token along with the refresh token, which you can use to refresh the access token. The `expires_in` field specifies how long the access token will last (in seconds).

```json theme={null}
{
  "token_type": "bearer",
  "refresh_token": "na1-aaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "access_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "hub_id": 1234567,
  "scopes": [
    "oauth",
    "crm.objects.contacts.write",
    "crm.objects.contacts.read"
  ],
  "expires_in": 1800
}
```

## Refresh an access token

Using a refresh token, you can generate a new access token by making a URL-form encoded `POST` request to `/oauth/v3/token`. In the request body, you'll specify the `grant_type`, `client_id`, `client_secret`, and `refresh_token`.

```shell theme={null}
curl --request POST \
  --url https://api.hubspot.com/oauth/v3/token \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data client_id=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee \
  --data client_secret=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee \
  --data refresh_token=na1-aaaa-bbbb-cccc-dddd-eeeeeeeeeeee \
  --data grant_type=refresh_token
```

| Parameter       | Type   | Description                                                                                   |
| --------------- | ------ | --------------------------------------------------------------------------------------------- |
| `grant_type`    | String | Must be `refresh_token` for the request to generate new access tokens from the refresh token. |
| `refresh_token` | String | The refresh token value.                                                                      |
| `client_id`     | String | The app's client ID.                                                                          |
| `client_secret` | String | The app's client secret.                                                                      |

## Retrieve access token metadata

To get information about an OAuth access token, including the user that the token was created for and their corresponding Hub ID, make a `POST` request to `/oauth/v3/token/introspect`, providing the following form URL-encoded properties in the request body:

| Property          | Type   | Description                                                                                                                            |
| ----------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| `client_id`       | String | The app's client ID.                                                                                                                   |
| `client_secret`   | String | The app's client secret.                                                                                                               |
| `token_type_hint` | String | The type of token you're querying, which can be either `access_token` or `refresh_token`.                                              |
| `refresh_token`   | String | If you specified `refresh_token` as the `token_type_hint`, then provide the `refresh_token` as an additional property in your request. |
| `access_token`    | String | If you specified `access_token` as the `token_type_hint`, then provide the `access_token` as an additional property in your request.   |

For example, if you wanted to query for details about the `refresh_token` of `na1-aaaa-bbbb-cccc-dddd-eeeeeeeeeeee`, your form URL-encoded request body would include:

```json theme={null}
{
  "client_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "client_secret": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "token_type_hint": "refresh_token",
  "refresh_token": "na1-aaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
}
```

You'll receive a response containing information about the user's access token and their HubSpot account. The code blocks below provide example responses when querying for both `access_token` and `refresh_token` grant types.

<Card>
  <Tabs>
    <Tab title="Access token metadata">
      ```json theme={null}
        {
          "active": true,
          "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
          "hub_id": 1234567,
          "user_id": 222222,
          "client_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
          "app_id": 1234444,
          "user": "jdoe@hubspot.com",
          "hub_domain": "example.com",
          "scopes": [
            "oauth",
            "crm.objects.contacts.read",
            "crm.objects.contacts.write"
          ],
          "signed_access_token": {
            "expiresAt": 1769213087721,
            "scopes": "xxxxxxxxxxxxxxxxxxxxxxxx",
            "hubId": 1234567,
            "userId": 222222,
            "appId": 1234444,
            "signature": "xxxxxxxxxxxxxxxxxxxxxxxx",
            "scopeToScopeGroupPks": "xxxxxxxxxxxxxxxxxxxxxxxx",
            "newSignature": "xxxxxxxxxxxxxxxxxxxxxxxx",
            "hublet": "na1",
            "trialScopes": "",
            "trialScopeToScopeGroupPks": "",
            "isUserLevel": false,
            "isPrivateDistribution": true
          },
          "expires_in": 1789,
          "is_private_distribution": true,
          "token_use": "access_token",
          "token_type": "Bearer"
        }
      ```
    </Tab>

    <Tab title="Refresh token metadata">
      ```json theme={null}
        {
          "active": true,
          "token": "na1-aaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
          "hub_id": 1234567,
          "user_id": 222222,
          "client_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
          "app_id": 1234444,
          "user": "jdoe@hubspot.com",
          "hub_domain": "example.com",
          "scopes": [
            "crm.objects.contacts.write",
            "oauth",
            "crm.objects.contacts.read"
          ],
          "token_use": "refresh_token",
          "token_type": "Bearer"
        }
      ```
    </Tab>
  </Tabs>
</Card>

## Error handling

When querying for access or refresh tokens, if an OAuth error occurs, the response will resemble the following:

```json theme={null}
{
  "error": "invalid_grant",
  "error_description": "refresh token is invalid, expired or revoked",
  "status": "BAD_REFRESH_TOKEN",
  "message": "refresh token is invalid, expired or revoked"
}
```

This response fully conforms to [RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749) of the OAuth 2.0 standard, which many OAuth libraries and tools expect. Note that the HubSpot-specific `status` and `message` fields that were included in previous versions of the OAuth token API endpoints are still available for backwards compatibility.
