Skip to content

Dynamic Form Builder

PulseGate's form builder embeds lead-capture forms with one script. Site key and form ID are required — the form does not render without them. Create a site and form in the dashboard, add authorized domains, then use PulseGate.init({ siteKey }) and PulseGateForm.render({ formId, ... }). 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({ siteKey: 'pk_live_xxx' })required. Sets site key and form endpoint.
  • PulseGateForm.configure() — sets common config (hidden fields, theme) once.
  • PulseGateForm.render({ formId: '...', target, ... })formId required. Fetches schema from backend; domain must be authorized.
  • 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.

Script URLs

ScriptUse case
pulse.jsRecommended. Tracking + form in one; call PulseGate.init({ siteKey }) so endpoint and site key are set.
form.js + tracker.jsSeparate tracking and form; set PulseGateForm.configure({ endpoint: '/lead.capture' }) or full URL.

Required setup

  • Site key — Set in PulseGate.init({ siteKey: 'pk_live_xxx' }) (from the dashboard). Without it, the form shows "Form unavailable. Site key and form ID are required."
  • Form ID — Pass in every render() call, e.g. render({ formId: 'enquiry_form', target: '.lead-form', ... }).
  • Authorized domains — Add your page origin to the site in the dashboard. See Multi-form / sites.

Quick Start (single script)

Add the script, call PulseGate.init({ siteKey }), then configure and render with formId. Your page origin must be in the site's authorized domains.

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

<script src="https://your-worker.dev/pulse.js"></script>
<script>
  PulseGate.init({ siteKey: 'pk_live_xxx' });
  PulseGateForm.configure({
    hidden: { SOURCE: 'WebSite', TEAM: 'sales' }
  });
  PulseGateForm.render({
    formId: 'enquiry_form',
    target: '.lead-form',
    hidden: { PROJECT: 'SunsetVillas', LOCATION: 'pune' },
    countryCode: { enabled: true, required: true, defaultValue: '+91' },
    submitText: 'Enquire Now'
  });
</script>

The script fetches the form schema from /forms/config (domain is validated). 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 schema (fields) can be defined in the dashboard. DOMAIN is auto-detected from the page if not set. Example with minimal overrides:

