Skip to main content

HubSpot integration

Watch HubSpot CRM records change in real time and create, update, search, and merge them on behalf of your clients.

What it does

The HubSpot integration connects a client CRM once and gives you the whole CRM surface: 33 real-time webhook triggers, 7 polling triggers, and 38 actions covering records, associations, properties, lists, owners, pipelines, files, forms, workflows, subscription preferences, custom events, and blog posts.

The six generic Record actions — Create, Update, Get, Delete, Search, and List — take an objectType and work against every HubSpot object type, not a fixed four. The dropdown offers all 35 standard types (contacts through quotes, invoices, orders, carts, leads, and the newer appointments/courses/listings/services/projects objects), and switching the field to expression mode lets you target a custom object by its objectTypeId (2-XXXXXXX).

HubSpot triggers and actions are in beta

Every HubSpot trigger and action currently ships with a beta badge. Trigger nodes are hidden in the editor unless beta nodes are turned on for your workspace.

Set up your OAuth app (agency, one time)

A HubSpot connection runs through your agency's own OAuth app unless TaskJuice's HubSpot app is available, in which case your own app is optional (see Which OAuth app a connection uses). With your own app, the consent screen your client sees carries your brand, not ours. You build that app once and reuse it for every client.

Two HubSpot accounts are involved, doing different jobs:

AccountWho owns itWhat it holds
Your agency's HubSpot accountYouThe OAuth app. No CRM data.
The client's portalThe clientThe contacts, deals, and tickets workflows act on

A free HubSpot account is enough to host the app.

Creating the app requires HubSpot's CLI

HubSpot removed the browser form for creating OAuth apps on 23 June 2026, and offers no API for it. The CLI is now the only way to obtain a client ID and secret. You run it once, when you set your agency up — never per client, and your clients never run it.

  1. Create the app with the HubSpot CLI

    Install the CLI with npm i -g @hubspot/cli, authenticate against your agency's HubSpot account with hs init, then run hs project create and choose OAuth as the authentication method. Upload it with hs project upload.

  2. Add the redirect URL

    Set the app's redirectUrls in app-hsmeta.json to your TaskJuice callback URL, then run hs project upload again. Redirect URLs live in the project file, so they change by re-upload rather than in the browser.

  3. Copy the client ID and secret

    In HubSpot, go to Development → Projects, open your project, click the app under Project Components, and open the Auth tab. Both values are under Client credentials.

  4. Add your app keys in TaskJuice

    Open Apps → HubSpot, paste the client ID and secret under Your own OAuth app, and select the scopes your workflows need from the table below.

  5. Approve each client's portal

    On the app's page in HubSpot, click Add approved account(s) and select the client portal. Repeat for each client you connect.

A privately distributed app reaches 10 client portals

HubSpot caps a private-distribution OAuth app at 10 approved accounts — or 100 when the app is created in a HubSpot Solutions Partner portal. If you expect to pass either ceiling, create the app in your Solutions Partner portal from the start; moving an app between portals later means re-creating it and re-authorizing every client.

HubSpot's service keys are not an alternative here. They are single-account credentials that cannot authenticate webhooks, so they cannot drive the triggers below.

Connect a HubSpot account

HubSpot connects through OAuth 2.0. There is no API key or secret to paste. The person who authorizes must be a HubSpot super admin, because HubSpot only lets super admins approve a connected app.

  1. Add the connection from the node

    Open a workflow, add any HubSpot trigger or action, and connect the account from the node's connection field.

  2. Sign in to HubSpot as a super admin

    Sign in and pick the HubSpot account (portal) to connect.

  3. Approve the requested permissions

    Approve the scopes HubSpot lists. Your agency's OAuth app decides which of the scopes below are requested.

For the full connect flow that applies to every OAuth app, see Connect an account.

Scopes

HubSpot scopes are granted per connection by the agency's own OAuth app. Request only what your workflows use — each action below names the scope it needs.

SurfaceScopes
Contacts, companies, deals, ticketscrm.objects.<type>.read / .write, tickets
Products, line items, commerce objectscrm.objects.products.*, crm.objects.line_items.*, e-commerce
Custom objectscrm.objects.custom.read / .write, crm.schemas.custom.read
Property schemascrm.schemas.<type>.read
Ownerscrm.objects.owners.read
Listscrm.lists.read / crm.lists.write
Conversationsconversations.read
Formsforms
Workflowsautomation
Filesfiles, files.write
Subscription preferencescommunication_preferences.read_write
Custom behavioral eventsanalytics.behavioral_events.send
Blog postscontent

