Pipeloom Docs
ConnectorsSources

HubSpot

Set up the HubSpot source connector.

Sync modes, namespaces and the columns Pipeloom adds are explained once in Connector concepts; workspace variables and custom components in Orchestration.

This page contains the setup guide and reference information for the HubSpot source connector.

Prerequisites

  • HubSpot Account

  • For Pipeloom: a Private App access token or a Service Key

Setup guide

Step 1: Set up HubSpot

For Pipeloom:

- Private App setup (Recommended): If you are authenticating via a Private App, you will need to use your Access Token to set up the connector. Please refer to the official HubSpot documentation to learn how to obtain the access token. If your HubSpot account can no longer create Private Apps, create a Service Key instead and enter it in the same Access token field.

- OAuth setup: If you are using OAuth to authenticate on Pipeloom, please refer to HubSpot's detailed walkthrough. To set up the connector, you will need to acquire your:

  • Client ID
  • Client Secret
  • Refresh Token

For more details, refer to HubSpot's authentication documentation.

Service keys

HubSpot is retiring the creation of legacy Private Apps: accounts created on or after September 28, 2026 can't create new Private Apps from that date, and all other accounts lose the ability on October 26, 2026. Existing Private Apps and their access tokens keep working.

For accounts that can't create a Private App, use a HubSpot Service Key (public beta). A Service Key is a static bearer token with the same pat- format and the same per-object scopes as a Private App access token, so the connector accepts it without any special configuration:

  1. In HubSpot, go to Development, then Keys > Service keys, and click Create service key.
  2. Add the scopes for the streams you want to sync (see Step 2). HubSpot has deprecated the legacy tickets and e-commerce scopes, so the Service Key scope picker might not offer them; Step 2 lists their granular replacements.
  3. Create the key, click Show, and copy it.
  4. In Pipeloom, choose the Private App authentication method and paste the Service Key into the Access token field.

A Service Key can only carry scopes that your HubSpot account and user already have. If HubSpot returns 403 for a stream, first confirm that the key has the scope listed in Step 2. If the scope is present, your account's subscription tier most likely doesn't include that object (for example, the leads stream needs the Leads object, which requires Sales Hub Professional or Enterprise).

Step 2: Configure the scopes for your streams (Private App and Service Key only)

These instructions are only relevant if you are using a Private App or a Service Key for authentication. You can ignore this if you are authenticating via OAuth.

To set up a Private App or Service Key, you must manually configure scopes to ensure Pipeloom can sync all available data. Each scope relates to a specific stream or streams. Refer to HubSpot's documentation on scopes for instructions.

The legacy tickets and e-commerce scopes are deprecated and might not be available when you create a Service Key. Where the table lists tickets, grant crm.objects.tickets.read (and crm.schemas.tickets.read for ticket properties and pipelines). Where it lists e-commerce, grant crm.objects.products.read, crm.objects.line_items.read, and crm.schemas.line_items.read.