html
<div class="lead-form"></div>
<script src="https://your-worker.dev/pulse.js"></script>
<script>
  PulseGate.init({ siteKey: 'pk_live_xxx' });
  PulseGateForm.render({
    formId: 'enquiry_form',
    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).

Multi-form / sites (form schema from backend)

When using sites and authorized domains, pass siteKey in PulseGate.init() and formId in render(). The script fetches the form schema from GET /forms/config, merges it with your configure/render options, and submits with siteKey and formId so the server can validate the domain and attribute the lead.

Prerequisites (dashboard):

  1. Create a site and note its site key (e.g. pk_live_xxx).
  2. Add authorized domains for that site (e.g. https://yoursite.com, yoursite.com, or localhost for dev). The request Origin or Referer host must match.
  3. Create a form for the site with a form ID (e.g. enquiry_form). Optionally set form JSON (fields, theme, target, submitText, etc.); these are merged with your render() options (render wins on conflict).
  4. Ensure the site and form are enabled (dashboard toggles). If disabled, /forms/config and /lead.capture return 403.

Optional: target from backend — If you omit target in render() but pass siteKey and formId, the script uses the backend form JSON target if present (e.g. #lead-form or .my-form). If the backend has no target, the default is #lead-form. You can also add [data-pulsegate-form] on the container so "Form unavailable" is shown there when config fetch fails.

Backend form JSON (dashboard): When using multi-form, the form's JSON in the dashboard can include any of: target, fields, theme, submitMode, showBranding, inputClassName, showLabels, submitClassName, usePulseGateStyles, hidden, submitText, title, subtitle, countryCode. Merge order: backend → configure() → render(), so your render() options override the backend and configure values.

html
<div class="lead-form"></div>
<script src="https://your-worker.dev/pulse.js"></script>
<script>
  PulseGate.init({ siteKey: 'pk_live_xxx' });
  PulseGateForm.configure({
    theme: { primaryColor: '#6b8d14' },
    submitMode: 'redirect',
    showBranding: false,
    inputClassName: 'form-control',
    showLabels: false,
    submitClassName: 'btn btn-primary'
  });
  PulseGateForm.render({
    formId: 'enquiry_form',
    target: '.lead-form',
    hidden: { PROJECT: 'Godrej Ivara Kharadi', LOCATION: 'Pune' },
    submitText: 'Enquire Now'
  });
</script>

If the backend returns form fields (e.g. ContactName, ContactPhone), those are used; otherwise the default or configure()/render() fields apply. Submissions are sent to POST /lead.capture with siteKey and formId in the payload. When formId and siteKey are present, render() returns a Promise (resolves when the config fetch and render complete).

When the form does not load (multi-form):

SymptomCauseFix
"Form unavailable." or 403 on configDomain not allowed or site/form disabledAdd the page origin (e.g. https://yoursite.com) to the site's authorized domains; ensure site and form are enabled in the dashboard.
"Unable to load form."Network error or server errorCheck console; ensure worker URL and CORS are correct.
Form shows then submissions fail with 403Site or form was disabled after loadSubmissions are rejected when site/form is disabled; re-enable in dashboard. Config responses are no-store so a refresh should show the disabled state.

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 and LOCATION in hidden. TEAM defaults to umikoindia; PAGE_URL is auto-set from the current page (origin + path). DOMAIN is optional — when omitted, the server uses PAGE_URL for redirect and referer.

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')
DOMAINOptional. Source for redirect when set (hostname or path e.g. '/upcoming-pune/v/'). When not set, server uses PAGE_URL for redirect and referer

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. For multi-form, set siteKey in PulseGate.init({ siteKey }) so it is available to render().

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. When both siteKey (from PulseGate.init) and formId are set, returns a Promise that resolves with the instance(s) after the backend config is fetched.

OptionTypeDefaultDescription
targetstring | ElementRequired unless using backend form with target in form JSON. CSS selector (e.g. .lead-form, #lead-form) or DOM element. With multi-form you can omit and use backend target (default #lead-form).
formIdstringForm ID for multi-form/sites (e.g. enquiry_form). With siteKey set in init, triggers fetch from /forms/config and sends in submission.
siteKeystringfrom PulseGate.init()Override site key for this render; usually set once in PulseGate.init({ siteKey }).
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
usePulseGateStylesbooleantrueWhen false, no pg-* CSS is injected and no pg-* classes are added to the form. Use your site’s CSS only; validation/loading state uses data-invalid, data-loading, and inline display where needed.
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)
iconstringIcon class(es) for an icon inside the input (e.g. fas fa-user for Font Awesome). Renders as <span class="icon-inside"><i class="…"></i></span>; input wrapped in pg-input-wrap. Include your icon font on the page.

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)
DOMAINOptional (server uses PAGE_URL when unset)Redirect/referer source; set in hidden to override PAGE_URL
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 are what you usually set; PAGE_URL is auto-set, DOMAIN optional)
    hidden: {
      PROJECT: 'Sunset Villas',
      LOCATION: 'Pune',
      TEAM: 'umikoindia',                        // omit to use default
      SOURCE: 'WebSite'
      // DOMAIN: optional; omit to use PAGE_URL for redirect
    },

    // 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:

  • usePulseGateStyles — Set to false when you want no PulseGate CSS or pg-* classes on the form. The script will not inject any styles and will not add any pg-* classes to the markup. Use this when your site already has its own form styles and you want to avoid any class or style conflicts. Validation and loading state use data-invalid and data-loading attributes and inline display for error messages; you can style these in your CSS (e.g. input[data-invalid="true"], button[data-loading="true"]) if needed.
  • 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) — unless usePulseGateStyles is false, in which case no pg-* classes are added.
  • 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.

3. Use only your website CSS (no pg-* classes or styles)

Set usePulseGateStyles: false so the form does not add any PulseGate styles or pg-* classes. The markup uses only your inputClassName / submitClassName (and IDs) so it fits entirely into your site’s design system.

javascript
PulseGateForm.render({
  target: '.lead-form',
  usePulseGateStyles: false,
  inputClassName: 'form-control',
  submitClassName: 'btn btn-primary',
  showLabels: true,
  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: 'Submit'
});
  • Validation state: invalid inputs get data-invalid="true". You can style them with input[data-invalid="true"], select[data-invalid="true"], etc.
  • Loading state: the submit button gets data-loading="true" while submitting; the spinner element (id {formId}-spinner) is shown via inline display. Style button[data-loading="true"] if you want to reflect loading in your CSS.
  • Error messages: inline error text and the error banner are shown/hidden with inline display; no pg-* classes are used.

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() }
  • Multi-form (siteKey + formId): Returns a Promise that resolves with the above after config is fetched.

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


Quick reference

GoalCode
Simplest embedPulseGate.init(); then PulseGateForm.render({ target: '.lead-form', hidden: { PROJECT: '…', LOCATION: '…' }, submitText: 'Enquire Now' });
Multi-form (schema from dashboard)PulseGate.init({ siteKey: 'pk_live_xxx' }); then PulseGateForm.render({ formId: 'enquiry_form', target: '#lead-form', submitText: '…' }); (add domain in dashboard)
Override endpointPulseGate.init({ endpoint: 'https://…' }); or PulseGateForm.configure({ endpoint: '/lead.capture' });
No redirect (ajax)submitMode: 'ajax' in configure or render
Hide labels / use your CSSshowLabels: false or usePulseGateStyles: false with inputClassName / submitClassName