# HubSpot — CRM API: configuration

> Canonical URL: https://developers.apideck.com/apis/crm/hubspot/configuration

# How to create an OAuth app in HubSpot

This guide is for the **application owner**, the team that ships an integration on Apideck. You
will create a HubSpot app, register Apideck's redirect URL and the connector's scopes, and paste
the resulting Client ID and Client Secret into the Apideck Dashboard. Once that is done, your
consumers can authorize their own HubSpot accounts without any further work from you.

The same app covers both unified APIs this connector serves, **CRM** and **Lead**, so scopes,
redirect URL and webhooks are configured once.

## Before you start

- A **HubSpot account**. Any edition works, including the free CRM. Legacy *developer* accounts
  were converted to standard HubSpot accounts on March 9, 2026, so the account you already sign
  in with is the one you use; there is no separate developer portal login to create.
- The **HubSpot CLI**, installed and authenticated (`npm install -g @hubspot/cli@latest`, then
  `hs account auth`). HubSpot documents a minimum CLI version on the
  [create-an-app page](https://developers.hubspot.com/docs/apps/developer-platform/build-apps/create-an-app).
  Update before you start rather than debugging an old client.
- Access to your Apideck [HubSpot connector settings](https://platform.apideck.com/configuration/crm/hubspot).
  That page is filed under CRM and configures the connector for both the CRM and Lead APIs.

> **Already have a HubSpot app?** Existing public apps built on the legacy flow continue to
> function as-is; you do not need to rebuild anything. Skip to steps 4 (Redirect URL) and
> 5 (Scopes) below, which are the only two settings Apideck depends on. Migrating an existing app to the current platform is optional; see
> [Migrate an app](https://developers.hubspot.com/docs/apps/developer-platform/build-apps/migrate-an-app/overview)
> if you want the newer tooling.

## 1. Create your app

New HubSpot apps are built on the **Projects-based developer platform**, driven from the CLI. The
older "Create an app" button in the developer UI no longer creates public apps: HubSpot disabled
legacy public app creation on **May 26, 2026** for accounts created on or after that date, and on
**June 23, 2026** for all older accounts. If you are looking for that button, this is why it is
gone; the flow below replaces it.

```bash
hs account auth          # authenticate the CLI against your HubSpot account
hs project create        # scaffold the project
```

At the prompts:

| Prompt | Choose |
|--------|--------|
| Base contents | **App** |
| Distribution | **Marketplace**: see step 2 |
| Auth | **OAuth** |
| Features | Include **Webhooks** if you want event delivery (step 7) |

Then upload the project so HubSpot creates the app and issues credentials:

```bash
hs project upload
```

The app-level settings in steps 2–6 are configured in the project's `app-hsmeta.json` file and
applied by re-running `hs project upload`. Webhook subscriptions are the exception: they live in
their own `*-hsmeta.json` file under `src/app/webhooks/`, covered in step 7. HubSpot's
[Create a new app using the CLI](https://developers.hubspot.com/docs/apps/developer-platform/build-apps/create-an-app)
and [App configuration](https://developers.hubspot.com/docs/apps/developer-platform/build-apps/app-configuration)
pages are the authoritative reference for the file's full shape.

## 2. Distribution mode

Choose **Marketplace** distribution (`"distribution": "marketplace"`) with `"type": "oauth"`. This
is the mode that lets other companies install your app in *their* HubSpot accounts, which is what
an Apideck integration does. (Listing on the App Marketplace is a separate, optional step: see
step 9. Choosing marketplace distribution does not publish anything.)

Distribution mode also decides which rate-limit regime you run under, so it is worth getting right
the first time:

- **Marketplace / public distribution**: each connected HubSpot account carries its own request
  allowance, so one busy consumer cannot starve the others. This is the regime the Overview's
  At a Glance rate-limit figure describes.
- **Private distribution** (a single-account app): requests are billed against that one HubSpot
  account's tier instead: Free and Starter get 100 requests/10s and 250,000/day, Professional 190
  requests/10s and 625,000/day, Enterprise 190 requests/10s and 1,000,000/day. HubSpot's API Limit
  Increase add-on raises these. A private app cannot serve multiple consumers, so pick this only
  for a genuinely single-tenant integration.

Full detail lives in HubSpot's
[usage guidelines](https://developers.hubspot.com/docs/developer-tooling/platform/usage-guidelines).

## 3. Basic information

Set the app's name, description, logo and support contact. Your consumers see these on the HubSpot
authorization screen when they connect, so use the branding of *your* product: this is the screen
that tells them who is asking for access to their CRM.

## 4. Redirect URL

Register Apideck's callback as a valid OAuth redirect URL. It must be **exactly**:

```
https://unify.apideck.com/vault/callback
```

In `app-hsmeta.json` this goes in the `auth.redirectUrls` array:

```json
"auth": {
  "type": "oauth",
  "redirectUrls": ["https://unify.apideck.com/vault/callback"],
  "requiredScopes": ["..."],
  "optionalScopes": []
}
```

Any other value (a localhost URL from a scaffold template, your own backend, a trailing slash)
makes HubSpot reject the authorization with a redirect URI mismatch. Run `hs project upload` after
editing.

## 5. Scopes: these must match exactly

This is the step that most often breaks a first connection. **The scopes configured on your HubSpot
app must exactly match the scopes Apideck requests during OAuth.** Any mismatch (a missing scope,
or a scope Apideck requests that your app does not declare) fails consumer authorization with an
`insufficient scopes` error rather than degrading gracefully.

The connector currently requests the following set (identical for CRM and Lead):

```
crm.schemas.companies.read
crm.schemas.companies.write
crm.schemas.contacts.read
crm.schemas.contacts.write
crm.schemas.deals.read
crm.schemas.deals.write
crm.objects.owners.read
crm.objects.contacts.read
crm.objects.contacts.write
crm.objects.companies.read
crm.objects.companies.write
crm.objects.deals.read
crm.objects.deals.write
crm.lists.read
crm.lists.write
sales-email-read
```

Add these to `auth.requiredScopes` in `app-hsmeta.json` and upload.

**This list is current as of writing, not a contract.** Before you configure the app, check the
scope list shown on your [Apideck HubSpot connector settings](https://platform.apideck.com/configuration/crm/hubspot)
page: that is the authoritative source for what Apideck will actually request, and it is what
your app has to match. If you override the scopes on the Apideck side, match your override, not
the list above.

Two additions to consider:

- If you call HubSpot endpoints through the **Proxy API** that fall outside the connector's own
  operations, add the scopes those endpoints need as well.
- HubSpot's [scopes reference](https://developers.hubspot.com/docs/apps/developer-platform/build-apps/authentication/scopes)
  documents what each scope grants and which HubSpot editions include it.

## 6. Copy your Client ID and Secret into Apideck

After `hs project upload`, retrieve the credentials HubSpot issued:

1. Run `hs project open` to open the project page in HubSpot.
2. Under **Project Components**, click your app's name.
3. Open the **Auth** tab and copy the **Client ID** and **Client secret** from the
   **Client credentials** section.

Paste both into the [Apideck HubSpot connector settings](https://platform.apideck.com/configuration/crm/hubspot)
under **Use your own client credentials**, and save.

![Apideck HubSpot connector settings: client credentials](https://res.cloudinary.com/apideck/image/upload/v1682082012/docs/connectors/hubspot/apideck-Hubspot-client-secret.png)

That completes the credential handoff: from this point Apideck runs the OAuth flow against *your*
app, and your consumers never see or handle the Client Secret. Apideck also handles token refresh:
HubSpot access tokens are short-lived (30 minutes) and are renewed automatically.

## 7. Register webhook subscriptions (optional)

Webhook subscriptions on HubSpot are **app-level, not per-connection**: you register one
subscription set in your app, and it delivers events for every consumer who has connected. You do
this once, manually: it is not created for you when a consumer connects, and it is not repeated
per customer.

**Step 1: get the target URL.** Open your
[Apideck HubSpot connector settings](https://platform.apideck.com/configuration/crm/hubspot) and
copy the full **Webhook Execute URL** from the bottom of the page.

![Apideck HubSpot connector settings: Webhook Execute URL](https://res.cloudinary.com/apideck/image/upload/v1682077519/docs/connectors/hubspot/apideck-Hubspot-webhook.png)

**Step 2: configure the subscriptions.** In your project, add a `*-hsmeta.json` file under
`src/app/webhooks/` with the Apideck Webhook Execute URL as `targetUrl`. HubSpot's
[Configure a webhook subscription](https://developers.hubspot.com/docs/apps/developer-platform/add-features/configure-webhooks)
page documents the file's exact shape, and one detail on that page matters more than any other
here: `subscriptions` is not one flat list. It splits into three arrays, and **only two of them
work with Apideck**:

- **`legacyCrmObjects`**: the classic event names (`contact.creation`, `deal.propertyChange`, …).
  **Use this array** for the contact, company and deal events in the table below.
- **`hubEvents`**: holds `contact.privacyDeletion`. **Use this array** for that one event.
- **`crmObjects`**: HubSpot's newer `object.*` event format, which that page recommends as the
  standard. **Do not use it for this connector**: HubSpot accepts the subscription, but Apideck
  dispatches on the classic event names, so everything delivered in the `object.*` format is
  dropped silently: no error on either side, just no events arriving.

Then `hs project upload`.

Subscribe using the classic event names below; these are the subscription types the connector
recognizes, and each maps to a unified Apideck event:

| HubSpot subscription type | Apideck unified event |
|---|---|
| `contact.creation` | `contact.created` |
| `contact.propertyChange` | `contact.updated` |
| `contact.deletion` | `contact.deleted` |
| `contact.privacyDeletion` | `contact.deletion_requested` |
| `company.creation` | `company.created` |
| `company.propertyChange` | `company.updated` |
| `company.deletion` | `company.deleted` |
| `deal.creation` | `opportunity.created` |
| `deal.propertyChange` | `opportunity.updated` |
| `deal.deletion` | `opportunity.deleted` |

Contacts, companies and deals are the full extent of the connector's event coverage. There are no
engagement events: activities, notes, calls, meetings and tasks are not delivered by webhook, so
poll those resources instead if you need to track changes to them.

**Step 3: subscribe your own application to the Apideck events.** Registering the HubSpot side
only gets events as far as Apideck; you still need a subscription on the Apideck side to receive
them. Follow
[How to subscribe to webhook events](https://developers.apideck.com/guides/webhooks#how-to-subscribe-to-webhook-events).

![Apideck: create a webhook subscription](https://res.cloudinary.com/apideck/image/upload/v1682077519/docs/connectors/hubspot/apideck-webhook-subscribe.png)

## 8. Test the app

You do not need a separate set of credentials for testing: HubSpot uses the same app for test and
production accounts, and the API host never changes. What varies is the account you connect:

- **A developer test account** is the cleanest option and is free. In your HubSpot account, go to
  **Development > Testing > Test Accounts** and click **Create developer test account**. You can
  create up to 10, each with a 90-day trial of many Enterprise features. A test account expires
  after 90 days with no API calls against it and can be renewed from the same page. See
  [HubSpot account types](https://developers.hubspot.com/docs/getting-started/account-types).
- **Apideck's temporary shared HubSpot test credentials** let you exercise the connector before
  your own app exists. Ask [Apideck Support](https://help.apideck.com/en/articles/7774658) for
  access.
- **Your own production HubSpot account** works too, but the connector writes real records. Be
  deliberate about which account you point a test run at.

## 9. Listing and certification (optional)

Nothing in this guide requires an App Marketplace listing: your app can serve consumers on
marketplace distribution without ever being listed. Both paths below are free, with no revenue
share.

- **Listing** on the App Marketplace requires the app to have active installs in at least 3
  HubSpot accounts, plus HubSpot's listing content requirements. "Active install" means a unique
  production account unaffiliated with your organization that has shown app activity recently;
  your own developer test accounts do not count toward either threshold.
- **App Certification** is requested after listing and requires substantially more traction, 60
  active installs as of writing (raised from 6 in May 2024), along with a minimum period listed
  and operational quality expectations such as a high API success rate and staying inside rate
  limits. It renews on a rolling two-year cycle.

Both thresholds are HubSpot policy and do change; confirm the current numbers on HubSpot's
[certification requirements](https://developers.hubspot.com/docs/apps/developer-platform/list-apps/apply-for-certification/certification-requirements)
and
[listing requirements](https://developers.hubspot.com/docs/apps/developer-platform/list-apps/listing-your-app/app-marketplace-listing-requirements)
pages before planning around them, and ask your HubSpot contact for an expected timeline on any
review.

## What your consumers do next

With credentials saved, scopes matched and the redirect URL registered, connecting is entirely
self-service for your consumers: they authorize their HubSpot account through Apideck Vault and
choose a default pipeline and stage for new opportunities. The authorizing user needs HubSpot's
**App Marketplace Access** permission (super admins have it by default) when new scopes are being
granted.

## FAQ and troubleshooting

### "Insufficient scopes were provided. Please contact the app developer"

The scopes on your HubSpot app do not match what Apideck requests. This is an exact-match check,
not a subset check, so it fires both when your app is missing a scope Apideck requests and when
the two lists simply differ.

To fix it, compare two lists side by side: the scope list on your
[Apideck HubSpot connector settings](https://platform.apideck.com/configuration/crm/hubspot) page,
and `auth.requiredScopes` in your app's `app-hsmeta.json` (or the **Auth** tab of the app in
HubSpot). Reconcile them, run `hs project upload`, and have the consumer authorize again: the
error is raised at authorization time, so the retry has to be a fresh authorization.

Common causes:

- One or more CRM scopes from step 5 missing on the app.
- Scopes overridden on the Apideck side for one integration and never mirrored onto the app.
- Proxy API calls that need scopes beyond the connector's own set.

### Authorization fails with a redirect URI mismatch

`auth.redirectUrls` must contain `https://unify.apideck.com/vault/callback` character for
character. Scaffolded projects ship with a localhost placeholder; replacing it and re-uploading is
easy to forget.

### I can't find "Create an app" in HubSpot

That button created legacy public apps and no longer does: creation was disabled for all accounts
by June 23, 2026. Use the CLI flow in step 1 instead. Apps you created before the cutoff are
unaffected and keep working.

### A consumer's connection stopped working

Access tokens are refreshed automatically, and HubSpot documents no scheduled expiry for refresh
tokens. What does end a connection is action on the HubSpot side: if the consumer uninstalls your
app from their HubSpot account or revokes access, the connection stops working and they have to
authorize again. Uninstalling in HubSpot also removes them from your app's install count.

### No webhook events for activities, notes or calls

Expected: see step 7. Contact, company and deal events are the whole set; poll for the rest.

### HubSpot returns 429

You are over the rate limit for the regime your distribution mode puts you in (step 2). The CRM
Search API has its own, lower limit and does not draw on the general allowance, so search-heavy
traffic is the usual first suspect. Back off and retry; if a consumer legitimately needs more
throughput, note that HubSpot's API Limit Increase add-on raises the tiered limits for private
apps only; it does not lift the per-account allowance for a marketplace-distributed app. Reducing
call volume (batch endpoints, caching, webhooks instead of polling) is the available lever there;
HubSpot's [usage guidelines](https://developers.hubspot.com/docs/developer-tooling/platform/usage-guidelines)
cover the mitigation options and the response headers that let you track usage.


[API reference](https://developers.apideck.com/apis/crm/reference.md) · [Coverage matrix](https://developers.apideck.com/apis/crm/coverage)
