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
-
Open the HubSpot developer account and create a new app — this is your own app, not Botseon's.
-
Under Redirect URL, paste the redirect URL from
docs/connectors/README.md— with the quickstart's defaultBOTSEON_PUBLIC_URL, that is:http://127.0.0.1:3000/api/oauth/hubspot/callback -
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. Taggedwrite; the approval card names the contact's email.hubspot.update_deal— changes a deal's properties. Taggedwrite; the approval card names the deal.hubspot.log_note— logs a note against a contact or deal. Taggedwrite, notexternal_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, ordeal), 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.