Capture email addresses from users who start filling out HubSpot forms but abandon them before submitting. This solution automatically creates or updates HubSpot contacts with a tag indicating form abandonment.
- Client-Side Script: Lightweight JavaScript snippet that detects email input in HubSpot forms and sends data when forms are abandoned
- Cloudflare Worker: Serverless endpoint that receives abandonment data and creates/updates HubSpot contacts via the HubSpot API
- A HubSpot account
- A Cloudflare account (free tier works)
- Node.js and npm installed locally
- Access to your website's HTML to add the capture script
- Log into your HubSpot account
- Navigate to Settings → Integrations → Private Apps
- Click Create a private app
- Give it a name (e.g., "Form Abandonment Capture")
- Under Scopes, select:
crm.objects.contacts.write- Required to create/update contacts
- Click Create app
- Copy the Access Token - you'll need this for the Cloudflare Worker
You'll need to create custom properties to track form abandonment data:
-
Navigate to Settings → Properties → Contact properties
-
Click Create property
-
Create the following properties:
Property 1: Form Abandonment Status
- Label:
Form Abandonment Status - Internal name:
form_abandonment_status - Type: Single-line text
- Description: Status indicating form abandonment
Property 2: Form Abandonment Page
- Label:
Form Abandonment Page - Internal name:
form_abandonment_page - Type: Single-line text
- Description: URL of the page where form was abandoned
Property 3: Form Abandonment Form ID (Optional)
- Label:
Form Abandonment Form ID - Internal name:
form_abandonment_form_id - Type: Single-line text
- Description: HubSpot form ID that was abandoned
Property 4: Form Abandonment Form Name (Optional)
- Label:
Form Abandonment Form Name - Internal name:
form_abandonment_form_name - Type: Single-line text
- Description: Name of the form that was abandoned
- Label:
cd worker
npm installIf you haven't used Wrangler before, you'll need to authenticate:
npx wrangler loginSet your HubSpot Private App token as a Cloudflare Worker secret:
npx wrangler secret put HUBSPOT_TOKENWhen prompted, paste your HubSpot Private App access token.
npm run deployAfter deployment, Wrangler will display your Worker URL (e.g., https://hubspot-form-abandonment.your-subdomain.workers.dev). Save this URL - you'll need it for the next step.
- Open
snippet/capture.js - Find the
WORKER_URLconstant near the top of the file - Replace
'https://your-worker-name.your-subdomain.workers.dev'with your actual Worker URL from Step 2.4
const WORKER_URL = 'https://hubspot-form-abandonment.your-subdomain.workers.dev';Add the capture script to any page that contains HubSpot forms. You can do this in several ways:
Add this before the closing </body> tag on your pages:
<script src="https://your-domain.com/path/to/capture.js"></script>Or inline the script directly:
<script>
// Paste the entire contents of snippet/capture.js here
</script>- Create a new Custom HTML tag
- Paste the contents of
snippet/capture.js - Set trigger to fire on all pages (or specific pages with forms)
If you're using HubSpot's tracking code, you can add the script there as well.
Important: The script must load after HubSpot's form script loads, or use the MutationObserver fallback (which is already included).
- Email Detection: The script monitors HubSpot forms on the page and detects when an email address is entered
- Capture Triggers: Email is captured when:
- User leaves the email field (
blurevent) - User types in the email field (debounced)
- User navigates away from the page (
beforeunloadevent)
- User leaves the email field (
- Submission Detection: If the form is successfully submitted, no abandonment data is sent
- Contact Creation: The Worker receives the email and creates/updates a HubSpot contact with:
- The email address
form_abandonment_statusset to "Started form - did not finish"- Additional metadata (page URL, form ID, form name)
- Navigate to a page with a HubSpot form
- Enter a test email address in the form
- Do not submit the form
- Navigate away from the page or close the tab
- Check your HubSpot contacts - you should see a new contact (or updated contact) with:
- The email address you entered
form_abandonment_status= "Started form - did not finish"form_abandonment_page= URL of the page
- Navigate to a page with a HubSpot form
- Enter a test email address
- Submit the form successfully
- Check HubSpot - the contact should be created through normal form submission, but should NOT have the abandonment status
Open your browser's developer console. You should see log messages like:
[Form Abandonment] Initialized
[Form Abandonment] Listeners attached to form
[Form Abandonment] Email captured: test@example.com
If you see errors, check:
- Worker URL is correct in
capture.js - HubSpot token is set correctly in Cloudflare Worker secrets
- Custom properties exist in HubSpot with correct internal names
- Private App has the correct scopes
The Worker accepts POST requests with the following JSON payload:
{
"email": "user@example.com",
"formId": "optional-form-id",
"formName": "Optional Form Name",
"pageUrl": "https://example.com/page"
}Response (200 OK):
{
"success": true,
"message": "Contact created/updated"
}Error Response (400 Bad Request):
{
"error": "Invalid or missing email address"
}Edit worker/src/index.ts and change the form_abandonment_status value:
form_abandonment_status: 'Your custom status text',In snippet/capture.js, modify the EMAIL_CAPTURE_DELAY constant:
const EMAIL_CAPTURE_DELAY = 2000; // millisecondsTo capture additional form fields or metadata, modify:
- The payload structure in
snippet/capture.js - The
AbandonmentDatainterface inworker/src/index.ts - The contact properties mapping in the Worker
- Check Worker Logs: Use
wrangler tailto see real-time logs - Verify Token: Ensure
HUBSPOT_TOKENsecret is set correctly - Check Scopes: Verify Private App has
crm.objects.contacts.writescope - Property Names: Ensure custom property internal names match exactly (case-sensitive)
- Check Console: Look for initialization messages
- Form Loading: Ensure HubSpot forms load before the script (or use MutationObserver fallback)
- Form Selectors: Verify your HubSpot forms use standard HubSpot form structure
The Worker uses HubSpot's upsert functionality (create or update by email). If you're seeing duplicates:
- Check if email addresses have different casing (HubSpot may treat them as different)
- Verify the email field is being used as the unique identifier
- The Worker URL is public (it's in client-side JavaScript)
- The Worker validates email format before sending to HubSpot
- Consider adding rate limiting or IP-based restrictions if needed
- The HubSpot token is stored securely as a Cloudflare Worker secret
- Cloudflare Workers: Free tier includes 100,000 requests/day
- HubSpot API: Free tier includes 100 API calls/10 seconds
- For high-traffic sites, monitor usage and upgrade plans as needed
For issues or questions:
- Check Cloudflare Worker logs:
wrangler tail - Check browser console for client-side errors
- Verify HubSpot API limits and quotas
MIT