Skip to content

HubSpot Connection

Connect to HubSpot to manage CRM objects (contacts, companies, deals, tickets) and drive workflows from HubSpot events via the HubSpot Trigger.

Configuration

FieldDescriptionRequired
Private App Access TokenPortal-scoped token used by every action nodeYes
Client SecretPublic/developer app's Client Secret; used to verify webhook signaturesOnly for the HubSpot Trigger
App IDNumeric ID of your developer appOnly for automatic webhook URL updates
Developer API KeyDeveloper-account-scoped key used to call HubSpot's Webhooks Settings APIOnly for automatic webhook URL updates

The Private App Access Token alone is enough for the action nodes (Contacts, Deals, Companies, Tickets). The other three fields only matter if you also want to use the HubSpot Trigger.

Quick start (action nodes only)

If you only need to read/write CRM objects, you can skip everything below the Access Token row.

  1. Log in to HubSpot as a super admin.
  2. Settings (top-right gear) → IntegrationsPrivate AppsCreate a private app.
  3. Basic Info tab — name your app.
  4. Scopes tab — add the CRM scopes your workflows need:
    • crm.objects.contacts.read, crm.objects.contacts.write
    • crm.objects.companies.read, crm.objects.companies.write
    • crm.objects.deals.read, crm.objects.deals.write
    • …and so on for tickets/quotes/etc.
  5. Create appContinue creating to confirm.
  6. Copy the Access Token (starts with pat-na1-... or pat-eu1-...) into the connection.
  7. Save.

Adding HubSpot Trigger support

The HubSpot Trigger fires on CRM events your developer app subscribes to (e.g. contact.creation, deal.propertyChange). To make it work end-to-end, HubSpot needs to know where to POST those events. The connection supports two ways to set that URL:

You provide the connection with an App ID + Developer API Key. On save, the backend calls PUT /webhooks/v3/{appId}/settings to point HubSpot's webhook delivery at this connection's callback URL. Re-seeds and WEBHOOK_BASE_URL changes are automatically picked up.

Setup:

  1. Create a developer app (a different app type from the Private App above):
    • Go to https://developers.hubspot.com → log in with your developer account.
    • AppsCreate app.
    • App Info tab — name, description, logo.
    • Auth tab — copy the Client ID and Client Secret. The Client Secret goes into the connection.
    • Webhooks tab — skip URL configuration for now; the backend will set it.
    • App Info tab → copy the App ID (a numeric value) into the connection.
  2. Developer API Key:
    • Still on https://developers.hubspot.com → click your developer account name (top-right) → Account (or Profile & Preferences) → API Key tab.
    • Generate Key or copy the existing one. This is account-level, not app-level — one key works for every developer app you own.
    • Paste into the connection's Developer API Key field.
  3. Save the connection. Status switches to active and HubSpot's webhook target URL is now:
    <WEBHOOK_BASE_URL>/api/conn-webhook/<connection-webhook-token>
  4. Subscribe to events (still in the HubSpot developer dashboard, since subscriptions are semantic and we don't touch them):
    • Your app → Webhooks tab → Create subscription.
    • Pick the event type your trigger workflow filters on (e.g. contact.creation).
    • Save.
  5. Install the developer app on the test portal:
    • On the developer dashboard click your app → Install URL (OAuth).
    • Visit the URL in a browser; HubSpot prompts you to install on a portal of your choice. This is the portal whose events you'll receive.

Developer key vs Private App token

The two HubSpot credentials this connection holds look similar but are scoped completely differently:

  • Private App Access Token (pat-na1-...) — portal-scoped. Lets us read/write CRM data inside that portal. One per portal.
  • Developer API Keydeveloper-account-scoped. Lets us manage developer apps you own (including webhook settings). One per developer account, covers every app you own.

You need both when using the trigger.

Option B — Manual (paste the URL yourself)

If you can't generate a Developer API Key (e.g. you don't own the developer account), the trigger still works — just leave App ID and Developer API Key blank.

Setup:

  1. Save the connection with only Access Token + Client Secret. Status will be pending_manual_setup.
  2. Find the webhook URL — either from the connection's Webhook URL display in the UI, or:
    bash
    curl -H "Authorization: Bearer $TOKEN" \
      https://<your-deployment>/api/connections/<connection-id>/webhook-info \
      | jq '.data.url'
  3. Open your developer app → Webhooks tab → paste the URL into Target URL and save.
  4. Add event subscriptions (e.g. contact.creation).

Trade-off vs Option A: if the webhook token changes (re-seed, environment swap), you have to repeat steps 2–3.

Required scopes (action nodes)

The Private App's scopes determine which action nodes work. Common picks:

ActionScope
Contacts (read/write)crm.objects.contacts.read, crm.objects.contacts.write
Companiescrm.objects.companies.read, crm.objects.companies.write
Dealscrm.objects.deals.read, crm.objects.deals.write
Ticketscrm.objects.tickets.read, crm.objects.tickets.write

For the developer app (Trigger side), only the Webhook scope and the per-event scopes (e.g. crm.objects.contacts.read) are needed.

Self-fire & loop hazard

HubSpot fires webhook events for ANY change on a subscribed object — including changes made via the same app's API calls. That means:

  • An action workflow that creates a contact via the Contacts node will fire contact.creation back to the HubSpot Trigger workflow on the same connection. This is useful for integrations:test self-loops.
  • A trigger workflow that filters on contact.creation AND creates another contact in response → infinite loop.

Mitigations:

  • End trigger workflows in non-creating nodes (a switch with no rules, a datastore write, an email send) — the bundled fixture uses a no-op switch for this reason.
  • Or filter on contact.propertyChange for a specific property the trigger workflow doesn't touch.

Troubleshooting

SymptomLikely causeFix
connection_test failed: ... 401 on saveAccess Token wrong, revoked, or scope mismatchRegenerate the Private App access token, double-check selected scopes.
Status pending_manual_setup after saving with App ID + Developer KeyApp ID isn't numeric, or Developer Key invalidApp ID is from the developer app's Basic Info page (a number like 1234567). Developer Key is from your developer account → Account → API Key.
webhook_status=error with 403 / scope insufficientDeveloper Key was generated by an account that doesn't own this appUse the API Key from the developer account that owns this app. One account, many apps.
invalid_url or URL must be HTTPS on saveWEBHOOK_BASE_URL isn't HTTPS or isn't publicly reachableSet WEBHOOK_BASE_URL to a public HTTPS endpoint (Cloudflare tunnel in dev) and re-save.
Trigger workflow never fires after a real changeApp not installed on this portal, or wrong event subscribedRe-visit the developer app's Install URL (OAuth) for this portal; verify subscriptions cover the event your trigger filters on.

Usage

Once created, this connection is available to:

TIP

Private app tokens inherit the scopes you select during creation. Add only what your workflows actually need to keep blast radius small.