Authorization
What is authorization?
Authorization is the process of granting an integration access to a user’s Notion data. That process varies depending on whether or not the integration is public or internal.
Link Preview integrations — a subcategory of public integrations — use two-way OAuth, which differs from the authorization flow described in this guide.
See the Build a Link Preview integration guide to learn more about Link Preview authorization.
What is an internal integration?
An internal integration allows Notion workspace members to interact with the workspace through the Notion REST API. Each internal integration is tied to a single, specific workspace and only members within the workspace can use the integration. After an internal integration is added to a workspace, members must manually give the integration access to the specific pages or databases that they want it to use.
What is a public integration?
Public integrations can be used by any Notion user in any workspace. They allow members to interact with their workspace using Notion’s REST API once the integration has been properly authorized.
Public integrations follow the OAuth 2.0 protocol. This allows workspace members to give access to Notion pages directly through the auth flow, without having to open each Notion workspace page directly and manually give permission to the integration. (More on this below.)
Public integrations can technically be used without permitting workspace pages access as long as the auth flow is completed and an access token is created — a process which will be described in detail below. For example, if a public integration only needs to interact with the Notion User endpoints, it does not need to be given access to workspace pages.
For more details on the differences between public and internal integrations, refer to the getting started page.
Read on to learn how to set up the auth flows for internal and public integrations.
Internal integration auth flow set-up
To use an internal integration, start by creating your integration in the integration’s settings page.
The internal integration will be associated with the workspace of your choice. You are required to be a workspace owner to create an integration.

Click the New integration button on the My integrations page to create a new integration.
Once the integration is created, you can update its settings as needed under the Configuration tab and retrieve the integration token in this tab.
The integration token will be used to authenticate REST API requests. The integration sends the same token in every API request.

Integration permissions
Before an integration can interact with your Notion workspace page(s), the page must be manually shared with the integration. To share a page with an integration, visit the page in your Notion workspace, click the ••• menu at the top right of a page, scroll down to Add connections, and use the search bar to find and select the integration from the dropdown list.
Once the integration is shared, you can start making API requests. If the page is not shared, any API requests made will respond with an error.
Your integration token is a secret. To keep your integration secure, never store the token in your source code or commit it in version control. Instead, read the token from an environment variable. Use a secret manager or deployment system to set the token in the environment.
Making API requests with an internal integration
Any time your integration is interacting with your workspace, you will need to include the integration token in the Authorization header with every API request. However, if you are using Notion’s SDK for JavaScript to interact with the REST API, the token is set once when a client is initialized.
If you are not using the Notion SDK for JavaScript, you will also need to set the Notion-Version and Content-type headers in all of in your requests, like so:
If you receive an error response from the API, check if the integration has been properly added to the page. If this does not solve the problem, refer to our Status codes page for more information.
Public integration auth flow set-up
When an integration is made public, any Notion user in any workspace can use it.
Since a public integration is not tied to a single workspace with a single integration token, public integrations instead follow the OAuth 2.0 protocol to authorize an integration to interact with a workspace.
How to make a public integration
Select New Integration in your integration dashboard and select Public in the integration Type during the creation flow. You may also edit an existing internal integration to convert to Public.

You will need to fill out the form with additional information, including your company name, website, and redirect URI(s).
The redirect URI is the URI your users will be redirected to after authorizing the public integration. To learn more, read OAuth’s description of redirect URIs.
Public integration authorization overview
Once your integration has been made public, you can update your integration code to use the public auth flow.
As an overview, the authorization flow includes the following steps. Each step will be described in more detail below.
- Navigate the user to the integration’s authorization URL. This URL is provided in the integration’s settings page.
- After the user selects which workspace pages to share, Notion redirects the user to the integration’s redirect URI and includes a
codequery parameter. The redirect URI is the one you specified in your integration’s settings page. - You will make a
POSTrequest to create an access token , which will exchange the temporarycodefor an access token. - The Notion API responds with an access token and some additional information.
- You will store the access token for future API requests. View the API reference docs to learn about available endpoints.
Notion redirects the user to the integration’s redirect URI and includes a code parameter
When you first created the public integration, you specified a redirect URI. If the user follows the prompt to Allow access for the integration, then Notion generates a temporary code and sends a request to the redirect URI with the following information in the query string:
To complete the next set, you will need to retrieve the code query parameter provided in the redirect. How you retrieve this value will vary depending on your app’s tech stack.
In a React component, for example, the query parameters are made available through the useRouter() hook:
Send the code in a POST request to the Notion API
The integration needs to exchange the temporary code for an access_token.
To set up this step, retrieve the code from the redirect URI.
Next, you will need to send the code as part of a POST request to Notion’s token endpoint: https://api.notion.com/v1/oauth/token.
This endpoint is described in more detail in the API reference docs for creating a token.
The request is authorized using HTTP Basic Authentication. The credential is a colon-delimited combination of the integration’s CLIENT_ID and CLIENT_SECRET, like so:
You can find both of these values in the integration’s settings.
Note that in HTTP Basic Authentication, credentials are base64 encoded before being added to the Authorization header.
The body of the request contains the following JSON-encoded fields:
The following is an example request to exchange the authorization code for an access token:
The Node-equivalent of this example would look something like this:
Notion responds with an access_token , refresh_token, and additional information
Notion responds to the request with an access_token, refresh_token, and additional information. The access_token will be used to authenticate subsequent Notion REST API requests. The refresh_token will be used to refresh the access token, which generates a new access_token.
The response contains the following JSON-encoded fields:
Token request failures
If something goes wrong when the integration attempts to exchange the code for an access_token, then the response contains a JSON-encoded body with an "error" field. Notion uses the common error codes from the OAuth specification.
The integration stores the access_token and refresh_token for future requests
You need to set up a way for your integration to store both the access_token and refresh_token that it receives. The access_token is used to make authorized requests to the Notion API, and the refresh_token is used to generate a new access_token.
Tips for storing and using token access
- Setting up a database is a typical solution for storing access tokens. If you’re using a database, then build relations between an
access_token,refresh_token, and the corresponding Notion resources that your integration accesses with that token. For example, if you store a Notion database or page ID, relate those records with the correctaccess_tokenthat you use to authorize requests to read or write to that database or page, and therefresh_tokenfor ongoing token lifecycle support.. - Store all of the information that your integration receives with the
access_tokenandrefresh_token. You never know when your UI or product requirements might change and you’ll need this data. It’s really hard (or impossible) to send users to repeat the authorization flow to generate the information again. - The
bot_idreturned along with your tokens should act as your primary key when storing information.
Refreshing an access token
Refreshing an access token will generate a new access token and a new refresh token.
You will need to send the refresh_token provided from Step 4 as part of a POST request to Notion’s token endpoint: https://api.notion.com/v1/oauth/token.
This endpoint is described in more detail in the API reference docs for refreshing a token.
The request is authorized using HTTP Basic Authentication. The credential is a colon-delimited combination of the integration’s CLIENT_ID and CLIENT_SECRET, like so:
You can find both of these values in the integration’s settings.
Note that in HTTP Basic Authentication, credentials are base64 encoded before being added to the Authorization header.
The body of the request contains the following JSON-encoded fields:
The following is an example request to exchange the refresh_token for a new access token and new refresh token
The Node-equivalent of this example would look something like this:





