Setting Up User ID & Login Data for Cross-Device Attribution in GA4

Standard

Cross-device attribution helps you understand user behavior when the same person interacts across mobile, desktop, and other platforms. By implementing User ID tracking, you create a unified view of the customer journey—leading to better personalization, attribution accuracy, and conversion analysis.

🎯 Why Use User ID?

  • ✅ Recognize users across devices and browsers
  • ✅ Combine anonymous and authenticated sessions
  • ✅ Improve conversion funnel visibility
  • ✅ Reduce inflated user counts
  • ✅ Enable richer audience segmentation


✅ Prerequisites

  • GA4 property created
  • Web GTM and Server-Side GTM containers
  • Your platform supports user authentication (login)
  • Server-side endpoint or backend access to enrich user ID
  • Optionally: Consent Mode compliance


🔧 Step-by-Step Guide


🔹 Step 1: Make User ID Available on Login/Authenticated Pages

Expose the authenticated user ID from your server into the frontend via JavaScript or dataLayer.

Example (dataLayer push):

<script>
dataLayer.push({
  event: 'user_authenticated',
  user_id: 'USER_12345'
});
</script>

👉 Ideally fire this on all pages where the user is logged in (post-login, dashboard, checkout, etc.)

🔹 Step 2: Create a User ID Variable in Web GTM

  1. Go to Variables > New
  2. Type: Data Layer Variable
  3. Name: user_id
  4. Data Layer Variable Name: user_id
  5. Save as: DLV - user_id


🔹 Step 3: Pass User ID in GA4 Configuration Tag

Modify your GA4 Configuration Tag to include the user_id.

gtag('config', 'G-XXXXXXX', {
  user_id: 'USER_12345'
});

In GTM:

  • Edit GA4 Config Tag
  • Under Fields to Set:
    • Field Name: user_id
    • Value: {{DLV - user_id}}

✅ This automatically adds user_id to all GA4 events.


🔹 Step 4: Forward User ID to Server-Side GTM

If you’re using Server-Side GTM, your GA4 Config tag should point to:

https://gtm.yourdomain.com

GA4 event hits sent to this endpoint will now include user_id.

🔹 Step 5: Capture User ID in Server-Side GTM

In ssGTM, extract user_id from GA4 Client request:

Create a Variable:

  • Variable Type: Event Data
  • Variable Name: user_id

return event.data.user_id || request.body?.events?.[0]?.user_id;

🔹 Step 6: Forward to GA4 (Server-Side Event Tag)

In ssGTM:

  • Create a GA4 Event Tag
  • Set user_id as a User Property

User Properties:

Property Name Value
user_id {{Event Data - user_id}}

✅ Also attach to custom events like purchase, login, lead_submit, etc.

🔹 Step 7: (Optional) Sync User ID with CRM via HTTP Request

To connect GA4 activity to your CRM, you can send the user_id with event context:

Example HTTP POST Tag in ssGTM:

POST /user-event-sync
{
  "user_id": "USER_12345",
  "event": "purchase",
  "transaction_id": "ORD789",
  "timestamp": "2024-05-30T08:01:00Z"
}

✅ Use it to push activity into Salesforce, HubSpot, or your own backend.

🔹 Step 8: GA4 Reporting with User ID

Go to GA4 Admin > Reporting Identity
Set Blended or Observed (GA will combine User ID + Device ID)

Once set:

  • Go to User Explorer Report
  • You’ll see User ID-based journeys
  • Filter by segments like “logged-in users” or “multi-session buyers”


🔹 Step 9: Ensure Consent Compliance

If you use Consent Mode, make sure user_id is only sent after consent.

gtag('consent', 'update', {
ad_storage: 'granted',
analytics_storage: 'granted'
});

Only push user_id to dataLayer after consent granted.

🔹 Step 10: Debug and QA

  • Use GA4 DebugView to verify user_id
  • Use Chrome DevTools > Network > collect? to inspect payload
  • Use Server GTM Preview Mode to inspect forwarded user_id
  • Enable Realtime Reports > filter by device & user ID


🔒 Tips for Security and Privacy

  • Never expose PII (email, name, phone) in GTM or GA4
  • Use hashed values if needed (SHA256)
  • Sanitize and validate user ID before sending to GA
  • Apply Consent Mode & Privacy Policy disclosures

✅ Summary Table

Step Action
1 Add user_id to dataLayer
2 Create GTM variable
3 Add to GA4 Config tag
4 Route via Server GTM
5 Extract in ssGTM
6 Send as user property to GA4
7 (Optional) Sync with CRM
8 Enable GA4 Reporting Identity
9 Respect consent mode
10 Debug thoroughly

 

Preventing Duplicate Conversions in Server-Side eCommerce Setup

Standard

Duplicate conversions in eCommerce analytics can wreak havoc on attribution, ROAS measurement, and optimization strategies. When you’re using Server-Side Google Tag Manager (ssGTM) along with web tagging, the risk of double-counting conversions increases—especially when both the client and server send the same event.

🔍 Why Duplicate Conversions Happen

Typical causes of duplicate conversions:

  • GA4 web + server both send purchase
  • Google Ads fires both client-side and via ssGTM
  • Meta Pixel & CAPI firing same purchase without deduplication
  • Webhook or backend fires same conversion alongside GTM

Without deduplication logic, these can result in inflated revenue, misattributed campaigns, and inaccurate marketing spend analysis.

🛠 Prerequisites

  • GA4 Setup via Web & Server GTM
  • Meta Pixel + CAPI integration
  • Google Ads setup via both browser and server tags
  • Access to GTM web and server containers
  • Ability to modify your purchase event tracking code

🚀 Step-by-Step Implementation

✅ Step 1: Generate a Unique event_id on Purchase

To prevent duplication, generate a UUID-based event ID client-side and attach it to both web and server events.

