Skip to content

Dynamic Form Builder

PulseGate provides a single script at /pulse.js that loads both session tracking and the form builder. Add one script tag, call PulseGate.init(), then configure and render forms with PulseGateForm.configure() and PulseGateForm.render(). The endpoint defaults to the script’s origin, so no URL config is required for typical setups.

  • Single script: https://your-worker.dev/pulse.js — use this for new integrations.
  • PulseGate.init() — initializes tracking and sets the form endpoint to the script origin (default path /lead.capture).
  • PulseGateForm.configure() — sets common config (hidden fields, theme) once.
  • PulseGateForm.render() — renders form(s) into all elements matching the target selector.
  • Hidden fields from configure() and render() are merged — per-render values win on conflicts.
  • Default submit mode is traditional POST — browser follows the lead API’s 301 redirect to the thank-you page.

Scripts are minified in CI; run npm run build:form locally if you change the form or tracker source.

Quick Start (single script)

Add the script, call PulseGate.init(), then configure and render your form. Omit endpoint to use the default /lead.capture on the script’s origin.

html
<div class="lead-form"></div>

<script src="https://your-worker.dev/pulse.js"></script>
<script>
  PulseGate.init();
  PulseGateForm.configure({
    hidden: { SOURCE: 'WebSite', TEAM: 'sales' }
  });
  PulseGateForm.render({
    target: '.lead-form',
    hidden: { PROJECT: 'SunsetVillas', LOCATION: 'pune' },
    countryCode: { enabled: true, required: true, defaultValue: '+91' },
    fields: [
      { name: 'FIRSTNAME', label: 'Full Name', type: 'text', required: true },
      { name: 'EMAIL', label: 'Email', type: 'email', required: true },
      { name: 'PHONE', label: 'Phone', type: 'tel', required: true }
    ],
    submitText: 'Enquire Now'
  });
</script>

Every .lead-form div gets a form with hidden fields SOURCE, TEAM, PROJECT, LOCATION. On submit, the browser POSTs to the worker’s /lead.capture endpoint and follows the 301 to the thank-you page.

Minimal integration (PROJECT, LOCATION, DOMAIN)

The form always includes FIRSTNAME, PHONE, EMAIL, and COUNTRYCODE. Default TEAM is umikoindia. DOMAIN is auto-detected from the page if not set. So integrators typically only need to pass PROJECT and LOCATION (and optionally DOMAIN):

html
<div class="lead-form"></div>
<script src="https://your-worker.dev/pulse.js"></script>
<script>
  PulseGate.init();
  PulseGateForm.render({
    target: '.lead-form',
    hidden: { PROJECT: 'Sunset Villas', LOCATION: 'Pune' },
    submitText: 'Enquire Now'
  });
</script>

No need to pass fields or countryCode unless you want to customize; TEAM defaults to umikoindia, DOMAIN is taken from window.location.hostname.

Endpoint: Omit endpoint to use default /lead.capture on the script origin. To override: PulseGate.init({ endpoint: '...' }) or PulseGate.init({ formEndpoint: '...' }), or set a path or full URL in PulseGateForm.configure({ endpoint: '/lead.capture' }). The legacy path /lead is deprecated (sunset 2026-06-01).

Alternative: separate scripts (tracker.js + form.js)

If you prefer to load tracking and form separately (e.g. tracker in head, form later), use /tracker.js and /form.js and set the session endpoint explicitly. With separate scripts you must set PulseGateForm.configure({ endpoint: '...' }) to a full URL or path (e.g. '/lead.capture'), since there is no PulseGate.init() to set it from the script origin.

html
<div class="lead-form"></div>

<!-- Replace your-worker.dev with your PulseGate worker URL -->
<script src="https://your-worker.dev/pulse.js"></script>
<script>
  PulseGateForm.configure({
    hidden: { SOURCE: 'WebSite', TEAM: 'sales' }
  });
  PulseGateForm.render({
    target: '.lead-form',
    hidden: { PROJECT: 'SunsetVillas', LOCATION: 'pune' },
    countryCode: { enabled: true, required: true, defaultValue: '+91' },
    fields: [
      { name: 'FIRSTNAME', label: 'Full Name', type: 'text', required: true },
      { name: 'EMAIL', label: 'Email', type: 'email', required: true },
      { name: 'PHONE', label: 'Phone', type: 'tel', required: true }
    ],
    submitText: 'Enquire Now'
  });
