HubSpot connector setup

Operator·4 minutes to read

This page is the setup half — creating the OAuth app HubSpot needs in its own developer console and getting its client id and secret into this deployment. Authorising a connection afterwards is the Authenticate control on the plugin sheet, which is what actually sends you to HubSpot.

An unlisted OAuth app is capped at 25 installs until a Marketplace listing clears review — that review is still outstanding for this build, and it gates customers rather than this setup.

1. Create the OAuth app in the HubSpot developer console

  1. Open the HubSpot developer account and create a new app — this is your own app, not Botseon's.

  2. Under Redirect URL, paste the redirect URL from docs/connectors/README.md — with the quickstart's default BOTSEON_PUBLIC_URL, that is:

    http://127.0.0.1:3000/api/oauth/hubspot/callback
    
  3. Save. The console shows you a Client ID and a Client secret — copy both now.

2. Scopes

Request the narrowest set that covers the tools this connector exposes (F-PLUG-1: minimum scopes per capability, read and write requested separately). These are the granular scopes, which went GA on 2026-09-08; the classic broad scopes (contacts, tickets) still work today but are deprecated and stop working after 2027-03-01, and this connector never requests them:

Read:

  • oauth — identify the HubSpot account.
  • crm.objects.contacts.read — search and read contacts.
  • crm.objects.deals.read — search and read deals.

Write, on top of the read set:

  • crm.objects.contacts.write — create a contact.
  • crm.objects.deals.write — change a deal, and log a note against a contact or deal.

3. Enter the app

One command, with the client secret on standard input — there is no flag that takes it and no environment variable that holds it (see docs/connectors/README.md):

printf %s "$CLIENT_SECRET" | botseon oauth-app set \
  --org <organisation id> --slug hubspot \
  --client-id <the client ID from step 1> \
  --auth-url https://app.hubspot.com/oauth/authorize \
  --token-url https://api.hubspot.com/oauth/v3/token \
  --configured-by <your user id>

The token URL is HubSpot's v3 endpoint, not the api.hubapi.com/oauth/v1/token of HubSpot's legacy guide: v3 answers with the scopes the member granted, and v1 answers with none, which would leave every account connected with no tool a bot may use.

It stores the secret encrypted and prints the redirect URL from step 1 back, so you can check the two match. botseon oauth-app show --org <organisation id> --slug hubspot says whether an app is configured and under which client id; nothing prints the secret back.

Never put either value in a bot's description, a memory, or anywhere else a model reads as ordinary text.

4. What this connector does and does not do

  • hubspot.search_contacts — search contacts in the portal.
  • hubspot.get_contact — one contact and its properties.
  • hubspot.search_deals — search deals in the portal.
  • hubspot.get_deal — one deal and its properties.
  • hubspot.create_contact — creates a contact. Tagged write; the approval card names the contact's email.
  • hubspot.update_deal — changes a deal's properties. Tagged write; the approval card names the deal.
  • hubspot.log_note — logs a note against a contact or deal. Tagged write, not external_send: a note is internal to the portal and reaches nobody outside the organisation. The bot says which kind of record it means (objectType: contact, the default, or deal), because HubSpot links a note to a contact and to a deal through different association types (202 and 214).

Every call goes to the CRM v3 object endpoints under https://api.hubapi.com; this connector never calls a v1 object endpoint. Access tokens are short-lived (around 30 minutes), so the refresh arm matters here. HubSpot has a Frankfurt data centre, but whether EU hosting changes the API base URL is explicitly unverified, so this connector calls api.hubapi.com regardless of where a portal is hosted.

This connector's catalogue row is available: true since 2026-10-21 (an operator ruling ahead of its live smoke). The live smoke, packages/connectors/src/hubspot.live.test.ts, runs every night on main once the operator has stored HUBSPOT_TEST_TOKEN and the test OAuth app's HUBSPOT_TEST_CLIENT_ID, HUBSPOT_TEST_CLIENT_SECRET and HUBSPOT_TEST_REFRESH_TOKEN (runbook); until then its cases skip. Every PR runs packages/connectors/src/hubspot.wire.test.ts, which sends all seven tools and a token renewal through the real safeFetch and fakes only the socket below it. Every member can press Add on it under Plugins; signing in needs the app from section 3, and with --deployment one app serves every organisation. Until it is set, Authenticate sends the member back to the plugin sheet and nothing is stored.

Last verified against build 3f515ac.