<script>
function generateUUIDv4() {
  return ([1e7]+-1e3+-4e3+-8e3+-1e11)
    .replace(/[018]/g, c =>
      (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
    );
}

const purchase_event_id = generateUUIDv4();

dataLayer.push({
  event: 'purchase',
  transaction_id: 'ORD123',
  value: 129.99,
  currency: 'USD',
  event_id: purchase_event_id,
  items: [
    {
      item_id: 'SKU123',
      item_name: 'Product A',
      quantity: 1,
      price: 129.99
    }
  ]
});
</script>

✅ Step 2: Send event_id with Web GA4 Purchase Event

In Web GTM:

  • Event Name: purchase
  • Parameters:
    • transaction_id: {{DLV - transaction_id}}
    • value: {{DLV - value}}
    • currency: {{DLV - currency}}
    • event_id: {{DLV - event_id}}

✅ Ensure your GA4 Configuration tag points to Server Endpoint like:


https://gtm.yourdomain.com

✅ Step 3: Server-Side GTM: Handle Deduplication

In ssGTM, the GA4 client receives the purchase event. Now use event_id to filter duplicates.

🔹 Option A: Use Event Storage (via Firestore/Redis)

Create a custom template or use Cloud Function that stores each processed event_id for 7–30 days.

const eventId = request.body?.events?.[0]?.params?.event_id;

// Sample logic
if (isDuplicate(eventId)) {
  return {}; // Stop processing
} else {
  markAsProcessed(eventId);
  fireConversionTag();
}

Implement this in a Custom Client or Custom Template.

🔹 Option B: Deduplicate with GA4 Event Tag Trigger

Use a trigger in ssGTM that checks if event_id exists:

Custom Variable:

return request.body?.events?.[0]?.params?.event_id;

Trigger Condition:

event_name equals purchase
event_id does not equal (blank)

Tag only fires if event_id exists and has not been used before (via lookup logic).

✅ Step 4: Meta Pixel + CAPI Deduplication

Meta supports event_id for deduplication between browser pixel and server-side CAPI.

In server GTM, for Meta AddToCart or Purchase:

{
  "event_name": "Purchase",
  "event_time": {{Timestamp}},
  "event_id": "{{event_id}}",
  "user_data": {
    "em": "{{hashed_email}}"
  },
  "custom_data": {
    "value": {{value}},
    "currency": "{{currency}}",
    "content_ids": ["SKU123"]
  }
}

Facebook will deduplicate if both pixel and CAPI send the same event_id within 48 hours.

✅ Step 5: Google Ads Deduplication (Enhanced Conversions)

Use gclid and conversion_timestamp to deduplicate Enhanced Conversions server-side.

You can also use order_id (transaction_id) as the key.

In Google Ads server tag:

  • Pass transaction_id
  • Set logic in trigger to fire only if not already sent

Custom variable for order ID:

return request.body?.events?.[0]?.params?.transaction_id;

Optional: Add Firestore or Redis lookup to skip if already sent.

✅ Step 6: Debugging and Monitoring

  • Use GA4 DebugView to monitor duplicate events
  • Monitor Conversions API Diagnostics in Facebook Events Manager
  • Track server-side logs to identify repeated event IDs
  • Store all processed IDs in a DB or cloud bucket with timestamp

🔒 Bonus: Consent-Aware Deduplication

Respect user consent using GTM Consent APIs.

gtag('consent', 'default', {
  ad_storage: 'denied',
  analytics_storage: 'granted'
});

Ensure your logic routes conversions based on consent settings AND still applies deduplication downstream.

📊 Reporting Verification

  • Use GA4 Explore to verify 1 conversion per order ID
  • Facebook CAPI diagnostics should not show “duplicate events”
  • Google Ads should report only 1 conversion per transaction_id

🧠 Best Practices Summary

Area Strategy
GA4 Use event_id & ssGTM trigger logic
Meta (Pixel + CAPI) Use event_id deduplication
Google Ads Use transaction_id or gclid
Server Logic Use Firestore, Redis, or memory
Consent Management Use Consent Mode API

 

Tracking Add-to-Cart Events Reliably with Server-Side Tagging

Standard

Add-to-Cart is a critical micro-conversion event. However, traditional client-side tracking is unreliable due to browser limitations (ITP/ETP), ad blockers, and consent requirements. Server-Side Tagging using Google Tag Manager (ssGTM) ensures reliable, secure, and privacy-compliant tracking—ideal for capturing every Add-to-Cart action across eCommerce platforms.

🚀 Benefits of Tracking Add-to-Cart via Server-Side

  • Higher data reliability (even under ITP & adblockers)
  • First-party data control
  • Precise attribution for remarketing
  • Reduced client-side load & improved page speed
  • Consent-aware compliant tracking

✅ Prerequisites

  • Web GTM container installed on your site
  • Server-Side GTM container set up at gtm.yourdomain.com
  • GA4 Measurement Protocol API secret
  • Optional: Google Ads, Meta Pixel, or other ad platform IDs

🔧 Step-by-Step Implementation

🔹 Step 1: Push Add-to-Cart Data to Data Layer

In your website’s frontend, ensure this dataLayer push fires when a user adds an item to cart:

<script>
dataLayer.push({
  event: 'add_to_cart',
  ecommerce: {
    currency: 'USD',
    value: 79.99,
    items: [
      {
        item_id: 'SKU_12345',
        item_name: 'Product Name',
        quantity: 1,
        price: 79.99
      }
    ]
  }
});
</script>

🔹 Step 2: Create Custom Event Trigger in Web GTM

  • Trigger Type: Custom Event
  • Event Name: add_to_cart

🔹 Step 3: Create GA4 Event Tag in Web GTM

  • Tag Type: GA4 Event
  • Event Name: add_to_cart
  • Parameters:
    • currency: {{DLV - ecommerce.currency}}
    • value: {{DLV - ecommerce.value}}
    • items: {{DLV - ecommerce.items}}
  • Configuration Tag:
    • Use a GA4 Configuration tag that sends hits to your server container endpoint:

https://gtm.yourdomain.com

Also include your Measurement Protocol API Secret in the configuration tag.

🔹 Step 4: Configure GA4 Client in Server GTM

In your Server GTM container:

  • Create a GA4 Client.
    • This parses requests sent from GA4 web tags.
    • Ensures the Add-to-Cart event is received by the server container.

🔹 Step 5: Create GA4 Event Tag in Server GTM

This tag forwards Add-to-Cart events to GA4 using Measurement Protocol.

  • Tag Type: GA4 Event Tag
  • Event Name: add_to_cart
  • Parameters Mapping:
    • currency: {{Event Data - currency}}
    • value: {{Event Data - value}}
    • items: {{Event Data - items}}
  • Measurement ID: G-XXXXXXXXXX
  • API Secret: From GA4 Admin > Data Streams > Measurement Protocol

✅ You can extract items, value, and currency from the event.data object or request body like this:

return event.data.items || request.body?.events?.[0]?.params?.items;

🔹 Step 6: (Optional) Google Ads Add-to-Cart Conversion

To track Add-to-Cart as a conversion in Google Ads:

  • Tag Type: Google Ads Conversion
  • Conversion ID/Label: From Google Ads UI
  • Trigger: Only fire if event_name equals add_to_cart
  • Value: Use {{Event Data - value}}

🧠 Use a variable like this to extract event_name:

return request.body.events?.[0]?.name;

Set trigger condition:

event_name equals add_to_cart

🔹 Step 7: (Optional) Facebook Add-to-Cart with CAPI

Use HTTP Request Tag to send Add-to-Cart via Meta Conversions API:

POST https://graph.facebook.com/v18.0/<PIXEL_ID>/events?access_token=<TOKEN>

{
  "event_name": "AddToCart",
  "event_time": {{Timestamp}},
  "action_source": "website",
  "event_source_url": "{{Page URL}}",
  "user_data": {
    "em": "{{Hashed Email}}",
    "client_ip_address": "{{Client IP}}",
    "client_user_agent": "{{User Agent}}"
  },
  "custom_data": {
    "currency": "{{Currency}}",
    "value": "{{Value}}",
    "content_ids": ["SKU_12345"],
    "content_type": "product"
  }
}

🔹 Step 8: Test and Debug

  • Use GTM Server Preview Mode
  • Validate Web GTM events are forwarded correctly
  • Use GA4 Realtime report to verify add_to_cart events
  • Use Tag Assistant, GA Debugger, or console.log for browser inspection

🔹 Step 9: GA4 Ecommerce Reporting

Once configured:

  • Use Monetization > Ecommerce purchases
  • Use Events > Filter by add_to_cart
  • Breakdown by item_name, item_id, or other dimensions

To build cart abandonment analysis, combine add_to_cart with begin_checkout and purchase.

🔒 Consent Mode Compatibility

If you’re using Consent Mode:

gtag('consent', 'default', {
  ad_storage: 'denied',
  analytics_storage: 'granted'
});

Set up server-side logic to block or allow tags based on consent settings forwarded from the browser.

📌 Summary

Step Description
1 Push Add-to-Cart to dataLayer
2 Create Web GTM trigger + GA4 tag
3 Route through your ssGTM endpoint
4 Use GA4 Client in Server GTM
5 Forward Add-to-Cart event to GA4 via ssGTM
6-7 Optionally send to Google Ads & Meta
8-9 Test, debug, and report in GA4

 

Using Server-Side GTM for Funnel Step and Checkout Tracking

Standard

Tracking user journeys across funnel steps and checkout stages is critical for conversion optimization. Traditional client-side tracking is often blocked by browsers and ad blockers. Server-Side Google Tag Manager (ssGTM) provides a robust alternative—delivering more accurate, reliable data for analytics and marketing platforms.

Why Use Server-Side Tracking for Funnel Steps?

  • ✅ Bypasses browser restrictions (ITP, ETP, ad blockers)
  • ✅ Secure & privacy-compliant (Consent Mode + first-party cookies)
  • ✅ Reduced data loss in multi-step forms and single-page checkouts
  • ✅ Improved attribution for Ads & Analytics

🔧 Prerequisites

  • A working Server-Side GTM container
  • GA4 property and Measurement Protocol API Secret
  • Web GTM container with dataLayer tracking enabled
  • Custom server domain (e.g., gtm.yourdomain.com)
  • Access to Dev Tools for frontend edits

🚀 Step-by-Step Implementation

🔹 Step 1: Define Funnel Steps & Checkout Events

In your Web GTM container, push funnel steps to the dataLayer. Example funnel steps:

// Funnel Step 1: Product View
dataLayer.push({
  event: 'funnel_step',
  step_name: 'view_product',
  step_number: 1
});

// Funnel Step 2: Add to Cart
dataLayer.push({
  event: 'funnel_step',
  step_name: 'add_to_cart',
  step_number: 2
});

// Funnel Step 3: Checkout Started
dataLayer.push({
  event: 'funnel_step',
  step_name: 'begin_checkout',
  step_number: 3
});

// Funnel Step 4: Payment Info
dataLayer.push({
  event: 'funnel_step',
  step_name: 'add_payment_info',
  step_number: 4
});

🔹 Step 2: Create GTM Triggers for Each Funnel Step

In your Web GTM, create a Custom Event Trigger:

  • Trigger Type: Custom Event
  • Event Name: funnel_step
  • Filters (Optional):
    • step_name equals begin_checkout, add_payment_info, etc.

🔹 Step 3: Send Funnel Steps to GA4 via Web Tag

Use a GA4 Event tag with custom parameters:

  • Tag Type: GA4 Event
  • Event Name: funnel_step
  • Parameters:
    • step_name: {{DLV - step_name}}
    • step_number: {{DLV - step_number}}

Enable server-side forwarding:

  • Choose your GA4 Configuration Tag with Measurement Protocol configured to point to gtm.yourdomain.com.

🔹 Step 4: Configure GA4 Tag in Server-Side GTM

In Server GTM:

  1. Create a GA4 Client
    • Type: GA4
    • Accepts data from your Web GTM
    • Maps requests from gtm.yourdomain.com
  2. Create a GA4 Event Tag (Forwarded)
    • Triggered by the GA4 Client
    • No change needed if it’s a pass-through
  3. Validate in Tag Assistant or Preview mode
    • You should see funnel_step events arriving server-side

🔹 Step 5: (Optional) Send Funnel Steps to Google Ads via ssGTM

If you’re tracking funnel steps for remarketing:

Create a Google Ads Conversion Tag in ssGTM:

  • Conversion ID: From your Google Ads account
  • Conversion Label: Unique per funnel stage (e.g., view_cart_label)
  • Trigger: GA4 Client
  • Condition: event_name equals funnel_step + step_name = begin_checkout

Use a custom variable to extract step_name from the incoming request.

// Server GTM - Custom Variable
return request.body['events'][0]['params']['step_name'];

Then use that in your trigger condition.

🔹 Step 6: Funnel Completion Tracking

Track final conversion in your checkout:

dataLayer.push({
  event: 'purchase',
  transaction_id: 'ORD123',
  value: 129.99,
  currency: 'USD',
  items: [{
    item_id: 'SKU123',
    item_name: 'Product A',
    quantity: 1,
    price: 129.99
  }]
});

Send this to GA4 & also route through Server-Side GTM using the same Measurement Protocol method.

🧪 Testing and Debugging

  • Use GTM Server Preview Mode to verify incoming GA4 requests
  • Use GA Debugger Extension or Tag Assistant
  • Validate event payloads using Realtime reports in GA4
  • Use console.log or DOM validation to ensure dataLayer events fire as intended

🔐 Consent Mode Compatibility

Ensure funnel steps respect consent if using Consent Mode:

gtag('consent', 'update', {
  analytics_storage: 'granted'
});

Also forward consent status to your server endpoint for privacy compliance.

📊 Reporting in GA4

Use Explore > Funnel Analysis to view drop-off at each step:

  • Dimension: step_name, step_number
  • Metric: Event count, users

Build custom Path Analysis to track drop-off or re-engagement between funnel steps.

✅ Final Thoughts

Using Server-Side GTM for funnel and checkout tracking ensures:

  • Reliable, tamper-proof data collection
  • Better ad platform integration (Google Ads, Meta, etc.)
  • More control over sensitive transaction data
  • Enhanced security and attribution accuracy

🧠 Why Use Server-Side Pinterest Tagging?

Standard

Traditional client-side tags can be blocked by browser extensions or privacy settings. Server-side tagging ensures:

  • Improved attribution accuracy
  • Reliable conversion tracking
  • Enhanced compliance with privacy regulations

Adding Product Catalog Tracking enables Dynamic Retargeting Ads using your product feed.

⚙️ Requirements

Tools & Platforms

  • Google Tag Manager Server-Side (sGTM)
  • Google Tag Manager Web Container
  • Pinterest Business Account
  • Product Catalog Feed (XML or CSV)
  • Pinterest Tag ID
  • Cloud hosting (e.g., Google Cloud Run or App Engine)

🔧 Step-by-Step Implementation

1. Set Up Pinterest Conversion API Access

You need your Pinterest Tag ID and an Access Token.

a. Generate Access Token:

  • Go to: https://ads.pinterest.com
  • Navigate to Business Access > Conversion Access Token
  • Generate token for server-side event posting

2. Prepare Your sGTM Environment

 

a. Create a GTM Server Container:

  • Go to tagmanager.google.com
  • Click “Admin” > “Create Container” > Choose Server
  • Deploy via App Engine (recommended) or Cloud Run
# For App Engine
gcloud app deploy

Once deployed, note your sGTM Endpoint URL (e.g., https://gtm.example.com)

3. Pinterest Tag Template for sGTM

a. Import a Custom Template (Pinterest Server Tag)

If Pinterest doesn’t provide one officially, create one:

  • Go to Templates > New Tag Template
  • Use this sample code:
const log = require('logToConsole');
const sendHttpRequest = require('sendHttpRequest');
const JSON = require('JSON');

const eventName = data.event_name;
const userData = data.user_data;
const eventTime = Math.floor(Date.now() / 1000);

const payload = {
  event_name: eventName,
  event_time: eventTime,
  action_source: "website",
  user_data: userData,
  custom_data: data.custom_data || {}
};

sendHttpRequest(
  'https://api.pinterest.com/v5/events',
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${data.access_token}`
    }
  },
  JSON.stringify({
    data: [payload]
  })
);

log(`Sent Pinterest Event: ${eventName}`);

b. Add Fields:

  • event_name
  • user_data (e.g., email hash, IP, user agent)
  • custom_data (e.g., product info)
  • access_token

4. Send Events from Web GTM to Server GTM

a. Create Web GTM Tag (Pinterest Event Trigger)

window.dataLayer.push({
  event: 'pinterest_server_event',
  event_name: 'AddToCart',
  user_data: {
    em: 'hashed_email_here',
    client_ip_address: '{{user_ip}}',
    user_agent: navigator.userAgent
  },
  custom_data: {
    currency: 'USD',
    value: 59.99,
    content_ids: ['SKU123'],
    content_type: 'product'
  }
});

5. Create Tag in Server GTM for Pinterest

a. Variables

Extract event_name, user_data, and custom_data from event object

b. Trigger

  • Custom Event Trigger: pinterest_server_event

c. Tag

  • Use your custom Pinterest CAPI tag
  • Pass the required fields:
    • event_name
    • user_data
    • custom_data
    • access_token (stored as Constant variable)

🛍️ Product Catalog Setup

6. Prepare and Upload Product Feed

a. Create Catalog Feed File

CSV Format Example:

id,title,description,link,image_link,price,availability
1234,Blue T-Shirt,"Cotton, size M",https://store.com/p/1234,https://store.com/i/1234.jpg,29.99 USD,in stock

XML Format Example:

<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0">
<channel>
  <title>Product Feed</title>
  <item>
    <g:id>1234</g:id>
    <g:title>Blue T-Shirt</g:title>
    <g:description>Cotton, size M</g:description>
    <g:link>https://store.com/p/1234</g:link>
    <g:image_link>https://store.com/i/1234.jpg</g:image_link>
    <g:price>29.99 USD</g:price>
    <g:availability>in stock</g:availability>
  </item>
</channel>
</rss>

b. Upload to Pinterest

  • Go to Catalog Manager
  • Add new data source URL or schedule via FTP/HTTPS

7. Enable Dynamic Retargeting

Pinterest matches content_ids from your server events with the catalog feed.

Ensure:

  • The content_ids in custom_data match the id field in feed
  • Events like PageVisit, ViewCategory, AddToCart, and Purchase are firing

Example:

{
  "event_name": "Purchase",
  "custom_data": {
    "content_ids": ["1234"],
    "currency": "USD",
    "value": 99.00,
    "order_id": "ORDER789"
  }
}

✅ Final Testing & Validation

Use Pinterest Tag Helper Chrome Extension

  • Validate real-time event tracking
  • Confirm server-side events match product catalog

Use Pinterest CAPI Event Logging Tool (Beta)

  • Request from your Pinterest rep

📌 Best Practices

  • Hash PII (SHA-256) before sending email or phone
  • Set up failover logging for failed Pinterest requests in sGTM
  • Include Consent Mode logic if in EU
  • Regularly sync product catalog to keep IDs updated
  • Use Debug Mode in GTM for end-to-end testing

🚀 Bonus: Add Pinterest Purchase Deduplication

To avoid duplicate tracking:

  • Send client-side event_id along with server-side
  • Include event_id field in both
{
"event_name": "Purchase",
"event_id": "xyz123",
...
}

Pinterest will deduplicate based on event_id.

🔚 Summary

Setting up server-side Pinterest tag with product catalog tracking provides robust, privacy-compliant conversion tracking and retargeting capabilities. You’ll gain better accuracy, reduce reliance on browser-based tracking, and unlock dynamic ad features using your catalog.

 

 

📦 Advanced Guide: Snapchat CAPI Setup via Server Container for Product Sales Tracking

Standard

As modern browsers tighten restrictions on client-side tracking (cookies, ad blockers, ITP), server-side tagging has become critical for accurate conversion tracking. This article guides you step-by-step through setting up Snapchat’s Conversions API (CAPI) using Google Tag Manager Server-Side (sGTM) to track Product Sales events.

🎯 Why Use Snapchat CAPI via Server-Side GTM?

  • Improved Attribution Accuracy
  • Bypass Ad Blockers & Cookie Restrictions
  • First-Party Data Reliability
  • Enhanced Pixel Data Resilience

🧰 Prerequisites

Before we start, ensure you have:

  1. Snapchat Ads account
  2. Snapchat Pixel ID and Access Token
  3. Google Tag Manager Server Container setup
  4. Custom backend (Node.js, Python, etc.) or Shopify/BigCommerce
  5. GTM Web Container already implemented on your site

🔐 Step 1: Generate Snapchat Access Token & Pixel

  1. Go to your Snapchat Ads Manager.
  2. Navigate to Business Settings > Pixels.
  3. Create a Snap Pixel and copy the Pixel ID.
  4. Under Business API, generate a CAPI Access Token.

🌐 Step 2: Set Up GTM Server-Side Container

  • Go to tagmanager.google.com.
  • Create a new Server Container.
  • Deploy the server container on App Engine (GCP) or Cloud Run.
  • Set up a Custom Domain (e.g., tags.yoursite.com) for the server GTM.

Configure the sGTM container to accept requests from your website’s domain.

⚙️ Step 3: Send Purchase Events to GTM Server Container

Modify your frontend tracking (e.g., GTM Web Container or Shopify Script Editor) to send purchase data to the server.

Here’s a GTM Web Tag example to send data to your sGTM:

fetch('https://tags.yoursite.com/collect', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    event_name: 'Purchase',
    email: 'user@example.com',
    phone: '+1234567890',
    value: 129.99,
    currency: 'USD',
    transaction_id: 'txn_123456789',
    items: [
      {
        item_id: 'sku_001',
        item_name: 'Sneakers',
        quantity: 1,
        price: 129.99
      }
    ]
  })
});

This sends purchase data to the GTM server container for further processing.

📥 Step 4: Create Custom Snapchat CAPI Tag in GTM Server Container

In your Server Container:

🔧 a) Create a New Tag

  • Tag Type: Custom HTTP Request
  • Name: Snapchat CAPI Purchase

🔧 b) Set Up HTTP Request Code

Here’s an advanced Snapchat CAPI HTTP Request Template:

const pixelId = 'YOUR_PIXEL_ID';
const accessToken = 'YOUR_ACCESS_TOKEN';

const requestBody = {
  pixel_id: pixelId,
  event_type: 'PURCHASE',
  timestamp: new Date().toISOString(),
  event_conversion_type: 'WEB',
  event_tag: 'purchase',
  user: {
    email: data.email ? hash(data.email) : undefined,
    phone: data.phone ? hash(data.phone) : undefined
  },
  properties: {
    currency: data.currency || 'USD',
    price: data.value,
    transaction_id: data.transaction_id,
    items: data.items || []
  }
};

const response = sendHttpRequest('https://tr.snapchat.com/v2/conversion', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify(requestBody)
});

return { response: response.body };

function hash(input) {
  return CryptoJS.SHA256(input.trim().toLowerCase()).toString();
}

💡 You may need to include CryptoJS if it’s not built-in, or pre-hash from your site.

🔄 c) Trigger Configuration

Trigger the tag on custom event — e.g., event_name equals Purchase.

🧪 Step 5: Test Snapchat CAPI Integration

  • Go to Snapchat Events Manager.
  • Choose your Pixel > View Events.
  • Use Snap Test Event Code for debugging if needed.
  • Test a purchase event via your website.
  • Validate if events are arriving from server-side (event source should say CAPI).

🔐 Step 6: Secure & Optimize the Data Layer

  • Hash all user PII using SHA256
  • Use first-party domain in sGTM
  • Set proper CORS headers on sGTM server
  • Log and monitor failed requests

📈 Optional Enhancements

Feature Benefit
Queue + Retry Logic Handles Snapchat API timeouts/failures
BigCommerce/Shopify Hooks Automate server events from purchases
Multi-Event CAPI Tags Add AddToCart, ViewContent, etc.
Offline Conversion Uploads Match sales post-click using CSV uploads

📁 Example JSON Payload to Snapchat CAPI

{
"pixel_id": "abc123",
"event_type": "PURCHASE",
"event_conversion_type": "WEB",
"timestamp": "2025-05-29T16:20:00Z",
"user": {
"email": "hashed_email",
"phone": "hashed_phone"
},
"properties": {
"currency": "USD",
"price": 199.99,
"transaction_id": "order_789",
"items": [
{
"item_id": "sku789",
"item_name": "Wireless Earbuds",
"quantity": 1,
"price": 199.99
}
]
}
}

🔍 Troubleshooting Tips

Problem Solution
Events not showing in UI Use test event token or check logs in sGTM
Hashing errors Ensure SHA256 and lowercase + trimmed input
CORS/403 Errors Adjust server container headers for custom domains
API Throttling Monitor limits (200 QPS per account – Snapchat)

 

✅ Summary

Setting up Snapchat CAPI via GTM Server-Side for product sales allows eCommerce stores to track purchase events more reliably, securely, and with better ad optimization.

If you’re investing in Snapchat Ads, CAPI is not optional—it’s essential.

TikTok Events API Server-Side Tracking for eCommerce Stores

Standard

🎯 Advanced Guide to TikTok Events API Server-Side Tracking for eCommerce Stores


Server-side tracking via TikTok’s Events API provides reliable data collection for conversion tracking, even with increasing restrictions on client-side cookies and ad blockers. For eCommerce businesses, this ensures better attribution, optimized ad delivery, and accurate reporting.

🧠 Why Use TikTok Events API?

  1. Bypass Ad Blockers: Server-side events aren’t affected by browser restrictions.
  2. Reliable Attribution: No dependency on JavaScript tags or cookies.
  3. Improved ROAS: More accurate event tracking enables TikTok’s algorithm to better optimize ads.

🛠️ Prerequisites

  • TikTok Pixel ID
  • Access to TikTok Events Manager
  • TikTok API Access Token
  • Backend Server (e.g., Node.js / Express)
  • eCommerce platform integration (custom or Shopify/BigCommerce via webhook)

📦 Step-by-Step Implementation

Step 1: Create a TikTok Pixel

  1. Go to TikTok Ads Manager.
  2. Navigate to Assets > Events.
  3. Create a new Web Event and select Manual Setup.
  4. Save your Pixel ID and generate an Access Token under Events API settings.

Step 2: Set Up the Server (Node.js Example)

Install required packages:

npm init -y
npm install express axios dotenv body-parser

Create .env file:

TIKTOK_PIXEL_ID=YOUR_PIXEL_ID
TIKTOK_ACCESS_TOKEN=YOUR_ACCESS_TOKEN

Create server.js:

const express = require('express');
const axios = require('axios');
require('dotenv').config();
const bodyParser = require('body-parser');

const app = express();
app.use(bodyParser.json());

const TIKTOK_API_URL = 'https://business-api.tiktok.com/open_api/v1.3/event/track/';

app.post('/tiktok/purchase', async (req, res) => {
    const { user, event, products, value } = req.body;

    const payload = {
        pixel_code: process.env.TIKTOK_PIXEL_ID,
        event: event || 'Purchase',
        timestamp: new Date().toISOString(),
        context: {
            user: {
                external_id: user.external_id, // Hashed identifier
                email: user.email,             // Optional hashed email
                phone_number: user.phone_number // Optional hashed phone
            },
            ip: req.ip,
            user_agent: req.headers['user-agent']
        },
        properties: {
            value: value,
            currency: 'USD',
            contents: products.map(p => ({
                content_id: p.id,
                content_name: p.name,
                price: p.price,
                quantity: p.quantity
            })),
            content_type: 'product'
        }
    };

    try {
        const response = await axios.post(
            TIKTOK_API_URL,
            { data: [payload] },
            {
                headers: {
                    'Access-Token': process.env.TIKTOK_ACCESS_TOKEN,
                    'Content-Type': 'application/json'
                }
            }
        );

        res.status(200).json({ success: true, tiktokResponse: response.data });
    } catch (error) {
        console.error('TikTok API error:', error.response?.data || error.message);
        res.status(500).json({ success: false, error: error.message });
    }
});

const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

Step 3: Hash User Data for Compliance

TikTok requires hashed PII (personally identifiable information). Use SHA256 before sending:

const crypto = require('crypto');

function hashSHA256(data) {
    return crypto.createHash('sha256').update(data.trim().toLowerCase()).digest('hex');
}

Update the payload:

context: {
    user: {
        external_id: hashSHA256(user.id),
        email: hashSHA256(user.email),
        phone_number: hashSHA256(user.phone_number)
    },
    ...
}

Step 4: Triggering Server-Side Events

Use your eCommerce backend or webhook (Shopify, WooCommerce) to send event data to your /tiktok/purchase endpoint.

Example webhook payload:

{
    "user": {
        "id": "12345",
        "email": "user@example.com",
        "phone_number": "+1234567890"
    },
    "event": "Purchase",
    "value": 99.99,
    "products": [
        {
            "id": "sku123",
            "name": "Sneakers",
            "price": 99.99,
            "quantity": 1
        }
    ]
}

Step 5: Validate Events in TikTok Events Manager

  1. Go to TikTok Ads Manager > Events > Your Pixel.
  2. Use the Test Events tab to verify incoming server-side events.
  3. Confirm Event Match Quality for hashed user data.

🔐 Best Practices & Tips

Best Practice Why It Matters
✅ Hash PII using SHA256 Complies with TikTok’s data handling requirements
✅ Use HTTPS Ensures secure data transmission
✅ Send accurate IP/User Agent Helps TikTok match users and improve attribution
✅ Log TikTok API responses Useful for debugging or auditing data flow
✅ Monitor API Rate Limits Avoid request throttling (1000 QPS limit)

 

🧪 Optional Enhancements

  • Queueing & Retry Logic: Use a queue (e.g., RabbitMQ or Bull) to handle retries on API failures.
  • Multiple Event Types: Track AddToCart, ViewContent, CompletePayment, etc.
  • SDK Integration: TikTok offers SDKs in other languages (Python, PHP).

🚀 Final Thoughts

Implementing TikTok’s Events API server-side gives your eCommerce store the edge in attribution accuracy and ad efficiency. Unlike browser-side tracking, server-side ensures that your conversion data remains robust against ad blockers, browser updates, and third-party cookie restrictions.

If you’re scaling your store or investing heavily in TikTok ads, server-side tracking isn’t just an option—it’s a necessity.

Google Ads Enhanced Conversions with Server-Side Tagging

Standard

🚀 Advanced Guide: Implementing Google Ads Enhanced Conversions with Server-Side Tagging

🔍 What Are Enhanced Conversions?

Enhanced Conversions for Google Ads allow you to send first-party customer data (e.g., email, name, phone number) to Google in a hashed format (SHA-256). This improves conversion tracking accuracy, especially in privacy-restricted environments like iOS or with cookie restrictions.

When paired with Server-Side Tagging, you gain:

  • Better data control
  • Improved user privacy compliance
  • Reduced client-side load
  • Enhanced tracking resilience

🛠️ Tools You’ll Need

  • Google Tag Manager (Web + Server Container)
  • Google Ads Account with Conversion Tracking Setup
  • Google Cloud Platform (GCP) for deploying the server container
  • Access to your website code (to extract and send user data)

📐 Architecture Overview

[User Browser] → [Web GTM] → [Send User Data] →
    → [Server GTM (on GCP)] → [Google Ads Enhanced Conversion API]

🔧 Step 1: Set Up Google Ads Enhanced Conversions

  1. Go to Google Ads → Tools & Settings → Conversions.
  2. Create or select a conversion action.
  3. Enable Enhanced Conversions under “Enhanced Conversions” section.
  4. Choose the API method (for server-side).
  5. Accept terms and save.

🗃️ Step 2: Create Web GTM Tag to Send Data

You need to collect PII data (email, name, phone, etc.) on the client side and pass it to the server container.

🧩 Add Custom JavaScript Variable

In Web GTM, create a JavaScript Variable to extract user PII (e.g., from checkout form):

function() {
  return {
    email: document.querySelector('#email')?.value,
    phone: document.querySelector('#phone')?.value,
    first_name: document.querySelector('#first-name')?.value,
    last_name: document.querySelector('#last-name')?.value
  };
}

🏷️ Create a Custom Tag to Send Data to Server

Create a new Tag (Tag Type: HTTP Request or Custom Image) in Web GTM:

  • Tag Type: Custom Image Tag
  • URL: https://<your-server-container-url>/collect
  • Custom Parameters (Query String): plaintextCopyEdit
email={{User Data.email}}&phone={{User Data.phone}}&first_name={{User Data.first_name}}&last_name={{User Data.last_name}}&conversion_id=AW-CONVERSION_ID&event_name=purchase

Fire this tag on conversion trigger (e.g., “Purchase Complete”).

🖥️ Step 3: Deploy Server-Side GTM Container

  1. In GTM, create a Server Container.
  2. Deploy it on Google Cloud App Engine:
    • Google will guide you through provisioning the App Engine.
    • You’ll get a server URL like: https://gtm-yourproject.uc.r.appspot.com
  3. Enable Enhanced Conversions in your container.

🔍 Step 4: Create a Custom Client in Server Container

Create a Client to parse the incoming HTTP request from the web container.

✍️ Client Code Example

// Enhanced Conversion Client in Server GTM
const parsedUrl = require('url').parse(request.url, true);
const query = parsedUrl.query;

const userData = {
  email: query.email,
  phone_number: query.phone,
  first_name: query.first_name,
  last_name: query.last_name,
  conversion_id: query.conversion_id,
  event_name: query.event_name
};

// Assign data to event object
eventData = {
  user_data: userData,
  event_name: query.event_name,
  conversion_id: query.conversion_id
};

// Send to tags
return {
  eventData,
  logType: 'INFO'
};

🧩 Step 5: Set Up Google Ads Enhanced Conversions Tag in Server GTM

  1. Go to Tags → New → Tag Configuration.
  2. Select Google Ads Conversion Tracking.
  3. Fill out the following:
    • Conversion ID: AW-XXXXXXXXX
    • Conversion Label: Your label from Ads
    • User Data: Map the fields from the eventData
      • Email: {{eventData.user_data.email}}
      • Phone: {{eventData.user_data.phone_number}}
      • First Name: {{eventData.user_data.first_name}}
      • Last Name: {{eventData.user_data.last_name}}
  4. Hash PII using built-in hashing functions (or pre-hash on client).

 

Note: Google requires SHA-256 hashing. Server GTM does this automatically if you pass raw values and set “Hash data” to TRUE.

🧪 Step 6: Test and Debug

  1. Use Preview Mode in Web GTM to ensure tag fires with PII.
  2. Use Server GTM Preview to verify:
    • Client receives request
    • Event data is parsed
    • Google Ads Tag is fired with correct payload
  3. In Google Ads:
    • Go to Conversions → Check Enhanced Conversions column
    • It should say “Recording (API)” after some time.

✅ Best Practices

  • Consent Mode: Ensure PII is only sent with user consent (GDPR/CCPA).
  • Hashing: Use SHA-256 for any data that’s sent unhashed.
  • Security: Use HTTPS and authenticate requests to prevent misuse.
  • Data Layer: Use a structured data layer to manage PII cleanly.

💡 Bonus: Optional – Use Cloud Function to Pre-Process

If you want to hash on server manually or process complex logic:

Example Google Cloud Function (Node.js)

const crypto = require('crypto');

exports.enhancedConversion = (req, res) => {
  const data = req.query;

  function hash(value) {
    return crypto.createHash('sha256').update(value.trim().toLowerCase()).digest('hex');
  }

  const hashedData = {
    email: hash(data.email),
    phone: hash(data.phone),
    first_name: hash(data.first_name),
    last_name: hash(data.last_name)
  };

  // Send to Server GTM or Google Ads API
  // Example: res.redirect(`https://gtm-server-url.com/collect?...`);

  res.status(200).send('Data processed and hashed.');
};

Deploy it and replace the Web GTM tag’s URL with your function’s URL.

📊 Conclusion

mplementing Enhanced Conversions with Server-Side Tagging helps improve attribution accuracy while enhancing user privacy. While it involves multiple components (client, server, GTM, Ads), the modular nature of GTM makes it scalable and manageable.

🚀 Advanced Guide to Facebook Conversion API (CAPI) Setup via GTM Server for eCommerce

Standard

🧠 Why Use Facebook CAPI?

With growing privacy restrictions (like iOS14+ and browser limitations), relying solely on Facebook Pixel results in lost conversions and incomplete event data. Facebook Conversion API (CAPI) sends events directly from your server to Facebook, ensuring higher event match quality and more accurate tracking.

🧰 Prerequisites

Before starting, ensure you have the following:

  • Facebook Business Manager access
  • Facebook Pixel ID
  • Facebook Events Manager API access
  • Google Tag Manager (Web + Server Container)
  • GTM server container deployed (e.g., on Google Cloud, Vercel, AWS)
  • Your eCommerce platform access (e.g., Shopify, WooCommerce, Magento)
  • Basic knowledge of JavaScript, GTM, HTTP requests

⚙️ Step 1: Deploy GTM Server-Side Container

Option 1: Using Google Cloud Platform (GCP)

  1. Go to GTM server setup
  2. Create a new Server Container
  3. Choose Google Cloud Platform
  4. Select App Engine Standard, region (e.g., us-central), and proceed
  5. Once deployed, you’ll get a GTM Server URL, e.g., https://gtm.yourdomain.com

💡 Tip: Set up a custom subdomain like gtm.yourstore.com and point it to your GCP instance for better domain consistency and debugging.

🧱 Step 2: Configure GTM Web Container

A. Add GTM Web Container to Site

Install your GTM web container in your site’s <head> and <body> sections. Use the standard code from the GTM interface.

B. Create Events to Forward

  1. In GTM Web, create tags that capture key eCommerce events:
  • PageView
  • ViewContent
  • AddToCart
  • InitiateCheckout
  • Purchase

These can be either through built-in triggers or using dataLayer.push() from your platform.

Example Data Layer for Purchase:

<!-- wp:code -->
<pre class="wp-block-code"><code>/window.dataLayer = window.dataLayer || &#91;];
window.dataLayer.push({
  event: 'purchase',
  ecommerce: {
    transaction_id: 'T12345',
    value: 99.99,
    currency: 'USD',
    content_ids: &#91;'sku123'],
    contents: &#91;{id: 'sku123', quantity: 1}],
  }
});
</code></pre>
<!-- /wp:code -->

🛰️ Step 3: Enable GA4 to Server-Side Forwarding (Optional)

To avoid duplicate tagging:

  1. Enable GA4 event forwarding in Web GTM to Server GTM.
  2. Use the GA4 Configuration Tag, and set transport_url to your server container:

🛠️ Step 4: Set Up Facebook Tag in GTM Server Container

A. Install Facebook Tag Template

  1. Go to Server Container → Templates
  2. Search the Community Template Gallery
  3. Install: Facebook Conversion API Tag by Stape.io or other verified authors
  4. Click “Add to Workspace”

B. Configure the Facebook CAPI Tag

Create a new Tag in the server container:

  • Tag Type: Facebook CAPI
  • Pixel ID: Your Facebook Pixel ID
  • Access Token: From Facebook Events Manager (see below)
  • Event Name: dynamic from incoming request
  • Test Event Code: (for initial debugging)
  • Event ID: Use event_id from web layer (to deduplicate)
  • User Data: Email, phone, IP, user agent, etc.

C. Get Access Token

  1. Go to Facebook Events Manager
  2. Choose your Pixel → Settings → Conversion API
  3. Generate a System User Access Token

🔄 Step 5: Send Events from Web to Server

A. Modify Web GTM to Send HTTP Requests

Use the GA4 Client in the Server GTM container to receive incoming hits

In Web GTM, set GA4 Configuration Tag:

{
  measurement_id: "G-XXXXXXX",
  transport_url: "https://gtm.yourdomain.com",
}

All events (purchase, add to cart, etc.) will be sent via GA4 to the server GTM.

B. Create Triggers for Events

In the server container, you need to create triggers that match incoming event names.

  1. Trigger Type: Custom Event
  2. Event Name: match purchase, add_to_cart, etc.
  3. Set Variables: capture user data, event parameters from payload

🧬 Step 6: Data Enrichment and Matching

To increase match quality, include:

  • Client IP Address
  • User-Agent
  • fbc/fbp cookies
  • Hashed user data: email, phone number

You can use the built-in Facebook User Data variable or create custom JavaScript variables.

Example: Hashed Email

function() {
  var email = {{User Email}};
  return email ? sha256(email.trim().toLowerCase()) : undefined;
}

🧪 Step 7: Test with Facebook Events Manager

  1. Go to Events Manager
  2. Select your Pixel → Test Events
  3. Enter your Test Event Code in GTM Tag configuration
  4. Perform actions on your site (purchase, add to cart)
  5. Events should appear in real-time

🧹 Step 8: Finalize and Publish

  • Remove test code
  • Turn on deduplication using event_id across both browser and server
  • Publish both Web and Server containers
  • Monitor in Events Manager for real-time and historical performance

🧾 Bonus: Sample CAPI Tag Code in GTM Server (Custom)

If you’re writing a fully custom HTTP tag in Server GTM, here’s a basic fetch example using JavaScript:

const payload = {
  event_name: 'Purchase',
  event_time: Math.floor(Date.now() / 1000),
  user_data: {
    em: ["HASHED_EMAIL"],
    ph: ["HASHED_PHONE"],
    client_ip_address: request.headers['x-forwarded-for'],
    client_user_agent: request.headers['user-agent'],
  },
  custom_data: {
    value: 99.99,
    currency: 'USD',
    content_ids: ['sku123'],
    contents: [{id: 'sku123', quantity: 1}]
  },
  event_source_url: request.referer,
  action_source: 'website'
};

fetch('https://graph.facebook.com/v18.0/<PIXEL_ID>/events?access_token=<ACCESS_TOKEN>', {
  method: 'POST',
  body: JSON.stringify({ data: [payload] }),
  headers: {
    'Content-Type': 'application/json'
  }
});

🧠 Pro Tips

Use event deduplication (same event_id in pixel and CAPI)

Enable debugging with Test Event Code

Use server logs in GTM Server (Preview → Network tab)

Set up automatic retries for failed CAPI requests using templates

Monitor Event Match Quality in Events Manager regularly

🧾 Summary


Step Action
1 Deploy Server GTM
2 Track events in Web GTM
3 Configure GA4/HTTP forwarding
4 Install and configure Facebook Tag in Server GTM
5 Send events from Web to Server
6 Enrich user data
7 Test using Test Event Code
8 Publish and Monitor

Building a Custom Headless Commerce Server-Side Tracking Architecture

Standard

🚀 Overview

As modern eCommerce shifts toward headless commerce architectures, tracking customer interactions using server-side tracking becomes both a necessity and a competitive advantage. Traditional client-side tracking (via JavaScript snippets from platforms like Google Analytics or Meta Pixel) faces limitations due to ad blockers, browser privacy controls (like ITP), and performance overheads.

🧱 Architecture Overview

[Frontend (React, Next.js, etc.)]
|
| 1. Sends tracking event via REST or GraphQL
v
[Tracking API / Microservice (Node.js/Express)]
|
| 2. Processes and enriches event data
v
[Event Dispatcher]
|
| 3a. Stores raw events to a database (PostgreSQL, S3, etc.)
| 3b. Forwards events to external tools (Google Analytics, Meta Pixel)
v
[External Systems & Internal Warehouse]

🛠️ Step-by-Step Implementation

🔹 Step 1: Define the Tracking Schema

Use a structured schema (e.g., JSON) for all tracking events to ensure consistency.

// event.schema.json
{
  "event": "product_view",
  "timestamp": "2025-05-29T12:34:56Z",
  "user": {
    "id": "user_123",
    "anonymous_id": "anon_456"
  },
  "context": {
    "ip": "192.168.1.1",
    "user_agent": "Mozilla/5.0"
  },
  "properties": {
    "product_id": "sku123",
    "price": 49.99
  }
}

.


🔹 Step 2: Build the Tracking API

Use a lightweight Node.js + Express setup.

npm init -y
npm install express body-parser axios uuid
// server.js
const express = require('express');
const bodyParser = require('body-parser');
const { v4: uuidv4 } = require('uuid');
const { sendToGoogleAnalytics, sendToMetaPixel } = require('./dispatchers');

const app = express();
app.use(bodyParser.json());

app.post('/track', async (req, res) => {
  const event = {
    id: uuidv4(),
    ...req.body,
    received_at: new Date().toISOString()
  };

  try {
    // Store locally (optional)
    console.log('Received Event:', event);

    // Forward to third-party systems
    await Promise.all([
      sendToGoogleAnalytics(event),
      sendToMetaPixel(event)
    ]);

    res.status(200).json({ status: 'success', eventId: event.id });
  } catch (err) {
    console.error('Tracking Error:', err);
    res.status(500).json({ status: 'error', error: err.message });
  }
});

app.listen(3000, () => console.log('Tracking server running on port 3000'));

🔹 Step 3: Implement Third-Party Dispatchers

📈 Google Analytics 4 via Measurement Protocol

// dispatchers.js
const axios = require('axios');

const GA_MEASUREMENT_ID = 'G-XXXXXXXXXX';
const GA_API_SECRET = 'your-secret';
const GA_ENDPOINT = `https://www.google-analytics.com/mp/collect?measurement_id=${GA_MEASUREMENT_ID}&api_secret=${GA_API_SECRET}`;

async function sendToGoogleAnalytics(event) {
  const payload = {
    client_id: event.user.anonymous_id || '555',
    events: [
      {
        name: event.event,
        params: {
          ...event.properties
        }
      }
    ]
  };
  await axios.post(GA_ENDPOINT, payload);
}

🧠 Meta Conversions API

const META_PIXEL_ID = 'your-pixel-id';
const META_ACCESS_TOKEN = 'your-access-token';

async function sendToMetaPixel(event) {
  const url = `https://graph.facebook.com/v18.0/${META_PIXEL_ID}/events?access_token=${META_ACCESS_TOKEN}`;

  const payload = {
    data: [
      {
        event_name: event.event,
        event_time: Math.floor(new Date(event.timestamp).getTime() / 1000),
        user_data: {
          client_ip_address: event.context.ip,
          client_user_agent: event.context.user_agent
        },
        custom_data: {
          ...event.properties
        }
      }
    ]
  };

  await axios.post(url, payload);
}

module.exports = {
  sendToGoogleAnalytics,
  sendToMetaPixel
};

🔹 Step 4: Frontend Integration

Send tracking events from the headless frontend (React, Next.js, Vue, etc.) via API call:

// Example in Next.js
await fetch('/api/track', {
  method: 'POST',
  body: JSON.stringify({
    event: 'product_view',
    timestamp: new Date().toISOString(),
    user: {
      id: 'user_123',
      anonymous_id: 'anon_456'
    },
    context: {
      ip: 'USER_IP', // Captured server-side ideally
      user_agent: navigator.userAgent
    },
    properties: {
      product_id: 'sku123',
      price: 49.99
    }
  }),
  headers: {
    'Content-Type': 'application/json'
  }
});

🧩 Identity Resolution & Session Management

Server-side tracking gives you more control over identity merging:

  • Track both user_id (authenticated) and anonymous_id (pre-login)
  • Use tools like Redis or PostgreSQL to manage identity maps
  • Merge profiles when a user logs in
if (event.user.id && event.user.anonymous_id) {
  // Merge logic here
}

🧪 Monitoring & Resilience

  • Use a message queue (e.g., RabbitMQ, Kafka) to decouple ingestion from dispatching.
  • Retry failed events and log to an error queue.
  • Add observability via Prometheus, Datadog, or Sentry.

Example for retry handling:

try {
  await sendToGoogleAnalytics(event);
} catch (err) {
  logToErrorQueue('ga_failed', event);
}

🧱 Data Warehouse Ingestion (Optional)

Use a pipeline to write all events to a central store like BigQuery, Snowflake, or S3.

Example: save raw JSON to S3

const AWS = require('aws-sdk');
const s3 = new AWS.S3();

async function saveEventToS3(event) {
  await s3.putObject({
    Bucket: 'tracking-events-bucket',
    Key: `events/${event.id}.json`,
    Body: JSON.stringify(event),
    ContentType: 'application/json'
  }).promise();
}

🔒 Privacy & Compliance

  • Respect GDPR/CCPA: allow users to opt-out
  • Hash PII before sending to platforms (especially Meta)
  • Encrypt sensitive data in transit and at rest

🏁 Final Thoughts

Building a custom server-side tracking solution in a headless commerce architecture provides control, performance, and flexibility that off-the-shelf tools can’t match.

Benefits:

Centralized observability

Bypass ad blockers

Full control over event schemas

Resilient to browser-side privacy changes