</script>

Multiple Different Forms on One Page

Use different class names for different form configs. With the single script, call PulseGate.init() once, then configure and multiple render() calls:

html
<div class="hero-form"></div>
<div class="sidebar-callback"></div>
<div class="hero-form"></div>  <!-- same config as the first one -->

<script src="https://your-worker.dev/pulse.js"></script>
<script>
  PulseGate.init();
  PulseGateForm.configure({
    hidden: { SOURCE: 'WebSite', TEAM: 'sales' }
  });

  // All .hero-form elements get the full enquiry form
  PulseGateForm.render({
    target: '.hero-form',
    hidden: { PROJECT: 'SunsetVillas', LOCATION: 'pune' },
    countryCode: { enabled: true, required: true, defaultValue: '+91' },
    fields: [
      { name: 'FIRSTNAME', label: 'Full Name', type: 'text', required: true },
      { name: 'EMAIL', label: 'Email', type: 'email', required: true },
      { name: 'PHONE', label: 'Phone', type: 'tel', required: true }
    ],
    submitText: 'Enquire Now'
  });

  // All .sidebar-callback elements get a compact callback form
  PulseGateForm.render({
    target: '.sidebar-callback',
    hidden: { PROJECT: 'GreenMeadows', LOCATION: 'mumbai' },
    fields: [
      { name: 'FIRSTNAME', label: 'Name', type: 'text', required: true },
      { name: 'PHONE', label: 'Phone', type: 'tel', required: true }
    ],
    submitText: 'Call Me Back'
  });
</script>

Both forms inherit SOURCE and TEAM from configure(), but have different PROJECT and LOCATION values.

How Hidden Fields Merge

configure({ hidden: { SOURCE: 'WebSite', TEAM: 'sales' } })
           +
render({ hidden: { PROJECT: 'Villas', TEAM: 'marketing' } })
           =
Final hidden fields: { SOURCE: 'WebSite', TEAM: 'marketing', PROJECT: 'Villas' }

Per-render values override global values when the same key exists.

Default and configurable fields

Form fields (always included) — FIRSTNAME, EMAIL, COUNTRYCODE, and PHONE are always on the form by default. Add more via fields or hidden as needed.

What integrators typically set — For most integrations you only need to pass PROJECT, LOCATION, and DOMAIN in hidden. TEAM defaults to umikoindia; DOMAIN is auto-detected from the page hostname if omitted.

FieldTypeDescription
FIRSTNAMEvisibleFull name
EMAILvisibleEmail address
COUNTRYCODEvisibleCountry dial code (selector; enabled by default)
PHONEvisiblePhone number
TEAMhiddenTeam assignment (default 'umikoindia' if not set in configure() / render())

Configurable hidden fields (set per form) — Pass these via hidden in configure() or render():

FieldDescription
LOCATIONLocation or site (e.g. 'Whitefield, Bangalore')
PROJECTProject or campaign name (e.g. 'Sky Towers')
DOMAINSource domain (auto-detected from window.location.hostname if not set)

You can override TEAM in configure() or render() and add custom hidden fields as needed.

The script auto-captures campaign parameters from the page URL:

ParameterSourceDescription
gclidGoogle AdsGoogle Click Identifier
gbraidGoogle AdsApp-to-web measurement
wbraidGoogle AdsWeb-to-app measurement
utm_sourceAnyCampaign source
utm_mediumAnyCampaign medium
utm_campaignAnyCampaign name
utm_termAnyCampaign keyword
utm_contentAnyCampaign content variant
fbclidMeta AdsFacebook Click Identifier
msclkidBing AdsMicrosoft Click Identifier

KEYWORD auto-resolution (when not set in hidden fields):

  • utm_term present → uses its value
  • gclid / gbraid / wbraidgoogle-ads
  • fbclidfacebook-ads
  • msclkidbing-ads

Campaign params are sent as hidden fields prefixed with __ (e.g. __gclid, __utm_source).

