Recipe 05, Form submission to CRM
Goal: a "Contact us" form on your website creates / updates a contact in HubSpot and notifies your sales team.
Difficulty: Beginner · Time: ~15 minutes · Connectors: Form trigger, HubSpot, Slack
Prerequisites
- HubSpot credential with contact write access.
- Slack credential and a sales channel (
#sales-inbound).
Finished workflow
┌──────────┐ ┌───────────┐ ┌────────────┐ ┌──────────────┐
│ Form │───▶│ Transform │───▶│ HubSpot: │───▶│ Slack: │
│ Trigger │ │ │ │ upsert │ │ notify team │
└──────────┘ └───────────┘ │ contact │ └──────────────┘
└────────────┘Step-by-step
1. Form trigger
Replace Start with Form Trigger:
Fields:
firstName(text, required)lastName(text, required)email(email, required)company(text)message(textarea)consent(checkbox, label: "I agree to be contacted by Acme")
Visibility: Public.
CAPTCHA: on (forms get bot traffic).
2. Transform
Add a Transform node:
map:
email: {{ $trigger.email }}
firstName: {{ $trigger.firstName }}
lastName: {{ $trigger.lastName }}
company: {{ $trigger.company }}
message: {{ $trigger.message }}
consent: {{ $trigger.consent }}
source: "Website contact form"3. HubSpot upsert
Add HubSpot node:
- Credential: HubSpot
- Operation:
Upsert contact - Match on:
email - Fields:
- email, firstname, lastname, company → from Transform
- lead_source → "Website contact form"
- hs_marketing_status → if
consentis true,subscribed; elseunsubscribed
4. Slack notification
Add Slack node:
- Operation:
Send message - Channel:
#sales-inbound - Text:
:wave: New inbound lead! *{{ $trigger.firstName }} {{ $trigger.lastName }}* (<mailto:{{ $trigger.email }}|{{ $trigger.email }}>) from *{{ $trigger.company }}* > {{ $trigger.message }} HubSpot: {{ $node["HubSpot"].json.url }}
5. Save and activate
Try it
- Get the form URL from the Form Trigger inspector. Open it in a browser.
- Submit a test entry with your own email.
- Check HubSpot, contact created (or your own contact updated).
- Check the Slack channel, message posted.
Variations
Add MX-record validation
Bot submissions often use disposable email domains. Add an HTTP node calling a service like email-verify.io to check before the HubSpot step, and route invalid emails to a "rejected" path.
Trigger downstream automation in HubSpot
Set a custom property on the contact (source_channel = website), HubSpot's own automation can pick up from there.
Multi-language
Add a Switch node based on $trigger._meta.locale to route to localised Slack messages and lead-source values.
Lead scoring with AI
Add an AI Agent node after the Form Trigger to score the lead's quality (based on the message text and company) before pushing to HubSpot. Tag the HubSpot contact with the score.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Form submits but no Slack message | Slack credential missing scope | Add chat:write scope; re-auth |
| HubSpot creates duplicates | Not upserting on email |
Ensure operation is Upsert contact with email as match field |
| Spam submissions | No CAPTCHA, public form | Enable CAPTCHA on the Form Trigger |
| HubSpot rejects with "consent required" | Marketing settings require explicit consent | Conditionally set marketing fields only when consent is true |
Next
Found something out of date? This page lives in the Flero docs content set.