HubSpot Access and API Setup

Version: 1.6
Date: April 21, 2026
Owner: AI Team


1. Purpose

This guide explains how Brainforge team members and Cursor agents should access HubSpot.

Agents in Cursor (default): use HubSpot MCP first for anything the MCP tools can do (search, reads, property discovery, and other supported CRM operations). Use the REST API / tools/hubspot-api-service only when MCP does not support the operation, when MCP is impractically slow for the volume (e.g. scripted rollups, WBR dry-run markdown), or when the user explicitly wants a CLI/script path.

Humans: the HubSpot web app remains the right place for extended review, settings, and manual work that is easier in the UI.


2. Access Policy

Agents: HubSpot MCP first

In Cursor (and similar clients wired to this repo’s MCP launcher), treat HubSpot MCP as the standard for CRM context: resolve record IDs, search companies/contacts/deals, inspect properties, and answer questions without leaving chat.

When to use the REST API or service CLI instead

Use tools/hubspot-api-service, direct curl, or other automation when:

  • Capability gap — MCP does not expose the write or association pattern you need (e.g. scripted POST /crm/v3/objects/notes with association payloads, log-new-opportunity, partner CLI batch patterns).
  • Performance / volume — many sequential calls or large extracts are faster or clearer as a script than through MCP in a single thread.
  • Explicit automation — scheduled jobs, CI, or npm run hubspot:* workflows (WBR dry-run, property audits with --apply, etc.).

Humans: HubSpot web app

Use the HubSpot app for:

  • Long-form pipeline review, settings, and imports
  • Manual edits when the UI is faster than describing the change in chat
  • Confirming CRM state before staffing or delivery decisions when you are already in HubSpot

Repo wiring (Cursor / OpenCode): mcp.json starts node on scripts/hubspot-mcp-stdio.mjs, which:

  1. Loads HUBSPOT_ACCESS_TOKEN (or PRIVATE_APP_ACCESS_TOKEN) from the process environment and, if missing, from optional gitignored files in order: apps/platform/.env.local, repo-root .env.local, repo-root .env.hubspot.mcp (via dotenv).
  2. Sets PRIVATE_APP_ACCESS_TOKEN for HubSpot’s official @hubspot/mcp-server and dynamic-imports its ESM entry (avoids broken require() on "type": "module").

One-time install: From repo root run npm run hubspot:install so @hubspot/mcp-server exists under tools/hubspot-api-service/node_modules/ (declared as a devDependency there). Commit the updated tools/hubspot-api-service/package-lock.json after install. Restart Cursor after installing.

Why not raw npx in mcp.json? On Windows, Cursor often spawns MCP without cmd.exe; npx/npx.cmd batch scripts then fail or never start. node + a repo script is reliable.

Why not only ${env:HUBSPOT_ACCESS_TOKEN} in mcp.json? Cursor’s MCP subprocess can have a minimal environment; reading the same token from apps/platform/.env.local matches how developers already configure the platform.

Why not the hosted URL https://mcp.hubspot.com in Cursor? That OAuth path conflicts with Cursor’s dynamic client registration expectations; HubSpot does not support DCR → Incompatible auth server: does not support dynamic client registration. The stdio + private app token path avoids that.

Remote MCP (advanced): HubSpot remote MCP + MCP Auth App + static OAuth in clients that support it; not what committed .cursor/mcp.json uses.

OpenCode / OpenWork: opencode.jsonc uses the same nodescripts/hubspot-mcp-stdio.mjs command (with ${workspaceFolder} where supported).


3. When to Use the API (after MCP)

Typical triggers:

  • User says: “log this in HubSpot”, “add to HubSpot”, “log in HS”, “make sure it’s in HubSpot as an activity” — resolve company/contact/deal IDs with MCP when possible; create the note or other write via API when MCP cannot perform the write.
  • After Slack/email/call touchpoints that should count in GTM metrics
  • Any outreach or engagement that should be visible on the company/contact/deal record

Agent behavior: Prefer MCP for lookup and confirmation; use the HubSpot API (this playbook + tools/hubspot-api-service where applicable) for writes and for operations MCP does not support. Do not only document a checklist in the vault; perform the CRM write when the user asked for HubSpot logging.


4. Credentials

1Password

  • Vault: Brainforge AI Team (or Employee for personal)
  • Item: Store an item (for example HubSpot API) with a token that supports the required write scopes.
  • Field: Store the token as credential or password.