Expand to review scopes
StreamRequired Scope
campaignscontent
companiescrm.objects.companies.read, crm.schemas.companies.read
contact_listscrm.lists.read
contactscrm.objects.contacts.read
association_streamsRead scopes for the selected source and target objects
Custom CRM Objectscrm.objects.custom.read
custom_object_association_streamscrm.objects.custom.read and the read scope for any selected standard object
deal_pipelinescrm.objects.contacts.read
dealscrm.objects.deals.read, crm.schemas.deals.read
deals_archivedcrm.objects.deals.read, crm.schemas.deals.read
email_eventscontent
email_subscriptionscontent
engagementscrm.objects.companies.read, crm.objects.contacts.read, crm.objects.deals.read, tickets, e-commerce
engagements_emailssales-email-read
formsforms
form_submissionsforms
goalscrm.objects.goals.read
leadscrm.objects.leads.read, crm.schemas.leads.read
list_membershipscrm.lists.read
line_itemse-commerce
ownerscrm.objects.owners.read
productse-commerce
contacts_property_historycrm.objects.contacts.read
companies_property_historycrm.objects.companies.read
deals_property_historycrm.objects.deals.read
subscription_changescontent
ticketstickets
userscrm.objects.users.read, settings.users.read
account_detailsoauth
deal_splitscrm.objects.deals.read
propertiesNo additional scopes required
workflowsautomation
contacts_web_analyticsbusiness-intelligence, crm.objects.contacts.read
companies_web_analyticsbusiness-intelligence, crm.objects.companies.read
deals_web_analyticsbusiness-intelligence, crm.objects.deals.read
tickets_web_analyticsbusiness-intelligence, tickets
engagements_calls_web_analyticsbusiness-intelligence, crm.objects.contacts.read
engagements_emails_web_analyticsbusiness-intelligence, crm.objects.contacts.read, sales-email-read
engagements_meetings_web_analyticsbusiness-intelligence, crm.objects.contacts.read
engagements_notes_web_analyticsbusiness-intelligence, crm.objects.contacts.read
engagements_tasks_web_analyticsbusiness-intelligence, crm.objects.contacts.read
goals_web_analyticsbusiness-intelligence, crm.objects.goals.read
line_items_web_analyticsbusiness-intelligence, e-commerce, crm.objects.line_items.read
products_web_analyticsbusiness-intelligence, e-commerce

Step 3: Set up the HubSpot connector in Pipeloom

For Pipeloom:

  1. Navigate to the Pipeloom dashboard.
  2. From Pipeloom, click Sources, then click on + New Source and select HubSpot from the list of available sources.
  3. Enter a Source name of your choosing.
  4. From the Authentication dropdown, select your chosen authentication method:
    • (Recommended) To authenticate using a Private App, select Private App and enter the Access Token for your HubSpot account. A Service Key goes in the same field.
    • (Not Recommended:) To authenticate using OAuth, select OAuth and enter your Client ID, Client Secret, and Refresh Token.
  5. (Optional) For Start date, use the provided datepicker or enter the date in the following format: yyyy-mm-ddThh:mm:ssZ. The data added on and after this date will be replicated. If not set, "2006-06-01T00:00:00Z" (HubSpot creation date) will be used as start date. It's recommended to provide a start date relevant to your data to optimize synchronization.
  6. (Optional) Set the CRM Search Lookback Window in minutes to re-fetch data for CRM Search streams (e.g. contacts, companies, deals, tickets) for a specified number of minutes before the state from the previous sync. This helps capture missing records in CRM Search streams.
  7. (Optional) Set the Property History Lookback Window in minutes to re-fetch data for property history streams (deals_property_history, contacts_property_history, companies_property_history). This helps capture records that may be missed due to cursor drift caused by HubSpot calculated properties. A value of 43200 (30 days) is a reasonable starting point.
  8. (Optional) Enable Treat dynamic number and boolean properties as strings if your destination rejects records because HubSpot returns values that don't match the declared number or boolean type. See Destination type conversion errors in Troubleshooting for details.
  9. Click Set up source and wait for the tests to complete.

Experimental streams

Enable the Enable experimental streams toggle to sync the Web Analytics streams. These read HubSpot's Events API (/events/event-occurrences/2026-03) and emit web analytics events (page views and form submissions) for each parent object:

  • contacts_web_analytics
  • companies_web_analytics
  • deals_web_analytics
  • tickets_web_analytics
  • engagements_calls_web_analytics
  • engagements_emails_web_analytics
  • engagements_meetings_web_analytics
  • engagements_notes_web_analytics
  • engagements_tasks_web_analytics
  • goals_web_analytics
  • line_items_web_analytics
  • products_web_analytics

These streams require HubSpot Marketing Hub Enterprise and the business-intelligence scope in addition to each stream's parent-object read scope (see the scopes table in Step 2). They begin syncing from the configured Start date with fresh state.

Association streams

Use Association Between Objects Streams to sync relationships between two standard HubSpot CRM objects. Add one item for each relationship you want to sync. Each item creates one stream that reads IDs from the source object, calls HubSpot's associations batch read API, and emits one record for each association.