To disconnect, remove the connection in TaskJuice. A HubSpot super admin can also remove the connected app from HubSpot's connected-apps settings.

Triggers

Real-time webhook triggers

HubSpot delivers these as webhooks, so a run starts as soon as the event happens. HubSpot batches events into a single POST; TaskJuice fans that batch out to one run per event.

HubSpot webhook payloads are minimal — they carry the record ID and, for property changes, the changed property name and value, but not the record. Pair any trigger with a Get Record action to hydrate the full object.

TriggerSlugHubSpot subscription type
Contact Createdhubspot/contact-createdcontact.creation
Contact Deletedhubspot/contact-deletedcontact.deletion
Contact Mergedhubspot/contact-mergedcontact.merge
Contact Restoredhubspot/contact-restoredcontact.restore
Contact Association Changedhubspot/contact-association-changedcontact.associationChange
Contact Privacy Deletedhubspot/contact-privacy-deletedcontact.privacyDeletion
Company Createdhubspot/company-createdcompany.creation
Company Deletedhubspot/company-deletedcompany.deletion
Company Mergedhubspot/company-mergedcompany.merge
Company Restoredhubspot/company-restoredcompany.restore
Company Association Changedhubspot/company-association-changedcompany.associationChange
Deal Createdhubspot/deal-createddeal.creation
Deal Stage Changedhubspot/deal-stage-changeddeal.propertyChange + dealstage
Deal Deletedhubspot/deal-deleteddeal.deletion
Deal Mergedhubspot/deal-mergeddeal.merge
Deal Restoredhubspot/deal-restoreddeal.restore
Deal Association Changedhubspot/deal-association-changeddeal.associationChange
Ticket Createdhubspot/ticket-createdticket.creation
Ticket Deletedhubspot/ticket-deletedticket.deletion
Ticket Mergedhubspot/ticket-mergedticket.merge
Ticket Restoredhubspot/ticket-restoredticket.restore
Ticket Association Changedhubspot/ticket-association-changedticket.associationChange
Product Createdhubspot/product-createdproduct.creation
Product Deletedhubspot/product-deletedproduct.deletion
Product Mergedhubspot/product-mergedproduct.merge
Product Restoredhubspot/product-restoredproduct.restore
Line Item Createdhubspot/line-item-createdline_item.creation
Line Item Deletedhubspot/line-item-deletedline_item.deletion
Line Item Mergedhubspot/line-item-mergedline_item.merge
Line Item Restoredhubspot/line-item-restoredline_item.restore
Line Item Association Changedhubspot/line-item-association-changedline_item.associationChange
Conversation Startedhubspot/conversation-createdconversation.creation
Conversation Message Receivedhubspot/conversation-new-messageconversation.newMessage
Conversation Deletedhubspot/conversation-deletedconversation.deletion
Conversation Privacy Deletedhubspot/conversation-privacy-deletedconversation.privacyDeletion

A deal moving between pipeline stages fires Deal Stage Changed in real time. To react to any other field changing, use the Updated Contact or Updated Record polling trigger below.

Polling triggers

TriggerSlugChecks for
New Form Submissionhubspot/new-form-submissionNew submissions on a selected form.
New Contact in Listhubspot/new-contact-in-listContacts newly added to a list, including active (dynamic) lists.
New Engagementhubspot/new-engagementEngagements (notes, calls, emails, meetings, tasks) created after the workflow is first published. Editing an older one does not fire it.
New Recordhubspot/new-recordNewly created records of any object type — including custom objects.
Updated Contacthubspot/updated-contactRecently modified contacts.
Updated Recordhubspot/updated-recordRecently modified records of any other object type.
New Blog Posthubspot/new-blog-postNewly created blog posts.

New Record and Updated Record are the only way to trigger on a custom object. HubSpot publishes no webhook subscription type per custom object, so a poll over the search endpoint is the supported path. They are also useful when a property-change webhook would be too noisy. Contacts are watched by Updated Contact rather than Updated Record, because HubSpot stores a contact's modified date under a different property name than every other object.

Each cycle emits one run carrying an items array. Add a Loop node over items to process each record individually; leave it out for digest-style workflows.

Trigger output

Webhook triggers share a common core, with per-family additions.

