@agentforge-io/connectors-hubspot
v3.0.0
Published
HubSpot connector for AgentForge — contacts, deals, and engagement tools wired to per-user OAuth2 credentials managed by the core ConnectorRegistryService.
Readme
@agentforge-io/connectors-hubspot
First-class HubSpot connector for AgentForge. Plugs into
ConnectorRegistryService.register(...) from @agentforge-io/core and gives
your agents seven tools that read and write contacts, deals, and engagement
timelines.
import { hubspotConnector } from '@agentforge-io/connectors-hubspot';
connectorRegistry.register(
hubspotConnector({
clientId: process.env.HUBSPOT_OAUTH_CLIENT_ID!,
clientSecret: process.env.HUBSPOT_OAUTH_CLIENT_SECRET!,
}),
);Same shape and lifecycle as @agentforge-io/connectors-google: register once
at boot, the registry handles OAuth, refresh, encryption, per-user token
storage, and tool materialization.
Tools
All seven tools are auto-registered the moment a user authorizes the connector in the dashboard. Each one returns a JSON-stringified payload — the model can quote individual fields.
| Tool | Use it when… |
|---|---|
| hubspot_search_contacts | The user mentions an email, name, phone, or company. Always call this before hubspot_create_contact. |
| hubspot_get_contact | You already have a contactId and need fresh properties. |
| hubspot_create_contact | Search returned nothing for the email. HubSpot 409s on duplicates — search first. |
| hubspot_update_contact | Enrich an existing contact (plan tier, source, phone, custom property…). |
| hubspot_create_deal | The user shows buying intent or commits to a plan. Pass contactId so the deal lands on their timeline. |
| hubspot_update_deal | Move a deal stage, bump the amount, or set closedate on close. |
| hubspot_log_engagement | Drop a note / call / email / meeting summary on a contact so the human rep sees what the agent did. |
Default contact return properties: email, firstname, lastname, phone, company,
lifecyclestage. Override per call with the properties argument.
Setup
1. Create the HubSpot OAuth app
- Sign in at https://developers.hubspot.com → Apps → Create app.
- Under the Auth tab, set the Redirect URL to your AgentForge's
connector callback (the platform exposes it at
https://<your-host>/connectors/callback— the URL is the same one you configured for Google, just registered as an additional redirect). - Scopes — copy the default set the connector requests:
oauthcrm.objects.contacts.readcrm.objects.contacts.writecrm.objects.deals.readcrm.objects.deals.writecrm.schemas.contacts.readcrm.schemas.deals.read
- Save → copy the Client ID and Client Secret.
If you plan to read companies, custom objects, or marketing data later, add the matching scopes here and pass them via the connector's
scopesoption. The default set is intentionally tight — fewer scopes mean fewer consent prompts at authorize time.
2. Drop the creds into AgentForge
In the admin dashboard → Settings → Secrets, set:
HUBSPOT_OAUTH_CLIENT_IDHUBSPOT_OAUTH_CLIENT_SECRET
The platform's HostConnectorsModule listens for changes on those two keys
and reconfigures the connector live — no server restart needed. The
Directory card flips from "Needs admin setup" to "Connect".
3. End-user authorizes
Each end user clicks Connect on the HubSpot card → standard OAuth consent
on HubSpot → redirect back with the authorization code → AgentForge encrypts
and stores the access + refresh tokens against their userId.
Agents owned by that user (or public-chat agents whose
connectorOwnerUserId points to that user) now see the seven hubspot__*
tools in their picker, grouped under "HubSpot".
Typical agent loop
A common "MVP that sells" flow:
- Visitor opens the chat widget.
- Agent qualifies via conversation.
hubspot_search_contacts({ query: visitor_email })— dedupe gate.hubspot_create_contact({ email, firstname, lastname, lifecyclestage: 'lead' })on miss;hubspot_update_contacton hit.hubspot_log_engagement({ contactId, type: 'note', body: transcript })so the human rep has full context.- When the user agrees to a plan, kick off your existing checkout flow (Stripe/LemonSqueezy), then in the webhook handler:
hubspot_create_deal({ name, amount, stage: 'closedwon', contactId }).
The connector tools live alongside built-ins (current_time, fetch_url,
web_search) and any other connector you've registered, so a single agent
can search the web, hit HubSpot, send a Gmail confirmation, and check the
clock without leaving the loop.
Custom and extra properties
Every HubSpot property (built-in or custom) is reachable via the
properties map on create_contact, update_contact, create_deal, and
update_deal:
// Tool call the model would make
{
"email": "[email protected]",
"firstname": "Alice",
"properties": {
"utm_source": "twitter",
"plan_interest": "pro_annual",
"custom_text_field": "..."
}
}No connector update required to support new fields — just create the property in HubSpot and let the agent fill it.
Error handling
The HTTP wrapper handles two retry classes transparently:
- 429 Too Many Requests — honors
Retry-After, up to 2 retries. - 5xx — single retry with ~500ms jitter.
- 401 Unauthorized — not retried in the connector; the
ConnectorRegistryServicecatches it, refreshes the access token, and the agent runner re-dispatches the tool call.
Anything else surfaces as a HubSpotApiError whose message includes the
HTTP status and the first 500 chars of the response body. The model reads
that string verbatim and will typically correct course (e.g. drop a bad
property name on a 400, switch to update_contact on a 409).
Versioning
This package ships in lockstep with the rest of the @agentforge-io/*
family via Changesets' fixed-group config. Bump it together with core
and nest to avoid mismatched OAuth or tool-registry contracts.
See HUBSPOT_CONNECTOR_SDD.md at the repo root for the full design
rationale, including the choice vs Attio/Pipedrive/Salesforce.