API Reference

PulseGateForm.configure(config)

Sets global defaults inherited by all render() calls. Call once after PulseGate.init() when using the single script.

OptionTypeDescription
endpointstringWith pulse.js, omit to use default /lead.capture on script origin. Otherwise: full URL or path (e.g. '/lead.capture') resolved against script origin.
hiddenobjectCommon hidden fields (SOURCE, TEAM, etc.)
themeobjectVisual customization
submitTextstringDefault button label
submitModestringDefault submit mode
showBrandingbooleanShow "Powered by PulseGate"
inputClassNamestringExtra CSS class(es) applied to all inputs (for host-site styling)
showLabelsbooleanWhether to render field labels (default true); set false to hide and control layout via CSS
submitClassNamestringExtra CSS class(es) applied to the submit/CTA button (for host-site styling)

Any option set in configure() can be overridden per render() call.

PulseGateForm.render(config)

Renders form(s) into all elements matching target. Returns a single instance object or an array if multiple elements matched.

OptionTypeDefaultDescription
targetstring | ElementRequired. CSS selector (class or ID) or DOM element
hiddenobject{}Per-form hidden fields (merged with global). Use for LOCATION, PROJECT, DOMAIN, or to override TEAM.
fieldsarrayFIRSTNAME, EMAIL, COUNTRYCODE, PHONEVisible field definitions; add or replace as needed
countryCodeobject{ enabled: true }Country dial code field; set enabled: false to disable
submitModestring'redirect''redirect' = traditional POST, 'ajax' = XHR
titlestringForm heading
subtitlestringSubheading
submitTextstring'Submit'Button label
loadingTextstring'Submitting...'Button text during submit
successTitlestring'Thank You!'Success heading (ajax mode)
successMessagestringautoSuccess text (ajax mode)
themeobjectVisual customization (merged with global)
showBrandingbooleantrueShow branding footer
inputClassNamestringExtra CSS class(es) on all inputs (e.g. 'my-input' or 'form-control input-lg') for host-site styling
showLabelsbooleantrueWhether to show field labels; set false to hide labels (form gets class pg-form-no-labels for your CSS)
submitClassNamestringExtra CSS class(es) on the submit/CTA button (e.g. 'btn btn-primary') for host-site styling
onBeforeSubmitfunctionReturn false to cancel submit
onSuccessfunction(data)Called after submit
onErrorfunction(error)Called on failure (ajax mode)

Field Definition

PropertyTypeDefaultDescription
namestringRequired. POST field name (FIRSTNAME, EMAIL, etc.)
labelstringRequired. Visible label
typestring'text'text, email, tel, number, select, textarea
requiredbooleanfalseMust have value
placeholderstringPlaceholder text
minLengthnumberMin characters
maxLengthnumberMax characters
patternstringCustom regex
validationMessagestring'Invalid format'Pattern error message
optionsarrayFor select: string[] or { value, label }[]
rowsnumberFor textarea: visible rows
defaultValuestringPre-filled value or default selected option
classNamestringExtra CSS class(es) for this field’s input (in addition to global inputClassName)

countryCode Option

The country dial code selector is enabled by default and posts COUNTRYCODE to the lead API. When COUNTRYCODE and PHONE are adjacent, they render inline in one row on desktop and stack on mobile. Set countryCode: { enabled: false } to disable.

If defaultValue is not set, the form defaults from the user’s locale (e.g. navigator.language / navigator.languages) so the country code select is pre-filled by location when possible.

PropertyTypeDefaultDescription
enabledbooleantrueInclude country code field; set to false to disable
requiredbooleanfalseMark country code field required
defaultValuestringfrom localeSelected dial code (+1, +91, etc.); omit to use locale-based default
positionstring'beforePhone''beforePhone' or 'afterPhone'
namestring'COUNTRYCODE'Field name sent to API
labelstring'Country Code'Label text
placeholderstring'Select country code'Select placeholder
optionsarraybuilt-in global list (alphabetical)Custom dial-code options
classNamestringExtra CSS class(es) for the country code select only (e.g. a narrower dropdown); merged with global inputClassName

Common Hidden Fields

