🚀 Executive Summary

TL;DR: This guide outlines how to automate syncing Webflow form submissions directly to HubSpot CRM, eliminating manual CSV exports and imports. By leveraging Webflow webhooks and a custom Python Flask application, businesses can achieve real-time lead capture and streamline their marketing and sales workflows.

🎯 Key Takeaways

  • Authentication requires a HubSpot Private App Token with `crm.objects.contacts.write` scope and a Webflow API Key for webhook configuration.
  • A Webflow webhook with ‘Form submission’ trigger type sends form data as a POST request to a custom Python application endpoint.
  • The Python Flask script parses the Webflow webhook payload, extracts form fields (matching Webflow field ‘Name’ attributes), and formats them for the HubSpot CRM API (matching HubSpot internal property names) to create new contacts.

Syncing Webflow Forms to HubSpot CRM

Syncing Webflow Forms to HubSpot CRM

Hey everyone, Darian here. Let’s talk about a real time-saver. Before I automated this process, I was manually exporting CSVs from Webflow and importing them into HubSpot for our marketing team. It was a tedious, error-prone task that burned at least a couple of hours every week. This simple integration not only gave me that time back but also ensures our sales team gets leads in near real-time. It’s a huge win, and I’m going to walk you through my exact setup.

Prerequisites

Before we dive in, make sure you have the following ready:

  • An active Webflow account with a site that has at least one form block.
  • A HubSpot account with permissions to create Private Apps (usually requires admin access).
  • A place to host a small Python script. This could be a simple server, a PaaS like Heroku, or a serverless function.
  • Python 3 installed on your machine.

The Step-by-Step Guide

Our goal is to use a Webflow Webhook to send form data to our Python application, which will then format and forward that data to the HubSpot CRM API. Let’s get started.

Step 1: Get Your API Keys

First things first, we need to authenticate with both services.

  1. HubSpot Private App Token: In your HubSpot portal, navigate to Settings (the gear icon) > Integrations > Private Apps. Create a new private app, give it a name like “Webflow Form Sync,” and grant it the necessary scopes. For creating contacts, you’ll need crm.objects.contacts.write. Once created, HubSpot will give you an Access Token. Copy this and keep it somewhere safe.
  2. Webflow API Key: In your Webflow dashboard, go to your Site Settings > Integrations tab. Scroll down to the API Access section and generate an API key. We’ll primarily use the webhook feature, but having this is good practice for any future integrations.

Step 2: Set Up the Webflow Webhook

Now, let’s tell Webflow where to send the data when a form is submitted.

  1. In your Webflow Site Settings, go to the Integrations tab.
  2. Scroll down to the Webhooks section and click “Add Webhook”.
  3. Set the Trigger Type to “Form submission”.
  4. For the Webhook URL, you’ll enter the URL where our Python application will be listening. We haven’t built it yet, so for now, you can use a temporary URL from a service like webhook.site to inspect the data structure. You’ll update this later to your production URL.
  5. Click “Add Webhook”. Now, every time someone fills out a form on your site, Webflow will send a POST request to that URL.

Pro Tip: When you’re first setting this up, submitting your Webflow form and inspecting the payload at a temporary endpoint is invaluable. You’ll see the exact structure and field names (`name`, `data.email`, etc.) that you need to parse in your script. It takes the guesswork out of the equation.

Step 3: Write the Python Handler Script

This is where the magic happens. We’ll write a simple web server using Flask that listens for the webhook, processes the data, and sends it to HubSpot. I’ll skip the standard virtualenv setup since you likely have your own workflow for that. Just make sure you install the necessary libraries; you can do this by running `pip install Flask requests python-dotenv` in your terminal.

First, create a `config.env` file in your project directory to securely store your HubSpot token. Never hardcode secrets in your script!


# config.env
HUBSPOT_ACCESS_TOKEN="your-private-app-token-goes-here"

Next, here is the main Python script. I’ll call it `app.py`.


import os
import requests
from flask import Flask, request, jsonify
from dotenv import load_dotenv

# Load environment variables from config.env
load_dotenv('config.env')

app = Flask(__name__)

# Your HubSpot Private App Access Token
HUBSPOT_TOKEN = os.getenv('HUBSPOT_ACCESS_TOKEN')
HUBSPOT_API_URL = "https://api.hubapi.com/crm/v3/objects/contacts"

