Connect HubSpot
On this page
- Prerequisites
- Step 1: Load the plugin into the engine
- Step 2: Make the plugin available to the agent
- Step 3: Create a HubSpot Private App token
- Step 4: Enable the tools
- Step 5: Give the tools the token
- Step 6: Custom properties and search filters
- Step 7: Tell the agent how to use it
- Verification
- Caveats
- See also
The HubSpot tools - contacts, deals, tickets, tasks, notes, meetings, email, companies, files - are a plugin, polyant-ai/polyant-hubspot-plugin . They used to ship inside the engine; they were pulled out so a deployment that does not use HubSpot does not carry them. This guide takes one agent from nothing to a working CRM query: load the plugin, make it available to the agent (with managed services, by a grant and a workspace assignment), generate a Private App token, enable the tools, give them the token, and verify in the Playground.
Coming from an older release? The names changed on extraction. The plugin namespace carries the “hubspot” part, so
hubspotContactis nowhubspot:contact,hubspotDealishubspot:deal, and so on. The catalogue entry is a different row: an agent that had the old tool enabled has to have the new name enabled.hubspot:filealso renamed itspdfHandleparameter tofileHandle.
Prerequisites
- A HubSpot account where you can create a Private App (admin permission)
- A Polyant agent you can edit (the slug, e.g.
my-bot) - An authenticated admin session (or a bearer token for API calls)
- With managed services, a platform admin for the grant, and an organization Admin or Owner for the workspace assignment
Step 1: Load the plugin into the engine
The loader scans absolute roots listed in PLUGIN_DIRS, then the immediate subdirectories of the engine’s convention plugin directory. Put the plugin in one of the two, at the release tag that matches Polyant 1.2.0 (v1.1.1):
git clone --depth 1 --branch v1.1.1 \
https://github.com/polyant-ai/polyant-hubspot-plugin.git \
packages/engine/src/plugins/hubspotUse a real directory, not a symlink. An out-of-tree plugin owns its dependencies, so install them in place. Restart the engine: the manifest is read at boot, and the tools land in the catalogue namespaced as hubspot:<name>.
For a container image, drop the plugin into the same convention directory at build time, or pass PLUGIN_REPOS and the git_token build secret and let the plugins build stage clone the pinned ref. See Plugins are installed per organization for how the runtime treats what it finds.
Step 2: Make the plugin available to the agent
Open source: there is nothing to do here. The open-source engine has no plugin grants and no workspace assignments, so a loaded plugin’s tools are in the catalogue of every agent. Go on to Step 3.
With managed services: loading a plugin does not make it available to anyone. Two boundaries stand between the engine and the agent, and an inner assignment cannot restore a revoked outer grant:
- A platform admin grants the plugin to your organization, from Admin Console → Plugins → Image plugins (
/platform/settings/plugins) or from the organization’s own page in the Admin Console. A plugin installed from the marketplace instead needs no grant: an Owner or Admin installs it at Settings → Installed plugins, see What an organization installs. - Someone holding
workspace.assignment:manageon the organization assigns it to the workspace the agent lives in, from Workspaces → Plugins. The organization’s Admin and Owner roles carry it. A role bound only on the workspace is not enough, whatever it is called: the permission is checked at organization level, so that the people subject to a workspace’s allow-list cannot lift it themselves.
A workspace created after the allow-list shipped starts with nothing assigned, and granting a plugin to the organization assigns it nowhere. Where only the workspace half is missing, the agent’s Tools tab says so with a banner counting the plugins the agent uses that its workspace does not carry.
Step 3: Create a HubSpot Private App token
- Open app.hubspot.com/private-apps (or, in HubSpot: Settings → Integrations → Private Apps).
- Click Create a private app.
- Under Scopes, enable at minimum:
- CRM -
crm.objects.contacts.read/write,crm.objects.deals.read/write,crm.objects.companies.read/write - Tickets -
tickets(read/write) - Tasks / Engagements -
crm.objects.tasks.read/write,crm.objects.notes.read/write,crm.objects.meetings.read/write - Email (only if you intend to use
hubspot:sendEmail)
- CRM -
- Save the app and copy the Access token (starts with
pat-). HubSpot only shows it once.
Step 4: Enable the tools
Open the agent → Tools → Enable tools. Switch the picker to Plugins, choose the HubSpot plugin and select the tools you need; each one shows Will ask for: with the token it needs. Confirm with Enable N.
| Tool | Purpose |
|---|---|
hubspot:contact | Search, create, update contacts; supports HubSpot custom properties |
hubspot:deal | Search/create/update deals, move stages |
hubspot:ticket | Ticket lifecycle with priority + open/closed filters |
hubspot:createTask | Create a follow-up task (with due date, assignee) |
hubspot:note | Attach a note to a contact, deal, or company |
hubspot:meeting | Log a meeting against a CRM record |
hubspot:sendEmail | Send a one-to-one email through HubSpot |
hubspot:getCompany | Look up a company by name or domain |
hubspot:file | Upload a generated file to HubSpot Files, or find the PDF attached to a contact |
hubspot:file with action: "upload" does not take bytes. It takes a handle minted by whichever tool produced the file, through the engine’s short-lived artifact store. A handle is bound to the conversation that produced it and one read consumes it, so two plugins exchange a file without importing each other and without the bytes ever passing through the model.
The artifact store is bounded: each file is at most 10 MB and lives at most 10 minutes, and the engine process holds at most 100 MB or 1,000 handles in total. A file that must outlive the handoff goes through a storage tool such as the extra plugin’s extra:fileUpload instead.
The tools are enabled in the page’s draft. Nothing is saved yet.
Step 5: Give the tools the token
The token is the parameter hubspot_api_key. A tool that cannot run without it opens its panel straight on the field when you enable it; otherwise click any of the HubSpot rows. Under Parameters, paste the token and click Save parameters. The field is shown once and its Asked for by line names every enabled HubSpot tool, because they all share the one key: there is no per-tool token.
Then click the page’s Save to confirm the enabled tools. Until the token is set, each HubSpot row says Missing … and the tools are not served to the model.
Via API, the token is an agent secret (lowercase, snake_case - the case matters; the tools look it up with that exact key):
curl -s -X PUT http://localhost:4000/api/instances/my-bot/secrets \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"secrets": [
{ "key": "hubspot_api_key", "value": "pat-eu1-..." }
]
}'Secrets are AES-256-GCM encrypted at rest. Every tool in the plugin declares this one key and receives it through its runtime context; the engine hands a tool only the keys it declared, so nothing else in the plugin sees it.
Step 6: Custom properties and search filters
Two extension points are worth knowing about, because they unlock most of the agent-specific behavior without changing tool code.
hubspot:contact accepts a generic customProperties map (string → string) for create/update - you can write any HubSpot custom property (e.g. evento, source_campaign) without the tool knowing about it. For search it also exposes filters (HubSpot’s filter group syntax), returnProperties (which properties to project), limit, and after (pagination cursor):
curl -s -X POST http://localhost:4000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"model": "my-bot",
"messages": [{
"role": "user",
"content": "Find contacts where evento equals web-summit and return name + email, max 20."
}]
}'The agent will translate that to a hubspot:contact search call with filters: [{ propertyName: "evento", operator: "EQ", value: "web-summit" }], returnProperties: ["firstname", "lastname", "email"], and limit: 20.
hubspot:ticket supports priority (HIGH / MEDIUM / LOW), an openOnly boolean to skip closed tickets, and a createdAfter ISO date for time-windowed queries - all useful from the prompt without writing a new tool.
Step 7: Tell the agent how to use it
The tools are now mounted, but the agent still needs to know when to call them. Update the agent prompt (Prompt section → Tooling) with a short policy, for example:
When the user asks about a contact or wants to log activity, use the
hubspot:tools. For custom HubSpot properties (e.g.evento), pass them throughcustomProperties. Never invent property names - only use ones the user explicitly mentioned or that you have already seen in the conversation.
Agent-specific property names belong in the prompt, never in tool code - the tools are deliberately property-agnostic.
Verification
- Open Playground, ask: “find contacts named Mario”.
- In the conversation trace you should see a
hubspot:contacttool call withaction: "search"andcriteria.name: "Mario", followed by a tool result containing the matching contacts. - If the tool is not there at all, first check its row on the Tools tab: Missing … means the token from Step 5 is not set, and the tool is withheld until it is. Otherwise the plugin is loaded but not reaching the agent: check the organization grant and the workspace assignment from Step 2.
- If the call returns
success: falsewith an auth error, double-check the secret key is exactlyhubspot_api_key(lowercase) and the Private App scopes cover contacts.
Caveats
- Private App tokens are long-lived but tied to the HubSpot user who created them. If that user is deactivated, the token stops working - rotate it from a service account.
- HubSpot rate-limits at 100 req / 10 s per portal on the v3 CRM API. Heavy batch operations (large imports, bulk re-syncs) should run outside the agent loop.
hubspot:sendEmailrequires the transactional email add-on on the HubSpot side - without it, the tool returns a 403 from HubSpot at runtime.