X Ads Data Layer Setup | OpsBlu Docs

X Ads Data Layer Setup

Complete data layer implementation for Google Tag Manager on X (Twitter) Ads. Covers page metadata, user properties, ecommerce data, and custom event.

Data Layer Overview

A structured data layer enables dynamic X Pixel event firing with accurate conversion and user data.

Base Structure

window.xDataLayer = window.xDataLayer || {
  page: {},
  user: {},
  product: {},
  cart: {},
  transaction: {}
};

E-commerce Data Layer

Product Page

window.xDataLayer.product = {
  id: 'SKU_12345',
  name: 'Blue Widget',
  price: 49.99,
  category: 'widgets',
  brand: 'WidgetCo'
};

// Fire X event
twq('event', 'tw-PIXEL_ID-ViewContent', {
  content_ids: [window.xDataLayer.product.id],
  content_name: window.xDataLayer.product.name,
  value: window.xDataLayer.product.price.toString(),
  currency: 'USD'
});

Shopping Cart

window.xDataLayer.cart = {
  items: [
    { id: 'SKU_12345', name: 'Blue Widget', price: 49.99, quantity: 2 },
    { id: 'SKU_67890', name: 'Red Widget', price: 59.99, quantity: 1 }
  ],
  subtotal: 159.97,
  itemCount: 3
};

Transaction

window.xDataLayer.transaction = {
  id: 'ORDER_12345',
  revenue: 176.97,
  tax: 12.00,
  shipping: 5.00,
  currency: 'USD',
  items: window.xDataLayer.cart.items
};

// Fire purchase event
twq('event', 'tw-PIXEL_ID-Purchase', {
  value: window.xDataLayer.transaction.revenue.toString(),
  currency: window.xDataLayer.transaction.currency,
  transaction_id: window.xDataLayer.transaction.id,
  num_items: window.xDataLayer.cart.itemCount.toString(),
  content_ids: window.xDataLayer.transaction.items.map(item => item.id)
});

User Data Layer

window.xDataLayer.user = {
  id: 'USER_12345',
  email: '', // Will be hashed
  status: 'logged_in',
  segment: 'high_value'
};

// Hash and include in events
async function hashEmail(email) {
  const msgBuffer = new TextEncoder().encode(email.toLowerCase().trim());
  const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
  const hashArray = Array.from(new Uint8Array(hashBuffer));
  return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}

async function trackWithUserData() {
  const hashedEmail = await hashEmail(window.xDataLayer.user.email);

  twq('event', 'tw-PIXEL_ID-Purchase', {
    value: '99.99',
    currency: 'USD',
    transaction_id: 'ORDER_12345',
    email_address: hashedEmail
  });
}

Helper Functions

function fireXEventFromDataLayer(eventName) {
  let eventData = {};

  switch(eventName) {
    case 'Purchase':
      eventData = {
        value: window.xDataLayer.transaction?.revenue?.toString(),
        currency: 'USD',
        transaction_id: window.xDataLayer.transaction?.id,
        num_items: window.xDataLayer.cart?.itemCount?.toString()
      };
      break;

    case 'AddToCart':
      const lastItem = window.xDataLayer.cart?.items?.slice(-1)[0];
      eventData = {
        content_ids: [lastItem?.id],
        value: lastItem?.price?.toString(),
        currency: 'USD'
      };
      break;
  }

  if (typeof twq !== 'undefined') {
    twq('event', 'tw-PIXEL_ID-' + eventName, eventData);
  }
}

GTM Data Layer Integration

Sync with GTM

// GTM data layer push
dataLayer.push({
  'event': 'purchase',
  'ecommerce': {
    'purchase': {
      'actionField': {
        'id': 'ORDER_12345',
        'revenue': '99.99'
      }
    }
  }
});

// Sync to X data layer
window.xDataLayer.transaction = {
  id: dataLayer[dataLayer.length - 1].ecommerce.purchase.actionField.id,
  revenue: parseFloat(dataLayer[dataLayer.length - 1].ecommerce.purchase.actionField.revenue),
  currency: 'USD'
};

Best Practices

  • Initialize data layer before X Pixel
  • Convert numbers to strings for value parameters
  • Hash email client-side before sending
  • Clear data layer on SPA navigation
  • Validate data before firing events
  • Document schema for team