Read HubSpot contacts, companies, deals, and tickets, with dry-run writes and generic CRM object reads.
- Other
- hubspot
- crm
- contacts
- companies
- deals
- tickets
- objects
- oauth
- private-app
- multi-account
- License
- MIT
- Published
- August 22, 2026
- Pinned commit
acffe37- Rating
- No ratings yet
- Forks
- 0
- Stars
- 0
- Adaptation effort
- —
README
@kody/hubspot
Intent
Provide reusable, account-agnostic HubSpot CRM helpers so Kody agents can read
contacts, companies, deals, tickets, and other CRM objects through a saved
hubspot / hubspot-* OAuth integration or a private-app token — without
hand-rolling REST. Mutations are previewable with dryRun: true and only run
live after confirm: true.
This listing is meant to be forked. After you fork, connect your HubSpot
portal. Do not treat the live @kody/hubspot package storage as yours.
When To Use
- List, get, or search contacts, companies, deals, and tickets
- Read any other CRM object type through the generic object helpers
- Preview or apply contact / company / deal / ticket writes after confirmation
- Call an unwrapped HubSpot REST path through
./request - Connect more than one HubSpot portal via
integrationName/account
Auth
HubSpot has no built-in Kody OAuth app. Choose one lane:
| Lane | When to use | Credential |
|---|---|---|
| OAuth (recommended) | Multi-account, refresh tokens, shared helpers | Saved integration hubspot or hubspot-<purpose> |
| Private app token | Fastest for a single portal | User secret hubspotPrivateAppToken (or hubspotPrivateAppToken-<purpose>) |
Both lanes send Authorization: Bearer …. Required API hosts:
api.hubapi.com and api.hubspot.com. Approve them in the account
secrets UI.
Lane A — BYO OAuth
- Create an OAuth app from https://developers.hubspot.com/docs/apps/developer-platform/build-apps/create-an-app
- Set the redirect URI exactly to
https://kody.codes/connect/oauth - Enable the required CRM scopes listed below (
oauthis required on every HubSpot OAuth app). Addcrm.objects.custom.readas an optional scope if you need custom objects on Enterprise portals. - Connect while signed in to Kody:
- Paste the HubSpot client id and client secret into the Kody wizard (never
into chat). Approve hosts
api.hubapi.comandapi.hubspot.com. - Reconnect later with https://kody.codes/connect/oauth?provider=hubspot
OAuth authorize URL: https://app.hubspot.com/oauth/authorize. Token URL:
https://api.hubapi.com/oauth/v3/token. Flow: confidential (client secret).
HubSpot scopes are space-separated.
To connect a second portal, change provider (for example
provider=hubspot-work) and pass integrationName: 'hubspot-work' on every
call.
Lane B — Private app token
- Create a private app from https://developers.hubspot.com/docs/apps/legacy-apps/private-apps/overview (Development → Legacy apps) and grant the same CRM scopes.
- Save the access token (do not paste the value in chat):
For a second portal/token, use a distinct secret name such as
hubspotPrivateAppToken-work and pass secretName: 'hubspotPrivateAppToken-work'
(or account: 'work', which resolves to hubspot-work /
hubspotPrivateAppToken-work).
Scopes
| Scope | Needed for |
|---|---|
oauth | Required on every HubSpot OAuth app; ./account, ./smoke-test |
crm.objects.contacts.read | ./list-contacts, ./get-contact, ./search-contacts |
crm.objects.contacts.write | ./create-contact, ./update-contact |
crm.objects.companies.read | ./list-companies, ./get-company, ./search-companies |
crm.objects.companies.write | ./create-company, ./update-company |
crm.objects.deals.read | ./list-deals, ./get-deal, ./search-deals |
crm.objects.deals.write | ./create-deal, ./update-deal |
tickets | Ticket reads and writes |
crm.objects.custom.read | Generic custom-object reads (Enterprise, optional) |
If HubSpot returns 401/403 or a missing-scope error, helpers throw a message that names the missing scope and the next setup URL (reconnect OAuth with that scope, or save a private-app token).
Multiple accounts
Every export accepts:
integrationName/integration— exact saved OAuth name (hubspot-work)account—work→hubspot-work;hubspot-workused as-is; omitted →hubspotsecretName— private-app token secret overrideauth—'oauth'or'privateApp'when both exist
Do not hard-code a personal portal id or alias.
Safety
Mutating helpers require confirm: true. Pass dryRun: true to inspect the
REST payload without calling HubSpot. ./request treats GET and CRM search
POST as read-only; other POST / PUT / PATCH / DELETE need confirmation.
Do not write live CRM records from a smoke test.
Exports
| Export | Description |
|---|---|
. | Package overview, connect URLs, export map |
./accounts | Resolve integration/secret names and report what is connected |
./smoke-test | Local helper checks plus optional live portal read (no email) |
./account | Authenticated portal id presence / account type / timezone |
./objects | Generic list for any CRM object type |
./list-objects | Generic list (objectType required) |
./get-object | Generic get (objectType + id) |
./search-objects | Generic search |
./list-contacts / ./get-contact / ./search-contacts | Contact reads |
./create-contact / ./update-contact | Contact writes (dryRun / confirm) |
./list-companies / ./get-company / ./search-companies | Company reads |
./create-company / ./update-company | Company writes |
./list-deals / ./get-deal / ./search-deals | Deal reads |
./create-deal / ./update-deal | Deal writes |
./list-tickets / ./get-ticket / ./search-tickets | Ticket reads |
./create-ticket / ./update-ticket | Ticket writes |
./request | Generic REST escape hatch |
./types | Shared TypeScript types |
Smoke test
import smokeTest from 'kody:@kody/hubspot/smoke-test'
export default async function main() {
return await smokeTest()
}Without credentials this returns { ok: true, live: false } plus the connect
and private-app URLs. After OAuth or a private-app token is saved it reads
/account-info/v3/details and returns { live: true, hasPortalId } without
email.
Preview a mutation without credentials:
import createContact from 'kody:@kody/hubspot/create-contact'
export default async function main() {
return await createContact({
properties: { email: 'pat@example.com', firstname: 'Pat' },
dryRun: true,
})
}Examples
import listContacts from 'kody:@kody/hubspot/list-contacts'
import listDeals from 'kody:@kody/hubspot/list-deals'
export default async function main() {
const contacts = await listContacts({ limit: 10 })
const deals = await listDeals({ limit: 10 })
return { contactCount: contacts.items.length, dealCount: deals.items.length }
}import getObject from 'kody:@kody/hubspot/get-object'
import searchObjects from 'kody:@kody/hubspot/search-objects'
export default async function main() {
const listing = await getObject({ objectType: 'p_listings', id: '123' })
const matches = await searchObjects({
objectType: 'contacts',
query: 'acme',
limit: 10,
})
return { listingId: listing.id, matches: matches.items.length }
}import createDeal from 'kody:@kody/hubspot/create-deal'
export default async function main() {
const preview = await createDeal({
properties: { dealname: 'Acme renewal', amount: '12000' },
dryRun: true,
})
// After the user confirms the exact deal name and amount:
return await createDeal({
properties: { dealname: 'Acme renewal', amount: '12000' },
confirm: true,
})
}Unwrapped REST:
import request from 'kody:@kody/hubspot/request'
export default async function main() {
return await request({
path: '/crm/v3/objects/contacts',
query: { limit: 5, properties: 'email,firstname' },
})
}Notes
- REST base:
https://api.hubapi.com - Helpers project slim CRM records (
id, selectedproperties, timestamps). Use./requestwhen you need extra fields or associations payloads. - Generic writes stay on
./request. Typed create/update helpers cover only contacts, companies, deals, and tickets. - Look up a contact by email with
idProperty: 'email'. - This package is not affiliated with or endorsed by HubSpot, Inc.
Branding
The community icon is HubSpot's official orange sprocket (#FF7A59) from the
HubSpot brand mark as published in Simple Icons.
Paths are unmodified. HubSpot® is a trademark of HubSpot, Inc.
Share this listing at https://kody.codes/@kody/hubspot
Docs
Report this listing
Log in to report this listing.