younifyd
Menu

Connectors

HubSpot

Work with HubSpot CRM — contacts, companies, deals, tickets, products, line items, quotes, engagements, associations, pipelines, lists, files, and more — through its REST API. Covers the shared request/response model and every module's endpoints.

On this page

Connecting HubSpot

Every action needs a HubSpot connection configured with an Access Token — a HubSpot Private App access token (Settings → Integrations → Private Apps → your app → Auth), or an OAuth access token. It's sent as a Bearer token on every request automatically; there's no per-action auth configuration.

The Private App must be granted the scopes for the objects you use (e.g. crm.objects.contacts.read / crm.objects.contacts.write). A request for an object whose scope is missing comes back as a 403 — see Error handling below for how that surfaces.

All requests go to https://api.hubapi.com.

How HubSpot actions are structured

Each action is one HubSpot API operation. They're grouped in the action picker by module (Contacts, Companies, Deals, …) — the sections below match those modules. Action names are generated from the HubSpot operation, so they read like "List", "Create", "Read", "Update", "Archive", "Search", "Create a batch of contacts" rather than hand-written phrases — search the picker by the object plus the verb.

Every action exposes the same shape of configuration fields, assembled onto the request for you:

  • Path parameters — top-level fields named exactly as in the endpoint path (e.g. contactId, dealId, objectType). Required whenever the path has that segment; interpolated into the URL.
  • Query parameters — top-level fields for each query parameter the operation declares. Common ones on read operations: limit (page size), after (paging cursor), properties (comma-separated property names to return), associations (comma-separated object types whose associated IDs to include), archived (include archived records).
  • Request Body — for POST / PATCH / PUT operations, a structured field with one input per property the spec declares (for CRM objects this is an inputs array for batch operations, or a properties object for single-record operations).
  • Override Request Body + Request Body (Raw) — a toggle and a free-text field: turn the toggle on to bypass the structured fields and send raw JSON you write yourself. Use this for HubSpot request shapes the structured form doesn't capture cleanly (nested filter groups, association payloads).
  • Forward All Headers — forward headers from the workflow's incoming request (off by default).
  • Additional Headers / Additional Query Parameters — key/value escape hatches for anything not already covered; these override declared values with the same name.

Reading the response

Every action's result lands at {{<stepReference>.response...}} (see Step Reference), in the same envelope the HTTP Connector uses:

  • {{<stepReference>.response.status}} — the HTTP status code.
  • {{<stepReference>.response.statusText}} — the HTTP status text.
  • {{<stepReference>.response.headers.<header-name>}} — a response header.
  • {{<stepReference>.response.data.<field>}} — the parsed HubSpot response body.

HubSpot's own body shapes are consistent across the CRM object modules:

  • A single record (read / create / update / merge) — {{<stepReference>.response.data.id}}, {{<stepReference>.response.data.properties.<name>}} (e.g. {{<stepReference>.response.data.properties.email}}), {{<stepReference>.response.data.createdAt}}, {{<stepReference>.response.data.archived}}, and {{<stepReference>.response.data.associations}} when associations were requested. So a step named "Read" (reference read) resolves a contact's email at {{read.response.data.properties.email}}.
  • A list (the "List" / getPage operation) — {{<stepReference>.response.data.results}} is the array (e.g. {{<stepReference>.response.data.results[0].id}}), and {{<stepReference>.response.data.paging.next.after}} is the cursor to pass back as after for the next page (absent on the last page).
  • A search{{<stepReference>.response.data.results}} plus {{<stepReference>.response.data.total}} (total matching count) and {{<stepReference>.response.data.paging.next.after}}.
  • A batch operation (create / read / update / upsert) — {{<stepReference>.response.data.results}} is the array of affected records, and {{<stepReference>.response.data.status}} is COMPLETE (HTTP 200/201) or, on a partial failure (HTTP 207), the response also carries a {{<stepReference>.response.data.errors}} array — check {{<stepReference>.response.status}} to tell the two apart.
  • An archive / delete / GDPR-delete — HubSpot returns 204 No Content with an empty body, so there's nothing under .response.data; confirm success with {{<stepReference>.response.status}} being 204.

