Connecting HubSpot
On the Connections screen, connect HubSpot in one of two modes. Whichever you pick, we verify the credentials, resolve your portal, and probe which objects the token can reach — all at connect time. Credentials are encrypted at rest and never shown again after you connect.
OAuth vs Private App token #
- OAuth — click Connect via OAuth and authorize the app. The right scopes are requested for you, and tokens refresh automatically. Best for most users.
- Private App token — click Use a key instead and paste a Private App
access token (
pat-na1-…). Best when you can’t install an OAuth app. You’re responsible for granting the matching scopes on the private app yourself.
Heads-up: in Private App mode the granted scope list isn’t introspectable, so the app can’t tell up front whether property creation is allowed. The Custom fields panel will still let you try — see Custom fields.
OAuth scopes requested #
When you connect via OAuth, the app requests exactly these scopes:
| Scope | What it’s for |
|---|---|
oauth |
Base scope for the OAuth install. |
crm.objects.companies.read / .write |
Match, create & update Companies from Stripe customers. |
crm.objects.contacts.read / .write |
Create & update Contacts for individual customers. |
crm.objects.deals.read / .write |
Deal object access. |
crm.objects.products.read / .write |
Products from Stripe prices. |
crm.objects.subscriptions.read / .write |
Native Subscriptions (Commerce Hub only). |
crm.objects.line_items.read / .write |
Subscription and invoice line items. |
crm.objects.invoices.read / .write |
Create & track native HubSpot invoices (all tiers). |
crm.schemas.{companies,contacts,deals,subscriptions,invoices}.write |
Only for the Create fields button — creating the stripe_* custom properties. There’s no products schema scope; product property creation rides on crm.objects.products.write instead. |
e-commerce |
Legacy e-commerce scope covering products. |
The crm.schemas.*.write scopes are only needed for the Custom fields panel’s
Create fields button. If you add the stripe_* properties yourself, you can
skip them — or grant them only for the objects you actually sync. Note there is no
crm.schemas.products.write scope in HubSpot; creating product properties relies
on crm.objects.products.write instead, so don’t add a products schema scope (it
would break the whole authorize request).
Reconnect after a scope change. OAuth scopes are baked into the token at authorization time. If you need different scopes later (for example, to enable property creation), reconnect via OAuth so a fresh token is issued.
Commerce Hub gating #
Only the native Subscription object requires Commerce Hub. Everything else — Companies, Contacts, Products, Line items, and native Invoices — works on all hub tiers (Free and up). On connect we probe read access to each object; subscriptions access doubles as the Commerce Hub signal. If your portal doesn’t have Commerce Hub, connect anyway — every other object still syncs.
Invoice settings (if you sync invoices) #
The sync mirrors each Stripe invoice’s lifecycle onto the native HubSpot invoice (draft → open → paid/voided). HubSpot locks an invoice’s fields once it’s open unless you allow edits, so before your first invoice sync, enable this in your portal:
Settings → Objects → Invoices → Setup → Invoice management
- ✅ Allow edits on open invoices — required. Without it, every update to an already-open invoice (status changes, refreshed Stripe figures) is rejected by HubSpot.
- ✅ Allow invoice deletion (recommended) — lets a reconcile clean up invoices that no longer exist in Stripe. Note HubSpot won’t delete an invoice that’s tied to a payment.
How status and validity work #
A HubSpot connection is valid when a portal id resolves. On connect we call
HubSpot’s token-info endpoint (OAuth) or account-info endpoint (Private App) to
read your portal id; if none resolves, the credentials are treated as invalid
and the connection is saved with status error and the message so you can fix
and retry.
Once connected, the card shows the auth mode, your portal id, and an Object access panel listing which objects the token can reach. Any object marked with an ✗ means the matching scope or permission is missing — grant it and re-test, or syncing that object will fail.
Testing and disconnecting #
- Test re-verifies the stored credentials, re-resolves the portal id, and re-runs the object-access probe.
- Disconnect removes the connection and its stored credentials.
GET /v1/connections
GET /v1/connections/hubspot
GET /v1/connections/hubspot/oauth-url
POST /v1/connections/hubspot/key
POST /v1/connections/hubspot/test
DELETE /v1/connections/hubspot
The OAuth flow returns to /api/connections/hubspot/callback.
Where to next #
- Custom fields — the opt-in
stripe_*properties and how to create them. - Connecting Stripe — connect the other side.