Connect Bubble to HubSpot Marketing Hub using the API Connector with a Private App access token in a Private Authorization header. Tokens do not expire by default, so there is no OAuth dance or refresh logic to build. Sync Bubble sign-ups into HubSpot contacts, submit form data to trigger nurturing sequences, and pull email campaign stats — all from Bubble's visual workflow editor.
| Fact | Value |
|---|---|
| Tool | HubSpot Marketing Hub |
| Category | Marketing |
| Method | Bubble API Connector |
| Difficulty | Beginner |
| Time required | 35–50 minutes |
| Last updated | July 2026 |
Sync Bubble with HubSpot Marketing Hub and automate your entire lead-to-nurture pipeline
HubSpot Marketing Hub manages the full funnel from lead capture to email automation to conversion reporting. When Bubble is your frontend — whether a SaaS app, a client portal, or a lead-capture form — every new signup or form submission can flow automatically into HubSpot's contact database and trigger the nurturing sequences your marketing team has already configured.
The integration uses HubSpot's Private App authentication model, which is the recommended approach since HubSpot deprecated legacy API keys. A Private App token does not expire by default, which makes it ideal for Bubble: you create the token once, store it privately in the API Connector, and the integration keeps working without any scheduled refresh logic.
Bubble founders use this integration for three primary workflows: auto-enrolling new Bubble users as HubSpot contacts (triggering welcome email sequences), submitting Bubble form data directly to HubSpot form endpoints (without needing the HubSpot form embed), and pulling campaign performance metrics into a custom Bubble reporting dashboard — all without writing a single line of backend code.
Integration method
HubSpot API v3 via Bubble's built-in API Connector plugin, authenticated with a HubSpot Private App access token stored in a Private Authorization header.
Prerequisites
- A Bubble account (any plan — the API Connector plugin is available on all plans including Free)
- A HubSpot account with Marketing Hub enabled — at least Marketing Hub Starter ($15/mo/seat) is required to access marketing-specific API endpoints such as email campaign stats and workflow enrollment
- Admin access to your HubSpot portal to create a Private App (Settings → Integrations → Private Apps)
- Basic familiarity with Bubble's workflow editor and Repeating Groups
Step-by-step guide
Create a HubSpot Private App and select scopes
Log into your HubSpot account. Click the **Settings** gear icon (top-right navigation bar), then in the left sidebar navigate to **Integrations → Private Apps**. Click **Create a private app**. Give the app a descriptive name such as 'Bubble Integration'. On the Scopes tab, you must explicitly select each permission category your Bubble integration will need. HubSpot scopes are additive — selecting the wrong or incomplete set of scopes causes 403 errors later, and you cannot add scopes to an existing Private App without deleting and recreating it. For the workflows in this tutorial, select at minimum: - `crm.objects.contacts.write` — to create and update contacts - `crm.objects.contacts.read` — to read contact data - `forms` — to submit form data programmatically - `marketing-email` — to read email campaign statistics (Marketing Hub Starter or above required) Click **Create app**. HubSpot displays your access token once — this is the only time it is shown in full. Copy it immediately and save it somewhere secure (a password manager, a Bubble App Data field, or your notes — but not in code or a visible text element). Important: this token grants whatever scopes you selected across your entire HubSpot portal. Treat it like a password.
Pro tip: If you are unsure which scopes you need, select them all at Private App creation — it's easier to have extra scopes than to recreate the app later. You can see the current scopes at any time from Settings → Integrations → Private Apps → your app → Scopes tab.
Expected result: A HubSpot Private App is created. The access token (a long string starting with 'pat-na1-' or similar) is copied and saved securely. The token is visible only once at creation.
Install the API Connector and configure the HubSpot group
Open your Bubble app editor. Click the **Plugins** tab in the left sidebar, then **Add plugins**. Search for **API Connector** (the official Bubble plugin) and click **Install**. After installation, click **API Connector** in the Plugins tab to expand the configuration panel. Click **Add another API** and name the group **HubSpot Marketing**. Under **Shared headers for all calls**, add two headers: 1. Header name: `Authorization` — Value: `Bearer YOUR_PRIVATE_APP_TOKEN` (replace with your actual token) — enable the **Private** checkbox. This is the critical security step: the Private flag tells Bubble to execute this header server-side only, never exposing the token to browser network traffic. 2. Header name: `Content-Type` — Value: `application/json` — leave Private unchecked (this is not sensitive). Set the shared base URL to `https://api.hubapi.com` — all HubSpot API v3 endpoints share this base URL, with the path specified at the individual call level. Bubble proxies every API Connector call from its own servers. A HubSpot Private App token grants full portal access across its configured scopes — keeping it Private in the header is essential.
1{2 "group_name": "HubSpot Marketing",3 "base_url": "https://api.hubapi.com",4 "shared_headers": [5 {6 "name": "Authorization",7 "value": "Bearer <your_private_app_token>",8 "private": true9 },10 {11 "name": "Content-Type",12 "value": "application/json",13 "private": false14 }15 ]16}Pro tip: If your Bubble app already has a HubSpot CRM group configured from a different integration, create a separate group called 'HubSpot Marketing' for clarity — you can use the same token across groups, and organizing calls by purpose makes workflows easier to maintain.
Expected result: The HubSpot Marketing API group is created with two shared headers: Authorization (Private, containing your token) and Content-Type. The base URL is set to https://api.hubapi.com.
Add the Initialize Call and verify authentication
Inside the HubSpot Marketing group, click **Add another call**. Name it **Get Contacts** (this will be your verification call). Set the method to **GET** and enter the path: `/crm/v3/objects/contacts`. No body is needed for a GET request. Set **Use as** to **Data**. Click **Initialize call** — Bubble sends the request with your Private Authorization header and displays the response in the preview panel. A successful response shows a JSON object with a `results` array, each element being a HubSpot contact object with `id`, `properties`, and `createdAt` fields. Bubble auto-detects these fields and makes them accessible in dynamic expressions. If the call returns a **401 Unauthorized**: the token value in the Authorization header is incorrect or was not copied completely. Open the header, re-paste the token, and retry. If the call returns a **403 Forbidden**: the token is valid but your Private App is missing the `crm.objects.contacts.read` scope. Go back to HubSpot → Settings → Private Apps → your app → Scopes, add the missing scope, and regenerate. Note: updating scopes on an existing Private App does generate a new token — copy it and update the Bubble API Connector header. This Initialize Call is also how Bubble learns the shape of HubSpot's contact response — field names detected here are reusable in all contact-related workflows.
Pro tip: The Initialize Call must return a real successful response for Bubble to detect response fields. If your HubSpot account has no contacts yet, create one manually in HubSpot first so the API returns at least one result.
Expected result: The Initialize Call returns a 200 response with a results array of contacts. Bubble lists detected fields including id, properties, createdAt, and updatedAt. The API Connector shows the call as successfully initialized.
Wire the Create Contact action to the Bubble sign-up workflow
Add a new call inside the HubSpot Marketing group. Name it **Create Contact**. Set method to **POST** and path to `/crm/v3/objects/contacts`. Under Body, select **JSON** as the body type. The HubSpot contacts API expects a `properties` object wrapping all contact field values. Add dynamic parameters for email, firstname, and lastname: ```json { "properties": { "email": "<email>", "firstname": "<firstname>", "lastname": "<lastname>" } } ``` Toggle `email`, `firstname`, and `lastname` values to dynamic in Bubble's body editor. Set **Use as** to **Action** (since this creates data, not retrieves it). Click **Initialize call** using a real test email address for the `email` parameter — HubSpot requires a valid email format and will return a 400 if the test email is invalid or already exists as a contact. Now wire this to the sign-up workflow: in Bubble's Workflow editor, find the workflow that creates a new User (or your sign-up button's workflow). Add a step at the end: **Plugins → HubSpot Marketing - Create Contact**. Bind `email` to the sign-up email input's value (or `Current User's email` if the User is already created), `firstname` to the first name input, and `lastname` to the last name input. This means every Bubble registration triggers a HubSpot contact creation, which in turn fires any HubSpot Marketing Hub workflows assigned to new contacts (welcome sequences, sales assignment, etc.).
1{2 "properties": {3 "email": "<email>",4 "firstname": "<firstname>",5 "lastname": "<lastname>"6 }7}Pro tip: HubSpot deduplicates contacts by email address. If a contact with the same email already exists, the POST returns a 409 Conflict. Handle this in Bubble by adding a conditional check: if the API call result returns an error, trigger an 'Update Contact' PATCH call instead. Or configure the HubSpot Private App with an upsert endpoint to handle both cases automatically.
Expected result: The Create Contact call is initialized. Wiring it to the sign-up workflow means each new Bubble registration automatically creates a HubSpot contact. Check HubSpot Contacts in your portal to verify a test contact appears within seconds of completing the sign-up flow.
Add the Form Submission action to trigger HubSpot nurturing
HubSpot forms are a powerful trigger mechanism — submitting to a form endpoint creates or updates a contact AND fires any Marketing Hub workflows (email sequences, lifecycle stage changes, deal creation) associated with that form. Add a new call inside the HubSpot Marketing group. Name it **Submit Form**. Set method to **POST** and path to: `/submissions/v3/integration/submit/<portalId>/<formGuid>` You need two static values: - **portalId**: your HubSpot portal's numeric ID, visible in the URL when logged into HubSpot: `app.hubspot.com/contacts/{portalId}/` - **formGuid**: the unique ID of the HubSpot form you want to submit to, found in the form's embed code at HubSpot → Marketing → Lead Capture → Forms → {your form} → Embed → the `formId` value in the embed snippet In Bubble's API Connector, replace `<portalId>` and `<formGuid>` in the path with your actual numeric values (they don't change, so hardcoding is fine). The body format uses a `fields` array: ```json { "fields": [ { "name": "email", "value": "<email>" }, { "name": "firstname", "value": "<firstname>" }, { "name": "message", "value": "<message>" } ], "context": { "pageUri": "https://yourapp.bubbleapps.io/contact", "pageName": "Contact Page" } } ``` Toggle `email`, `firstname`, and `message` to dynamic. Wire this call to a Bubble form's submit button workflow. This approach lets you use Bubble's native form elements while still triggering HubSpot automation sequences — no HubSpot embed code required on the page.
1{2 "fields": [3 { "name": "email", "value": "<email>" },4 { "name": "firstname", "value": "<firstname>" },5 { "name": "message", "value": "<message>" }6 ],7 "context": {8 "pageUri": "https://yourapp.bubbleapps.io/contact",9 "pageName": "Contact Page"10 }11}Pro tip: RapidDev's team has wired dozens of Bubble apps to HubSpot nurture sequences this way — the form submission endpoint is significantly more powerful than a simple contact create because it fires HubSpot's full workflow automation engine. If you're unsure which form to use, create a dedicated 'Bubble Submissions' form in HubSpot with all your fields pre-configured, then use its GUID here. Free scoping call at rapidevelopers.com/contact.
Expected result: The Submit Form call is configured. Submitting a Bubble form fires the HubSpot form submission endpoint, which creates or updates the contact in HubSpot and triggers any workflows associated with that form. Verify by checking HubSpot → Marketing → Forms → Submissions for your test entry.
Pull email campaign statistics into a Bubble dashboard
For founders who want a custom reporting view of their HubSpot email campaigns inside Bubble, the Marketing Hub API provides endpoint for campaign-level metrics. Add a new call inside the HubSpot Marketing group. Name it **Get Email Campaigns**. Set method to **GET** and path to `/marketing/v3/emails`. Set **Use as** to **Data**. Click **Initialize call** — this returns a list of your email campaigns with metadata including `name`, `subject`, `sendDate`, and content ID. For per-campaign statistics, add a second call named **Get Campaign Stats**. Set method to **GET** and path to `/marketing/v3/emails/<contentId>/statistics/by-filters` where `<contentId>` is a dynamic parameter bound to the selected campaign's ID. Initialize this call with a real campaign content ID from the previous call's results. Build the UI: 1. Add a Repeating Group with Type = Get Email Campaigns and Data source = the Get Email Campaigns API call result's `results` list. 2. Inside the Repeating Group, show `Current cell's name`, `Current cell's subject`, and a clickable element to load stats. 3. When a campaign row is clicked, trigger a workflow calling Get Campaign Stats with the row's content ID, then display the open rate, click rate, and delivery metrics in a popup or separate panel. Note: Email campaign endpoints require the `marketing-email` scope on your Private App and Marketing Hub Starter or above on your HubSpot subscription. Free CRM accounts cannot access this endpoint.
Pro tip: HubSpot's API rate limit is 100 requests per 10 seconds per portal. For a dashboard that loads campaign stats for multiple campaigns simultaneously, add a small delay between calls using a Bubble 'Wait' action in the workflow rather than firing all requests in parallel.
Expected result: The Repeating Group displays a list of HubSpot email campaigns with names and subject lines. Clicking a campaign triggers the stats call and displays open rate, click rate, and delivery count for that campaign.
Common use cases
Auto-enroll Bubble sign-ups as HubSpot contacts
Every time a new user registers on your Bubble app, a workflow automatically creates a corresponding contact in HubSpot Marketing Hub. This triggers any HubSpot lead nurturing sequences configured for new contacts — welcome emails, onboarding drips, or SDR assignment — without any manual data transfer.
Create a Bubble workflow triggered on new User sign-up that calls the HubSpot API Connector to create a contact with the user's email, first name, and last name. After the API call succeeds, set a custom User field 'HubSpot synced' to yes.
Copy this prompt to try it in Bubble
Submit Bubble form data to HubSpot for lead nurturing
Bubble contact forms or lead capture pages submit data directly to a HubSpot form endpoint using the HubSpot Form Submissions API. HubSpot receives the submission, creates or updates the contact, and triggers any workflows associated with that form — such as assigning a deal stage or sending a follow-up email sequence — without needing the HubSpot form embed code on the Bubble page.
Build a Bubble page with Name, Email, Company, and Message inputs. On submit, call the HubSpot API Connector form submission endpoint with the entered values. Show a success message after the API call completes and clear the form inputs.
Copy this prompt to try it in Bubble
Marketing campaign analytics dashboard
Founders managing HubSpot email campaigns build a Bubble dashboard that pulls open rates, click rates, and delivery metrics from HubSpot Marketing Hub's API, displaying them in a Repeating Group alongside campaign names and send dates — creating a read-only reporting view without logging into HubSpot.
Create a Bubble page with a Repeating Group of HubSpot email campaigns showing campaign name, send date, open rate, and click-through rate. Add a date range filter with two date inputs and a Refresh button that re-queries HubSpot for campaigns sent within that range.
Copy this prompt to try it in Bubble
Troubleshooting
Initialize Call fails with 'There was an issue setting up your call' in Bubble's API Connector
Cause: The API Connector cannot reach HubSpot's API, or the Authorization header format is incorrect. This often occurs when the Bearer token is pasted without the `Bearer ` prefix, or if the base URL is missing.
Solution: Open the HubSpot Marketing group settings. Verify the Authorization header value reads exactly `Bearer YOUR_TOKEN` — including the space between 'Bearer' and the token. Verify the base URL is `https://api.hubapi.com` (with https, no trailing slash). Re-paste the token if needed and retry Initialize Call.
API calls return 403 Forbidden even though the token is valid
Cause: The Private App is missing one or more required scopes for the endpoint being called. HubSpot enforces scope-level permissions strictly — a valid token without the right scope always returns 403.
Solution: Go to HubSpot Settings → Integrations → Private Apps → your app → Scopes tab. Identify which scope is missing (the 403 response body usually names the required scope). Add the missing scope and save. Important: adding scopes to an existing Private App generates a new token — copy it immediately and update the Authorization header value in the Bubble API Connector.
Create Contact returns 409 Conflict when a Bubble user signs up
Cause: A HubSpot contact with the same email address already exists. HubSpot uses email as the unique identifier for contacts and rejects duplicate creation attempts.
Solution: Add a conditional branch to your Bubble sign-up workflow: after the Create Contact API call, check if the result contains an error category of 'CONFLICT'. If yes, use a PATCH call to `/crm/v3/objects/contacts/{id}` to update the existing contact instead. Alternatively, use HubSpot's upsert endpoint `/crm/v3/objects/contacts/upsert` which creates or updates based on email — this avoids the conditional logic entirely.
All HubSpot API calls suddenly return 401 Unauthorized after working correctly
Cause: The Private App token has been revoked. This happens when someone deletes the Private App from HubSpot's settings, regenerates the token (e.g., after a scope change), or the HubSpot portal itself is deleted or suspended.
Solution: Go to HubSpot Settings → Integrations → Private Apps. If the app still exists, check if the token was rotated — a scope addition triggers a new token. Copy the new token and update the Authorization header in Bubble's API Connector. If the app was deleted, recreate it with the same scopes and update Bubble.
Marketing email statistics endpoint returns 403 even with correct scopes
Cause: The HubSpot account is on the free CRM tier. Marketing Hub email campaign statistics require Marketing Hub Starter or above — the free CRM does not include access to marketing-specific API endpoints.
Solution: Verify your HubSpot subscription includes Marketing Hub (at least Starter at $15/mo/seat). From HubSpot's main navigation, go to Settings → Account → Subscriptions to confirm. If upgrading is not an option, limit the Bubble integration to CRM contact management endpoints (create, read, update contacts) which are available on all HubSpot plans.
Form submission returns 400 Bad Request with 'Invalid email address' error
Cause: The email value being passed to the HubSpot form submission endpoint contains an invalid format, an empty string, or is mapping to the wrong Bubble input field.
Solution: In the Submit Form API Connector call, verify the `email` field in the `fields` array is bound to the correct Bubble input element. Add a Bubble workflow condition before the API call: only proceed if `EmailInput's value contains @` — this catches obvious format issues before they reach HubSpot. Also confirm the HubSpot form has an `email` field configured (field names in the fields array must match the internal names in your HubSpot form).
Best practices
- Always mark the HubSpot Private App token as Private in the API Connector Authorization header — this token grants access to your entire HubSpot portal across all configured scopes, and exposing it in browser network traffic is a serious security risk.
- Document the Private App name, creation date, and configured scopes in your Bubble app's admin notes or team wiki. When a team member needs to rotate the token or the integration breaks, they need to know where the credential came from and which scopes to re-select.
- Select scopes conservatively when creating the Private App — only request what your Bubble integration actually uses. Fewer scopes reduces blast radius if the token is ever exposed. Add scopes later (with a token rotation) as new use cases arise.
- Handle the 409 Conflict response on contact creation with an upsert or update fallback. In a live Bubble app, it is common for users to have been previously added to HubSpot via another channel — a silent failure on duplicate creation breaks your contact sync silently.
- Store the portalId and formGuid values in a Bubble App Data field or as constants in the API Connector path rather than hardcoding them in multiple calls. If you add a second HubSpot form, having these values in one place makes updates easier.
- Add privacy rules to any Bubble Data Type that caches HubSpot contact or campaign data. Marketing data — email engagement rates, contact lifecycle stages, form submissions — should only be visible to admin users of your Bubble app, not to regular app users.
- Monitor HubSpot's API rate limit (100 requests per 10 seconds per portal) if your Bubble app creates contacts at high volume, such as during a bulk import or a viral signup event. Add a small delay between workflow steps when creating multiple contacts in a loop using Schedule API Workflow (paid Bubble plan).
- Test your complete Bubble-to-HubSpot workflow end-to-end using a test email address before launching to real users. Verify the contact appears in HubSpot, the correct workflow triggers, and the nurturing email is sent — this confirms the scope, endpoint path, and body format are all correct before production traffic flows through.
Alternatives
The HubSpot CRM integration covers contacts, deals, pipelines, and tickets — standard CRM data management. This Marketing Hub page adds email campaigns, form submissions triggering nurturing sequences, and marketing analytics. Use the CRM integration for sales-focused Bubble apps; use this Marketing Hub integration when your primary goal is marketing automation and lead nurturing.
Mailchimp is a strong alternative for email marketing automation, especially for smaller lists. It uses an API key instead of OAuth-style tokens. Mailchimp's audience and tagging model differs from HubSpot's contact property model. Choose HubSpot Marketing Hub if you already use HubSpot CRM for sales; choose Mailchimp for standalone email marketing without CRM integration.
SendGrid is a transactional email platform — ideal for sending individual notification emails (password reset, order confirmation) rather than marketing campaigns. HubSpot Marketing Hub manages full campaign lifecycle, segmentation, and nurturing flows. Use SendGrid for transactional emails triggered by Bubble events; use HubSpot Marketing Hub for lead nurturing sequences and campaign analytics.
Frequently asked questions
Do HubSpot Private App tokens expire?
No — HubSpot Private App tokens do not expire by default. This is a key advantage over HubSpot's deprecated legacy API keys and over OAuth tokens (which typically expire in hours or days). The token remains valid until you explicitly revoke or delete the Private App from HubSpot Settings → Integrations → Private Apps. This eliminates the need for token-refresh Backend Workflows in Bubble, which would require a paid plan.
What is the difference between using the HubSpot CRM integration and the Marketing Hub integration?
The HubSpot CRM integration focuses on contacts, deals, pipelines, and tickets — managing the sales side of HubSpot. This Marketing Hub integration adds marketing-specific endpoints: submitting data to HubSpot forms to trigger email sequences, reading email campaign statistics (open rates, click rates), and accessing marketing analytics. Both use the same API base URL and Private App authentication, but they require different API scopes. A single Private App can cover both if you select all needed scopes.
Does my HubSpot account need a paid Marketing Hub plan for this integration?
It depends on which endpoints you use. Creating and reading contacts via the CRM API works on HubSpot's free CRM tier. However, marketing-specific endpoints — email campaign statistics, workflow enrollment, and some form features — require at least Marketing Hub Starter ($15/mo/seat). If marketing campaign stats return a 403 error despite correct scopes, your HubSpot subscription plan is the most likely cause.
What happens if I need to change the scopes on my HubSpot Private App?
Adding or removing scopes on an existing Private App generates a new access token — the old one is rotated. HubSpot displays the new token once immediately after saving scope changes. Copy it right away and update the Authorization header value in your Bubble API Connector group. You cannot add scopes to an existing app without triggering this token rotation, so plan scope selection carefully at creation time.
Can I use a single Private App token for both my Bubble integration and other tools?
Technically yes — the token is just an access credential for your HubSpot portal. However, sharing tokens across systems is a security anti-pattern: if one integration is compromised, all systems using that token lose access simultaneously when you rotate it. Create a separate Private App for each integration (Bubble, Zapier, your website, etc.) so you can rotate credentials independently.
How do I submit data from a Bubble form to HubSpot without using HubSpot's embed code?
Use HubSpot's Form Submissions API endpoint: POST to `/submissions/v3/integration/submit/{portalId}/{formGuid}` with a `fields` array containing name/value pairs matching your HubSpot form's field names. This is exactly what Step 5 in this tutorial covers. You get the portalId from your HubSpot URL and the formGuid from the form's embed code in HubSpot → Marketing → Lead Capture → Forms → {your form} → Embed. This approach lets you design the form entirely in Bubble while still triggering all HubSpot automations connected to that form.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation