Connect Unbounce with HubSpot

Implementation Guide

Overview: Connecting Unbounce and HubSpot

Unbounce is purpose-built for conversion rate optimisation — it's where marketing teams build, A/B test, and deploy landing pages that are decoupled from the main website CMS and optimised for a single conversion goal. HubSpot is the downstream system of record: the CRM and marketing automation platform where converted contacts are nurtured, qualified, and handed off to sales. The gap between these two systems is a classic conversion leakage point. A prospect fills out a form on an Unbounce landing page and that submission may sit in Unbounce's lead management panel for hours before a marketing operations team member manually exports it and imports it into HubSpot, by which point the prospect's intent signal has cooled significantly.

A programmatic integration between Unbounce and HubSpot solves this leakage at the architectural level. Form submissions are pushed into HubSpot as Contact records in near real time — typically within seconds of submission. Campaign attribution is preserved. HubSpot workflows fire immediately, initiating nurture sequences, lead scoring updates, and sales rep notifications. This guide covers the full implementation, from Unbounce's webhook configuration to HubSpot's API authentication model and field mapping, with specific guidance for both custom code and iPaaS-based deployments.

Core Prerequisites

Unbounce Requirements:

  • An Unbounce account on the Launch, Optimize, or Accelerate plan (Webhook integrations require a paid plan; they are not available on the free trial).
  • Admin or Author access to the Unbounce workspace containing the landing pages you want to connect. Webhook configuration is a page-level setting.
  • Familiarity with Unbounce's Form Confirmation Dialogs and Webhooks integration panel, accessible per page via the Integrations tab in the Unbounce page builder.
  • An awareness that Unbounce sends form submission data as application/x-www-form-urlencoded POST bodies by default — not JSON. Your middleware must parse form-encoded bodies, or you can configure Unbounce to send JSON if using a custom script approach.

HubSpot Requirements:

  • A HubSpot account with Super Admin access or a user with the CRM > Contacts and Marketing > Lists permissions.
  • A Private App created in HubSpot under Settings > Integrations > Private Apps. Private Apps replaced the legacy API Key (HAPI key) authentication model in November 2022. Legacy HAPI keys are fully deprecated and will not work.
  • The Private App's Access Token (format: pat-na1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx). Required OAuth 2.0 scopes for this integration: crm.objects.contacts.write (create and update contacts), crm.objects.contacts.read (read contacts for deduplication), crm.lists.write (add contacts to static lists), automation (enroll contacts in workflows — note this scope requires a HubSpot Marketing Hub Professional or Enterprise subscription).
  • Knowledge of HubSpot's Contact Property Internal Names for all fields you intend to populate. These differ from the display labels visible in the HubSpot UI. Retrieve them via GET https://api.hubapi.com/crm/v3/properties/contacts.
  • If enrolling contacts in workflows, you need the Workflow ID, retrievable via the HubSpot Workflows API: GET https://api.hubapi.com/automation/v4/flows.

Field Mapping Prerequisites:

  • A documented mapping between Unbounce form field names and HubSpot contact property internal names. Unbounce field names are defined in the page builder (e.g., email, first_name, company, custom fields like job_title_unbounce). HubSpot internal names are snake_case strings like email, firstname, lastname, company, jobtitle. Custom HubSpot properties have internal names you define when creating them.
  • An agreed strategy for handling the Unbounce page_name and page_url fields (which Unbounce includes in every form submission) — these should be mapped to HubSpot properties such as hs_analytics_source_data_1 or a custom unbounce_page_name__c property for attribution tracking.

Top Enterprise Use Cases

1. Instant Lead Capture with Immediate Workflow Enrollment The canonical use case. A prospect submits a form on an Unbounce PPC landing page for a gated content offer (e.g., a whitepaper or ROI calculator). The integration creates or updates the HubSpot Contact, sets the lead source to the Unbounce page name, and immediately enrolls the contact in a HubSpot nurture workflow that sends the content asset, followed by a multi-step email sequence. This entire flow executes within 5–10 seconds of form submission, while the prospect is still engaged with the brand.

