Website
HubSpot CMS
Route HubSpot forms on HubSpot pages, from the updated and the legacy forms editor.
Keep your HubSpot forms on your HubSpot pages. HubSpot still saves every submission to the contact, sends its notifications and runs your workflows. Once HubSpot has saved a submission, Cauliflower sends the lead to a router and opens the right rep's calendar on top of the form's thank-you message.
This guide is for sites hosted on HubSpot's Content Hub. The same snippet works for HubSpot forms embedded anywhere else, such as on WordPress or a custom site. Only the place you paste it changes.
Before you start
- A router that is Live, with the Website form entry point turned on in its trigger. The starter router,
inbound, already is. - The router's slug, shown on Routers → your router → Install, along with your Cauliflower URL and workspace slug.
- Permission to edit your HubSpot website settings or pages. Which head and footer HTML options you can edit depends on your HubSpot subscription.
Set it up
Allow your domains
In Cauliflower, open Settings → Workspace → Widget security & qualification → Allowed website origins and add every domain your HubSpot pages are served from, one per line. If you test pages on your account's HubSpot system domain, which ends in hs-sites.com or a regional variant such as hs-sites-eu1.com, add that too:
https://example.com
https://www.example.com
https://example-12345.hs-sites.comAdd your account's own system domain, never a wildcard such as *.hs-sites.com, which would let every HubSpot site use your workspace. If the list is empty, every website is allowed. See Widget security.
Add the code to your site
In HubSpot, click the settings icon and go to Content → Pages. Choose your domain from the dropdown, or Default settings for all domains, paste this into Site footer HTML and save. Replace https://cal.example.com with your Cauliflower URL, acme with your workspace slug and inbound with your router's slug.
<script>
(function (w, d) {
w.cauliflower = w.cauliflower || function () { (w.cauliflower.q = w.cauliflower.q || []).push(arguments) };
var s = d.createElement("script"); s.async = true; s.src = "https://cal.example.com/embed.js"; d.head.appendChild(s);
})(window, document);
cauliflower("init", { workspace: "acme" });
// HubSpot forms: HubSpot saves the submission as usual, then Cauliflower routes the lead and opens the calendar.
(function () {
var ROUTER = "inbound";
// IDs of the forms to route, from each form's embed code. Leave empty to route every form.
var FORMS = [];
function route(formId, lead) {
if (FORMS.length && FORMS.indexOf(formId) === -1) return;
cauliflower("route", { router: ROUTER, lead: lead });
}
// Forms built in HubSpot's updated forms editor
window.addEventListener("hs-form-event:on-submission:success", function (event) {
HubSpotFormsV4.getFormFromEvent(event).getFormFieldValues().then(function (values) {
var lead = {};
values.forEach(function (field) {
// Names look like "0-1/email": 0-1 is a contact property, 0-2 a company property.
var parts = field.name.split("/");
var name = parts.pop();
if (parts[0] === "0-2") name = name === "name" ? "company" : "company_" + name;
lead[name] = field.value;
});
route(event.detail.formId, lead);
});
});
// Forms built in the legacy forms editor
window.addEventListener("message", function (event) {
var d = event.data;
if (d && d.type === "hsFormCallback" && d.eventName === "onFormSubmitted") route(d.id, (d.data && d.data.submissionValues) || {});
});
})();
</script>If your domain already has code in its own Site footer HTML, add the snippet there. HubSpot uses a domain's own footer HTML instead of the one under Default settings for all domains, so code added to the defaults wouldn't load on that domain.
The same code, with your URL, workspace and router already filled in, is on Routers → your router → Install: the loader under 1. Add the loader and the rest on the HubSpot tab. To load it on one page only, open the page in the editor, click Settings → Advanced and paste it into Footer HTML instead.
Choose the forms to route
With FORMS left empty, every HubSpot form on your site is routed. That's rarely what you want once a newsletter signup or a content download form shares the site, so list the forms that should open a calendar:
var FORMS = ["a1b2c3d4-5678-90ab-cdef-1234567890ab", "f9e8d7c6-5432-10fe-dcba-0987654321fe"];A form's ID is the long ID in its embed code: data-form-id for forms from the updated editor, formId for legacy forms. To send different forms to different routers, copy the snippet once per router, each with its own ROUTER and FORMS.
Show a thank-you message, not a redirect
A redirect sends the visitor to another page right after HubSpot saves the submission, which closes the calendar before they can book. Set each routed form to show an inline thank-you message instead:
- For forms built in the updated forms editor, set it in the form's post-submit options.
- For legacy forms on HubSpot pages, the page decides: in the page editor, select the form module, expand Thank you and choose Display an inline thank you message.
Remove any redirect to a HubSpot meetings scheduling page as well, since the Cauliflower calendar takes its place. For leads who should go to another page, add a Redirect step to the router path instead. See Path steps.
Publish and test
Save the settings, and update the page if you added the code to one page's Footer HTML. Open a page with a routed form, submit it with a teammate's email address, since test submissions are real and send meeting invitations, and check that:
- The calendar opens shortly after the thank-you message appears.
- The submission appears on the contact in HubSpot.
- The router's Logs tab in Cauliflower shows the lead and the path it matched.
What the code does
HubSpot forms announce a saved submission with an event on the page. Forms from the updated forms editor fire hs-form-event:on-submission:success, and legacy forms send an onFormSubmitted message. The snippet listens for both, so it works whichever editor a form was built in, and you can mix them on one site.
For each submission it:
- Checks the form's ID against
FORMS. - Reads the submitted values. Property names from the updated editor, like
0-1/email, lose their prefix, and company properties becomecompany_…, with the company name ascompany. - Calls
route, which adds the page URL, the referrer and UTM parameters, sends the lead to the router and shows the outcome.
Because it waits for HubSpot to confirm the submission, a lead that HubSpot rejects, for example because a required field is missing, is never routed. The visitor sees the same outcome as with any widget form: the calendar for qualified leads, the path's message for disqualified or assigned leads, or a redirect.
Field names
HubSpot forms submit contact properties by their internal names, and Cauliflower recognizes the standard ones directly:
| HubSpot property | Cauliflower field |
|---|---|
email | Email (required) |
firstname, lastname | First name, last name |
company | Company |
phone | Phone |
country | Country |
Company property name | Company |
Anything else, such as jobtitle or numemployees | A custom field with the same name, for example numemployees |
To route on a custom field in the router builder, add it under the router's Form fields with the property's internal name as the key, for example numemployees for a "Number of employees" dropdown. HubSpot shows each property's Internal name in its property settings. Company properties from the updated editor arrive with a company_ prefix, so a company "Number of employees" property becomes company_numberofemployees. See Paths and conditions.
Using HubSpot as your CRM
HubSpot forms and Cauliflower work well with the HubSpot CRM integration, and most HubSpot CMS sites will want it:
- Route existing customers to their owner. With Match against your CRM on, Cauliflower looks the lead up by email and domain before routing, so an ownership path can send known contacts and companies to their
hubspot_owner_id. - One contact per person. HubSpot keeps one contact per email address. When Cauliflower updates the CRM after routing or booking, it finds the contact HubSpot's form created by email and updates it, rather than adding a second one.
- One system assigns owners. If your router writes owners back, with Set the record owner on an Update CRM step or the CRM switch on an Assign to step, and a HubSpot workflow also rotates contact owners when these forms are submitted, the two overwrite each other. Keep one of them for routed forms.
Embed the whole router instead
To skip the HubSpot form, embed the router's hosted page: a form built from the router's fields, your team's photos and the calendar, all in one. Copy the code under Routers → your router → Install → Embed the hosted page, then in the page editor add a rich text module, choose Insert → Embed and paste it. It doesn't need the footer code above, and submissions go to HubSpot through the CRM integration rather than as form submissions. See Hosted router page.
Go-live checklist
- The router is Live and its Website form entry point is on.
- The footer code is saved on the right domain, with your Cauliflower URL, workspace slug and router slug.
FORMSlists every form that should open a calendar, and no newsletter or content download forms.- Those forms show an inline thank-you message, with no redirect or HubSpot meetings link.
- Allowed website origins lists every domain the pages are served from.
- A test submission opens the calendar, shows up on the contact in HubSpot and in the router's Logs.
- A test with a personal email address, or anything else your paths disqualify, shows the message you expect.
Troubleshooting
Open the browser's developer console on the live page to see errors from Cauliflower. The most common problems:
| What you see | Cause and fix |
|---|---|
| The thank-you message appears, but no calendar opens and nothing is in the router's Logs | The code isn't loading or the form isn't listed. Check that the footer HTML was saved on the domain the page uses, not only under Default settings for all domains, and that the form's ID is in FORMS. |
| The calendar opens, then the page changes | The form, or for a legacy form the page's form module, redirects after submission. Switch it to an inline thank-you message. |
| A newsletter or content form opens a calendar | FORMS is empty, so every form is routed. List the forms that should route. |
| "A valid email is required to route a lead" | The form has no email field. Cauliflower needs the contact's email to route. |
| "This website is not allowed to use this workspace's widget" | The domain isn't in Allowed website origins. Add it, including the system domain if you test on it. |
| "This router is paused" or "This router doesn't accept website form submissions" | Set the router to Live and turn on its Website form entry point. |
| "Too many submissions. Please wait a minute." | Public routing allows 30 submissions per minute from one IP address. Wait a minute between test bursts. |
| The calendar opens, but the lead goes down the wrong path | Open the lead from the router's Logs tab. Its page shows the Submitted data that arrived and how each path's conditions were evaluated. |
If your site sends a Content Security Policy, allow your Cauliflower URL in script-src, connect-src and frame-src, or the browser blocks the widget, the routing request or the calendar.