pex

Form Tracking

The Apex snippet automatically detects and intercepts form submissions on your site — including forms built without a native <form> tag. When a visitor submits a form, the snippet sends a tracking event and can auto-identify the visitor by email, all without any code changes.

How It Works

Apex handles two kinds of forms:

Native <form> elements

On page load, the snippet queries all <form> elements and attaches a submit event listener to each one. When a form is submitted, three things happen in order:

  1. Hidden fields are injected into the form before it posts
  2. A form_submit event is sent to /api/events via beacon
  3. If an email is found, the visitor is automatically identified

Formless regions (React, Plasmic, etc.)

Many modern frameworks build forms using input fields and buttons without wrapping them in a <form> tag. The snippet detects these "formless regions" by scanning for groups of 2+ input fields near a submit-like button. When the button is clicked:

  1. A form_submit event is sent with source: "formless"
  2. If an email field is found in the region, the visitor is automatically identified

Info

Formless detection uses heuristics: it looks for containers with multiple input/textarea fields and a button whose text matches common submit patterns (Submit, Send, Sign Up, Subscribe, etc.). If your form uses a non-standard button label, consider using apex.track() as described below.

Hidden Field Injection

The snippet adds these hidden <input> fields to the form at submit time:

Field NameValuePurpose
apex_visitor_idThe visitor's UUIDLinks the form submission to the anonymous visitor profile
apex_utm_sourceUTM source valueAttribution data from the apex_attr cookie
apex_utm_mediumUTM medium valueAttribution data from the apex_attr cookie
apex_utm_campaignUTM campaign valueAttribution data from the apex_attr cookie
apex_experimentActive experiment IDWhich experiment was running when the form was submitted
apex_variantAssigned variantWhich variant the visitor saw

These fields are only added if a value exists. If the visitor has no UTM data, those fields are omitted. If a field with the same name already exists in the form, the snippet will not overwrite it.

Tip

The hidden fields are added to the DOM right before the form posts. Your form processor (HubSpot, Mailchimp, custom backend, etc.) receives them as regular form fields. Most CRM platforms store unknown fields in a catch-all or custom properties area.

Automatic Email Detection

The snippet scans submitted forms for email addresses. It checks fields in this order:

  1. input[type="email"]
  2. input[name="email"], input[name="email_address"], input[name="user_email"], input[name="contact_email"]
  3. Any text input whose value matches an email regex pattern

When an email is found, the snippet automatically calls window.apex.identify(email) to stitch the anonymous visitor ID to a known identity. This means your CRM lead and the Apex visitor profile get linked without any custom code.

Identity Stitching

When identify() is called (either automatically from form detection or manually), the snippet sends a request to the identity API:

POST/api/identity/stitch

Links an anonymous visitor ID to a known email address

Payload includes the project key, visitor ID, email, optional metadata, and timestamp. The request is sent via navigator.sendBeacon to ensure delivery even as the page navigates away after form submission.

This creates a permanent link between the visitor's anonymous browsing history (pageviews, experiments, engagement) and their real identity. All past events retroactively associate with the identified user.

Manual Identification

If your form doesn't use a standard <form> element (e.g., a React controlled form or a JavaScript-driven flow), you can call identify manually:

// After your custom form submission logic
window.apex.identify(emailValue, {
  name: nameValue,
  company: companyValue,
  source: "pricing_page_form"
});

The second argument is an optional metadata object — any key/value pairs you want stored alongside the identity.

The Form Submit Event

Every form submission also generates a form_submit event sent to /api/events:

{
  "projectKey": "your-project-key",
  "type": "form_submit",
  "visitorId": "abc-123-def",
  "url": "https://yoursite.com/contact",
  "timestamp": "2025-07-15T10:30:00.000Z",
  "data": {
    "formAction": "https://yoursite.com/api/submit",
    "email": "visitor@example.com",
    "assignments": [
      { "experimentId": "exp-456", "variant": "variant_b" }
    ],
    "attribution": {
      "utm_source": "google",
      "utm_medium": "cpc",
      "gclid": "abc123"
    }
  }
}

This event is sent via beacon so it survives page navigation.

Working with Third-Party Forms

The snippet intercepts standard HTML <form> elements. Most third-party form builders (HubSpot, Typeform embeds, Gravity Forms) render as <form> tags and work automatically.

For forms built with modern frameworks (React, Vue, Plasmic, Webflow) that don't use <form> tags, the formless region detection handles most cases automatically.

Warning

Some embedded forms load inside iframes (e.g., Typeform hosted forms, some Calendly embeds). The snippet cannot intercept forms inside cross-origin iframes. For these, use window.apex.identify() in your own post-submission callback if the platform provides one.

Manual Form Tracking

If automatic detection doesn't catch your form (uncommon button labels, heavily abstracted UI frameworks, or custom submit flows), you can track submissions manually:

// Call this in your form's submit handler
window.apex.track("form_submit", {
  email: emailField.value,
  formName: "contact-form"
});

// If you also want to identify the visitor
window.apex.identify(emailField.value, {
  name: nameField.value,
  source: "contact_form"
});

This works alongside automatic detection — Apex deduplicates events from the same visitor within a short window.

What Gets Tracked

Form TypeDetectionEvent SourceHidden Fields
Native <form>Automaticform_submitYes (attribution injected)
Formless regionAutomaticform_submit (source: "formless")No (uses fetch/JS)
Manual apex.track()Your codeform_submitNo (you control the payload)
Cross-origin iframeNot supportedUse platform callbackN/A

Next Steps

  • Custom Events — send custom events via window.apex.track()
  • Experiments — how experiment data flows into form submissions
  • Installation — verify the snippet is running correctly