HubSpot Contacts (via hubspot CLI)
Drive HubSpot contact operations through the hubspot CLI. Every command emits a JSON envelope ({"success": true, "data": ...}) suitable for parsing.
Prerequisite: authentication
The CLI reads HUBSPOT_ACCESS_TOKEN (a HubSpot private-app token) from the environment. If a command returns exit code 3 with AUTH_ERROR, tell the user:
export HUBSPOT_ACCESS_TOKEN=<private-app-token>
Multi-account users can pass --profile <name> (reads [profiles.<name>] from ~/.config/hubspot-cli/config.toml).
Canonical commands
| Intent | Command |
|---|---|
| List recent contacts | hubspot crm contacts list --limit 10 |
| Paginate | hubspot crm contacts list --limit 50 --after <next_cursor> |
| Request extra properties | hubspot crm contacts list --properties phone,company,lifecyclestage |
| Get by ID | hubspot crm contacts get 12345 |
| Create from flags | hubspot crm contacts create --email jane@example.com --firstname Jane --lastname Doe |
| Create from JSON | hubspot crm contacts create --properties '{"email":"jane@x.com","lifecyclestage":"lead"}' |
| Update | hubspot crm contacts update 12345 --firstname Janet |
| Delete (archive) | hubspot crm contacts delete 12345 --yes |
| Simple search | hubspot crm contacts search --query "email:*@acme.com" --limit 25 |
| Filtered search | hubspot crm contacts search --filter-groups '[{"filters":[{"propertyName":"lifecyclestage","operator":"EQ","value":"customer"}]}]' |
| Batch create | hubspot crm contacts batch-create --inputs '[{"properties":{"email":"a@x.com"}},{"properties":{"email":"b@x.com"}}]' |
| Discover properties | hubspot discover properties contacts |
Filter-group operators
Common operator values for --filter-groups: EQ, NEQ, GT, GTE, LT, LTE, BETWEEN, IN, NOT_IN, HAS_PROPERTY, NOT_HAS_PROPERTY, CONTAINS_TOKEN, NOT_CONTAINS_TOKEN. Filter groups are OR'd; filters within a group are AND'd.
Default vs custom properties
Only email, firstname, lastname come back by default. For anything else (phone, company, lifecyclestage, hs_lead_status, hubspot_owner_id, custom properties) pass --properties prop1,prop2,.... Use hubspot discover properties contacts to list every valid property name.
Output envelope
{
"success": true,
"data": { "id": "12345", "properties": { "email": "...", "firstname": "..." } },
"paging": { "next_cursor": "abc", "has_more": true }
}
List endpoints put an array in data. Pagination: when paging.has_more is true, call again with --after <paging.next_cursor>.
Error codes (JSON error.code, exit code)
| Code | Exit | Action |
|---|---|---|
AUTH_ERROR |
3 | Tell the user to set HUBSPOT_ACCESS_TOKEN |
VALIDATION_ERROR / DUPLICATE_EMAIL |
5 | Show error.message; fix input |
NOT_FOUND |
6 | Contact ID doesn't exist or was archived |
RATE_LIMIT |
7 | Back off using error.retry_after_seconds |
SERVER_ERROR |
8 | Transient; retry once |
Required scopes
- Read:
crm.objects.contacts.read - Write:
crm.objects.contacts.write - Search:
crm.objects.contacts.read
Tips for the agent
- Prefer
searchoverlistwhen the user describes a filter ("contacts in lifecycle stage customer").listhas no filtering. - Always pass
--limitwhen the user says "a few" / "some" to keep responses small. - Use
--output json-prettyonly when showing the raw JSON to the user; use the defaultjsonwhen piping intojqor parsing. - Never echo the access token back to the user.