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.
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 plugin | Eager form discovery | Notes |
|---|---|---|
| Fluent Forms | Yes | Full list pulled from the Fluent forms table. Shares one funnel key with the browser engine for drop-off analytics. |
| Kadence Blocks Form | Lazy | The 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 Form | Yes | The standalone form post type in Kadence Pro. Full list discovered up front. |
| Contact Form 7 | Lazy | The your- field-name prefix is stripped before the PII heuristic runs, so your-email still matches. |
| WPForms | Lazy | Binds the WPForms process-complete hook. |
| Gravity Forms | Lazy | Binds the Gravity after-submission hook. |
| Elementor Pro Forms | Lazy | Covers both the classic Elementor Pro form widget and the newer Elementor V4 atomic form. |
| Ninja Forms | Lazy | Resolves 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:
- 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.
- 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
Landfield you want mapped to country, which the built-in heuristic leaves alone on purpose to avoid false matches likeDeutschlandorLandkreis). - 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 type | GA4 | Meta CAPI | TikTok |
|---|---|---|---|
| Lead (default) | generate_lead | Lead | Lead |
| Contact | contact | Contact | Contact |
| Application | submit_application | SubmitApplication | SubmitForm |
| Newsletter / Subscribe | subscribe | Subscribe | Subscribe |
| Registration | sign_up | CompleteRegistration | CompleteRegistration |
| Appointment | schedule | Schedule | BookAppointment |
| Trial | start_trial | StartTrial | StartTrial |
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 singlef/m, date of birth toYYYYMMDD, 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_leadfor GA4 andLeadfor 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_idat 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:
- Per-field skip to exclude one field's value while still tracking the lead (the skip marker above).
- Per-form whitelist to track only specific forms of a plugin.
- 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).