Skip to content
Tracking SOP

Enhanced Conversions + Meta CAPI Setup

The on-site User-Provided Data variable, the server-side CAPI tag, the manual override that made it actually work, and the planned Event ID fix.

Enhanced Conversions User-Provided Data Variable (Google, on-site)

  1. 1.GTM (Web container) → Variables → New.
  2. 2.Variable type: User-Provided Data (a distinct built-in GTM variable type — not a Data Layer Variable).
  3. 3.Configuration method: Manual.
  4. 4.Map Email → the existing DLV - user_email Data Layer Variable, Phone Number → DLV - user_phone.
  5. 5.Name it UPD - Lead Contact Data. Save.
  6. 6.Build a separate Google Ads: User-provided Data Event tag (this field was removed from the client-side Conversion Tracking tag by Google in April 2025 — a separate tag is the only current method): Conversion ID via the existing Account ID constant, User-provided data: the UPD variable, Trigger: the same event trigger as the corresponding conversion tag, Tag Sequencing: set to fire before the main conversion tag.

Meta CAPI Tag (Server-Side)

  1. 1.Server container → Tags → New → Community Template Gallery → search "Facebook Conversion API" by stape-io specifically (not "Facebook Leads API," which is for a different, CRM-webhook-driven mechanism).
  2. 2.Event Name Setup Method: "Inherit from client."
  3. 3.Action Source: "Website."
  4. 4.Facebook Pixel ID: reference via a Constant variable, kept separate from the Web container’s own Pixel ID constant (two different containers).
  5. 5.API Access Token: reference via a Constant variable — critically, this variable must be created in the Server container, never the Web container.
  6. 6.Test ID: leave empty for normal operation; only populate temporarily during active testing, and always clear it again before considering the tag production-ready — this exact step was missed once even in this build (caught and fixed via a live GTM export check).
  7. 7.Checkboxes: leave "Generate _fbp cookie if it does not exist" and "Enable Event Enhancement" checked (defaults); leave "Use Optimistic Scenario" unchecked (this would mask real API failures by always reporting success).
  8. 8.Server Event Data Override / User Data / Custom Data sections: leave "Automap" checked on all three — but see the Manual Override section below, since Automap alone did not pick up this project’s custom-named parameters.
  9. 9.Tag Execution Consent Settings: intended to be set to "Send data in case marketing consent given," not the default "Send data always." The template also exposes its own internal adStorageConsent parameter (currently "required") as a related but distinct mechanism — confirm which one is actually governing live behavior before treating this as settled.
  10. 10.Triggering: build a dedicated Custom Event trigger scoped to the specific event as it arrives server-side (e.g., Event name field: generate_lead, condition: {{Event Name}} equals generate_lead) — never trigger this tag on "all events."
  11. 11.Prerequisite, easy to miss: the Web-side GA4 Event - Generate Lead tag must have user_email and user_phone added as Event Parameters (mapped to the existing DLV variables) — confirmed live, this is in place. Without it, the CAPI tag’s Automap has nothing real to match on, even though it fires without error.

Meta CAPI User Data Manual Override (the fix that made it actually work)

Automap alone did not pick up custom-named parameters — this explicit override is required alongside it, not instead of it.

  1. 1.Server container → Variables → New → Event Data (not Data Layer Variable — that type doesn’t exist in server containers; Event Data is the correct server-side equivalent).
  2. 2.Event data key: params.user_email (the params. prefix is required — GA4’s actual data structure nests custom parameters here, not at the top level).
  3. 3.Name it Event Data - user_email. Save.
  4. 4.Repeat: key params.user_phone, name Event Data - user_phone.
  5. 5.Open Meta - CAPI (Generate Lead) → User Data section.
  6. 6.Leave "Automap User Data" checked.
  7. 7.Click "Add property" twice: Property Name Email → Property Value {{Event Data - user_email}}; Property Name Phone → Property Value {{Event Data - user_phone}}.
  8. 8.Save → Submit → Publish.
  9. 9.Verify: fresh test lead with a temporary Test Event Code in place, check Meta’s Test Events tool — the resulting Lead event’s "User data keys" should now list Email address and Phone number alongside Browser ID/IP address/User agent. Remove the test code again afterward — confirmed live, this cleanup step has been completed.

Event ID Deduplication (Meta Pixel + CAPI) — recommended, not yet built

Currently, client-side Pixel and server-side CAPI events for the same real conversion are deduplicated by Meta via an implicit fallback (browser cookie matching) — working, but not the precise, recommended method. Confirmed live: no event_id configuration exists anywhere yet, on either the Pixel tag, the GA4 event, or the CAPI tag — this is genuinely not yet built, not a documentation gap.

  1. 1.GTM (Web container) → Variables → New → Custom JavaScript, or simpler: a Data Layer Variable if a unique identifier is already available in the existing generate_lead dataLayer event (check for the lead’s lead_id before building something new).
  2. 2.If reusing the existing lead_id: build a Data Layer Variable reading it directly — ties the Meta event_id to the same identifier already used elsewhere (Google Ads Transaction ID, LinkedIn eventId).
  3. 3.Add this variable to the Meta - Lead (Generate Lead) Pixel tag: More Settings → Event ID field.
  4. 4.On the server side: add the same identifier as a GA4 Event Parameter on GA4 Event - Generate Lead (e.g., event_id → the same lead_id value) — mirroring the pattern already used for user_email/user_phone.
  5. 5.On the Meta - CAPI (Generate Lead) server tag: under Server Event Data Override (or wherever the template exposes an explicit Event ID mapping), map it to read the incoming event_id parameter.
  6. 6.Test via Meta’s Test Events tool once more: confirm the "Deduplicated" event now shows a real, matching Event ID value in that column, rather than blank.