> ## 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: f0e838fb-2e61-4285-a6bd-a3e1925b4a5a
---

# Manage OAuth access tokens with the 2026-03 API

> Learn how to use the 2026-03 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>;
};

The 2026-03 OAuth endpoints allow you to generate and manage access tokens for your app. To get started, you'll need to [create an app](/apps/developer-platform/build-apps/create-an-app) configured for OAuth authentication.

These endpoints improve on the [v1 endpoints](/api-reference/legacy/authentication/oauth-tokens/v1/guide) by requiring all parameters to be in the request body rather than as query parameters, which prevents sensitive values like your `client_id` and `client_secret` from appearing in server logs or other persistent logging.

Review the sections below on how to use these 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.

<Note>
  If you're using the [webhooks journal API](/api-reference/latest/webhooks-journal/guide), you can also generate access tokens using the [`client_credentials` grant type](#generate-a-client-credentials-access-token), which has a different authorization flow. Tokens with this grant type authenticate as your app rather than on behalf of a specific user, and can be used for managing webhook journal subscription configuration or process journal data.
</Note>

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/2026-03/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/2026-03/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/2026-03/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/2026-03/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.                                                                      |

## Generate a client credentials access token

The `client_credentials` grant type authenticates as your app rather than on behalf of a specific user. Unlike the `authorization_code` flow, client credentials tokens are app-level tokens used for the [webhooks journal API](/api-reference/latest/webhooks-journal/guide).

To generate a client credentials access token, make a URL-form encoded `POST` request to `/oauth/2026-03/token` with `grant_type=client_credentials`.

```shell theme={null}
curl --request POST \
  --url https://api.hubspot.com/oauth/2026-03/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 grant_type=client_credentials \
  --data 'scope=developer.webhooks_journal.read developer.webhooks_journal.subscriptions.read'
```

| Parameter       | Type   | Description                                                                                                                                 |
| --------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `grant_type`    | String | Must be `client_credentials`.                                                                                                               |
| `client_id`     | String | The app's client ID.                                                                                                                        |
| `client_secret` | String | The app's client secret.                                                                                                                    |
| `scope`         | String | A space-separated list of scopes to request for the token. Must include at least one valid scope (e.g., `developer.webhooks_journal.read`). |

In the response, you'll receive an access token. No refresh token will be included, as the `client_credentials` grant type doesn't require one for generating the access token.

```json theme={null}
{
  "token_type": "bearer",
  "access_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "expires_in": 1800
}
```

## 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/2026-03/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. |
| `token`           | String | The `access_token` or `refresh_token` value, based on the token type you specified in the `token_type_hint` property above.            |

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",
  "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.