2. A/B Test Winner Attribution Tracking Unbounce A/B tests generate form submissions from multiple page variants. By including the page_variant field (exposed in Unbounce's webhook payload as page_url) in the data mapped to HubSpot, teams can analyse conversion performance by variant in HubSpot's reporting dashboards, enabling data-driven decisions about which page variant to promote.

3. Static List Segmentation by Campaign Each Unbounce landing page corresponds to a specific marketing campaign (paid search, content syndication, event promotion, etc.). The integration can be configured to add the created Contact to a specific HubSpot Static List corresponding to that campaign. This enables campaign-specific list-based workflows, reporting, and suppression lists without requiring manual list management.

4. Deal Creation for High-Intent Offers For bottom-of-funnel landing pages such as free trial request forms, demo request pages, or pricing inquiry forms, the integration can go beyond Contact creation and automatically create a HubSpot Deal in the sales pipeline, associated with the Contact and their Company, set to a stage of Demo Requested or Trial Started, and assigned to the appropriate sales rep based on territory or round-robin assignment logic.

5. Progressive Profiling and Property Enrichment For returning visitors who submit forms on multiple Unbounce pages over time, the integration uses HubSpot's Contact upsert behaviour (update if email exists, create if not) to progressively enrich the Contact record. Each successive form submission adds new property values without overwriting existing ones (configurable in HubSpot's property settings as Append behaviour for multi-select properties), building a richer profile over multiple touchpoints.

Step-by-Step Implementation Guide

Phase 1: Configure the Unbounce Webhook

In the Unbounce page builder, select your landing page and navigate to the Integrations tab. Click Add New Integration and select Webhook. In the Webhook URL field, enter your middleware endpoint (e.g., https://integrate.yourdomain.com/webhooks/unbounce). Unbounce sends form data as an HTTP POST with Content-Type: application/x-www-form-urlencoded.

Alternatively, if you have access to Unbounce's custom scripts feature, you can intercept the form submission client-side using JavaScript and POST the data as JSON to your endpoint. However, the standard webhook integration is sufficient for most production use cases and does not require custom scripting.

Unbounce does not currently support webhook signature verification headers in its standard webhook integration. To secure your endpoint, implement one or more of the following: IP allowlisting (Unbounce publishes its outbound IP ranges), a pre-shared secret appended as a query parameter to your webhook URL (e.g., ?secret=your_random_string), or TLS mutual authentication if your infrastructure supports it.

A standard Unbounce form submission webhook POST body, URL-decoded, looks like this: page_id=abc12345&page_name=Q2+2026+PPC+Landing+Page&page_url=https%3A%2F%2Flp.yourdomain.com%2Fq2-ppc&variant=a&email=jane.doe%40acmecorp.com&first_name=Jane&last_name=Doe&company=Acme+Corporation&job_title=VP+of+Engineering&data.json=%7B%22email%22%3A%22jane.doe%40acmecorp.com%22%7D

Your middleware should URL-decode and parse this body, then extract the relevant fields for the HubSpot API call. Note the data.json field — Unbounce includes a JSON-encoded version of the form data in this field as well, which you may find easier to parse than the flat key-value structure.

Phase 2: Authenticate with HubSpot and Upsert a Contact

HubSpot's Private App access token is passed as a Bearer token in the Authorization header. Use the Contacts API v3 upsert endpoint, which creates a new Contact if the email does not exist or updates the existing Contact if it does:

curl -X POST https://api.hubapi.com/crm/v3/objects/contacts/upsert \
  -H 'Authorization: Bearer pat-na1-your-private-app-token' \
  -H 'Content-Type: application/json' \
  -d '{
    "inputs": [
      {
        "idProperty": "email",
        "id": "[email protected]",
        "properties": {
          "email": "[email protected]",
          "firstname": "Jane",
          "lastname": "Doe",
          "company": "Acme Corporation",
          "jobtitle": "VP of Engineering",
          "hs_lead_status": "NEW",
          "leadsource": "Paid Search",
          "unbounce_page_name": "Q2 2026 PPC Landing Page",
          "unbounce_page_url": "https://lp.yourdomain.com/q2-ppc",
          "unbounce_variant": "a"
        }
      }
    ]
  }'

The batch upsert endpoint (/crm/v3/objects/contacts/upsert) supports up to 100 contact records per request and uses idProperty: "email" to identify existing records. The response includes a results array with the outcome for each input: "status": "CREATED" or "status": "UPDATED", and the HubSpot id (numeric contact ID) of the resulting record.

Phase 3: Add Contact to a HubSpot Static List

Once you have the HubSpot Contact ID from the upsert response, add the contact to the campaign-specific static list:

curl -X PUT "https://api.hubapi.com/contacts/v1/lists/12345/add" \
  -H 'Authorization: Bearer pat-na1-your-private-app-token' \
  -H 'Content-Type: application/json' \
  -d '{
    "vids": [987654321]
  }'