FieldTypePresent onDescription
objectIdnumberAll except merge and association triggersHubSpot record ID
eventIdnumberAllUnique event ID for idempotency
portalIdnumberAllHubSpot portal/account ID
subscriptionTypestringAllThe HubSpot event type
occurredAtnumberAllUnix timestamp in milliseconds
changeSourcestringAllWhat caused the change
attemptNumbernumberAllDelivery attempt number
propertyNamestringDeal Stage ChangedChanged property name
propertyValuestringDeal Stage ChangedChanged property value

Merge triggers replace objectId with primaryObjectId, mergedObjectIds, newObjectId, and numberOfPropertiesMoved. Association Changed triggers replace it with associationType, fromObjectId, toObjectId, associationRemoved, and isPrimaryAssociation — read associationRemoved to tell an add from a remove.

Actions

Records

The six generic Record actions each take an objectType and work against any HubSpot object type.

ActionSlugWhat it does
Create Recordhubspot/create-recordCreates a record of any object type.
Update Recordhubspot/update-recordUpdates an existing record by ID.
Get Recordhubspot/get-recordRetrieves a single record by ID.
Delete Recordhubspot/delete-recordDeletes a record by ID.
Search Recordshubspot/search-recordsSearches with filters, sorting, and property selection.
List Recordshubspot/list-recordsLists records of one object type.
Upsert Contacthubspot/upsert-contactCreates or updates contacts in batch by a unique property.
Merge Recordshubspot/merge-recordsMerges two records. Contacts, companies, deals, tickets, orders, appointments, courses, services, and projects only.

Associations, properties, and metadata

ActionSlugWhat it does
Create Associationhubspot/create-associationAssociates two CRM records by association category and type ID.
Remove Associationhubspot/remove-associationRemoves an association between two records.
List Associationshubspot/list-associationsLists every record of one type associated with a record (max 500).
List Propertieshubspot/list-propertiesLists a object type's properties — use it to discover internal names.
List Pipelineshubspot/list-pipelinesLists pipelines and their stages for an object type.
Get Ownerhubspot/get-ownerRetrieves an owner by ID.
List Ownershubspot/list-ownersLists owners; filter by email to resolve an owner from an address.

Engagements

ActionSlugWhat it does
Create Notehubspot/create-noteCreates a note engagement.
Create Taskhubspot/create-taskCreates a task.
Log Callhubspot/create-callLogs a call activity.
Log Meetinghubspot/create-meetingLogs a meeting activity.
Log Emailhubspot/create-emailLogs an email activity.

All five require hs_timestamp.

Lists and marketing

ActionSlugWhat it does
Create Listhubspot/create-listCreates a manual, snapshot, or dynamic list.
Get Listhubspot/get-listFetches a list by ILS list ID.
Search Listshubspot/search-listsSearches lists by name, or pages through all of them.
Add Contact to Listhubspot/add-contact-to-listAdds records to a list.
Remove Contact from Listhubspot/remove-contact-from-listRemoves records from a list.
Submit Formhubspot/submit-formSubmits data to a HubSpot form.
Get Formhubspot/get-formReads a form definition and its fields.
Enroll Contact in Workflowhubspot/enroll-contact-in-workflowEnrols a contact by email in a workflow picked from the account.
Subscribe Contact to Email Typehubspot/subscribe-contact-to-emailOpts a contact into a subscription type.
Unsubscribe Contact from Email Typehubspot/unsubscribe-contact-from-emailOpts a contact out of a subscription type.
Send Custom Eventhubspot/send-custom-eventRecords a custom behavioral event occurrence.
List Email Eventshubspot/list-email-eventsLists marketing-email sends, opens, clicks and bounces in a time window.
Get Email Subscriptions Timelinehubspot/get-email-subscriptions-timelineLists opt-in/opt-out changes across the account.
Create Blog Posthubspot/create-blog-postCreates a blog post, as a draft unless published.
List Blog Postshubspot/list-blog-postsLists blog posts, filterable by created/updated time.

Files

ActionSlugWhat it does
Upload Filehubspot/upload-fileUploads a file reference from an earlier step.
Import File from URLhubspot/import-file-from-urlImports a file from a public URL. Returns a task, not a file.
Get File Signed URLhubspot/get-file-signed-urlGets a temporary URL for a private file.

Use Upload File when an earlier step produced a file, and Import File from URL when the file already lives at a public address.

Dynamic pickers

These fields populate from the connected account rather than being typed by hand: deal pipeline, owner, form, list, subscription type, and workflow. Every one also has an expression toggle, so you can bind a value from an earlier step instead of picking.

