Every Typeform submission sitting in a spreadsheet instead of your CRM is a lead going cold. Someone just told you their name, email, company, and job title — and that data is gathering dust in a tab you’ll check “later.” Meanwhile, your sales team has no idea the lead exists. This workflow fixes that permanently. You’ll build an n8n automation that catches every Typeform submission the moment it arrives, maps the fields to HubSpot contact properties, checks whether the person already exists in your CRM, and either creates a new contact or updates the existing one — all without you lifting a finger.
Prefer to skip the setup? Grab the ready-made template → and be up and running in under 10 minutes.
What You’ll Build
- A visitor fills out your Typeform (contact form, demo request, lead magnet — whatever you use).
- n8n instantly receives the submission via webhook and extracts the relevant fields: email, first name, last name, company, phone, and job title.
- The workflow searches HubSpot to check if a contact with that email already exists.
- If the contact exists, their record gets updated with the latest information. If they’re new, a fresh contact is created in HubSpot with all the form data.
- Your sales team sees the new or updated contact in HubSpot immediately — no manual entry, no lag, no missed leads.
How It Works — The Big Picture
The entire workflow runs as a single chain with one decision branch. Here’s the full flow at a glance:
┌──────────────────────────────────────────────────────────────────────────────┐ │ AUTO-CREATE HUBSPOT CONTACTS FROM TYPEFORM │ │ │ │ [Typeform Trigger] → [Map Fields] → [Check If Email Exists] → [Contact?] │ │ ↓ Yes ↓ No │ │ [Update Contact] [Create New] │ └──────────────────────────────────────────────────────────────────────────────┘
What You’ll Need
- n8n instance — self-hosted or n8n Cloud (any plan)
- Typeform account — free tier works, but you need at least one published form
- HubSpot account — free CRM tier is enough; you’ll need an OAuth2 app or private app token
- 5 minutes if you use the template, or about 25–30 minutes building from scratch
Estimated build time: 25–30 minutes from scratch, or under 10 minutes with the template.
Building the Workflow — Step by Step
1 Typeform Trigger (Typeform Trigger Node)
This node listens for new Typeform submissions in real time. Every time someone hits “Submit” on your form, n8n receives the full response payload instantly.
- In n8n, add a new node and search for Typeform Trigger.
- Connect your Typeform credential (API token — see the Credentials Guide for how to get one).
- In the Form dropdown, select the form you want to connect.
- Click Listen for Test Event, then go to your Typeform and submit a test response.
After the test submission, you’ll see the raw data from Typeform:
{"What is your email address?":"james.carter@gmail.com","What is your first name?":"James","What is your last name?":"Carter","What company do you work for?":"Ridgeline Analytics","What is your phone number?":"(555) 867-5309","What is your job title?":"VP of Marketing","submittedAt":"2026-04-07T14:23:00.000Z"}
Tip: Typeform uses the full question text as the field key. If you rename a question later, update the field references in the Map Fields node too.
2 Map Fields (Set Node)
Typeform’s field names are long question strings. This Set node transforms them into clean field names that match HubSpot’s default properties.
| Output Field | Expression | Purpose |
|---|---|---|
email |
{{ $json['What is your email address?'] }} |
Primary identifier for HubSpot |
firstname |
{{ $json['What is your first name?'] }} |
Contact first name |
lastname |
{{ $json['What is your last name?'] }} |
Contact last name |
company |
{{ $json['What company do you work for?'] }} |
Company association |
phone |
{{ $json['What is your phone number?'] }} |
Phone number |
jobtitle |
{{ $json['What is your job title?'] }} |
Job title property |
lead_source |
Typeform (static) |
Tracks lead origin |
Tip: If your Typeform uses different question text, update the expressions to match. The output field names should stay the same — they match HubSpot’s defaults.
3 Check If Email Exists (HubSpot Node — Search)
Before creating a contact, this node searches HubSpot by email address to check for duplicates.
- Add a HubSpot node. Set Resource to
Contact, Operation toSearch. - Add a filter: Property =
email, Operator =Equal, Value ={{ $json.email }}
4 Contact Exists? (IF Node)
Routes the flow: if {{ $json.total }} is greater than 0 → update the existing contact. Otherwise → create a new one.
The IF node’s output 0 (true) goes to Update, output 1 (false) goes to Create. Don’t mix them up.
5 Update Existing Contact (HubSpot Node — Update)
Connected to the true branch. Uses {{ $('Check If Email Exists').item.json.results[0].id }} as the Contact ID, and pulls field values from the Map Fields node.
6 Create New Contact (HubSpot Node — Create)
Connected to the false branch. Sets the email and all additional fields from the Map Fields node to create a fresh HubSpot contact.
Full System Flow
┌────────────────────────────────────────────────────────────────────────────────────────┐ │ VISITOR SUBMITS TYPEFORM │ │ ↓ │ │ [Typeform Trigger] ─── receives webhook payload ──→ [Map Fields] │ │ ↓ │ │ [Check If Email Exists] │ │ ↓ │ │ [Contact Exists?] │ │ ↓ YES ↓ NO │ │ [Update Contact] [Create Contact] │ └────────────────────────────────────────────────────────────────────────────────────────┘
Testing Your Workflow
- Activate the workflow in n8n by toggling the Active switch.
- Submit a test form response in Typeform using a test email not in HubSpot yet.
- Check HubSpot — you should see a new contact with all fields populated.
- Submit again with the same email but different company name.
- Check HubSpot again — the contact should show the updated company.
| Problem | Likely Cause | Fix |
|---|---|---|
| Workflow never triggers | Webhook not registered | Deactivate and reactivate the workflow. Verify webhook in Typeform settings. |
| HubSpot 401 error | Expired OAuth token | Re-authorize your HubSpot credential. Check scopes include contacts read+write. |
| Empty fields in HubSpot | Question text mismatch | Compare Typeform question keys with Map Fields expressions exactly. |
| Duplicate contacts | IF node misconfigured | Verify condition checks $json.total > 0. True → Update, False → Create. |
Frequently Asked Questions
Can I use this with HubSpot’s free CRM?
Yes. The free CRM includes full contact management and API access. Default fields like email, name, company, and phone are all available on the free plan.
What if the submission is missing some fields?
The workflow still runs — HubSpot leaves empty fields blank. It won’t overwrite existing data with empty values on updates.
Can I auto-add contacts to a HubSpot list or deal?
Yes — add a HubSpot node after Create New Contact set to “Add Contact to List” or “Create Deal”. The template covers core contact creation; lists and deals are easy extensions.
Does this work with Typeform’s free plan?
Yes, with the 10 responses/month limit. Webhooks work on all Typeform plans including free.
What if I use Google Forms or Tally instead?
Swap the Typeform Trigger for a Google Forms Trigger or Webhook node. Update the Map Fields expressions for the new payload. The HubSpot side stays identical.
Will this slow down my Typeform?
No. Typeform sends the webhook after submission is complete. The respondent sees the Thank You screen immediately. n8n processes asynchronously.
Get the HubSpot + Typeform Template
Skip the 30-minute build. Get the complete workflow JSON, setup guide, and credentials walkthrough — import into n8n and be live in under 10 minutes.
Instant download · Works on n8n Cloud and self-hosted
What’s Next?
- Auto-assign leads to sales reps — round-robin assignment based on territory or workload.
- Send a Slack notification — ping #new-leads when a contact is created.
- Enroll in a HubSpot sequence — automated welcome email within minutes.
- Score leads with AI — use OpenAI to evaluate company and job title before CRM entry.