Modules outside the CRM object pattern (Associations, Pipelines, Lists, Files, Meetings, Webhooks, Imports, Exports) return HubSpot's own payload for that endpoint at {{<stepReference>.response.data.<field>}} — the module sections below note the shape, and HubSpot's API reference has the exact fields.

Working with properties

HubSpot records carry their data in a properties object, not top-level fields. When creating or updating, the request body's properties is a flat map of HubSpot property names to values, e.g. {"email": "user@example.com", "firstname": "Ada", "lifecyclestage": "lead"}. When reading, only a default set of properties comes back unless you name them: set the properties query parameter to a comma-separated list (e.g. email,firstname,lastname,hs_lead_status).

Searching

The Search action on each CRM object module takes a request body with filterGroups (an array — filters within one group are AND'd, groups are OR'd), sorts, properties, limit, and after. This nested shape is easiest to build by turning on Override Request Body and writing the JSON directly, e.g.:

{
  "filterGroups": [
    { "filters": [ { "propertyName": "email", "operator": "EQ", "value": "user@example.com" } ] }
  ],
  "properties": ["email", "firstname", "lastname"],
  "limit": 10
}

Error handling

A non-2xx response from HubSpot does not fail the step — it's returned as a normal result with the real status code and HubSpot's error body at {{<stepReference>.response.data}} (typically {{<stepReference>.response.data.message}} and {{<stepReference>.response.data.category}}), so you can branch on {{<stepReference>.response.status}} in the workflow. Common cases: 401 (bad or expired token), 403 (missing Private App scope), 404 (record archived or wrong ID), 429 (rate limited — HubSpot's Retry-After is at {{<stepReference>.response.headers.retry-after}}).

Standard CRM object actions

Twelve modules — Contacts, Companies, Deals, Tickets, Products, Line Items, Quotes, Notes, Calls, Emails, Tasks, and Custom Objects — expose the same set of operations against /crm/v3/objects/<object>:

  • ListGET /crm/v3/objects/<object>. Query: limit, after, properties, associations, archived.
  • CreatePOST /crm/v3/objects/<object>. Body: { "properties": { … }, "associations": [ … ] }.
  • ReadGET /crm/v3/objects/<object>/{<object>Id}. Query: properties, associations, archived.
  • UpdatePATCH /crm/v3/objects/<object>/{<object>Id}. Body: { "properties": { … } }.
  • ArchiveDELETE /crm/v3/objects/<object>/{<object>Id}. Returns 204.
  • SearchPOST /crm/v3/objects/<object>/search. See Searching above.
  • Batch Create / Read / Update / UpsertPOST /crm/v3/objects/<object>/batch/{create|read|update|upsert}. Body: { "inputs": [ … ] } (each input is a { "properties": … }, a { "id": … }, or an { "id": …, "properties": … } depending on the operation).
  • Batch ArchivePOST /crm/v3/objects/<object>/batch/archive. Body: { "inputs": [ { "id": "…" } ] }. Returns 204.

Each module's section below only calls out what's different from this list (the object's path segment, extra operations like Merge, or a non-standard object-type id).

Contacts

Object: contacts. Path segment /crm/v3/objects/contacts, record path parameter contactId. Has all the standard CRM object actions described above, plus:

  • MergePOST /crm/v3/objects/contacts/merge. Body: { "primaryObjectId": "…", "objectIdToMerge": "…" }. Returns the surviving contact record.
  • GDPR deletePOST /crm/v3/objects/contacts/gdpr-delete. Body: { "objectId": "…" } (or idProperty + objectId). Permanently erases the contact; returns 204. This is irreversible — unlike Archive, which is a soft delete.

Example

To create a contact: turn on Override Request Body and send { "properties": { "email": "ada@example.com", "firstname": "Ada", "lastname": "Lovelace" } }. The new contact's ID is at {{<stepReference>.response.data.id}}.

Companies

Object: companies. Path segment /crm/v3/objects/companies, record path parameter companyId. Has all the standard CRM object actions, plus Merge (POST /crm/v3/objects/companies/merge).

Deals

Object: deals, addressed by its object-type id — the path segment is /crm/v3/objects/0-3, not /crm/v3/objects/deals. Record path parameter dealId. Has all the standard CRM object actions, plus Merge (POST /crm/v3/objects/0-3/merge). Deal stage and pipeline are properties (dealstage, pipeline) — use the Pipelines module to look up valid stage ids.

Tickets

Object: tickets. Path segment /crm/v3/objects/tickets, record path parameter ticketId. Has all the standard CRM object actions, plus Merge (POST /crm/v3/objects/tickets/merge).

Products

Object: products (the product library). Path segment /crm/v3/objects/products, record path parameter productId. Standard CRM object actions only (no Merge). Key properties: name, price, hs_sku.

Line Items

Object: line_items. Path segment /crm/v3/objects/line_items, record path parameter lineItemId. Standard CRM object actions only. A line item is normally created then associated with a deal (see Associations); key properties: name, quantity, price, hs_product_id.

Quotes

Object: quotes. Path segment /crm/v3/objects/quotes, record path parameter quoteId. Standard CRM object actions only.

Notes

Object: notes (an engagement). Path segment /crm/v3/objects/notes, record path parameter noteId. Standard CRM object actions only. Body content is the hs_note_body property; hs_timestamp is required on create. Associate the note with the record it's about via Associations.

Calls

Object: calls (an engagement). Path segment /crm/v3/objects/calls, record path parameter callId. Standard CRM object actions only. Key properties: hs_call_title, hs_call_body, hs_call_direction, hs_call_duration, hs_timestamp.

Emails

Object: emails (a logged email engagement — not the Marketing Email API). Path segment /crm/v3/objects/emails, record path parameter emailId. Standard CRM object actions only. Key properties: hs_email_subject, hs_email_text, hs_email_direction, hs_timestamp.

Meetings

Object: meetings — but this module uses HubSpot's Scheduler API (/scheduler/v3/meetings/...), which is a different shape from the CRM object modules:

  • Create a new calendar meeting eventPOST /scheduler/v3/meetings/calendar.
  • Get meeting scheduling pagesGET /scheduler/v3/meetings/meeting-links. Returns {{<stepReference>.response.data.results}}.
  • Book a meetingPOST /scheduler/v3/meetings/meeting-links/book.
  • Get the availability for a meetingGET /scheduler/v3/meetings/meeting-links/book/availability-page/{slug}. Path parameter slug.
  • List booking informationGET /scheduler/v3/meetings/meeting-links/book/{slug}. Path parameter slug.

To read or write meeting engagement records on a contact/deal timeline instead, use the Custom Objects module with object type meetings.

Custom Objects

The standard CRM object actions, parameterized: the path segment is /crm/v3/objects/{objectType} with a path parameter objectType. Set objectType to a custom object's fully-qualified name (e.g. p12345_projects) or to any standard object name — including ones without their own module, such as tickets, meetings, or feedback_submissions. Record path parameter objectId. Has Merge too (POST /crm/v3/objects/{objectType}/merge).

Associations

Links records of one type to records of another (contact ↔ deal, line item ↔ deal, note ↔ company, …). This module uses HubSpot's v4 associations batch API and has three actions, all POST /crm/v3/associations/{fromObjectType}/{toObjectType}/batch/{...} with path parameters fromObjectType and toObjectType:

  • Associate records (labelled).../batch/create. Body: { "inputs": [ { "from": { "id": "…" }, "to": { "id": "…" }, "types": [ { "associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 1 } ] } ] }.
  • Retrieve associations.../batch/read. Body: { "inputs": [ { "id": "<fromRecordId>" } ] }. Returns {{<stepReference>.response.data.results}}, each entry pairing a from id with its associated to ids.
  • Remove associations.../batch/archive. Body: same inputs shape as create. Returns 204.

Use the paired object types in path order, e.g. fromObjectType contacts, toObjectType deals. associationTypeId values come from HubSpot's association-schema endpoints or its docs (the default contact→deal type is 3, deal→contact is 4).

Owners

Read-only. One action:

  • Retrieve a specific owner by IDGET /crm/v3/owners/{ownerId}. Path parameter ownerId. Returns {{<stepReference>.response.data.id}}, {{<stepReference>.response.data.email}}, {{<stepReference>.response.data.firstName}}, {{<stepReference>.response.data.lastName}}, {{<stepReference>.response.data.userId}}.

Owner ids are what CRM properties like hubspot_owner_id hold, so this resolves an assignee to a name/email.

Pipelines

Manage pipelines and their stages for a CRM object type. All paths start /crm/v3/pipelines/{objectType} with path parameter objectType (e.g. deals, tickets):

  • Retrieve all pipelinesGET /crm/v3/pipelines/{objectType}. Returns {{<stepReference>.response.data.results}}.
  • Create a pipelinePOST /crm/v3/pipelines/{objectType}.
  • Return a pipeline by IDGET /crm/v3/pipelines/{objectType}/{pipelineId}. Path parameter pipelineId.
  • Replace a pipelinePUT /crm/v3/pipelines/{objectType}/{pipelineId}.
  • Delete a pipelineDELETE /crm/v3/pipelines/{objectType}/{pipelineId}.
  • Return an audit of all changes to the pipelineGET /crm/v3/pipelines/{objectType}/{pipelineId}/audit.
  • Return all stages of a pipelineGET /crm/v3/pipelines/{objectType}/{pipelineId}/stages.
  • Create a pipeline stagePOST /crm/v3/pipelines/{objectType}/{pipelineId}/stages.
  • Return a pipeline stage by ID / Replace a pipeline stage / Delete a pipeline stageGET / PUT / DELETE /crm/v3/pipelines/{objectType}/{pipelineId}/stages/{stageId}. Path parameter stageId.

Each stage object has an id and a label — a stage id is what a deal's dealstage property must be set to.

Lists

Manage HubSpot lists (formerly "contact lists"), their folders, and their membership. 26 actions against /crm/v3/lists/.... The frequently-used ones:

  • Fetch List by IDGET /crm/v3/lists/{listId}. Path parameter listId. Returns {{<stepReference>.response.data.list}}.
  • Search ListsPOST /crm/v3/lists/search. Body: { "query": "…", "listIds": [...], "offset": 0, "count": 100 }. Returns {{<stepReference>.response.data.lists}} and {{<stepReference>.response.data.total}}.
  • Retrieve List by NameGET /crm/v3/lists/object-type-id/{objectTypeId}/name/{listName}. Path parameters objectTypeId (e.g. 0-1 for contacts) and listName.
  • Fetch List Memberships Ordered by IDGET /crm/v3/lists/{listId}/memberships. Returns {{<stepReference>.response.data.results}} (record ids) and {{<stepReference>.response.data.paging}}.
  • Add Records to a List / Remove Records from a ListPUT /crm/v3/lists/{listId}/memberships/add / .../remove. Body: a JSON array of record ids. Only works on MANUAL and SNAPSHOT lists, not DYNAMIC ones.
  • Add and/or Remove Records from a ListPUT /crm/v3/lists/{listId}/memberships/add-and-remove.
  • Get lists record is member ofGET /crm/v3/lists/records/{objectTypeId}/{recordId}/memberships. Path parameters objectTypeId and recordId.
  • Create a List is not a single operation here — lists are created via POST /crm/v3/lists/ on HubSpot's side; this module's list-writing operations focus on folders, membership, filters (update-list-filters), renaming, restoring, and static-conversion scheduling.

The remaining actions cover list folders (create, delete, rename, move), idmapping (translate legacy numeric list ids to modern ids), and list lifecycle (restore a deleted list, schedule/cancel conversion of a dynamic list to static).

Files

Manage files and folders in the HubSpot File Manager. 20 actions across /files/v3/files/... and /files/v3/folders/...:

  • Upload filePOST /files/v3/files. This is a multipart/form-data upload; the structured form doesn't build multipart bodies, so this action is only usable when you already have the file content available to pass through — most workflows import by URL instead (next item).
  • Import file from URLPOST /files/v3/files/import-from-url/async. Body includes the source url, folderPath (or folderId), fileName, and access (PUBLIC_INDEXABLE, PUBLIC_NOT_INDEXABLE, PRIVATE). Returns a task id.
  • Check import statusGET /files/v3/files/import-from-url/async/tasks/{taskId}/status. Path parameter taskId.
  • Search filesGET /files/v3/files/search. Query parameters for name, extension, type, parentFolderIds, etc. Returns {{<stepReference>.response.data.results}}.
  • Retrieve file by ID / by pathGET /files/v3/files/{fileId} / GET /files/v3/files/stat/{path}. Path parameter fileId or path.
  • Get signed URL to access private fileGET /files/v3/files/{fileId}/signed-url. Returns {{<stepReference>.response.data.url}} (time-limited).
  • Replace file / Update file properties / Delete file by ID / GDPR-delete filePUT / PATCH / DELETE /files/v3/files/{fileId} and DELETE /files/v3/files/{fileId}/gdpr-delete.
  • Folder operations — create, search, retrieve by id or path, update properties, delete, and an async recursive property update with its own status endpoint, all under /files/v3/folders/....

Webhooks

Configure webhook subscriptions for a HubSpot app (not for a workflow — use a workflow's own webhook trigger for that). All paths are /webhooks/v3/{appId}/... with path parameter appId:

  • Read webhook settings / Update webhook settings / Delete webhook settingsGET / PUT / DELETE /webhooks/v3/{appId}/settings. Settings hold the target URL and throttling.
  • Read event subscriptionsGET /webhooks/v3/{appId}/subscriptions. Returns {{<stepReference>.response.data.results}}.
  • Create an event subscriptionPOST /webhooks/v3/{appId}/subscriptions. Body: { "eventType": "contact.propertyChange", "propertyName": "email", "active": true }.
  • Batch create event subscriptionsPOST /webhooks/v3/{appId}/subscriptions/batch/update.
  • Read / Update / Delete an event subscriptionGET / PATCH / DELETE /webhooks/v3/{appId}/subscriptions/{subscriptionId}. Path parameter subscriptionId.

Imports

Track CRM record imports (the import itself is started in the HubSpot UI or via the import-start endpoint). Three actions:

  • Get the information on any importGET /crm/v3/imports/{importId}. Path parameter importId. Returns {{<stepReference>.response.data.state}} (STARTED, PROCESSING, DONE, FAILED, CANCELED) and {{<stepReference>.response.data.metadata}}.
  • Cancel an active importPOST /crm/v3/imports/{importId}/cancel.
  • Retrieve errors for a specific importGET /crm/v3/imports/{importId}/errors. Returns {{<stepReference>.response.data.results}}.

Exports

Run and retrieve CRM data exports. Three actions:

  • Start an exportPOST /crm/v3/exports/export/async. Body describes the object type, format (CSV, XLSX), properties, and any filters. Returns {{<stepReference>.response.data.id}}.
  • Get Status of Export Including URL to DownloadGET /crm/v3/exports/export/async/tasks/{taskId}/status. Path parameter taskId. When {{<stepReference>.response.data.status}} is COMPLETE, the download link is at {{<stepReference>.response.data.result}} (a time-limited URL).
  • Retrieve details of a specific export by its unique IDGET /crm/v3/exports/export/{exportId}. Path parameter exportId.

Also applies here

Step Name

What it's for

Every step in a workflow gets a name — either one you set or a default based on the connector and action (e.g. "Get Order Details", "Send Welcome Email"). It's shown throughout the UI and in your execution history, and it's also the source for the step's Reference — a camelCase identifier auto-generated from the name (e.g. "Get Order Details" → getOrderDetails) — which is what you actually use in {{...}} expressions to read this step's output from later steps. See Step Reference.

Rules

  • Must be at least 2 characters, and 50 characters or fewer.
  • Must be unique within the workflow — reusing a name that's already taken will be rejected, with a suggested alternative (e.g. "Get Order Details 2").
  • Can't be empty.

Tips

  • Prefer a descriptive, human-readable name over a generic one — "Get Order Details" is easier to work with later than "HTTP Request 2", especially once a workflow has a dozen steps.
  • Renaming a step updates every reference to it elsewhere in the workflow automatically.

Step Reference

Syntax

Any input field can reference earlier data using {{expression}}. The expression is evaluated as JSONata — so simple dot-paths and more advanced queries (filters, functions) both work.

Referencing a step's output

Use the step's Reference — a camelCase identifier auto-generated from its Name (e.g. "Get Order Details" → getOrderDetails), shown read-only wherever the step's fields are configured — followed by the field path. Elsewhere in these docs this general pattern is written as {{<stepReference>.field.path}}:

{{getOrderDetails.response.data.id}}
{{getOrderDetails.response.status}}

The raw display name won't work here even though it's what you see in the UI — {{Get Order Details.response.data.id}} isn't valid, since a bare name containing spaces isn't a single JSONata identifier. Always use the camelCase Reference.

You can also reference steps by position instead of by reference:

{{steps[0].response.data.id}}

Referencing trigger data

{{trigger.headers.authorization}}
{{trigger.body.customerId}}
{{trigger.query.page}}
{{trigger.params.orderId}}
{{trigger.method}}
{{trigger.path}}

Referencing workflow variables

{{variables.myVariable}}

See Variables for the full list of variable types and more examples, including connection-type variables.

Referencing runtime variables

A separate, mutable namespace written by the Variable connector while a run is in progress — not the same as the workflow-level variables above:

{{runtimeVariables.myVariable}}

Notes

  • If an expression can't be resolved (a typo in a step name, a field that doesn't exist), it resolves to an empty string rather than failing the workflow — check your execution history if a value comes through blank.
  • Object values are automatically JSON-stringified when interpolated into a string field.

Execution Settings

Fire-and-forget

When enabled, the workflow doesn't wait for this step to complete before moving to the next one. Use it for steps whose result nothing downstream depends on — logging, analytics, notifications — so they don't add latency to the steps that matter.

Continue on error

When enabled, a failure in this step doesn't stop the workflow — execution continues to the next step. The failure is still recorded in the execution history; this just controls whether it's fatal.

Combine the two for steps that are genuinely optional to the outcome: fire-and-forget so they don't add latency, continue-on-error so a failure in them (e.g. an analytics endpoint being briefly down) doesn't take down an otherwise-successful workflow run.

Caching

What it does

When enabled, a step's result is cached for a configurable TTL (time-to-live, in seconds). If the step runs again with the same effective cache key before the TTL expires, the cached result is returned instead of re-running the step.

Configuring it

  • Enabled — turn caching on or off for this step.
  • TTL — how long (in seconds) a cached result stays valid.
  • Cache key template — an expression (supporting the same {{...}} step reference syntax used elsewhere) that determines what counts as "the same call". By default this is based on the step's resolved input; a custom template lets you cache more narrowly or broadly than that.

When to use it

Good candidates are steps that call something slow or rate-limited but return the same answer for the same input within a short window — a lookup against a rarely-changing external system, for example. Skip it for steps whose result must always be fresh (anything involving live inventory, pricing, or payment state).

Locking

What it does

When enabled, only one execution of this step (for a given lock key) can run at a time. If a second execution tries to run the same step while a lock is held, it waits until the lock is released or the hold period elapses.

Configuring it

  • Enabled — turn locking on or off for this step.
  • Lock key — an expression (supporting the same {{...}} step reference syntax used elsewhere) that determines what counts as "the same resource". By default the lock is scoped to the step itself; a custom key lets you lock per-customer, per-order, or any other identifier that needs serialized access.
  • Period — how long (in seconds) the lock is held before it's automatically released, in case an execution doesn't complete normally.

When to use it

Use it whenever concurrent executions could race on the same resource — e.g. two workflow runs both trying to update the same order's status at once. A lock key scoped to the order id ensures only one of them proceeds at a time.