Ad rails for agent-sourced leads

What reaches your ad platforms and GA4 when an agent-sourced request is completed: the AIAgentAction pixel event and its server mirror, lead-funnel conversions with value, GA4 generate_lead with agent_sourced, and the per-vertical policy.

Ad rails for agent-sourced leads

When a request on your site is completed, confirmed by the person on your card or by their own agent with their go-ahead, the lead is already in your inbox, and on its way to your CRM if you subscribe a lead webhook. This page covers the third place it can go: your ad platforms and your analytics, so the leads an agent brought you show up as conversions you can bid on and as a source you can compare with the rest.

Everything here runs from the same step that completes the lead. Nothing is sent before the request is complete, and nothing the person typed is sent at all.

What crosses, and what never does

Every event below carries the same seven fields, and only these:

FieldValuesDerived from
tool_namerequest_quote, request_consultation, book_appointment, request_callback, check_fit, get_pricing, start_trial, book_demo, otherthe tool the agent called, folded into this fixed list
service_categoryyour vertical slug (health verticals: the family only, e.g. elective-health)your configured vertical
urgency_bucketnow, this_week, this_month, planningthe urgency, timeline or deadline the person stated
timing_buckettoday, this_week, this_month, flexiblethe window they asked for
value_bandlow, mid, high, unknownthe value estimate against your vertical’s thresholds
agent_clientchatgpt, claude, gemini, copilot, perplexity, browser_webmcp, bridge, express, otherthe agent that acted
agent_sourcedtruealways, so you can report agent-sourced leads separately

The lead reference, the person’s name and contact details, what they wrote, the facts they stated and any dollar figure never cross. The allowlist is enforced in code: a build with a field outside this list fails its tests.

Level 1: AIAgentAction, on the page and from the server

When the request is completed, the beacon fires one event to every pixel already on your page, and the server sends the same event through the platform’s server API with the same event_id, so each platform keeps exactly one. A health practice’s page fires none, and a criminal-defense request sends nothing at all (see the per-vertical policy below). When the person’s own agent completed the request from its own servers, there is no page to fire on, so only the server half runs:

PlatformPage (beacon)Server (mirror)Dedup key
Metafbq('trackCustom', 'AIAgentAction', …, { eventID })Conversions APIeventID / event_id
TikTokttq.track('AIAgentAction', …, { event_id })Events APIevent_id
Google (gtag)ai_agent_action event——
Microsoft UETai_agent_action event——

event_id is ia_ followed by 16 hex characters: an opaque, deterministic key derived from your account, the lead and the funnel stage. It is derived, not stored, so a retried confirm or a later catch-up sweep produces the same key and cannot double count.

You need: the beacon on the page with data-pixel-enrichment (the same opt-in that enables the AIResearchVisit enrichment event), your pixels loaded, and Meta or TikTok connected in Audience Studio for the server mirror. Without the connection the page half still fires.

Level 2: lead-funnel conversions with value

Each funnel stage is uploaded as a conversion so the platforms can optimize toward the leads that turn into business, not just the ones that submit:

StageFires whenGoogle AdsMetaTikTokMicrosoft
lead_submittedthe request is completedSubmit lead formLeadSubmitFormSubmit lead form
lead_qualifiedcompletion, when the qualification verdict is “may qualify”Qualified leadQualifiedLeadQualifiedLeadQualified lead
bookeda booking lands, or you report a booked outcomeBook appointmentScheduleScheduleBook appointment
closed_wonyou report a closed_won outcomeConverted leadPurchaseCompletePaymentConverted lead

Value. The value you report with the outcome is used as is. Before that, the value is your vertical’s estimate weighted by the qualification score; it is logged in shadow until you turn value weighting on.

Google Ads conversion actions. Name your conversion actions exactly as in the table, or map each stage to an action id in your account settings under ads.google_conversion_actions ({ "lead_submitted": "customers/…/conversionActions/…", … }).

