Skip to main content

HubSpot

Proxy for the HubSpot CRM v3 API via the managed gateway.

What this connector does​

Gives agents gated access to a HubSpot account for:

  • Reading and writing Contacts, Companies, Deals, and Tickets.
  • Creating and reading Notes/engagements (activity logging).
  • Searching any CRM object type with filters or a free-text query.

All operations are gated by PBAC policy. Every call flows: agent → gateway → /introspect (with this connector's resource_type) → OPA decision → HubSpot API → response.

The connector supports two auth modes, chosen per managed resource at install time:

  • Per-user passthrough (idp_passthrough, recommended) — the gateway forwards each calling user's own HubSpot OAuth token (captured when they log in to PolicyArc through the HubSpot IdP). HubSpot enforces its own object-level permissions, and its audit shows the real person. HubSpot is OAuth 2.0 (not OIDC); PolicyArc resolves the user's identity via HubSpot's token introspection endpoint (/oauth/v3/token/introspect) — the bundled hubspot IdP preset wires this up.
  • Shared static token (static) — every call uses one operator-held HubSpot private-app token or service token via Authorization: Bearer. PolicyArc still enforces per-user policy and audit in front, but HubSpot sees a single app identity.

Create a HubSpot Projects app (for idp_passthrough)​

Per-user passthrough needs an OAuth app with a configurable redirect URL and scopes. HubSpot has three app types; only a Projects app (created with the HubSpot CLI) gives all of Client ID + Client Secret + redirect URL + scopes.

Steps (verify exact commands against current HubSpot CLI docs — the tooling changes, and this is the confusing part):

  1. Install + authenticate the CLI: npm i -g @hubspot/cli, then hs init.
  2. Create a project with a public-app component: hs project create (pick an app template).
  3. In the app's config (*-hsmeta.json / public-app settings):
    • Auth: OAuth.
    • Redirect URL: <your-AS-issuer>/oauth2/callback.
    • Required scopes (the 8 below).
  4. hs project upload to deploy, then copy the app's Client ID and Client secret.

Then register a HubSpot IdP in PolicyArc (provider key hubspot) with that Client ID/secret — pick HubSpot in the IdP setup wizard and the bundled preset fills the endpoints (authorize, token, and the introspection endpoint used to resolve the user). Users must log in to PolicyArc through that IdP for passthrough to capture their token.

Prerequisites​

  • A HubSpot account.
  • Passthrough mode: the Projects app + HubSpot IdP registration above.
  • Static mode: a HubSpot private-app token or service token with the scopes you want to gate, stored in a PolicyArc secret.

Install​

Pick the auth mode in the install form's Authentication field (defaults to idp_passthrough). The static-token field only appears when static is selected; passthrough installs need no secret.

Verify​

The gateway path is /gateway/<resource name>/<HubSpot REST path>. The install form names the resource after the connector id without its vendor prefix, and the name cannot be edited, so the resource answers at /gateway/hubspot/...:

curl "https://<your-as>/gateway/hubspot/crm/v3/objects/contacts" \
-H "Authorization: Bearer <token>"

In idp_passthrough mode, the default, <token> must be a user token, issued through an authorization-code login by a user who signed in with the hubspot IdP. A 200 with the contact list means policy allowed the call and HubSpot accepted the forwarded user token. A client_credentials token carries no user, so the gateway answers 502 with "Upstream auth failed: token resolution requires an authenticated subject". In static mode the gateway sends its configured token itself, and an agent's client_credentials token is enough. Getting tokens covers both grants.

What can go wrong​

SymptomCauseFix
401 from HubSpot (static mode)Token wrong, expired, or missing the needed scopeCheck the secret value and the token's scopes.
403 from the gatewayAgent lacks the required scopeRequest a token with the hs:* scope matching the route.
404 at the gatewayWrong pathGateway path is /gateway/<resource name>/<HubSpot REST path>; a default install is named hubspot. HubSpot CRM paths start with /crm/v3/....
401 with "error": "idp_link_required" (passthrough)The user has no linked hubspot token, or the linked token lacks a scope the connector now requiresOpen the link_url from the response body and sign in again.
502 "Upstream auth failed: token resolution requires an authenticated subject" (passthrough)The token is a client_credentials token, which carries no userUse a user token from an authorization-code login.
Passthrough subject not resolvedThe OAuth app's token introspection didn't return a userEnsure it's a Projects app (user-level token) with the scopes below.

Scopes​

ScopeMeaningRoutes
hs:contacts:read / hs:contacts:writeRead / mutate contactsget/list/create/update contacts
hs:companies:read / hs:companies:writeRead / mutate companiesget/list/create/update companies
hs:deals:read / hs:deals:writeRead / mutate dealsget/list/create/update deals
hs:tickets:read / hs:tickets:writeRead / mutate ticketsget/list/create/update tickets
hs:notes:read / hs:notes:writeRead / create notesget/create notes
hs:crm:searchSearch any CRM object typesearch_crm

HubSpot OAuth scopes to grant the Projects app (passthrough) — these map to the tools above:

oauth
crm.objects.contacts.read crm.objects.contacts.write
crm.objects.companies.read crm.objects.companies.write
crm.objects.deals.read crm.objects.deals.write
tickets

Scope notes (verified against a real HubSpot Projects app):

  • Tickets use the legacy tickets scope — standard apps do not offer granular crm.objects.tickets.read/write, and requesting them makes HubSpot reject the entire authorize with a scope mismatch (login can't complete).
  • Notes have no grantable scope on standard apps, yet create_note / get_note work without one — so no notes scope is requested. (If HubSpot ever rejects a note call on your hub, that's a per-hub permission issue, not a missing OAuth scope.)

Policy authors can scope a rule with input.context.rs_context.resource_type == "urn:connector:identos:hubspot". Per-object rules compare input.context.rs_context.resource_id with the bare object id path value, not with hubspot://contacts/.... search_crm has no object id in its path and carries the object type instead, such as contacts. The gateway passes the first path parameter the route's resource_id_template names (ConnectorV1Adapter.java).

Setup fields​

FieldRequiredDefaultPurpose
upstream_auth.typeyesidp_passthroughAuthentication — idp_passthrough (per-user, default) or static (shared token).
token_envconditional—Secret holding a HubSpot private-app/service token. Static mode only; hidden when idp_passthrough is selected.

MCP tools​

ToolScopeDescription
get_contact / list_contactshs:contacts:readGet / list contacts.
create_contact / update_contacths:contacts:writeCreate / update a contact.
get_company / list_companieshs:companies:readGet / list companies.
create_company / update_companyhs:companies:writeCreate / update a company.
get_deal / list_dealshs:deals:readGet / list deals.
create_deal / update_dealhs:deals:writeCreate / update a deal.
get_ticket / list_ticketshs:tickets:readGet / list tickets.
create_ticket / update_tickeths:tickets:writeCreate / update a ticket.
get_notehs:notes:readGet a note/engagement.
create_notehs:notes:writeCreate a note, optionally associated with CRM records.
search_crmhs:crm:searchSearch a CRM object type with filters/query.

The 19 tools are a deliberate, extensible subset of HubSpot's CRM API; more objects/endpoints can be added later via manifest edits (routes + tools + scopes).

Manifest reference​

  • ID: identos.hubspot
  • Version: 1.1.0
  • Resource type: urn:connector:identos:hubspot
  • Capabilities: mcp

Supported auth modes​

TypeDetails
idp_passthroughrequires IdP hubspot
staticscheme bearer; setup fields: token_env

Setup fields​

IDLabelDefaultSecret?Notes
upstream_auth.typeAuthenticationidp_passthroughnoidp_passthrough forwards each user's own HubSpot OAuth token (recommended). static uses a single shared private-app/service token via Authorization: Bearer.
token_envStatic token—yesPick a secret containing a HubSpot private-app or service token. Required only for the static auth mode. / shown when upstream_auth.type == 'static'

Scopes​

Scope
hs:contacts:read
hs:contacts:write
hs:companies:read
hs:companies:write
hs:deals:read
hs:deals:write
hs:tickets:read
hs:tickets:write
hs:notes:read
hs:notes:write
hs:crm:search

Routes​

MethodPatternScopeResource template
GET/crm/v3/objects/contacts/{id}hs:contacts:readhubspot://contacts/{{id}}
GET/crm/v3/objects/contactshs:contacts:readhubspot://contacts
POST/crm/v3/objects/contactshs:contacts:writehubspot://contacts
PATCH/crm/v3/objects/contacts/{id}hs:contacts:writehubspot://contacts/{{id}}
GET/crm/v3/objects/companies/{id}hs:companies:readhubspot://companies/{{id}}
GET/crm/v3/objects/companieshs:companies:readhubspot://companies
POST/crm/v3/objects/companieshs:companies:writehubspot://companies
PATCH/crm/v3/objects/companies/{id}hs:companies:writehubspot://companies/{{id}}
GET/crm/v3/objects/deals/{id}hs:deals:readhubspot://deals/{{id}}
GET/crm/v3/objects/dealshs:deals:readhubspot://deals
POST/crm/v3/objects/dealshs:deals:writehubspot://deals
PATCH/crm/v3/objects/deals/{id}hs:deals:writehubspot://deals/{{id}}
GET/crm/v3/objects/tickets/{id}hs:tickets:readhubspot://tickets/{{id}}
GET/crm/v3/objects/ticketshs:tickets:readhubspot://tickets
POST/crm/v3/objects/ticketshs:tickets:writehubspot://tickets
PATCH/crm/v3/objects/tickets/{id}hs:tickets:writehubspot://tickets/{{id}}
POST/crm/v3/objects/noteshs:notes:writehubspot://notes
GET/crm/v3/objects/notes/{id}hs:notes:readhubspot://notes/{{id}}
POST/crm/v3/objects/{objectType}/searchhs:crm:searchhubspot://{{objectType}}

MCP tools​

NameScopeDescription
get_contacths:contacts:readGet a HubSpot contact by record ID.
list_contactshs:contacts:readList HubSpot contacts (cursor-paginated).
create_contacths:contacts:writeCreate a HubSpot contact.
update_contacths:contacts:writeUpdate a HubSpot contact.
get_companyhs:companies:readGet a HubSpot company by record ID.
list_companieshs:companies:readList HubSpot companies (cursor-paginated).
create_companyhs:companies:writeCreate a HubSpot company.
update_companyhs:companies:writeUpdate a HubSpot company.
get_dealhs:deals:readGet a HubSpot deal by record ID.
list_dealshs:deals:readList HubSpot deals (cursor-paginated).
create_dealhs:deals:writeCreate a HubSpot deal.
update_dealhs:deals:writeUpdate a HubSpot deal.
get_tickeths:tickets:readGet a HubSpot ticket by record ID.
list_ticketshs:tickets:readList HubSpot tickets (cursor-paginated).
create_tickeths:tickets:writeCreate a HubSpot ticket.
update_tickeths:tickets:writeUpdate a HubSpot ticket.
create_notehs:notes:writeCreate a note/engagement, optionally associated with CRM records.
get_notehs:notes:readGet a note/engagement by record ID.
search_crmhs:crm:searchSearch a CRM object type with filters/query.