Lead intake
Any form vendor → the canonical lead.submitted event — identity-stitched to the visitor's ad-click session, value-bearing, idempotent across webhook retries.
Hogsend deliberately ships no form builder. Any form tool that can POST a
webhook — Heyflow, Perspective, Framer forms, Webflow, Typeform, a custom
React form — becomes a lead source in three steps. The result is the canonical
lead.submitted event: identity-stitched to the visitor's browser session
(and its campaign.arrived ad-click
touchpoints), optionally value-bearing, and idempotent across vendor webhook
retries.
The three pieces
1. @hogsend/js plants the attribution. On an attributed landing (a click
ID or utm_* in the URL) the browser SDK fires campaign.arrived
automatically and persists the attribution set as last-touch.
hogsend.getAttributionFields() returns a flat map to copy into the form's
hidden fields: hs_anonymous_id, any click IDs (fbclid, gclid, …),
utm_*, hs_landing_page, hs_captured_at.
2. A webhook source receives the submission. defineWebhookSource +
buildLeadSubmission (both exported by @hogsend/engine) normalize the
vendor payload into an ingestable event:
import { buildLeadSubmission, defineWebhookSource } from "@hogsend/engine";
export const leadFormSource = defineWebhookSource({
meta: {
id: "lead-form",
name: "Lead form",
auth: { header: "x-lead-form-secret", envKey: "LEAD_FORM_WEBHOOK_SECRET" },
},
transform: (payload) =>
buildLeadSubmission({ payload: payload as Record<string, unknown> }),
});Registered like any webhook source, it's
served at POST /v1/webhooks/lead-form with shared-secret header auth.
3. buildLeadSubmission splits the fields. hs_anonymous_id becomes the
anonymousId identity key — the email-anchored contact adopts the browser
session, so pre-submit ad clicks and the lead land on one contact. Click
IDs and utm_* ride as event properties under the same names
campaign.arrived uses; the remaining fields are stored as form answers; and
value/currency (e.g. from a quote-calculator step) ride first-class on the
event, so the lead is revenue-trackable from the very first touch.
The generic recipe (any vendor)
- Render the form with hidden inputs populated from
hogsend.getAttributionFields()(SPA: populate on mount; static HTML: a two-line inline script). - Point the vendor's webhook at
https://<your-api>/v1/webhooks/lead-formwith the headerx-lead-form-secret: $LEAD_FORM_WEBHOOK_SECRET. - Map the vendor's payload to a flat field map (most vendors already POST one):
{
"email": "lead@example.com",
"phone": "+447700900123",
"name": "Jane Doe",
"submission_id": "vendor-submission-uuid",
"value": 12500, // e.g. the calculator's quote estimate
"currency": "GBP",
"own_home": "yes", // any remaining fields = form answers
"property_type": "detached",
"hs_anonymous_id": "…", // ← from getAttributionFields()
"fbclid": "…",
"utm_source": "facebook",
"hs_landing_page": "https://example.com/solar/quote",
"hs_captured_at": "2026-07-12T09:00:00.000Z"
}submission_id dedups vendor retries (it becomes the
lead-submitted:<id> idempotency key). A payload with neither email nor
hs_anonymous_id is skipped — there is no identity to attach the lead to.
Vendor notes
- Heyflow — native webhooks POST a flat answers map; hidden fields are
"system fields"/URL-parameter fields, so
getAttributionFields()values can also arrive via URL params appended to the flow link. Partial-submit webhooks work with the same source (send a distinctsubmission_id). - Perspective — fires its webhook when a visitor converts to a lead; UTMs
and click IDs pass through hidden fields populated from the embedding
page's URL (Perspective forwards URL params). If the funnel lives on the
vendor's domain, append
getAttributionFields()as URL params on the CTA that links to the funnel. - Custom form (same page) — don't
hogsend.capture("lead.submitted", …)from the browser. POST server-side to your own endpoint and forward to the webhook source: a browser-originated event ispk_-trust-tier, and money-bearing conversion points reject browser sources by default (the forged-value guard).
What you get downstream
- The contact exists (email-anchored) with the browser session attached —
campaign.arrivedtouchpoints, later email/SMS clicks, and the lead all on one timeline. - Journeys can trigger on
lead.submitted— speed-to-lead flows, qualification sequences, long-term nurture. - Declaring
lead.submittedas a conversion point feeds it back to ad platforms (Meta CAPI) with the recovered click evidence, so the platform optimizes toward people who become leads, not people who click. - The revenue spine picks up the
valuefor per-contact revenue and the deals ledger once a CRM stage confirms it.
Webhook Sources & Custom Workflows
Author inbound webhook sources that turn external HTTP payloads into Hogsend events, reach for a built-in preset, and write custom Hatchet tasks for background work.
Revenue tracking
First-class value on events, the deals ledger with canonical stages, per-contact revenue, and the Studio surfaces — know what every contact and campaign is actually worth.