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()andrender()are merged — per-render values win on conflicts. - Default submit mode is traditional POST — browser follows the lead API’s
301redirect 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
| Script | Use case |
|---|---|
pulse.js | Recommended. Tracking + form in one; call PulseGate.init({ siteKey }) so endpoint and site key are set. |
form.js + tracker.js | Separate 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.
<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:
<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):
- Create a site and note its site key (e.g.
pk_live_xxx). - Add authorized domains for that site (e.g.
https://yoursite.com,yoursite.com, orlocalhostfor dev). The requestOriginorRefererhost must match. - 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 yourrender()options (render wins on conflict). - Ensure the site and form are enabled (dashboard toggles). If disabled,
/forms/configand/lead.capturereturn 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.
<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):
| Symptom | Cause | Fix |
|---|---|---|
| "Form unavailable." or 403 on config | Domain not allowed or site/form disabled | Add 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 error | Check console; ensure worker URL and CORS are correct. |
| Form shows then submissions fail with 403 | Site or form was disabled after load | Submissions 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.
<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:
<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.
| Field | Type | Description |
|---|---|---|
FIRSTNAME | visible | Full name |
EMAIL | visible | Email address |
COUNTRYCODE | visible | Country dial code (selector; enabled by default) |
PHONE | visible | Phone number |
TEAM | hidden | Team assignment (default 'umikoindia' if not set in configure() / render()) |
Configurable hidden fields (set per form) — Pass these via hidden in configure() or render():
| Field | Description |
|---|---|
LOCATION | Location or site (e.g. 'Whitefield, Bangalore') |
PROJECT | Project or campaign name (e.g. 'Sky Towers') |
DOMAIN | Optional. 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.
Google Ads & Campaign Tracking
The script auto-captures campaign parameters from the page URL:
| Parameter | Source | Description |
|---|---|---|
gclid | Google Ads | Google Click Identifier |
gbraid | Google Ads | App-to-web measurement |
wbraid | Google Ads | Web-to-app measurement |
utm_source | Any | Campaign source |
utm_medium | Any | Campaign medium |
utm_campaign | Any | Campaign name |
utm_term | Any | Campaign keyword |
utm_content | Any | Campaign content variant |
fbclid | Meta Ads | Facebook Click Identifier |
msclkid | Bing Ads | Microsoft Click Identifier |
KEYWORD auto-resolution (when not set in hidden fields):
utm_termpresent → uses its valuegclid/gbraid/wbraid→google-adsfbclid→facebook-adsmsclkid→bing-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().
| Option | Type | Description |
|---|---|---|
endpoint | string | With pulse.js, omit to use default /lead.capture on script origin. Otherwise: full URL or path (e.g. '/lead.capture') resolved against script origin. |
hidden | object | Common hidden fields (SOURCE, TEAM, etc.) |
theme | object | Visual customization |
submitText | string | Default button label |
submitMode | string | Default submit mode |
showBranding | boolean | Show "Powered by PulseGate" |
inputClassName | string | Extra CSS class(es) applied to all inputs (for host-site styling) |
showLabels | boolean | Whether to render field labels (default true); set false to hide and control layout via CSS |
submitClassName | string | Extra 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.
| Option | Type | Default | Description |
|---|---|---|---|
target | string | Element | — | Required 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). |
formId | string | — | Form ID for multi-form/sites (e.g. enquiry_form). With siteKey set in init, triggers fetch from /forms/config and sends in submission. |
siteKey | string | from PulseGate.init() | Override site key for this render; usually set once in PulseGate.init({ siteKey }). |
hidden | object | {} | Per-form hidden fields (merged with global). Use for LOCATION, PROJECT, DOMAIN, or to override TEAM. |
fields | array | FIRSTNAME, EMAIL, COUNTRYCODE, PHONE | Visible field definitions; add or replace as needed |
countryCode | object | { enabled: true } | Country dial code field; set enabled: false to disable |
submitMode | string | 'redirect' | 'redirect' = traditional POST, 'ajax' = XHR |
title | string | — | Form heading |
subtitle | string | — | Subheading |
submitText | string | 'Submit' | Button label |
loadingText | string | 'Submitting...' | Button text during submit |
successTitle | string | 'Thank You!' | Success heading (ajax mode) |
successMessage | string | auto | Success text (ajax mode) |
theme | object | — | Visual customization (merged with global) |
showBranding | boolean | true | Show branding footer |
inputClassName | string | — | Extra CSS class(es) on all inputs (e.g. 'my-input' or 'form-control input-lg') for host-site styling |
showLabels | boolean | true | Whether to show field labels; set false to hide labels (form gets class pg-form-no-labels for your CSS) |
submitClassName | string | — | Extra CSS class(es) on the submit/CTA button (e.g. 'btn btn-primary') for host-site styling |
usePulseGateStyles | boolean | true | When 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. |
onBeforeSubmit | function | — | Return false to cancel submit |
onSuccess | function(data) | — | Called after submit |
onError | function(error) | — | Called on failure (ajax mode) |
Field Definition
| Property | Type | Default | Description |
|---|---|---|---|
name | string | — | Required. POST field name (FIRSTNAME, EMAIL, etc.) |
label | string | — | Required. Visible label |
type | string | 'text' | text, email, tel, number, select, textarea |
required | boolean | false | Must have value |
placeholder | string | — | Placeholder text |
minLength | number | — | Min characters |
maxLength | number | — | Max characters |
pattern | string | — | Custom regex |
validationMessage | string | 'Invalid format' | Pattern error message |
options | array | — | For select: string[] or { value, label }[] |
rows | number | — | For textarea: visible rows |
defaultValue | string | — | Pre-filled value or default selected option |
className | string | — | Extra CSS class(es) for this field’s input (in addition to global inputClassName) |
icon | string | — | Icon 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.
| Property | Type | Default | Description |
|---|---|---|---|
enabled | boolean | true | Include country code field; set to false to disable |
required | boolean | false | Mark country code field required |
defaultValue | string | from locale | Selected dial code (+1, +91, etc.); omit to use locale-based default |
position | string | 'beforePhone' | 'beforePhone' or 'afterPhone' |
name | string | 'COUNTRYCODE' | Field name sent to API |
label | string | 'Country Code' | Label text |
placeholder | string | 'Select country code' | Select placeholder |
options | array | built-in global list (alphabetical) | Custom dial-code options |
className | string | — | Extra CSS class(es) for the country code select only (e.g. a narrower dropdown); merged with global inputClassName |
Common Hidden Fields
| Field | Default / behaviour | Purpose |
|---|---|---|
TEAM | Default 'umikoindia' | Team assignment; override in configure() or render() if needed |
LOCATION | Configurable | Set in hidden (e.g. 'Whitefield, Bangalore') |
PROJECT | Configurable | Set in hidden (e.g. project or campaign name) |
DOMAIN | Optional (server uses PAGE_URL when unset) | Redirect/referer source; set in hidden to override PAGE_URL |
SOURCE | Optional | Lead source (e.g. 'WebSite', 'FB'); set in hidden |
COUNTRYCODE | From visible selector | Sent from the country code field |
KEYWORD | Auto-detected from URL | Marketing keyword (from UTM/gclid/fbclid etc.) |
Theme Options
| Property | Default | Description |
|---|---|---|
primaryColor | #2563eb | Button and focus color |
borderRadius | 8px | Corner rounding |
fontFamily | inherit | Font stack |
backgroundColor | #ffffff | Form background |
textColor | #1f2937 | Input text color |
labelColor | #374151 | Label color |
borderColor | #d1d5db | Input border |
errorColor | #dc2626 | Error color |
successBg | #f0fdf4 | Success background |
successColor | #166534 | Success text |
Complete configuration sample
Below is a single example that includes every configuration option. Copy it and remove or change only what you need.
<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):
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 tofalsewhen you want no PulseGate CSS orpg-*classes on the form. The script will not inject any styles and will not add anypg-*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 usedata-invalidanddata-loadingattributes and inlinedisplayfor 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 tofalseto hide labels and control layout entirely with your CSS (e.g. placeholders only). When labels are hidden, the form wrapper gets the classpg-form-no-labelsso you can target it (e.g. reduce spacing) — unlessusePulseGateStylesisfalse, in which case nopg-*classes are added.- Per-field
className— On any field object, setclassNameto add extra class(es) to that field’s input only. countryCode.className— In thecountryCodeconfig, setclassNameto style the country code select separately (e.g.countryCode: { enabled: true, className: 'country-code-select' }).
Example: match a Bootstrap-style site and hide labels:
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:
<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:
.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.
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 withinput[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 inlinedisplay. Stylebutton[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; nopg-*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
| Goal | Code |
|---|---|
| Simplest embed | PulseGate.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 endpoint | PulseGate.init({ endpoint: 'https://…' }); or PulseGateForm.configure({ endpoint: '/lead.capture' }); |
| No redirect (ajax) | submitMode: 'ajax' in configure or render |
| Hide labels / use your CSS | showLabels: false or usePulseGateStyles: false with inputClassName / submitClassName |