Make your paid budget go further
Every ad click, form fill, quote and closed deal lands on one contact timeline — and the sale goes back to Meta server-side, with its value and the original click attached. Your campaigns optimize toward buyers, not clickers.
First-party click capture · Real values · Counted exactly once
You pay per click, but the platform optimizes on whatever you feed it. Feed it form-fills and it finds form-fillers. Between the click and the sale sits your CRM — and the ad platform never hears about it. That's spending on heads-or-tails.
Click in, sale out, nothing invisible in between
Each hop is an event on the same contact. The value that comes out the bottom is attributable to the click that went in the top.
- 01Ad click@hogsend/js fires campaign.arrived — fbclid, UTMs, the real timestamp
- 02Leadany form vendor's webhook → lead.submitted, stitched to the browser session
- 03DealCRM stages map to canonical ones; quoted and sold mint valued events
- 04ConversiondefineConversion matches the event — recorded once, value resolved
- 05Feedbackdelivered to Meta CAPI with the original click and a dedup id, retried until received
Pennies in, nothing leaks
Hogsend already runs your lifecycle marketing, so every touchpoint between click and revenue is on record: the landing, the emails they opened on day three, the SMS they clicked, the quote, the close. The same events that drive your journeys are the attribution evidence — each penny lands in the bucket instead of riding another coin flip.
A conversion point is a committed file
Declare which events count, what they're worth, and where they dispatch — reviewed like any other change.
import { defineConversion } from "@hogsend/engine";
export const dealSold = defineConversion({
id: "deal-sold",
trigger: { event: "deal.sold" },
destinations: ["meta-capi"],
// value defaults to the event's own first-class value;
// browser (pk_) events are rejected — values can't be forged
});
export const bigQuote = defineConversion({
id: "big-quote",
trigger: {
event: "deal.quoted",
where: (b) => b.prop("value").gte(10_000),
},
destinations: ["meta-capi"],
});The trigger uses the same condition builder journeys use — the event's first-class value is visible to it, so “only quotes over £10k” is one line.
import { createMetaCapiDestination } from "@hogsend/plugin-meta-capi";
const client = createHogsendClient({
journeys,
conversions,
conversionDestinations: [
createMetaCapiDestination({
pixelId: process.env.META_PIXEL_ID!,
accessToken: process.env.META_CAPI_TOKEN!,
}),
],
});The token is self-serve from Meta's Events Manager — no app review. Fired conversions are recorded durably, then delivered by a retrying task with a deterministic dedup id.
The whole loop, first-party
No third-party pixel dependency, no black-box attribution vendor. Every piece runs in your deployment and writes to your database.
Value on events
Any event can carry a monetary worth — a real column, not a property convention. Rollups are per-currency, never cross-summed, and your PostHog mirror sees the same numbers.
First-party click capture
Landings with fbclid, gclid, ttclid and eight more (or any utm_*) fire campaign.arrived automatically, deduped per session, persisted as last-touch.
Lead intake from any form
Heyflow, Perspective, Webflow, your own React form — one webhook source turns any vendor's POST into lead.submitted, identity-stitched to the ad click via hidden fields.
Deal funnels, event-native
defineFunnel gives you ordered stages that advance on your own events — trial.started, quote.sent, subscription.created — with money milestones that mint deal.quoted and deal.sold. Compose a CRM leg with crmPipeline, or run without one.
The deals ledger
CRM stages map onto canonical ones — lead, contacted, survey booked, quoted, sold — monotonically, so a late webhook never regresses a closed deal. Quoted and sold mint valued events.
Conversion points
defineConversion declares which events count, with a forged-value guard and three value sources. Fired instances are recorded once per event — replays can't double-fire.
Meta CAPI, done properly
Hashed identifiers per Meta's spec, fbc rebuilt from the real stored click (never fabricated), CRM-grade action_source, and a deterministic event_id reused across retries.
Revenue front and center
The Deals view puts the money where you can see it: sold last 30 days and lifetime, open pipeline, average order value, and time-to-close — per currency — over a board of every deal grouped by canonical stage. Contacts filter by minimum revenue and current deal stage, so “quoted over £10k and gone quiet” is a filter, not a spreadsheet afternoon.
Every model, computed once, kept forever
When a conversion fires, Hogsend writes a credit ledger across all eight attribution models — first-touch, last-touch, linear, time-decay, position-based and more — from the contact’s real touchpoint path. Switching lenses in reporting is instant and never re-derived. Studio keeps three numbers honest and separate: Attributed (fractional credit that sums to the real total), Influenced (reach, which never does), and Incremental (holdout-backed lift — the only number that says “caused”). An Unattributed bar shows what no touchpoint can explain, instead of hiding it.
Questions, answered
Short answers here; the docs go deeper.
Go deeper
The browser SDK captures them first-party the moment someone lands from an ad — fbclid, gclid, ttclid and eight more, with the real click timestamp — as a campaign.arrived event. At conversion time the engine recovers the contact's latest one and hands it to the destination.
Close the loop on revenue
Deploy Hogsend, point your lead form at it, and send Meta the sale — not the click.
Free to self-host · One scaffold command · No per-contact billing
pnpm dlx create-hogsend@latest my-app