- Documentation
- Integrations
- Apps
- HubSpot integration
HubSpot integration
Create, update, search, and watch HubSpot CRM contacts, companies, deals, and tickets on behalf of your clients.
What it does
The HubSpot integration connects a client CRM once and lets you read and write the four core object types: contacts, companies, deals, and tickets. You can create, update, fetch, search, and delete records, manage associations between them, add notes, add contacts to a static list, look up owners, and start workflows the moment a record changes in HubSpot.
Every record action takes an objectType parameter that is templated into the request, so the same Create Record or Update Record action works with HubSpot custom objects when you type the object type, not only the four built-in types. There is no dropdown that lists or introspects custom-object properties, so you supply the object type and property names yourself.
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. Expect the behavior described here to be stable, but treat the integration as evolving.
Connect a HubSpot account
HubSpot connects through OAuth 2.0. There is no API key, subdomain, or secret to paste. The person who authorizes the connection must be a HubSpot super admin, because HubSpot only lets super admins approve a connected app.
Open Connections in your workspace
Open the workspace in TaskJuice and go to Connections.
Choose HubSpot and start the connect flow
Select HubSpot and start the OAuth connect flow.
Sign in to HubSpot as a super admin
Sign in to the HubSpot account you want to authorize, using your own account or your client's, and pick the HubSpot account to connect.
Approve the requested CRM permissions
Approve the CRM read, write, and schema permissions HubSpot lists. TaskJuice requests the scopes in the table below.
Return to your workspace
TaskJuice returns you to the workspace with the connection ready to use in any workflow.
For the full connect flow that applies to every OAuth app, see Connect an account.
Scopes requested
TaskJuice requests these 11 scopes during authorization. All are requested by default.
| Scope | Grants |
|---|---|
crm.objects.contacts.read | Read contacts |
crm.objects.contacts.write | Create and update contacts |
crm.objects.companies.read | Read companies |
crm.objects.companies.write | Create and update companies |
crm.objects.deals.read | Read deals |
crm.objects.deals.write | Create and update deals |
crm.objects.tickets.read | Read tickets |
crm.objects.tickets.write | Create and update tickets |
crm.schemas.contacts.read | Read the contact property schema |
crm.schemas.companies.read | Read the company property schema |
crm.schemas.deals.read | Read the deal property schema |
To disconnect, remove the connection in TaskJuice, which deletes the stored credentials. A HubSpot super admin can also remove the connected app from HubSpot's connected-apps settings.
Triggers
HubSpot delivers all 10 triggers as real-time webhooks, so a run starts as soon as the event happens rather than on a poll interval. Each trigger fires once per business event: one contact created starts one run.
HubSpot webhook payloads are minimal. They carry the record ID and, for property changes, the changed property name and value, but not the full record. To act on the rest of a record's fields, pair the trigger with a Get Record action that fetches the full object by ID.
| Trigger | Slug | Fires when |
|---|---|---|
| Contact Created | hubspot/contact-created | A new contact is created. Payload carries the contact ID; use Get Record to fetch full data. |
| Contact Updated | hubspot/contact-updated | A contact property changes. |
| Contact Deleted | hubspot/contact-deleted | A contact is deleted. |
| Company Created | hubspot/company-created | A new company is created. Payload carries the company ID; use Get Record to fetch full data. |
| Company Updated | hubspot/company-updated | A company property changes. |
| Deal Created | hubspot/deal-created | A new deal is created. Payload carries the deal ID; use Get Record to fetch full data. |
| Deal Updated | hubspot/deal-updated | A deal property changes, except stage changes, which fire Deal Stage Changed instead. |
| Deal Stage Changed | hubspot/deal-stage-changed | A deal's pipeline stage changes. |
| Ticket Created | hubspot/ticket-created | A new ticket is created. Payload carries the ticket ID; use Get Record to fetch full data. |
| Ticket Updated | hubspot/ticket-updated | A ticket property changes. |
A deal moving between pipeline stages fires hubspot/deal-stage-changed, not hubspot/deal-updated. Choose the trigger that matches whether you care about stage moves specifically or any property change.
Trigger output
All 10 triggers share the same output shape.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
objectId | number | Yes | None | HubSpot record ID |
eventId | number | Yes | None | Unique event ID for idempotency |
portalId | number | Yes | None | HubSpot portal/account ID |
subscriptionType | string | Yes | None | Event type |
occurredAt | number | Yes | None | Unix timestamp in milliseconds |
changeSource | string | No | None | What caused the change |
attemptNumber | number | No | None | Delivery attempt number |
propertyName | string | No | None | Changed property name; present only on updated and stage-changed triggers |
propertyValue | string | No | None | Changed property value; present only on updated and stage-changed triggers |
The propertyName and propertyValue fields appear only on the updated and stage-changed triggers. Created and deleted triggers do not carry them. On hubspot/deal-stage-changed, propertyName is always dealstage and propertyValue is the new pipeline stage value.
Actions
There are 12 actions, all driven by HTTP configuration, and all require the HubSpot connection. The six record actions take an objectType config field. Its dropdown lists contacts, companies, deals, and tickets; typing a custom object type also works because the value is templated into the request URL.
| Action | Slug | What it does |
|---|---|---|
| Create Record | hubspot/create-record | Creates a contact, company, deal, or ticket. |
| Update Record | hubspot/update-record | Updates an existing record by ID. |
| Get Record | hubspot/get-record | Retrieves a single record by ID. |
| Delete Record | hubspot/delete-record | Deletes a record by ID. |
| Search Records | hubspot/search-records | Searches records with filters, sorting, and property selection. |
| List Records | hubspot/list-records | Lists records of one object type. |
| Upsert Contact | hubspot/upsert-contact | Creates or updates contacts in batch by a unique identifier property. |
| Create Association | hubspot/create-association | Associates two CRM records. |
| Remove Association | hubspot/remove-association | Removes an association between two records. |
| Create Note | hubspot/create-note | Creates a note engagement and optionally associates it with records. |
| Add Contact to List | hubspot/add-contact-to-list | Adds contacts to a static list. |
| Get Owner | hubspot/get-owner | Retrieves a HubSpot owner by ID. |
Record action inputs
| Action | Config | Required inputs | Optional inputs |
|---|---|---|---|
| Create Record | objectType (enum) | properties (object) | associations (array) |
| Update Record | objectType (enum) | recordId (string), properties (object) | None |
| Get Record | objectType (enum) | recordId (string) | properties (comma-separated string of property names to return) |
| Delete Record | objectType (enum) | recordId (string) | None |
| Search Records | objectType (enum) | filterGroups (array of filter-group objects with AND/OR logic) | sorts (array), properties (array), limit (number, default 10, max 100), after (cursor) |
| List Records | objectType (enum) | None | limit (number, default 10, max 100), after (cursor), properties (comma-separated string) |
Get Record takes its properties input as a comma-separated string of property names. Search Records and List Records take properties as an array. Match the shape to the action you are using.
Other action inputs
| Action | Required inputs | Optional inputs |
|---|---|---|
| Upsert Contact | inputs (array, each item { "properties": {...}, "idProperty": "email" }) | None |
| Create Association | fromObjectType, fromObjectId, toObjectType, toObjectId, associationSpec (array, e.g. [{"associationCategory":"HUBSPOT_DEFINED","associationTypeId":1}]) | None |
| Remove Association | fromObjectType, fromObjectId, toObjectType, toObjectId | None |
| Create Note | properties (object, e.g. {"hs_note_body":"...","hs_timestamp":"..."}) | associations (array) |
| Add Contact to List | listId (string), contactIds (array of contact ID numbers) | None |
| Get Owner | ownerId (string) | None |
A few behaviors worth knowing when you wire these up:
- Search Records returns
{ total, results[], paging }and paginates with theaftercursor. List Records returns{ results[], paging }with nototal. - Upsert Contact is contacts-only in this version and returns
status,results[],numErrors, anderrors[]. For other object types, use Create Record or Update Record. - Create Note stores the note text in
hs_note_body.
Dynamic pickers
Two fields populate from the connected HubSpot account instead of being typed by hand:
- A deal pipeline picker, useful when you set a deal's pipeline or stage.
- An owner picker, useful when you assign a record to an owner.
These are the only two dynamic pickers. There is no record picker for contacts, companies, deals, or tickets, so you supply record IDs from earlier steps or from a Search Records action.
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. Non-retryable errors stop the step.
| Error code | HTTP | Retryable | What it means and what to do |
|---|---|---|---|
VALIDATION_ERROR | 400 | No | HubSpot rejected the request. Check property names and required fields. |
AUTH_EXPIRED | 401 | No | The connection is no longer valid. Reauthorize the HubSpot connection, then resume affected runs. |
MISSING_SCOPES | 403 | No | The connection lacks a required permission. Reconnect as a super admin to grant it. |
OBJECT_NOT_FOUND | 404 | No | The record, list, or owner ID does not exist. |
OBJECT_ALREADY_EXISTS | 409 | No | A conflicting record already exists. Returned by Create Record only. |
RATE_LIMITED | 429 | Yes | HubSpot throttled the request. TaskJuice retries with backoff. |
PROVIDER_ERROR | 500, 502, 503 | Yes | HubSpot had a server error. TaskJuice retries. |
HubSpot access tokens are short-lived, and TaskJuice refreshes them for you, so you should not normally see an auth error. If a token cannot be refreshed (for example, the connected app was removed in HubSpot), the next action returns AUTH_EXPIRED and the connection needs reauthorization.
Known limitations
- Webhook payloads are minimal. Triggers deliver the record ID and, for changes, the changed property only, not the full record. Pair any trigger with a Get Record action to hydrate the full object.
- Search has a low, separate rate limit of about 4 requests per second. Heavy search use across many clients reaches it quickly. When HubSpot returns a 429, TaskJuice treats it as a retryable rate-limit error and retries with backoff. The non-search CRM endpoints use HubSpot's higher standard limit.
- Triggers and actions are in beta. Behavior may change as the integration matures.
- Upsert is contacts-only. Other object types must use Create Record or Update Record. There is no batch create or update action for arbitrary object types.
- Webhook subscriptions are not filtered per property at the source. A trigger fires for any matching change. Narrow the events you act on with a downstream Filter or Branch node.
- Custom objects work without schema introspection. You can target a custom object by typing its
objectType, but there is no dropdown that fetches custom-object property schemas, so you supply property names yourself. - HubSpot Workflows, Marketing Email, and Forms APIs are not supported. This integration covers CRM objects, associations, notes, lists, and owners only.