Configure these fields for each association stream:

  • From Object: The source object type, such as tickets, deals, or contacts.
  • To Object: The target object type, such as companies or contacts.
  • Stream Name: Optional. If you leave this empty, Pipeloom names the stream associations_<from_object>_<to_object>.

For example, set From Object to tickets and To Object to companies to create the associations_tickets_companies stream. After you add or edit association stream settings, refresh the source schema so the generated streams appear in the catalog.

Association stream records include:

  • from_id: The ID of the source object record.
  • to_id: The ID of the target object record.
  • association_type_id: HubSpot's numeric association type identifier.
  • category: The association category, such as HUBSPOT_DEFINED or USER_DEFINED.
  • label: The association label. This is null for unlabeled associations.

Association streams sync incrementally. Only associations for records modified since the last sync are fetched.

If you authenticate with a Private App or Service Key, grant read scopes for both selected objects. For example, a tickets-to-companies association stream needs the tickets (or, for a Service Key, crm.objects.tickets.read) and crm.objects.companies.read scopes.

Custom object association streams

Use Custom Object Association Streams to sync relationships where at least one side is a custom HubSpot object. Add one item for each relationship you want to sync.

Configure these fields for each custom object association stream:

  • From Object: The source object name or identifier.
  • To Object: The target object name or identifier.
  • Stream Name: Optional. If you leave this empty, Pipeloom names the stream associations_<from_object>_<to_object>.

After you add or edit custom object association stream settings, refresh the source schema so the generated streams appear in the catalog.

For custom objects, use either:

  • The custom object's fullyQualifiedName, such as p_my_custom_object.
  • The custom object's objectTypeId, such as 2-12345.

You can use standard object names, such as contacts, companies, or deals, for the standard-object side of the relationship.

Custom object association streams emit the same fields as standard association streams: from_id, to_id, association_type_id, category, and label. They also sync incrementally, the same way as standard association streams.

If you authenticate with a Private App or Service Key, grant crm.objects.custom.read and the read scope for any standard object in the relationship.

Supported sync modes

The HubSpot source connector supports the following sync modes:

  • Full Refresh
  • Incremental

:::note There are two types of incremental sync:

  1. Incremental (standard server-side, where API returns only the data updated or generated since the last sync)
  2. Client-Side Incremental (API returns all available data and connector filters out only new records) :::

Supported Streams

The HubSpot source connector supports the following streams:

Entity-Relationship Diagram (ERD)

Notes on the property_history streams

The property history streams (contacts_property_history, companies_property_history, deals_property_history) use Client-Side Incremental sync with a cursor timestamp to determine which records have changed since the previous sync.

HubSpot calculated properties — formula fields, rollup summaries, and analytics properties such as hs_analytics_* — always have a timestamp that mirrors the time of the latest sync, not the time of the last user-initiated change. This causes the sync cursor to advance past records that have not yet been synced, which can result in missing records.

To mitigate this, configure the Property History Lookback Window in the source settings. A value of 43200 (30 days) is a reasonable starting point. Because these streams use Append + Deduped sync mode, duplicate records from the lookback period are handled automatically.

Notes on the engagements stream

  1. Objects in the engagements stream can have one of the following types: note, email, task, meeting, call. Depending on the type of engagement, different properties are set for that object in the engagements_metadata table in the destination:
  • A call engagement has a corresponding engagements_metadata object with non-null values in the toNumber, fromNumber, status, externalId, durationMilliseconds, externalAccountId, recordingUrl, body, and disposition columns.
  • An email engagement has a corresponding engagements_metadata object with non-null values in the subject, html, and text columns. In addition, there will be records in four related tables, engagements_metadata_from, engagements_metadata_to, engagements_metadata_cc, engagements_metadata_bcc.
  • A meeting engagement has a corresponding engagements_metadata object with non-null values in the body, startTime, endTime, and title columns.
  • A note engagement has a corresponding engagements_metadata object with non-null values in the body column.
  • A task engagement has a corresponding engagements_metadata object with non-null values in the body, status, and forObjectType columns.
  1. The engagements stream uses two different APIs based on the length of time since the last sync and the number of records which Pipeloom hasn't yet synced.
  • EngagementsRecent if the following two criteria are met:
    • The last sync was performed within the last 30 days
    • Fewer than 10,000 records are being synced
  • EngagementsAll if either of these criteria are not met.

