@kody/hubspot
Read HubSpot contacts, companies, deals, and tickets, with dry-run writes and generic CRM object reads.
AGENTS.md
138 lines · 4.8 KB · Markdown@kody/hubspot — agent notes
Human setup and intent live in README.md. This file is for
agents: imports, smoke/dryRun execute snippets, and edge cases. Secrets and
OAuth by name only — never paste tokens. Do not disable live webhooks or
jobs.
Auth
| Call | Resolves to |
|---|---|
| omit | hubspot / hubspotPrivateAppToken |
account: 'work' | hubspot-work / hubspotPrivateAppToken-work |
integrationName: 'hubspot-work' | that exact OAuth name |
secretName: 'hubspotPrivateAppToken-work' | that exact private-app secret |
auth: 'oauth' | 'privateApp' | force lane when both exist |
Hosts: api.hubapi.com, api.hubspot.com. REST base: https://api.hubapi.com.
Do not hard-code a personal portal id or alias.
Import paths
| Export | Import |
|---|---|
| overview / connect URLs / export map | kody:@kody/hubspot |
| accounts | kody:@kody/hubspot/accounts |
| account | kody:@kody/hubspot/account |
| smoke-test | kody:@kody/hubspot/smoke-test |
| objects | kody:@kody/hubspot/objects |
| list-objects | kody:@kody/hubspot/list-objects |
| get-object | kody:@kody/hubspot/get-object |
| search-objects | kody:@kody/hubspot/search-objects |
| list-contacts | kody:@kody/hubspot/list-contacts |
| get-contact | kody:@kody/hubspot/get-contact |
| search-contacts | kody:@kody/hubspot/search-contacts |
| create-contact | kody:@kody/hubspot/create-contact |
| update-contact | kody:@kody/hubspot/update-contact |
| list-companies | kody:@kody/hubspot/list-companies |
| get-company | kody:@kody/hubspot/get-company |
| search-companies | kody:@kody/hubspot/search-companies |
| create-company | kody:@kody/hubspot/create-company |
| update-company | kody:@kody/hubspot/update-company |
| list-deals | kody:@kody/hubspot/list-deals |
| get-deal | kody:@kody/hubspot/get-deal |
| search-deals | kody:@kody/hubspot/search-deals |
| create-deal | kody:@kody/hubspot/create-deal |
| update-deal | kody:@kody/hubspot/update-deal |
| list-tickets | kody:@kody/hubspot/list-tickets |
| get-ticket | kody:@kody/hubspot/get-ticket |
| search-tickets | kody:@kody/hubspot/search-tickets |
| create-ticket | kody:@kody/hubspot/create-ticket |
| update-ticket | kody:@kody/hubspot/update-ticket |
| request | kody:@kody/hubspot/request |
| types | kody:@kody/hubspot/types |
Prefer static kody:@kody/hubspot/... imports from execute. Do not lead with
packages.invoke.
Smoke test (read-only)
import smokeTest from 'kody:@kody/hubspot/smoke-test'
export default async function main() {
return await smokeTest()
// Without credentials: { ok: true, live: false } + connect / private-app URLs
// With credentials: { live: true, hasPortalId } (no email)
}Do not write live CRM records from a smoke test.
dryRun / confirm
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.
import createContact from 'kody:@kody/hubspot/create-contact'
export default async function main() {
const preview = await createContact({
properties: { email: 'pat@example.com', firstname: 'Pat' },
dryRun: true,
})
// After the user confirms the exact properties:
return await createContact({
properties: { email: 'pat@example.com', firstname: 'Pat' },
confirm: true,
})
}Read helpers:
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 }
}Generic objects / unwrapped REST:
import getObject from 'kody:@kody/hubspot/get-object'
import searchObjects from 'kody:@kody/hubspot/search-objects'
import request from 'kody:@kody/hubspot/request'
export default async function main() {
const listing = await getObject({ objectType: 'p_listings', id: '123' })
const matches = await searchObjects({
objectType: 'contacts',
query: 'acme',
limit: 10,
})
const raw = await request({
path: '/crm/v3/objects/contacts',
query: { limit: 5, properties: 'email,firstname' },
})
return { listingId: listing.id, matches: matches.items.length, raw }
}Edge cases
- Look up a contact by email with
idProperty: 'email'. - 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. - Prefer dedicated helpers over assembling
./requestwhen one exists.