From 9400e81aaaf151db3624c5152236179885e5b2d9 Mon Sep 17 00:00:00 2001 From: Elijah Duffy Date: Thu, 18 Dec 2025 17:12:57 -0800 Subject: [PATCH] pixel: integrate conversion API --- src/lib/MetaPixel.svelte | 71 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/src/lib/MetaPixel.svelte b/src/lib/MetaPixel.svelte index c5d127b..e2d6106 100644 --- a/src/lib/MetaPixel.svelte +++ b/src/lib/MetaPixel.svelte @@ -16,6 +16,12 @@ using the MetaPixel component. * to prevent them from polluting real analytics data. */ testEventCode?: string; + /** + * if provided, all events fired will be passed to the server endpoint + * at this URL to be sent via the Conversion API. Any events sent + * without a event ID will be assigned a random one. + */ + conversionHref?: string; /** Advanced matching data */ advancedMatching?: AdvancedMatching; /** Initialization options */ @@ -26,6 +32,7 @@ using the MetaPixel component. private _pixelID: string; private _testEventCode?: string = undefined; private _trackingManager: MaybeGetter; + private _conversionClient?: ConversionClient = undefined; private static _baseLoaded: boolean = false; private static _registeredPixels: Record = {}; @@ -54,6 +61,18 @@ using the MetaPixel component. this._trackingManager = trackingManager; this._pixelID = pixelID; this._testEventCode = options?.testEventCode; + + const resolvedTrackingManager = resolveGetter(trackingManager); + if (options?.conversionHref && resolvedTrackingManager) { + this._conversionClient = new ConversionClient( + options.conversionHref, + resolvedTrackingManager + ); + } else if (options?.conversionHref) { + log.warn( + `Conversion Client ${options.conversionHref} for Meta Pixel [${this._pixelID}] not initialized - TrackingManager is required for user consent.` + ); + } } /** Loads the Meta Pixel base script. */ @@ -154,9 +173,34 @@ using the MetaPixel component. */ pageView() { if (!this.consentGuard()) return; - // Send the PageView event + + let eventID: string | undefined = undefined; + // Optionally, send to conversion API endpoint if configured + if (this._conversionClient) { + eventID = crypto.randomUUID(); + this._conversionClient + .trackEvent('PageView', { eventID }) + .then((response) => { + log.debug( + `Meta Pixel [${this._pixelID}] PageView event sent to Conversion API with Event ID: ${eventID}, Response: ${JSON.stringify( + response + )}` + ); + }) + .catch((error) => { + log.error( + `Meta Pixel [${this._pixelID}] Failed to send PageView event to Conversion API with Event ID: ${eventID}`, + error + ); + }); + } + + // Send the PageView event to Meta if (!dev || this._testEventCode) { - window.fbq('track', 'PageView', undefined, { test_event_code: this._testEventCode }); + window.fbq('track', 'PageView', undefined, { + test_event_code: this._testEventCode, + eventID + }); log.debug( `Meta Pixel [${this._pixelID}] PageView event sent${dev && ` (test code: ${this._testEventCode})`}.` ); @@ -173,6 +217,28 @@ using the MetaPixel component. */ track(event: K, params?: EventParamsByName[K], eventID?: string) { if (!this.consentGuard()) return; + + // Optionally, send to conversion API endpoint if configured + if (this._conversionClient) { + eventID = eventID ?? crypto.randomUUID(); + this._conversionClient + .trackEvent(event, { eventID: eventID, customData: params as any }) + .then((response) => { + log.debug( + `Meta Pixel [${this._pixelID}] ${event} event sent to Conversion API with Event ID: ${eventID}, Response: ${JSON.stringify( + response + )}` + ); + }) + .catch((error) => { + log.error( + `Meta Pixel [${this._pixelID}] Failed to send ${event} event to Conversion API with Event ID: ${eventID}`, + error + ); + }); + } + + // Send the PageView event to Meta if (!dev || this._testEventCode) { window.fbq('trackSingle', this._pixelID, event, params, { eventID, @@ -228,6 +294,7 @@ using the MetaPixel component. import { resolveGetter, type MaybeGetter } from './util/getter.ts'; import log from 'loglevel'; import { dev } from '$app/environment'; + import { ConversionClient } from './conversion/client.ts'; interface Props { /**