@app.route('/webflow-hook', methods=['POST'])
def webflow_webhook_handler():
    # Get the JSON data from Webflow's webhook
    payload = request.get_json()

    # In my production setups, I add more robust logging here
    print("Received data from Webflow:", payload)

    # Extract form fields from the 'data' object
    # The field names ('name', 'email', etc.) MUST match your Webflow form field names
    try:
        firstname = payload['data'].get('first-name', '')
        lastname = payload['data'].get('last-name', '')
        email = payload['data'].get('email')

        if not email:
            print("Email is missing, cannot create contact.")
            return jsonify({"status": "error", "message": "Email is required."}), 400

    except KeyError:
        # This happens if the 'data' object is missing
        print("Invalid payload structure from Webflow.")
        return jsonify({"status": "error", "message": "Invalid data structure."}), 400

    # Format the data for the HubSpot API
    hubspot_payload = {
        "properties": {
            "email": email,
            "firstname": firstname,
            "lastname": lastname,
            # Add any other custom properties you have in HubSpot
            "webflow_form_name": payload.get('name', 'N/A')
        }
    }

    # Set up the request headers
    headers = {
        'Authorization': f'Bearer {HUBSPOT_TOKEN}',
        'Content-Type': 'application/json'
    }

    # Send the data to HubSpot
    response = requests.post(HUBSPOT_API_URL, json=hubspot_payload, headers=headers)

    # Check the response from HubSpot
    if response.status_code == 201:
        print(f"Successfully created contact for {email}")
        return jsonify({"status": "success", "message": "Contact created."}), 200
    else:
        print(f"Error creating contact: {response.status_code} {response.text}")
        return jsonify({"status": "error", "message": "Failed to create contact in HubSpot."}), 500

if __name__ == '__main__':
    # For local development. For production, use a proper WSGI server like Gunicorn.
    app.run(port=5000, debug=True)

Step 4: Deploy and Finalize

Once your script is ready, you need to deploy it to a server so it has a public URL. After deploying, take that public URL (e.g., `https://your-app-domain.com/webflow-hook`) and update the Webhook URL in your Webflow settings (from Step 2). Now, submit a test form on your live site, and you should see the contact appear in HubSpot almost instantly.

Here’s Where I Usually Mess Up (Common Pitfalls)

  • Mismatched Field Names: This is the number one cause of failures. The keys you use to get data from the `payload[‘data’]` dictionary (e.g., `’first-name’`) must exactly match the “Name” attribute of your form fields in the Webflow Designer. They are case-sensitive!
  • HubSpot Internal Property Names: The property names you send to HubSpot (e.g., `firstname`, `lastname`) must match the internal names in your HubSpot CRM settings. Go to Settings > Properties to verify them. A custom property named “Company Size” in the UI might have an internal name of `company_size`.
  • Webhook Timeouts: Webflow expects a quick response (`200 OK`) from your webhook receiver. If your script does a lot of heavy lifting, it might time out. The script above is fast enough, but if you add more complex logic, consider acknowledging the request first and then processing the data asynchronously with a task queue.

Conclusion

And that’s it. You’ve now built a solid, automated bridge between your marketing site and your CRM. This “set it and forget it” solution eliminates manual work and ensures data flows where it needs to go, quickly and reliably. It’s a foundational piece of automation that pays for itself almost immediately. Now, go enjoy the time you just saved.

Darian Vance - Lead Cloud Architect

Darian Vance

Lead Cloud Architect & DevOps Strategist

With over 12 years in system architecture and automation, Darian specializes in simplifying complex cloud infrastructures. An advocate for open-source solutions, he founded TechResolve to provide engineers with actionable, battle-tested troubleshooting guides and robust software alternatives.


🤖 Frequently Asked Questions

âť“ How do I connect Webflow forms to HubSpot CRM for automated lead capture?

To connect Webflow forms to HubSpot CRM, configure a Webflow webhook to send form submission data to a custom Python Flask application. This application then processes the data and uses a HubSpot Private App Access Token to create or update contacts via the HubSpot CRM API.

âť“ How does this custom integration compare to manual data transfer or third-party tools?

This custom integration provides real-time data syncing and granular control over data mapping, significantly outperforming manual CSV exports and imports in efficiency and accuracy. It offers more flexibility than some generic third-party integration tools by allowing specific custom logic and direct API interaction without external service limitations.

âť“ What is a common pitfall when implementing this Webflow to HubSpot integration?

The most common pitfall is mismatched field names. Ensure the keys used to extract data from the Webflow webhook payload in your Python script (e.g., ‘first-name’) exactly match the ‘Name’ attribute of your Webflow form fields, and that the property names sent to HubSpot (e.g., ‘firstname’) match HubSpot’s internal property names.

Leave a Reply

Discover more from TechResolve - SaaS Troubleshooting & Software Alternatives

Subscribe now to keep reading and get access to the full archive.

Continue reading