Matching. A conversion matches on the click id captured when the person arrived (gclid, fbclid, ttclid, msclkid) or, for Enhanced Conversions for Leads, on the hashed email or phone the person confirmed. Hashed identifiers are attached only when the person granted marketing consent at confirm (email_marketing for the email hash, sms_marketing for the phone hash), or when your account records that your contact consent covers conversion measurement (ads.enhanced_conversions_consent = "contact"). No consent, no identifiers; the click id alone still matches. An agent’s relay never grants marketing consent, so a request completed by the person’s own agent carries hashed identifiers only under ads.enhanced_conversions_consent = "contact".

Level 5: GA4 generate_lead with agent_sourced

The server sends your own GA4 property one Measurement Protocol event per stage, so agent-sourced leads sit next to every other source in the reports you already use:

StageGA4 event
lead_submittedgenerate_lead
lead_qualifiedqualify_lead
bookedworking_lead
closed_wonclose_convert_lead

Each carries the seven fields above plus lead_stage, event_id, and value / currency when a value exists. Register agent_sourced (and any of the buckets you want to slice by) as event-scoped custom dimensions in GA4 Admin and the lead-source split appears in Explorations. The event’s client_id is the _ga cookie id the page relays at confirm, so the lead attaches to the visitor’s existing session; without the cookie a stable synthetic id is used per lead.

Where the credentials go. GA4 needs a Measurement ID (G-XXXXXXXXXX) and a Measurement Protocol API secret (GA4 Admin → Data streams → your web stream → Measurement Protocol API secrets → Create). Both live on your account settings under ga4:

{ "measurement_id": "G-XXXXXXXXXX", "api_secret": "…", "debug": false }

api_secret_encrypted (the secret stored encrypted at rest) is preferred over the plaintext field when both are present. The API secret can only post events to that stream; it cannot read anything. There is no dashboard field for this yet: send both values to your Inception Agents contact and they are set on your account. Set debug: true to route events to GA4’s validation endpoint while you check them in DebugView (validation-only: those events are not recorded).

Per-vertical policy

What the rails may do follows your vertical, and the protected classes cannot be widened by configuration:

Vertical classLead conversionsEnhanced ConversionsAudience policyNetwork mirror
Health (HIPAA-tagged verticals and the elective-health family)Google Ads only; no Meta, TikTok or Microsoft lower-funnel events, and no AIAgentAction on the page’s pixelswith consentnoneoff
Housing (fair-housing verticals: multifamily, senior living)every platformwith consentnoneoff
Law firmsGoogle Ads always; Meta, TikTok and Microsoft when the vertical allows lower-funnel eventswith consentpractice area and location onlythe practice area alone
Criminal defense (the practice, and any criminal-defense request at a law firm)none: nothing reaches an ad platform or GA4nonenoneoff
Everything elseGoogle Ads always; Meta, TikTok and Microsoft when the vertical allows lower-funnel eventswith consentas configured for the verticalon

The audience column records policy only: the rails on this page build no audience. An unknown or unreadable vertical resolves to the conservative default (Google Ads only, no audiences). Health and housing verticals never mirror to the shared Inception Agents network pixels, because a service category on a shared pixel is itself a protected signal. A law firm’s network events carry only the slug of one of its own practices (legal-family), never the words of a query or a place, and a firm that practises criminal defense sends nothing.

Verify it

  1. The lead’s ledger. Every completed stage appends an ad_rails_ran event to the lead’s event history with the event_id, the policy class and a per-platform result (synced, skipped:<reason>, failed:<reason>), so you can see exactly what fired for a given lead.
  2. Meta Test Events. With the beacon and Conversions API both connected, confirm a request of your own on the card on your page: Events Manager → Test Events shows one browser event and one server event for AIAgentAction with the same event_id, deduplicated. (The go-live live test is flagged TEST and never reaches ad platforms, so it does not show here.)
  3. GA4 DebugView. With debug: true, complete a request of your own and open DebugView: generate_lead arrives with agent_sourced = true and the buckets as parameters.
  4. Lead-source P&L. get_trace_attribution (and GET /api/v1/trace/attribution) returns a leadSources table — agent_webmcp, agent_bridge, agent_call, paid, organic, direct — with leads, verified, booked, closed-won and value per source, so agent-sourced leads can be compared with your other channels. agent_bridge also counts leads from your hosted /mcp and your express pages.