HubSpot
Twin of the HubSpot CRM API.
Twin of the HubSpot CRM API.
| Covers | api.hubapi.com |
| Auth | Private app tokens, OAuth, developer API key |
| Webhooks | Yes |
| Operations | 117 |
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.
amountarrives as"1500", a boolean as"true", and null as"". Any strictly typed model binding tointorboolbreaks, 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
errorsandnumErrorsin 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.