Technical Guide: How to Create a HubSpot Public App with OAuth 2.0 and Token Refresh

September 16, 2025
Technical Guide: How to Create a HubSpot Public App with OAuth 2.0 and Token Refresh

Step-by-step HubSpot OAuth guide: create a public app, exchange code for tokens, refresh access, and handle errors.

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 a refresh_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 optional state.

    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/authorize with client_id, redirect_uri, scope.
    • Token URL = https://api.hubapi.com/oauth/v1/token for 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.

    Subscribe to the Profitable Pathways Newsletter

    Quarterly Insights into marketing data, attribution, and scaling what works.

    Book a Strategy Session