At a Glance
HubSpot OAuth setup with token exchange, refresh, and best practices.
Quick Answer
To create a HubSpot public app with OAuth 2.0 you need a HubSpot developer account, a public app with Client ID/Secret, a secure HTTPS server, and an exact redirect URL. Use the authorization code flow to exchange the code for access/refresh tokens, then refresh tokens when the access token expires.
What is HubSpot OAuth 2.0?
HubSpot public apps authenticate with OAuth 2.0 Authorization Code flow.
- After a user installs your app, HubSpot redirects with an authorization
code. - You exchange this code for an
access_token(short-lived) and arefresh_token(long-lived). - When the access token expires, you use the refresh token to request a new one.
What are the prerequisites?
Before you start, you need:
- HubSpot Developer Account and Public App - Provides Client ID and Client Secret.
- A public server with HTTPS domain - Required for redirect and callback routes.
- Exact redirect URL - Must match app settings; HubSpot appends
?code=...to it. - Defined scopes - Control permissions requested during install.
- Secure storage for tokens - Database or secrets manager.
Reference: HubSpot OAuth Guide
Step-by-Step: How do you set up a HubSpot Public App?
Step 1. Create the app in HubSpot Developer
- Go to your Developer Account → Public Apps → New App.


- Copy your Client ID and Client Secret.

- Add your Redirect URL (must match your server route).

- Choose required scopes. Build your install URL using
client_id,redirect_uri,scope, and optionalstate.


Step 2. Deploy to a secure server
- Deploy at
https://yourapp.com. - Use HTTPS for
/oauth-callback. - Keep Client Secret on the server only.
Step 3. Set environment variables
Set these variables on your server or in your hosting provider secrets panel.
HUBSPOT_CLIENT_ID=your_client_id HUBSPOT_CLIENT_SECRET=your_client_secret HUBSPOT_SCOPES="crm.objects.contacts.read crm.objects.contacts.write" HUBSPOT_REDIRECT_URI="https://yourapp.com/oauth-callback" HUBSPOT_AUTH_URL="https://app.hubspot.com/oauth/authorize" HUBSPOT_TOKEN_URL="https://api.hubapi.com/oauth/v1/token"
Step 4. Build the install URL
Send the user to HubSpot to install your app.
https://app.hubspot.com/oauth/authorize ?client_id=HUBSPOT_CLIENT_ID &redirect_uri=https%3A%2F%2Fyourapp.com%2Foauth-callback &scope=crm.objects.contacts.read%20crm.objects.contacts.write &state=opaqueCsrfOrAccountHint
After consent, HubSpot redirects with ?code=....
Step 5. Exchange authorization code for tokens
Use the code once to get the first access_token and refresh_token.
curl --request POST \ --url https://api.hubapi.com/oauth/v1/token \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data 'grant_type=authorization_code' \ --data 'code=AUTH_CODE' \ --data 'redirect_uri=https://yourapp.com/oauth-callback' \ --data 'client_id=CLIENT_ID' \ --data 'client_secret=CLIENT_SECRET'
Response example
{
"access_token": "ACCESS_TOKEN",
"refresh_token": "REFRESH_TOKEN",
"expires_in": 1800,
"token_type": "bearer"
}
Store the refresh_token securely. The access token expires after expires_in seconds.
Step 6. Use the refresh token to get a new access token
When the access token expires, refresh it.
curl --request POST \ --url https://api.hubapi.com/oauth/v1/token \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data 'grant_type=refresh_token' \ --data 'refresh_token=REFRESH_TOKEN' \ --data 'client_id=CLIENT_ID' \ --data 'client_secret=CLIENT_SECRET'
Replace REFRESH_TOKEN, CLIENT_ID, and CLIENT_Secret with your real values. This request returns a new access token. Continue using the same refresh token unless HubSpot issues a new one.
Step 7. Handle common errors
- 401 invalid_grant → Refresh token revoked or scopes changed. Reinstall app.
- Insufficient scopes → Ensure all scopes are in the install URL.
How can this be implemented in n8n?
| Node | Configuration | Purpose |
|---|---|---|
| HTTP Request — Exchange code | POST .../oauth/v1/token with body from section 3.5. Use credentials for CLIENT_ID and CLIENT_SECRET. | Gets initial access and refresh tokens. |
| Set — Save tokens | Map and persist access_token, refresh_token, expires_in, and your hub_id linkage. | Saves tokens. |
| HTTP Request — Refresh | POST .../oauth/v1/token with grant_type=refresh_token, refresh_token, client_id, client_secret. | Gets a new access token when expired. |
| Error handling branches | Handle non 2xx and timeouts. | Reliability. |
What are best practices?
- Always use HTTPS for redirect and callback routes.
- Keep Client Secret server-side only.
- Request minimal scopes to reduce user friction.
- Store tokens with account linkage (map to Hub ID).
- Allow for long token strings in storage fields.
Quick Reference (HubSpot OAuth)
- Install URL =
https://app.hubspot.com/oauth/authorizewith client_id, redirect_uri, scope. - Token URL =
https://api.hubapi.com/oauth/v1/tokenfor both initial exchange and refresh. - Docs: HubSpot OAuth Guide
FAQ
Q: How long does a HubSpot access token last?
A: Typically 30 minutes (expires_in: 1800).
Q: Do refresh tokens expire?
A: They can be revoked if scopes change or the app is uninstalled. Otherwise, they are long-lived.
Q: Can I use one refresh token for multiple accounts?
A: No. Each HubSpot account generates a unique refresh token.