Because of this, the engagements stream can be slow to sync if it hasn't synced within the last 30 days and/or is generating large volumes of new data. To accommodate for this limitation, we recommend scheduling more frequent syncs.

Notes on the Forms and Form Submissions stream

This stream only syncs marketing forms.

Notes on the list_memberships stream

The list_memberships stream reads memberships for every list returned by the contact_lists stream by calling HubSpot's GET /crm/v3/lists/{listId}/memberships endpoint. A few behaviors are worth knowing about:

  • The stream only supports full refresh. HubSpot's memberships endpoint doesn't expose a cursor suitable for incremental sync.
  • Records are keyed by the composite primary key (recordId, listId). listId is emitted as a string so records serialize cleanly to Avro destinations.
  • Some lists returned by contact_lists reference an objectTypeId that isn't active for your portal (for example, Leads lists with objectTypeId 0-136). HubSpot's memberships endpoint rejects those lists with a 400 VALIDATION_ERROR / ListError.INVALID_OBJECT_TYPE_FOR_LIST. The connector skips those lists so they don't fail the sync, and logs an entry for each one. If you need memberships for those object types, enable the corresponding object in HubSpot or sync the object directly from its own stream (for example, leads).

Notes on the Custom CRM Objects

Custom CRM Objects will appear as streams available for sync, alongside the standard objects listed above.

If you set up your connections before April 15th, 2023 (on Pipeloom) or before 0.8.0 (OSS) then you'll need to do some additional work to sync custom CRM objects.

First you need to give the connector some additional permissions:

  • If you are using OAuth on Pipeloom go to the HubSpot source settings page in Pipeloom and re-authenticate via OAuth to allow Pipeloom the permissions to access custom objects.

  • If you are using OAuth on OSS, Private App, or Service Key auth go into the HubSpot UI where you created your Private App, Service Key, or OAuth application and add the crm.objects.custom.read scope to its scopes. See HubSpot's scopes documentation.

Then, go to the schema tab of your connection and click refresh source schema to pull in those new streams for syncing.

Limitations & Troubleshooting

Expand to see details about HubSpot connector limitations and troubleshooting.

Rate limiting

The connector is restricted by normal HubSpot rate limits. The burst limit applies per app, and the daily limit is shared across all apps within the same HubSpot account.

Product tierBurst (per 10 seconds)Daily (per account)
Free & Starter100250,000
Professional190625,000
Enterprise1901,000,000
API Limit Increase add-on250+1,000,000 per add-on (max 2)

Custom properties sync slowly

If you use custom properties in HubSpot, syncs take longer. Pipeloom doesn't alert you to the presence of custom properties, but you can check if you're using them with HubSpot's UI.