Replace 12345 with your HubSpot list ID and 987654321 with the numeric Contact VID from the upsert response. Note this endpoint uses the legacy v1 Lists API, which remains the primary interface for list membership management as of the time of writing — HubSpot's v3 List API is available but still evolving.

Phase 4: Enroll Contact in a HubSpot Workflow

To immediately enroll the newly created contact in a HubSpot workflow, use the Automation API:

curl -X POST "https://api.hubapi.com/automation/v4/flows/555666/enrollments" \
  -H 'Authorization: Bearer pat-na1-your-private-app-token' \
  -H 'Content-Type: application/json' \
  -d '{
    "objectType": "CONTACT",
    "objectId": "987654321"
  }'

Replace 555666 with your workflow ID. This endpoint requires the automation scope on your Private App, which in turn requires a Marketing Hub Professional or Enterprise HubSpot subscription. If your subscription does not include this scope, the API will return a 403 with "scope_not_found" in the error response.

Phase 5: Make (Integromat) Scenario Configuration

In Make, build the following scenario. Use a Webhooks > Custom Webhook module as the trigger and configure Unbounce to POST to the generated URL. Follow with a Tools > Set Variable module to decode the URL-encoded form body (Make's webhook module parses form-encoded bodies automatically if the Content-Type header is set correctly). Connect to an HTTP > Make a Request module configured as POST to https://api.hubapi.com/crm/v3/objects/contacts/upsert, with the Authorization: Bearer header and a JSON body constructed using Make's mapping interface. Use a JSON > Parse JSON module on the response to extract the Contact ID. Follow with a second HTTP > Make a Request module for the list membership call, and a third for workflow enrollment if applicable. Add error handling using Make's Error Handler with a Resume directive so that a failed list-add or workflow enrollment does not prevent the Contact creation from being logged.

Common Pitfalls & Troubleshooting

HTTP 409 Conflict — Contact Already Exists (Legacy API) If using the older POST /crm/v3/objects/contacts create endpoint instead of the upsert endpoint, creating a contact with an email address that already exists returns HTTP 409 with "CONTACT_EXISTS" in the error body. The response also includes the existing contact's ID in the error details. Migrate to the upsert endpoint (/crm/v3/objects/contacts/upsert) which handles this case natively. The upsert endpoint is the correct approach for all Unbounce integrations since you cannot guarantee a form submitter is a new contact.

HTTP 403 Forbidden — Missing Scopes If your HubSpot Private App's access token lacks a required scope (e.g., automation for workflow enrollment or crm.lists.write for list operations), the API returns HTTP 403 with a JSON body containing "category": "MISSING_SCOPES". Navigate to your HubSpot Private App settings, add the missing scopes, and regenerate the access token. Note that changing scopes requires re-authorisation, which generates a new token — update your secrets storage accordingly.

Unbounce Webhook Delivering Empty Field Values Unbounce's webhook only delivers fields that are present on the specific page's form. If you are using a single middleware endpoint to receive webhooks from multiple Unbounce pages with different form configurations, some pages will not send certain fields (e.g., a short-form page with only email and first_name will not send company or job_title). Your field mapping logic must handle missing fields gracefully by omitting them from the HubSpot payload rather than sending empty strings, as empty strings in HubSpot will overwrite existing Contact property values with blank data, degrading your data quality.

HubSpot Rate Limiting — HTTP 429 HubSpot's API enforces rate limits of 100 requests per 10 seconds (for Private Apps on most tiers) and a daily limit that varies by subscription. During high-traffic campaigns where hundreds of Unbounce form submissions arrive in rapid succession, you may breach the per-second limit. Implement a request queue with a rate limiter that enforces a maximum of 8 HubSpot API calls per second, providing headroom below the limit. HubSpot's 429 response includes a Retry-After header specifying the number of milliseconds to wait before retrying.

Form Field Name Mismatch Causing Empty HubSpot Records The most common data quality issue in this integration. Unbounce sends form data using the field names defined in the Unbounce page builder, which default to values like email, first_name, last_name but can be renamed by the page designer. If a field name in Unbounce does not match the key in your field mapping configuration, that field will be silently dropped. Audit your Unbounce page form field names by examining an actual webhook payload (log incoming payloads to a tool like Webhook.site during initial setup) and verify they match your mapping exactly, including case sensitivity.