op item list --vault "Brainforge AI Team" | grep -i hubspot
op read "op://Brainforge AI Team/HubSpot API/credential"

If no HubSpot write token exists yet, add one to 1Password before attempting agent-driven writes.

Environment variable (optional)

HUBSPOT_ACCESS_TOKEN=pat-na1-xxxxx

5. Create a Note (Activity) via API

Base URL

https://api.hubapi.com

Find company or contact ID

Agents: use HubSpot MCP search first (search_crm_objects / equivalent) with objectType companies or contacts and the known name, domain, or email.

Fallback: Search API POST /crm/v3/objects/companies/search (or /contacts/search) when MCP is unavailable, returns errors, or you need a filter MCP does not expose.

Humans: use the HubSpot app when already working there.

Create note and associate to company

Endpoint: POST /crm/v3/objects/notes

Headers:

  • Authorization: Bearer {access_token}
  • Content-Type: application/json

Body (note → company, associationTypeId 190):

{
  "properties": {
    "hs_timestamp": "2026-02-09T15:07:00.000Z",
    "hs_note_body": "Slack touch-base with Sigal: congrats on Breezy $10M pre-seed (Axios 2/2); asked how analytics is going; offered Brainforge help."
  },
  "associations": [
    {
      "to": { "id": "COMPANY_ID_STRING" },
      "types": [
        {
          "associationCategory": "HUBSPOT_DEFINED",
          "associationTypeId": 190
        }
      ]
    }
  ]
}

Note → contact (associationTypeId 202):

"associations": [
  {
    "to": { "id": "CONTACT_ID_STRING" },
    "types": [{ "associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 202 }]
  }
]

cURL example

curl -X POST "https://api.hubapi.com/crm/v3/objects/notes" \
  -H "Authorization: Bearer $HUBSPOT_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "properties": {
      "hs_timestamp": "2026-02-09T15:07:00.000Z",
      "hs_note_body": "Slack touch-base: [summary of outreach]"
    },
    "associations": [{
      "to": { "id": "223907255032" },
      "types": [{ "associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 190 }]
    }]
  }'

6. Association type IDs (reference)

From → ToassociationTypeId
Note → Company190
Note → Contact202
Company → Note189
Contact → Note201

7. Common agent workflow

  1. User asks to log something in HubSpot (for example, “log this touchpoint in HS”).
  2. Resolve company/contact/deal with HubSpot MCP when possible; use the app if the human is already there and prefers manual entry.
  3. If a write is required and MCP cannot do it, get token: op read "op://Brainforge AI Team/<HubSpot item>/credential" (or Brainforge Sales vault item such as Cursor HubSpot app — see team runbook) and use the API or tools/hubspot-api-service CLI.
  4. Build body: hs_timestamp (ISO 8601), hs_note_body (summary of the touchpoint).
  5. POST to https://api.hubapi.com/crm/v3/objects/notes with associations to company (190) and/or contact (202) when using the note API path.
  6. Confirm to the user that the activity was created and will appear in HubSpot metrics.

8. HubSpot CLI (developer platform)

For CLI-driven HubSpot work (projects, CMS, app deploy, hs open), use the pinned CLI under tools/hubspot-cli/ and a Personal Access Key. That flow is separate from the Private App token used for CRM REST calls in this guide. See hubspot-cli-setup.md.


9. API reference


10. Version history

  • v1.6 (April 21, 2026) — MCP-first for agents; API/CLI when MCP lacks capability or is too slow for volume; humans still use the app for deep UI work; workflow §7 updated
  • v1.5 (April 13, 2026) — Launcher script scripts/hubspot-mcp-stdio.mjs (node, not npx; ESM import; dotenv paths); @hubspot/mcp-server under tools/hubspot-api-service
  • v1.4 (April 13, 2026) — HubSpot MCP in repo uses stdio @hubspot/mcp-server + HUBSPOT_ACCESS_TOKEN (avoid Cursor DCR error with hosted MCP)
  • v1.3 (April 13, 2026) — Document repo .cursor/mcp.json / OpenCode HubSpot MCP env vars and MCP Auth App redirect
  • v1.2 (April 1, 2026) — Cross-link HubSpot CLI setup for developer-platform workflows
  • v1.1 (April 1, 2026) — Reframed access guidance: HubSpot app first, MCP fallback, API for writes/automation
  • v1.0 (February 9, 2026) — Initial guide for logging activities via HubSpot API