Troubleshooting

  • Enabling streams: Some streams, such as workflows, require specific scopes before they can be read. If the authenticated user does not have the necessary permissions, the connector logs a warning and skips the stream.

  • HubSpot object labels In HubSpot, a label can be applied to a stream that differs from the original API name of the stream. HubSpot's UI shows the label of the stream, whereas Pipeloom shows the name of the stream. If you are having issues seeing a particular stream your user should have access to, search for the name of the HubSpot object instead.

  • Unnesting top level properties: Since version 1.5.0, in order to offer users access to nested fields, we also denest the top-level fields into individual fields in the destination. This is most commonly observed in the properties field, which is now split into each attribute in the destination.

    For instance:

    {
      "id": 1,
      "updatedAt": "2020-01-01",
      "properties": {
        "hs_note_body": "World's best boss",
        "hs_created_by": "Michael Scott"
      }
    }

    becomes

    {
      "id": 1,
      "updatedAt": "2020-01-01",
      "properties": {
        "hs_note_body": "World's best boss",
        "hs_created_by": "Michael Scott"
      },
      "properties_hs_note_body": "World's best boss",
      "properties_hs_created_by": "Michael Scott"
    }
  • 401 Unauthorized Error (Private App Token or Service Key)

    If you authenticate using a Private App access token or a Service Key and receive a 401 Unauthorized error, the connector fails immediately with a configuration error asking you to update your token. These tokens are static and cannot be refreshed, so retrying won't help. To resolve this, generate a new access token in your HubSpot Private App settings or rotate your Service Key and update the connector configuration.

    If you authenticate using OAuth, the connector automatically refreshes expired tokens and retries the request. No action is needed unless the error persists, in which case you should re-authenticate the connector.

  • 403 Forbidden Error

    • HubSpot has scopes for each API call.

    • Each stream is tied to a scope and will need access to that scope to sync data.

    • Review HubSpot's OAuth scope documentation.

    • Additional permissions:

      feedback_submissions: Service Hub Professional account

      marketing_emails: Market Hub Starter account

      workflows: Sales, Service, and Marketing Hub Professional accounts

  • Check out common troubleshooting issues for the HubSpot source connector on our Pipeloom Forum.

  • Missing records in CRM Search streams (deals, companies, engagements_calls, engagements_emails, engagements_meetings, engagements_notes, engagements_tasks, contacts, deal_splits, leads, tickets):

    • If you notice missing records during incremental syncs, it may be due to irregularities in HubSpot's API behavior.
    • To mitigate this, configure the CRM Search Lookback Window in the source settings. This re-fetches data for a specified number of minutes before the state from the previous sync, helping to capture missing records.
  • Missing records in property history streams (deals_property_history, contacts_property_history, companies_property_history):

    • HubSpot calculated properties (formula fields, rollup summaries, analytics properties) can emit timestamps ahead of user-initiated changes, causing the sync cursor to advance past records that have not yet been synced.
    • To mitigate this, configure the Property History Lookback Window in the source settings. A value of 43200 (30 days) is recommended. Since these streams use Append + Deduped sync mode, duplicate records from the lookback period are handled automatically.
  • Destination type conversion errors on dynamic number / boolean properties (for example, JSON → NUMERIC, JSON → BOOL, or decimal-precision / scale errors on fields like hs_hd_ticket_ids, zendesk_requester_id, hs_task_send_default_reminder, or any companies.properties.* field):

    • HubSpot sometimes declares a property as number or boolean but returns values that cannot be cast to that type — for example, semicolon-separated IDs ("3092727991;3881228353;15895321999") in a number field, multi-value text in a boolean field, or numbers beyond the declared precision. When that happens the connector emits the raw string, but because the published schema still says number / boolean, strict destinations reject the record.
    • To resolve this, enable Treat dynamic number and boolean properties as strings (treat_numbers_and_booleans_as_strings) in the source configuration. When this option is on, the connector declares every HubSpot dynamic property that would otherwise be number or boolean as string, so the affected values land in the destination as strings rather than failing type conversion.
    • The toggle only affects HubSpot dynamic properties.* fields. Static schema fields and non-property fields are not changed.
    • After enabling the toggle you must refresh both the schema and the data, otherwise the new string type will not reach the destination. In Pipeloom:
      1. Open the affected connection.
      2. Click Refresh source schema and save the updated catalog — the affected columns should now be string.
      3. Trigger a Refresh data sync (or clear the affected streams and run a new sync) so the destination tables are rewritten with the new column type and previously-rejected records are re-emitted as strings.
    • The same steps apply if you later disable the toggle — refresh the schema and the data so the destination column types match the new catalog.

IP allow list

If you use Pipeloom and your organization restricts access to specific IPs, add the Pipeloom IP addresses to your allow list.

On this page