FieldDefault / behaviourPurpose
TEAMDefault 'umikoindia'Team assignment; override in configure() or render() if needed
LOCATIONConfigurableSet in hidden (e.g. 'Whitefield, Bangalore')
PROJECTConfigurableSet in hidden (e.g. project or campaign name)
DOMAINAuto-detected from hostnameSource domain; override in hidden if needed
SOURCEOptionalLead source (e.g. 'WebSite', 'FB'); set in hidden
COUNTRYCODEFrom visible selectorSent from the country code field
KEYWORDAuto-detected from URLMarketing keyword (from UTM/gclid/fbclid etc.)

Theme Options

PropertyDefaultDescription
primaryColor#2563ebButton and focus color
borderRadius8pxCorner rounding
fontFamilyinheritFont stack
backgroundColor#ffffffForm background
textColor#1f2937Input text color
labelColor#374151Label color
borderColor#d1d5dbInput border
errorColor#dc2626Error color
successBg#f0fdf4Success background
successColor#166534Success text

Complete configuration sample

Below is a single example that includes every configuration option. Copy it and remove or change only what you need.

html
<div class="lead-form"></div>
<script src="https://your-worker.dev/pulse.js"></script>
<script>
  PulseGate.init();

  // Optional: set defaults once for all forms on the page
  PulseGateForm.configure({
    endpoint: '/lead.capture',                    // or full URL; omit to use script origin
    hidden: {
      SOURCE: 'WebSite',
      TEAM: 'umikoindia'                         // default; override per render if needed
    },
    theme: {
      primaryColor: '#2563eb',
      borderRadius: '8px',
      fontFamily: 'inherit',
      backgroundColor: '#ffffff',
      textColor: '#1f2937',
      labelColor: '#374151',
      borderColor: '#d1d5db',
      errorColor: '#dc2626',
      successBg: '#f0fdf4',
      successColor: '#166534'
    },
    submitText: 'Submit',
    submitMode: 'redirect',                      // 'redirect' | 'ajax'
    showBranding: true,
    inputClassName: '',                          // e.g. 'form-control'
    showLabels: true,
    submitClassName: ''                          // e.g. 'btn btn-primary'
  });

  PulseGateForm.render({
    target: '.lead-form',                        // required: selector or DOM element

    // Hidden fields (PROJECT, LOCATION, DOMAIN are what you usually set)
    hidden: {
      PROJECT: 'Sunset Villas',
      LOCATION: 'Pune',
      DOMAIN: 'example.com',                     // omit to use window.location.hostname
      TEAM: 'umikoindia',                        // omit to use default
      SOURCE: 'WebSite'
    },

    // Visible fields (omit to use defaults: FIRSTNAME, EMAIL, COUNTRYCODE, PHONE)
    // Per field you can also use: minLength, maxLength, pattern, validationMessage, options (select), rows (textarea), defaultValue
    fields: [
      { name: 'FIRSTNAME', label: 'Full Name', type: 'text', required: true, placeholder: 'Your name', className: '' },
      { name: 'EMAIL', label: 'Email', type: 'email', required: true, placeholder: 'you@example.com' },
      { name: 'PHONE', label: 'Phone', type: 'tel', required: true, placeholder: '+91 98765 43210' }
      // COUNTRYCODE is added automatically unless countryCode.enabled is false
    ],

    // Country code select (omit to use defaults: enabled, locale-based default)
    countryCode: {
      enabled: true,
      required: false,
      defaultValue: '+91',                      // omit to use locale-based default
      position: 'beforePhone',                  // 'beforePhone' | 'afterPhone'
      name: 'COUNTRYCODE',
      label: 'Country Code',
      placeholder: 'Select country code',
      options: [],                              // omit to use built-in list
      className: ''                             // e.g. 'country-code-select'
    },

    submitMode: 'redirect',                     // 'redirect' | 'ajax'
    title: 'Get in touch',
    subtitle: 'We\'ll get back to you within 24 hours.',
    submitText: 'Enquire Now',
    loadingText: 'Submitting...',
    successTitle: 'Thank You!',
    successMessage: 'We received your information and will get back to you soon.',
    theme: {},                                  // merged with configure() theme
    showBranding: true,
    inputClassName: 'my-input',
    showLabels: true,
    submitClassName: 'my-cta',

    // Callbacks (optional)
    onBeforeSubmit: function () { return true; },  // return false to cancel
    onSuccess: function (data) { console.log('Success', data); },
    onError: function (err) { console.error('Error', err); }
  });
