Forms setup

Beaconry hooks the submission event of seven form plugins server-side and fires a lead conversion to every channel you have connected, with email, phone and any other recognised PII hashed before it leaves your server. No JavaScript glue, no per-form pixel snippets. Activate the plugin, toggle the form integration on, done.

Reading time: ~7 minLast updated: 2026-06-08

The seven adapters

Each form plugin gets a dedicated adapter that binds to that plugin's real submission hook (not a generic DOM listener). An adapter only registers itself when its host plugin is actually loaded, so an install with just Fluent Forms never carries Gravity code paths. All seven ship in every Beaconry build and are stable. There are no beta adapters.

Form pluginEager form discoveryNotes
Fluent FormsYesFull list pulled from the Fluent forms table. Shares one funnel key with the browser engine for drop-off analytics.
Kadence Blocks FormLazyThe inline block variant placed directly in a page. Appears in the mapping table after its first submission (block-scanning every post is too expensive to do eagerly).
Kadence Advanced FormYesThe standalone form post type in Kadence Pro. Full list discovered up front.
Contact Form 7LazyThe your- field-name prefix is stripped before the PII heuristic runs, so your-email still matches.
WPFormsLazyBinds the WPForms process-complete hook.
Gravity FormsLazyBinds the Gravity after-submission hook.
Elementor Pro FormsLazyCovers both the classic Elementor Pro form widget and the newer Elementor V4 atomic form.
Ninja FormsLazyResolves the form id from the Ninja submission payload.

The "eager discovery" column matters for setup ergonomics. Fluent and Kadence Advanced expose their full form list to a single query, so Beaconry can show every form in the mapping table the moment you open the Forms tab. The other plugins store forms in ways that are too costly to scan on every admin page load, so their rows appear after the first real submission. Tracking still works for those forms immediately, you just configure overrides after they have fired once.

Turning a form integration on

The Forms tab only appears in the Beaconry menu when at least one supported form plugin is active, so you never stare at an empty tab. Inside it, each detected plugin has a master toggle. Flip the toggle on and every form belonging to that plugin starts firing lead events on submit. There is nothing to paste into the form itself.

If you want to track only some of a plugin's forms, use the per-form whitelist on the same tab instead of the blanket toggle. An empty whitelist means "all forms"; adding entries narrows it to exactly those.

How auto-detection works

Beaconry never asks you to name your forms by hand. Two mechanisms keep the mapping table in sync with reality:

  1. Eager catalog sync. Every time the Forms tab renders, Beaconry asks each active adapter for its forms and merges them into the stored catalog. New forms get an empty default row, renamed forms get their label refreshed, and your existing per-form settings are left untouched. So a form you just built in Fluent or Kadence Advanced is configurable before it has ever been submitted.
  2. Lazy first-submission registration. For the plugins that can't be scanned cheaply (and for Kadence's inline block form), the first real submission registers the form. From that point it has a row in the table like any other.

Either way the form is keyed by a stable composite id (plugin_formid, sanitised), so the same form always maps to the same configuration row across renders and submissions.

The per-form field-mapping UI

When a visitor submits, Beaconry walks the submitted fields and tries to recognise the ones that are useful for advertiser match quality: email, phone, first name, last name, ZIP, city, state, country, gender, date of birth, and an external customer id. Recognition is a heuristic over the field's label and name, tuned for English, German and Spanish wording. So E-Mail, email_1, Ihre Mail-Adresse all resolve to the email slot; Vorname resolves to first name; PLZ and Postleitzahl resolve to ZIP.

The Forms tab renders a per-form table of every detected field next to what the heuristic resolved it to. Each row carries a dropdown so you can override the guess:

  • Leave it on the dynamic Auto (X) default to accept the heuristic.
  • Pick a different target to remap a field the heuristic mislabelled or missed (for example a German Land field you want mapped to country, which the built-in heuristic leaves alone on purpose to avoid false matches like Deutschland or Landkreis).
  • Pick Don't map to exclude a field entirely (see the next section).

