Connecting Craft CMS to HubSpot
Craft is a developer-oriented CMS, usually built bespoke by an agency, which means the implementation is whatever that agency decided and nothing is standard. HubSpot is where sales works, so a conversion that never reaches it is invisible to the people who decide what a lead was worth. The join between them succeeds or fails on one thing: whether the contact identity — email, or the hubspotutk cookie reaches the Craft Commerce order before the conversion happens, because HubSpot can only match a conversion it can tie back to a click it recognizes.
Free30 minutesNo obligation
Specific to this pair
Craft CMS to HubSpot: form entries and Commerce orders, one contact
Resolve Craft form entries and Commerce orders to one HubSpot contact by email, reading hubspotutk at submit time, never from a cached template. Send orders from the PHP order-complete listener as closed-won deals or contact properties, and keep one writer per object: the form plugin for contacts, your listener for orders and deals.
Many Craft sites are lead-generation sites first and stores second, which is where HubSpot fits. Two records matter: the form entry, from whichever form plugin or custom controller this build uses, and, if Commerce is installed, the completed order. Both should resolve to one HubSpot contact by email, and the form submission should carry the hubspotutk cookie so HubSpot can attach the visitor's earlier page views and set original source.
The Craft-specific trap is caching. If the form is rendered on a statically cached page, a hidden hubspotutk field baked into the HTML is the same value for every visitor, or empty, and HubSpot stitches strangers' sessions onto one contact. Read hubspotutk at submit time, never from a cached template. Commerce works the same way: send the order from the PHP order-complete listener using the order's email, as a closed-won deal for sales-assisted orders or as a contact property for small ones.
Check which HubSpot route this build already uses before adding one. Form plugins such as Freeform have HubSpot integrations, and a parallel API sync from a custom module creates a second write path the original agency may not know about. Pick one writer per object: the form plugin for contacts, your listener for orders and deals. Refunds should move the deal or update an order-value property, not delete the record.
Craft → HubSpot · what breaks
Why this join usually fails — Craft → HubSpot
- 01
The contact identity — email, or the hubspotutk cookie never reaches Craft
Craft's template caching and static caching plugins both serve stored output, and because every build is custom, where the capture code lives differs per site. There is no standard place to look.
- 02
Every install is bespoke
There is no standard Craft setup, so an approach that worked on another Craft site may have nothing to attach to on this one. Read the build before quoting.
- 03
Duplicate contacts
Two records for one person, each with half the history. Every downstream report is then wrong in a way that looks like a data problem rather than an integration problem.
- 04
Nothing reconciles afterwards
HubSpot shows the contact record and its timeline. A successful API response means HubSpot accepted the call, not that it resolved to the right contact — open the record and look.
Craft → HubSpot · the fix
What working looks like — Craft → HubSpot
The contact identity — email, or the hubspotutk cookie on every order
Conversions arriving in HubSpot
The revenue or conversion lands on the contact record and its associated deal, where sales can see it and where HubSpot's own attribution reporting can use it.
Reconciled against the source
Craft CMS's own records are the reference, not either dashboard.
Craft → HubSpot · architecture
How Craft and HubSpot actually connect
- 1
Craft CMS holds the truth
Craft installs are bespoke by nature. Establish how this particular build models a completed order before assuming anything transfers from another Craft site.
- 2
HubSpot needs a click to match
HubSpot needs to resolve the incoming data to an existing contact or create one. Without an email or a hubspotutk it creates duplicates, and duplicate contacts break every report built on top of them.
- 3
The identifier is the bridge
The contact identity — email, or the hubspotutk cookie, captured at the visit and carried into the Craft Commerce order.
- 4
The event source decides reliability
Fire from the event listener in PHP rather than a Twig template, which may be cached.
- 5
Deduplication at the destination
HubSpot deduplicates contacts on email address. Events and property updates against the same contact are idempotent by design, but a second contact created for the same person is not detected, and merging them afterwards is manual work.
Craft → HubSpot · normal or broken
Working vs. broken, side by side — Craft → HubSpot
Normal, not a fault
The contact identity — email, or the hubspotutk cookie present on the order
Visible on the record itself in Craft CMS, not merely captured somewhere on the site.
Sent from the Craft Commerce order-complete event
Deduplicated correctly
HubSpot deduplicates contacts on email address. Events and property updates against the same contact are idempotent by design, but a second contact created for the same person is not detected, and merging them afterwards is manual work.
Reconciles with the source
HubSpot's count and Craft CMS's records agree within a few percent over a closed window.
Actually broken
The contact identity — email, or the hubspotutk cookie missing on most records
Craft's template caching and static caching plugins both serve stored output, and because every build is custom, where the capture code lives differs per site. There is no standard place to look.
Sent from the browser
There is no standard Craft setup, so an approach that worked on another Craft site may have nothing to attach to on this one. Read the build before quoting.
Counted twice, or not at all
Two records for one person, each with half the history. Every downstream report is then wrong in a way that looks like a data problem rather than an integration problem.
Nobody has reconciled it
The dashboard shows a number and no one has compared it to Craft CMS.
Craft → HubSpot · diagnose
Diagnostic order — Craft → HubSpot
- 01
Count orders carrying the contact identity — email, or the hubspotutk cookie
- 02
Follow one real conversion end to end
- 03
Read how this specific build handles order completion
Assumptions carried from another Craft project are the main source of wasted time here.
- 04
Search for the test contact by email after sending
One record or two? Then check whether the properties landed on the contact or only on the form submission — they are different things and only the first is usable.
- 05
Read the match rate, not the success message
HubSpot shows the contact record and its timeline. A successful API response means HubSpot accepted the call, not that it resolved to the right contact — open the record and look.
Craft → HubSpot · prove
How it gets proven — Craft → HubSpot
Against Craft CMS's own records, not against either platform's dashboard.
Source of truth
Craft Commerce's order records in your own database, filtered to completed.
- The contact identity — email, or the hubspotutk cookie confirmed present on a live order.
- One contact record confirmed, not two, with the property visible on the contact rather than only on the submission.
- Order custom field confirmed populated on an order placed as an anonymous visitor through cache.
Craft → HubSpot · free audit
What we check in the free audit
- Count orders carrying the contact identity — email, or the hubspotutk cookie
- Follow one real conversion end to end
- Read how this specific build handles order completion
- Search for the test contact by email after sending
- Read the match rate, not the success message
Checked against
Craft Commerce's order records in your own database, filtered to completed.
Free30 minutesNo obligation
Craft → HubSpot · when not to buy
When you don't need this — Craft → HubSpot
- If HubSpot already reports conversions that reconcile against Craft CMS's records within a few percent, this join is working and there is nothing to buy.
- Measure identifier coverage yourself first — pull your last hundred orders and count how many carry the contact identity — email, or the hubspotutk cookie.
- If the original agency is still engaged, the fastest path is usually specifying the field and having them add the hook.
- HubSpot's native form and tracking code already handle the straightforward cases well. This is worth building when the conversion happens somewhere HubSpot's code cannot see.
Craft → HubSpot · what we deliver
What gets built — Craft → HubSpot
Identifier capture into Craft
Capture the identifier and store it as a custom field on the order through a Craft event listener, server-side.
Event source: the Craft Commerce order-complete event
Fire from the event listener in PHP rather than a Twig template, which may be cached.
Dispatch to HubSpot
The HubSpot API — contact create-or-update with the properties, or a custom behavioral event where you need the timeline entry rather than a property.
Reversals where they exist
Refunds and cancelled orders are recorded against the order.
Written guarantee
95%+ tracking accuracy
Measured against Craft Commerce's order records in your own database, filtered to completed.. Miss it and we keep working at no cost.
From $400 · See packages
FAQ
Craft → HubSpot questions, answered
Usually hubspotutk was missing or came from a cached page, so HubSpot couldn't link earlier sessions to the new contact. Check a test submission's contact timeline for page views before the form.
For high-value or sales-assisted orders, yes, closed won at the order total. For low-value retail orders, a contact property or timeline event is usually enough.
Almost always because the contact identity — email, or the hubspotutk cookie never reached the Craft Commerce order. Craft's template caching and static caching plugins both serve stored output, and because every build is custom, where the capture code lives differs per site. There is no standard place to look. Measure coverage before debugging the dispatch.
HubSpot resolves to a contact by email address, or by the hubspotutk cookie value which ties an anonymous visitor to their eventual contact record. Sending the hubspotutk with a form submission is what lets HubSpot stitch the pre-conversion page views to the contact, which is where the original source attribution comes from.
The Craft Commerce order-complete event. Fire from the event listener in PHP rather than a Twig template, which may be cached.
HubSpot shows the contact record and its timeline. A successful API response means HubSpot accepted the call, not that it resolved to the right contact — open the record and look.
More on Craft → HubSpot
- Craft → Google AdsConnecting Craft CMS to Google Ads
- Craft → MetaConnecting Craft CMS to Meta Conversions API
- Craft → GA4Connecting Craft CMS to GA4
RELATED GUIDES
Where Craft → HubSpot fits in the rest of your tracking
- Drupal Commerce → HubSpotConnecting Drupal Commerce to HubSpot
- Webflow → HubSpotConnecting Webflow to HubSpot
- Concrete CMS → HubSpotConnecting Concrete CMS to HubSpot
- Craft → 6senseConnecting Craft CMS to 6sense
- Craft → SalesforceConnecting Craft CMS to Salesforce
- Local servicesConversion tracking for local businesses, where most conversions bypass the website
- Statamic → HubSpotConnecting Statamic to HubSpot
- Umbraco → HubSpotConnecting Umbraco to HubSpot
Free · 30 min
Get Craft and HubSpot joined properly
Free30 minutesNo obligation