</script>

Minimal version (only what you usually need):

javascript
PulseGate.init();
PulseGateForm.render({
  target: '.lead-form',
  hidden: { PROJECT: 'My Project', LOCATION: 'Pune' },
  submitText: 'Enquire Now'
});

Embedding on external sites

When embedding the form on different websites, you can align styling with the host site using:

  • inputClassName — Extra class name(s) applied to every input, select, and textarea. Use your site’s existing form classes (e.g. form-control, my-site-input). Space-separated values are supported.
  • submitClassName — Extra class name(s) applied to the submit/CTA button (e.g. btn btn-primary).
  • showLabels — Set to false to hide labels and control layout entirely with your CSS (e.g. placeholders only). When labels are hidden, the form wrapper gets the class pg-form-no-labels so you can target it (e.g. reduce spacing).
  • Per-field className — On any field object, set className to add extra class(es) to that field’s input only.
  • countryCode.className — In the countryCode config, set className to style the country code select separately (e.g. countryCode: { enabled: true, className: 'country-code-select' }).

Example: match a Bootstrap-style site and hide labels:

javascript
PulseGateForm.render({
  target: '.lead-form',
  inputClassName: 'form-control',
  submitClassName: 'btn btn-primary',
  showLabels: false,
  fields: [
    { name: 'FIRSTNAME', label: 'Full Name', type: 'text', required: true, placeholder: 'Your name' },
    { name: 'EMAIL', label: 'Email', type: 'email', required: true, placeholder: 'Email' },
    { name: 'PHONE', label: 'Phone', type: 'tel', required: true, placeholder: 'Phone' }
  ],
  submitText: 'Submit'
});

Your CSS can target .pg-form .form-control, .pg-form .pg-submit (or your CTA class), and .pg-form-no-labels .pg-field as needed.

Custom CSS for submit button and inputs

You can style the submit button and inputs in two ways:

1. Add your own classes and style them

Pass inputClassName and submitClassName in render() (or configure()), then add CSS on your page that targets those classes:

html
<style>
  .my-input { border-radius: 6px; padding: 10px 14px; font-size: 1rem; }
  .my-cta { background: #059669; border-radius: 8px; padding: 12px 24px; }
  .my-cta:hover { background: #047857; }
</style>
<div class="lead-form"></div>
<script src="https://your-worker.dev/pulse.js"></script>
<script>
  PulseGate.init();
  PulseGateForm.render({
    target: '.lead-form',
    inputClassName: 'my-input',
    submitClassName: 'my-cta',
    hidden: { PROJECT: 'My Project', LOCATION: 'Pune' },
    submitText: 'Submit'
  });
</script>

The form adds your classes in addition to pg-input and pg-submit, so your rules apply (use the same specificity or higher if needed).

2. Target the built-in classes

Every input/select/textarea has class pg-input; the submit button has pg-submit. The form is wrapped in .pg-form. Add your own styles that target these:

css
.pg-form .pg-input {
  border-radius: 8px;
  padding: 0.75rem 1rem;
  border: 1px solid #e5e7eb;
}
.pg-form .pg-submit {
  background: linear-gradient(180deg, #2563eb, #1d4ed8);
  border-radius: 8px;
  font-weight: 600;
}

Scope with your container (e.g. .lead-form .pg-input) if you have multiple forms on the page.

Submit Modes

redirect (default)

Traditional form POST. Browser submits application/x-www-form-urlencoded data and follows the lead API's 301 to the thank-you page. This is the standard mode for marketing websites.

ajax

XHR POST with JSON body. The page doesn't navigate — the form shows an inline success message. Useful for SPAs or modals.

Return Value

render() returns:

  • Single match{ formId, reset() }
  • Multiple matches — Array of { formId, reset() }

Call reset() to restore a form to its initial state.