There is no record picker for contacts, companies, deals, or tickets — supply record IDs from earlier steps or from Search Records.

Errors

Actions map HubSpot's HTTP responses to TaskJuice error codes. Each action times out after 15 seconds and retries up to 2 times on retryable errors with exponential backoff.

Error codeHTTPRetryableWhat it means and what to do
VALIDATION_ERROR400NoHubSpot rejected the request. Check property names and required fields.
AUTH_EXPIRED401NoThe connection is no longer valid. Reauthorize the HubSpot connection, then resume affected runs.
MISSING_SCOPES403NoThe connection lacks a required permission. Reconnect and grant it.
OBJECT_NOT_FOUND404NoThe record, list, form, or owner ID does not exist.
OBJECT_ALREADY_EXISTS409NoA conflicting record already exists.
RATE_LIMITED429YesHubSpot throttled the request. TaskJuice retries with backoff.
PROVIDER_ERROR500, 502, 503, 504YesHubSpot had a server error or the gateway timed out. TaskJuice retries.

HubSpot access tokens are short-lived and TaskJuice refreshes them for you. If a token cannot be refreshed — for example the connected app was removed in HubSpot — the next action returns AUTH_EXPIRED.

Known limitations

  • Webhook payloads are minimal. Triggers deliver identifiers, not records. Pair any trigger with Get Record to hydrate the object.
  • Webhook subscriptions are configured on the HubSpot app, not per workspace. Your agency subscribes to event types once on its own HubSpot app; TaskJuice routes each delivery to the right workspace by portalId.
  • Search has a separate, low rate limit of roughly 4 requests per second. The non-search CRM endpoints use HubSpot's higher standard limit.
  • Tier-gated surfaces. Blog posts need Content Hub or Marketing Hub, custom behavioral events need Enterprise, and custom objects need Enterprise. Calls against a tier the portal does not have return MISSING_SCOPES.
  • Merge is narrower than the record actions. Only the object types listed under Merge Records expose a merge endpoint.
  • Upsert is contacts-only. Other object types use Create Record or Update Record.
  • No batch record actions. Create, update, and delete act on one record per call.
  • Email-event reads are window-based, not a trigger. GET /email/public/v1/events documents no sort order and offers no sort parameter, so any watermark over it would silently drop events. List Email Events takes an explicit start/end window instead, which is correct regardless of ordering.
  • Social media publishing is not available. HubSpot has withdrawn the Broadcast channel-listing endpoint, so there is no supported way to choose a social channel to publish to.
  • Custom objects have no schema picker. Bind the objectTypeId through the expression toggle and supply property names yourself — List Properties will tell you what they are.
  • One agency app reaches 10 client portals. HubSpot caps a private-distribution OAuth app at 10 approved accounts, or 100 when the app lives in a Solutions Partner portal. Listing on HubSpot's App Marketplace lifts the cap but requires the app to be publicly distributed.
  • No real-time "… Updated" webhook triggers. HubSpot no longer accepts a "any property changed" webhook subscription — it requires naming the property — so there are no real-time Contact/Company/Deal/Ticket Updated triggers. Use the Updated Contact or Updated Record polling trigger to react to changes, or Deal Stage Changed for pipeline moves.
  • Association events are chatty. One association call can produce dozens of associationChange deliveries — HubSpot emits the reverse direction too, and a merge or delete cascades one event per association the record had. Dedupe on eventId if you only want one run per change.
  • Products and line items cannot be merged. HubSpot returns 400 Merges are not supported for both, so there is no Product Merged or Line Item Merged trigger and Merge Records does not offer those types.
  • Enroll Contact in Workflow addresses the workflow's legacy ID, not the flow ID in the URL. HubSpot's enrollment endpoint only parses the older numeric workflow ID, which is not the ID shown in a workflow's browser URL. The picker resolves it for you; if you bind an expression instead, supply the workflowId from HubSpot's Workflows v3 API, not the URL.
  • Custom events need an active event definition. Send Custom Event answers 400 No active event definition found until the event is defined under Reports → Data Management → Custom events (or via the event-definitions API), and the connection's app needs the analytics.behavioral_events.send scope.
  • Blog posts need a blog. Create Blog Post takes the blog (contentGroupId) from the picker; a portal with no blog set up has nothing to pick.
  • Triggers and actions are in beta. Behavior may change as the integration matures.
Was this helpful?