Skip to Content
TwinsHubSpot

HubSpot

Twin of the HubSpot CRM API.

Twin of the HubSpot CRM API.

Coversapi.hubapi.com
AuthPrivate app tokens, OAuth, developer API key
WebhooksYes
Operations117

Coverage

The twelve CRM object types with CRUD, batch and search, associations in both the v3 and v4 dialects, properties, owners, pipelines, lists, users, account info, the OAuth flows and signed webhook deliveries. Real HubSpot puts the consent screen on app.hubspot.com and the API on api.hubapi.com, and the twin serves both path sets on the one base URL, so GET /oauth/authorize answers beside the CRM routes. Point your app’s authorize URL at the twin, because app.hubspot.com is not among the hostnames the proxy intercepts.

HubSpot carries the version in the path and offers no selector header. CRM routes are /crm/v3, with associations also on /crm/v4.

Credentials

Any well-formed pat-… token authenticates by default, so your existing private app token needs no change. Set auth.mode to enforced to accept only tokens the world knows. OAuth access tokens are always checked, because those are minted rather than pattern-matched. See Credentials. Scopes are enforced, and a token missing one gets HubSpot’s 403 with category: "MISSING_SCOPES".

Webhook subscription management is the exception. It takes the developer API key as a ?hapikey= query parameter and a bearer token there is a 401, while ?hapikey= on any other route is HubSpot’s sunset-auth 401.

Vendor specific details

  • Every property value is a string on the wire. amount arrives as "1500", a boolean as "true", and null as "". Any strictly typed model binding to int or bool breaks, and a truthiness check on "false" is silently wrong.
  • Search is immediately consistent here and is not in production. Real HubSpot has an indexing lag, so a write-then-search with no retry loop passes here and flakes for real.
  • Batch endpoints answer 207 on partial failure, with errors and numErrors in the body. Code that checks only whether the response was ok treats a half-failed batch as a success.
  • Delete is an archive. An archived record 404s on a read by id, as HubSpot’s HTML error page rather than JSON, unless you pass ?archived=true, and search never returns it. Archiving an id that does not exist still answers 204.

Its own notes

Once a sandbox is running, this twin serves its own operating notes at {control_url}/veris/manual: the credentials it accepts, the API versions it serves, and the fault statuses you can arm against it. They are generated from the same source as its behaviour, so they cannot drift.