Overrides are per-form, not global. The same field label can map to different slots in two different forms, and the row is annotated (auto) or (manual) so you can see at a glance which fields you have touched.

Separately from PII mapping, each form has a lead type. The default is a generic lead, but you can set a form to Contact, Application, Subscribe, Registration, Appointment, Trial, Download or Donation, or supply a fully custom event name per channel. The lead type is what decides the event name sent to each platform:

Lead typeGA4Meta CAPITikTok
Lead (default)generate_leadLeadLead
ContactcontactContactContact
Applicationsubmit_applicationSubmitApplicationSubmitForm
Newsletter / SubscribesubscribeSubscribeSubscribe
Registrationsign_upCompleteRegistrationCompleteRegistration
AppointmentscheduleScheduleBookAppointment
Trialstart_trialStartTrialStartTrial

The dispatcher resolves the matching event name for every connected channel from one type pick, so you choose the business meaning once and each platform receives the name it expects. An unset or unknown type falls back to a plain lead.

The manual skip marker

The PII heuristic is deliberately eager, because under-matching costs you advertiser match quality. But sometimes a field looks like PII and you do not want it forwarded. A "Reason for contact" field with the word "mail" in it, an internal reference number that pattern-matches the external-id rule, a free-text field that happens to contain the word "tel". For those, the field-mapping dropdown has a Don't map option.

Choosing it stores an explicit skip marker for that field on that form. The important part is that the marker is explicit: it does not just clear your override, it actively stops the built-in heuristic from re-matching the field as a fallback. So a field you skipped stays skipped even though its name still looks like email or phone. In the table it shows as a manual decision, not as an unmatched field, so you can tell "I excluded this on purpose" apart from "the heuristic found nothing here".

Use it whenever the heuristic is technically right about the wording but wrong about the intent. It is the clean alternative to renaming a form field just to dodge tracking.

What happens on submit

When a tracked form is submitted, Beaconry builds one canonical lead event and dispatches it through the same server-side forwarder every other event uses. Concretely:

  • The recognised fields are collected into a user-data block (em, ph, fn, ln, zp, ct, st, country, gender, date of birth, external id). Phone is normalised toward E.164, gender to a single f/m, date of birth to YYYYMMDD, all before hashing.
  • Every channel you have connected receives the event, each with the PII SHA-256 hashed per that channel's matching spec. The raw values never leave your server. A channel you have not connected is simply skipped.
  • The event name per channel comes from the form's lead type (see the table above). By default that is generate_lead for GA4 and Lead for the ad platforms.
  • The stable form key is attached as the content id, so Meta and TikTok stop warning about missing content details and you get a clean per-form audience to build on. The Beaconry lead-type label is recorded internally for the Logs and Live-Dashboard "Source" column, but is deliberately not written into content_category (that field is reserved for real product-catalog categories from the commerce adapters).
  • If hybrid mode is on for a channel, the browser engine injects a shared event_id at submit time so the vendor deduplicates the browser and server copies of the lead. See Hybrid Mode.

Because the submission already happened in PHP (the visitor clicked submit), there is no separate consent re-check at this layer. Consent is enforced once, centrally, in the dispatcher, the same gate that covers commerce and custom events.

Don't want to forward a whole form?

Three levers, in order of bluntness:

  1. Per-field skip to exclude one field's value while still tracking the lead (the skip marker above).
  2. Per-form whitelist to track only specific forms of a plugin.
  3. Plugin master toggle off to stop tracking that plugin's forms entirely.

Want to know where visitors abandon?

Lead tracking tells you who converted. Form-funnel analytics tells you who started and dropped off, and on which field. It is a separate, opt-in feature on the same Forms tab that records form starts, abandons and lead submits per form, with field names and counts only (never field values). It is analytics-only by construction: an abandon reaches GA4 at most (and only when you turn that on), never an ad channel, and commerce checkouts are excluded because they have their own view-to-purchase funnel. Enable it under Beaconry → Forms → Form-Funnel and read the per-form drop-off table there (starts, leads, abandons, conversion rate, top drop-off fields).