Connector · CRM and email lists
HubSpot, in every client’s workflows.
Add or update a contact, create a contact, or start when a deal moves to a stage, with the client’s own HubSpot account.
- Triggers
- 3start a workflow
- Actions
- 4steps in a workflow
Start a workflow when something happens in HubSpotBeta
By webhook where HubSpot sends one, by checking where it does not. A webhook is checked once a day to see it is still on, and if HubSpot turns it off the alert carries a Reconnect button.
- A deal moves to a stagePolledChecked every 5 minutes: a HubSpot private app cannot register a webhook. The token needs crm.objects.deals.read.
- A new contact is addedPolledA contact created in HubSpot (a form, an import, by hand), checked every 5 minutes. The token needs crm.objects.contacts.read.
- A support ticket is openedPolledA ticket created in HubSpot, checked every 5 minutes. The token needs the tickets scope.
Do it in HubSpot
A step with its own fields: values from the run go in by name, and what HubSpot answers is there for the next step.
- Add or update a contactCreates the contact, or updates the one with this email. A lead who submits twice is one contact.
- Create a contactA new contact. HubSpot refuses a second contact with the same email (409): the run fails and says so.
- Update a contactChange properties of one contact, found by its id or its email. Properties left out keep their value; a contact that does not exist fails the step.
- List contactsEvery contact, up to 1,000 (ten pages of 100), with their email, name, phone and company. Past that, the step says it stopped.
Connect the client’s HubSpot
On the client’s page in Thrum: Add credential, type HubSpot private app token.
In the client’s HubSpot: Settings → Integrations → Private Apps (Legacy apps) → Create → scopes crm.objects.contacts.write, and for the triggers crm.objects.contacts.read, crm.objects.deals.read and tickets → copy the access token. A private app needs no HubSpot review.
The key is stored encrypted and bound to that client: another client’s workflow cannot use it, and no template or pack copies it.
Via HTTP
Or by web request: create or update a contact
The lead goes straight into the client’s CRM, and a second enquiry from the same person updates the same contact. The web request step, filled in: paste it, swap in the client’s ids, add their key.
01
Add the client’s credential
On the client’s page in Thrum: Add credential, type API key. In the client’s HubSpot: Settings → Integrations → Private Apps → Create → scope crm.objects.contacts.write → copy the access token.
{ "api_key": "pat-…" }02
Add a web request step
Name it “Add to HubSpot”, set it as here, and pick the credential. Values in double braces are read from the run: here, the lead the form brought in.
{ "method": "POST", "url": "https://api.hubapi.com/crm/v3/objects/contacts/batch/upsert", "headers": { "Authorization": "Bearer {{ credential.api_key }}" }, "body": { "inputs": [ { "idProperty": "email", "id": "{{ lead.email }}", "properties": { "email": "{{ lead.email }}", "firstname": "{{ lead.name }}", "phone": "{{ lead.phone }}" } } ] } }03
What goes out, and what comes back
The whole name goes in First name. Ask for first and last name separately on the form to fill both.
POST https://api.hubapi.com/crm/v3/objects/contacts/batch/upsert content-type: application/json authorization: Bearer •••••••• { "inputs": [ { "idProperty": "email", "id": "nadia@example.com", "properties": { "email": "nadia@example.com", "firstname": "Nadia Karim", "phone": "+447700900222" } } ] }200 { "status": "COMPLETE", "results": [ { "id": "51", "new": true, "properties": { "email": "nadia@example.com", "firstname": "Nadia Karim", "phone": "+447700900222" }, "createdAt": "2026-09-28T09:05:00.000Z", "updatedAt": "2026-09-28T09:05:00.000Z", "archived": false } ], "startedAt": "2026-09-28T09:05:00.000Z", "completedAt": "2026-09-28T09:05:00.100Z" }
Watched like everything else
When HubSpot stops answering or refuses the client’s key, you hear about it, and your client does not.
- A failing request
- Tried 3 times, 5 seconds apart and doubling, only for errors a retry can fix
- A service that asks for a pause
- Given it, up to an hour
- Requests at once, per account
- 2, per worker; the rest wait their turn
- Timeout
- 15 seconds, unless the step sets its own
- Private and internal addresses
- Refused, on every redirect too
- The client’s key
- Encrypted, bound to the client, and kept out of every run record
- A step that keeps failing
- Marks the workflow Degraded and alerts you, not the client
More in CRM and email lists
- ActiveCampaignAdd or update a contact, subscribe to a list2 actions
- AttioAdd or update a person, update a person2 actions
BrevoAdd or update a contact, send an email, or start when a new contact is added2 actions · 3 triggers
- CloseCreate a lead, update a lead, or start when a new lead is added2 actions · 2 triggers
HubSpot and its logo are trademarks of their owner, used here only to say what Thrum works with; no endorsement is implied.
Thrum is cooking.
Get early access and you’ll be among the first in. Early members get a launch perk.