-
Notifications
You must be signed in to change notification settings - Fork 3
Hubspot example #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Hubspot example #25
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
1b6ca52
add hubspot example app
devkiran 07a0930
Update index.html
devkiran 949a935
finalize example app
devkiran 478b046
Update meeting.html
devkiran f357462
Merge branch 'main' into hubspot
devkiran f7a5363
Create README.md
devkiran File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| # HubSpot + Dub Conversion Tracking Demo | ||
|
|
||
| This is an example of how to track [HubSpot](https://www.hubspot.com/) form submissions and meeting bookings as [lead conversion events on Dub](https://dub.co/docs/conversions/leads/introduction). | ||
|
|
||
| Useful for tracking SaaS signups, contact-us form submissions, and sales meeting bookings back to the referral link that drove them. | ||
|
|
||
| ## Files | ||
|
|
||
| | File | Description | | ||
| | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | [`index.html`](./index.html) | Embeds a **HubSpot Form** on the page. When the form is ready, the `dub_id` click ID is read from the cookie set by `@dub/analytics` and written into a hidden form field so HubSpot forwards it to Dub on submission. | | ||
| | [`meeting.html`](./meeting.html) | Embeds the **HubSpot Meetings (scheduler) widget**. It listens for the `meetingBookSucceeded` `postMessage` from the iframe and then calls `dubAnalytics.trackLead()` with the booked contact's details. | | ||
|
|
||
| ## How it works | ||
|
|
||
| Both flows rely on the [`@dub/analytics` client-side script](https://dub.co/docs/sdks/client-side/introduction) setting a `dub_id` cookie when a visitor lands on your site via a Dub referral link. | ||
|
|
||
| - **Forms flow (`index.html`)**: listens for HubSpot's `hs-form-event:on-ready` event and populates a hidden field (with property name `dub_id`) on the form. Dub's HubSpot integration picks up the click ID from the submission and attributes it as a lead. | ||
| - **Meetings flow (`meeting.html`)**: listens to `window` messages from `https://meetings.hubspot.com`, and when a meeting is successfully booked, calls `dubAnalytics.trackLead()` with the contact's name and email in `deferred` mode. | ||
|
|
||
| ## Changes needed to make it dynamic | ||
|
|
||
| The files contain hardcoded IDs/domains for the demo. Replace the following before using them in your own project: | ||
|
|
||
| ### `index.html` | ||
|
|
||
| 1. **HubSpot portal ID** – update the script src and `data-portal-id` with your own portal ID: | ||
| ```html | ||
| <script | ||
| src="https://js.hsforms.net/forms/embed/<YOUR_PORTAL_ID>.js" | ||
| defer | ||
| ></script> | ||
| ... data-portal-id="<YOUR_PORTAL_ID>"</YOUR_PORTAL_ID> | ||
| ``` | ||
| 2. **HubSpot form ID** – replace `data-form-id` with the ID of the form you created in HubSpot: | ||
| ```html | ||
| data-form-id="<YOUR_FORM_ID>"</YOUR_FORM_ID> | ||
| ``` | ||
| 3. **Hidden `dub_id` field** – create a hidden field on your HubSpot form whose internal property name is `dub_id`, then update the `setFieldValue` call to match its object/property path (e.g. `0-1/dub_id`, `0-2/dub_id`, …): | ||
| ```js | ||
| HubSpotFormsV4.getForms()[0].setFieldValue( | ||
| "<OBJECT_TYPE_ID>/dub_id", | ||
| clickId | ||
| ); | ||
| ``` | ||
| 4. **Dub analytics `data-domains`** – replace `acme.link` with your own short link domain configured in Dub: | ||
| ```html | ||
| data-domains='{"refer":"<YOUR_DUB_DOMAIN>"}'</YOUR_DUB_DOMAIN> | ||
| ``` | ||
|
|
||
| ### `meeting.html` | ||
|
|
||
| 1. **Dub publishable key** – replace the placeholder with your workspace's publishable key from [Dub → Settings → API Keys](https://app.dub.co/settings/tokens): | ||
| ```js | ||
| s.setAttribute("data-publishable-key", "<YOUR_DUB_PUBLISHABLE_KEY>"); | ||
| ``` | ||
| 2. **Dub analytics `data-domains`** – replace `dub.sh` with your own short link domain: | ||
| ```js | ||
| s.setAttribute("data-domains", '{"refer":"<YOUR_DUB_DOMAIN>"}'); | ||
| ``` | ||
| 3. **Remove `data-api-host`** – this line points to a local Dub instance and should be removed in production so the script talks to Dub's hosted API: | ||
| ```js | ||
| s.setAttribute("data-api-host", "http://localhost:8888/api"); // remove this in production | ||
| ``` | ||
| 4. **HubSpot meeting link** – replace the scheduler slug with your own meeting link: | ||
| ```html | ||
| <div | ||
| class="meetings-iframe-container" | ||
| data-src="https://meetings.hubspot.com/<YOUR_MEETING_SLUG>?embed=true" | ||
| ></div> | ||
| ``` | ||
|
|
||
| ## Learn more | ||
|
|
||
| - [Dub Conversions – Leads](https://dub.co/docs/conversions/leads/introduction) | ||
| - [`@dub/analytics` client-side SDK](https://dub.co/docs/sdks/client-side/introduction) | ||
| - [HubSpot Forms embed](https://developers.hubspot.com/docs/cms/building-blocks/forms) | ||
| - [HubSpot Meetings embed](https://knowledge.hubspot.com/meetings-tool/embed-the-meetings-tool-on-an-external-page) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <title>Contact Us</title> | ||
| </head> | ||
| <body> | ||
| <h1>Contact us</h1> | ||
| <!-- Load HubSpot forms library --> | ||
| <script src="https://js.hsforms.net/forms/embed/47839131.js" defer></script> | ||
|
|
||
| <!-- Load Dub analytics script --> | ||
| <script | ||
| src="https://www.dubcdn.com/analytics/script.js" | ||
| defer | ||
| data-domains='{"refer":"acme.link"}' | ||
| ></script> | ||
|
|
||
| <div | ||
| class="hs-form-frame" | ||
| data-region="na1" | ||
| data-form-id="07b7409d-cf6b-4d9f-9926-76d55b591e88" | ||
| data-portal-id="47839131" | ||
| ></div> | ||
|
|
||
| <script> | ||
| // Read cookie value | ||
| function getCookie(name) { | ||
| const value = `; ${document.cookie}`; | ||
| const parts = value.split(`; ${name}=`); | ||
|
|
||
| if (parts.length === 2) { | ||
| return parts.pop().split(";").shift(); | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| // Listen for the form ready event | ||
| window.addEventListener("hs-form-event:on-ready", (event) => { | ||
| const clickId = getCookie("dub_id"); | ||
|
|
||
| if (!clickId) { | ||
| console.debug("clickId not found. Skipping lead tracking."); | ||
| return; | ||
| } | ||
|
|
||
| // Add the clickId to the form | ||
| // Note: Make sure you have a hidden field with connected property "dub_id" | ||
| HubSpotFormsV4.getForms()[0].setFieldValue("0-1/dub_id", clickId); | ||
| }); | ||
| </script> | ||
| </body> | ||
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <title>Contact Us</title> | ||
| </head> | ||
| <body> | ||
| <h1>Contact us</h1> | ||
|
|
||
| <!-- Load Dub analytics script --> | ||
| <script> | ||
| !(function (c, n) { | ||
| c[n] = | ||
| c[n] || | ||
| function () { | ||
| (c[n].q = c[n].q || []).push(arguments); | ||
| }; | ||
| ["trackClick", "trackLead", "trackSale"].forEach( | ||
| (t) => (c[n][t] = (...a) => c[n](t, ...a)) | ||
| ); | ||
| var s = document.createElement("script"); | ||
| s.defer = 1; | ||
| s.src = "https://dubcdn.com/analytics/script.conversion-tracking.js"; | ||
| s.setAttribute( | ||
| "data-publishable-key", | ||
| "dub_pk_tLnpzXvJAEik0UtzuzdmMJSn" | ||
| ); // Replace with your publishable key | ||
| s.setAttribute("data-domains", '{"refer":"dub.sh"}'); | ||
| s.setAttribute("data-api-host", "http://localhost:8888/api"); | ||
| document.head.appendChild(s); | ||
| })(window, "dubAnalytics"); | ||
| </script> | ||
|
|
||
| <div | ||
| class="meetings-iframe-container" | ||
| data-src="https://meetings.hubspot.com/kiran61?embed=true" | ||
| ></div> | ||
|
|
||
| <!-- Load Meetings Embed Script --> | ||
| <script | ||
| type="text/javascript" | ||
| src="https://static.hsappstatic.net/MeetingsEmbed/ex/MeetingsEmbedCode.js" | ||
| ></script> | ||
|
|
||
| <script> | ||
| // Read cookie value | ||
| function getCookie(name) { | ||
| const value = `; ${document.cookie}`; | ||
| const parts = value.split(`; ${name}=`); | ||
|
|
||
| if (parts.length === 2) { | ||
| return parts.pop().split(";").shift(); | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
devkiran marked this conversation as resolved.
|
||
|
|
||
| // Listen for the message event | ||
| window.addEventListener("message", function (event) { | ||
| // Check if the message is from the scheduling widget | ||
| if (event.origin === "https://meetings.hubspot.com") { | ||
| const clickId = getCookie("dub_id"); | ||
|
|
||
| if (!clickId) { | ||
| console.debug("clickId not found. Skipping lead tracking."); | ||
| return; | ||
| } | ||
|
|
||
| // Get the data from the event | ||
| const data = event.data; | ||
|
|
||
| if (data.meetingBookSucceeded) { | ||
| // Get the scheduled contact | ||
| const contact = | ||
| data.meetingsPayload.bookingResponse.postResponse.contact; | ||
|
|
||
| if (!contact) { | ||
| console.debug("contact not found. Skipping lead tracking."); | ||
| return; | ||
| } | ||
|
|
||
| console.debug("contact found", contact); | ||
|
|
||
| // Track the lead with the scheduled contact | ||
| const customerName = [contact.firstName, contact.lastName] | ||
| .filter(Boolean) | ||
| .join(" "); | ||
|
|
||
| dubAnalytics.trackLead({ | ||
| clickId, | ||
| mode: "deferred", | ||
| eventName: "Meeting scheduled", | ||
| customerExternalId: contact.email, | ||
| customerName: customerName, | ||
| customerEmail: contact.email, | ||
| }); | ||
| } | ||
| } | ||
| }); | ||
| </script> | ||
| </body> | ||
| </html> | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.