Installation
1 min
To install the Chord web pixel on your Shopify store:
- Ask the Chord team for the full web pixel code (see the example below).
- Navigate to Settings > Customer events in your Shopify store.
- Select Add custom pixel.
- Enter "Chord Analytics" as the name for your pixel.
- Select Add pixel.
- Review the customer privacy settings, and see our guide to consent management to understand how these settings will impact your event tracking.
- In the Code text box, copy and paste the provided web pixel code.
- Before you publish the provided web pixel, you need to update the settings const variables at the very top of the JavaScript code. See the configuration guide for a detailed explanation of each const variable. After you've updated the const variables, select Save.
- Once the pixel has been saved successfully, you can select Connect to enable the pixel on your Shopify store. Once you select Connect, the pixel will immediately load and start tracking events, so make sure you finalize the pixel settings first.
Shopify web pixel code (EXAMPLE ONLY)
const CDP_WRITE_KEY =
''
const CDP_DOMAIN = 'https://production.cdp.ingest.chord.co'
const CHORD_OMS_ID = ''
const CHORD_STORE_ID = ''
const CHORD_TENANT_ID = ''
const DEFAULT_LOCALE = 'en-US'
const STORE_DOMAIN = ''
const PIXEL_VERSION = '1.0.0'
let customerPrivacyStatus = init.customerPrivacy
let currency = init.data.cart?.cost?.totalAmount.currencyCode
let locale = DEFAULT_LOCALE
function getStorefrontAnonymousId() {
return document.cookie
.split('; ')
.find((c) => c.startsWith('__eventn_id='))
?.split('=')[1]
}
// ============================================================================
// CDP Initialization (lazy — loads p.js on first checkout event)
// ============================================================================
let cdpInitialized = false
function initCdp() {
if (cdpInitialized) return
if (!CDP_WRITE_KEY || !CDP_DOMAIN) return
cdpInitialized = true
window.chordQ = window.chordQ || []
window.chord = window.chord || {
identify: (...args) => window.chordQ?.push((cdp) => cdp?.identify(...args)),
page: (...args) => window.chordQ?.push((cdp) => cdp?.page(...args)),
track: (...args) => window.chordQ?.push((cdp) => cdp?.track(...args)),
}
const script = document.createElement('script')
script.async = true
script.src = `${CDP_DOMAIN}/p.js`
script.setAttribute('data-init-only', 'true')
script.setAttribute('data-namespace', 'chord')
script.setAttribute('data-write-key', CDP_WRITE_KEY)
document.head.appendChild(script)
const cdpUserId = window.chord?.user?.()?.userId
const userId = init.data.customer?.id
if (userId && userId !== cdpUserId) {
const traits = getInitCustomerTraits(init)
cdpIdentify(userId, traits)
}
}
// ============================================================================
// Consent Management
// ============================================================================
api.customerPrivacy.subscribe('visitorConsentCollected', (event) => {
customerPrivacyStatus = event.customerPrivacy
})
;(function identifyOnLoad() {
const customer = init.data.customer
if (!customer?.id) return
const traits = {}
if (customer.email) traits.email = customer.email
if (customer.firstName) traits.firstName = customer.firstName
if (customer.lastName) traits.lastName = customer.lastName
if (customer.phone) traits.phone = customer.phone
if (customer.ordersCount) traits.ordersCount = customer.ordersCount
forward('identify', customer.id, traits)
})()
// ============================================================================
// Event Subscriptions — All 13 Shopify events
// ============================================================================
// --- Storefront-only events (postMessage only) ---
analytics.subscribe('page_viewed', (event) => {
forward('page', {
path: event.context.document.location.pathname,
url: event.context.document.location.href,
title: event.context.document.title,
})
})
analytics.subscribe('product_viewed', (event) => {
const { productVariant } = event.data
forward('track', 'Product Viewed', formatProduct(productVariant))
})
analytics.subscribe('product_added_to_cart', (event) => {
const { cartLine } = event.data
if (!cartLine) return
const product = formatCartLine(cartLine)
forward('track', 'Product Added', {
...product,
products: [product],
})
})
analytics.subscribe('product_removed_from_cart', (event) => {
const { cartLine } = event.data
if (!cartLine) return
const product = formatCartLine(cartLine)
forward('track', 'Product Removed', {
...product,
products: [product],
})
})
analytics.subscribe('cart_viewed', (event) => {
const { cart } = event.data
if (!cart) return
forward('track', 'Cart Viewed', {
cart_id: cart.id,
currency: cart.cost.totalAmount.currencyCode,
value: cart.cost.totalAmount.amount,
products: cart.lines.map(formatCartLine),
})
})
analytics.subscribe('collection_viewed', (event) => {
const { collection } = event.data
forward('track', 'Product List Viewed', {
list_id: collection.id,
item_list_id: collection.id,
list_name: collection.title,
item_list_name: collection.title,
products: collection.productVariants.map(formatProduct),
})
})
analytics.subscribe('search_submitted', (event) => {
forward('track', 'Products Searched', {
query: event.data.searchResult.query,
})
})
// --- Checkout events (postMessage + direct CDP) ---
analytics.subscribe('checkout_started', async (event) => {
const { checkout } = event.data
// postMessage (sync, fire-and-forget)
forward('track', 'Checkout Started', formatCheckout(checkout))
// Direct CDP
const coupon = getCoupon(checkout)
const discount = getDiscount(checkout)
const props = {
checkout_type: 'Shopify',
coupon,
currency: checkout.currencyCode,
discount,
products: checkout.lineItems.map(getLineItem),
revenue: checkout.subtotalPrice.amount,
shipping: checkout.shippingLine?.price.amount,
tax: checkout.totalTax.amount,
value: checkout.subtotalPrice.amount,
}
await cdpTrack('Checkout Started', props)
})
analytics.subscribe('checkout_contact_info_submitted', async (event) => {
const { checkout } = event.data
// postMessage (sync, fire-and-forget)
// Use undefined for userId — email is not a stable ID; the real customer ID
// is set at checkout_completed when checkout.order.customer.id is available.
forward('identify', undefined, {
email: checkout.email,
phone: checkout.phone,
})
forward('track', 'Checkout Step Completed', {
checkout_id: checkout.token,
step: 1,
step_name: 'Contact Info',
})
// Direct CDP
const checkoutTraits = getCheckoutTraits(checkout)
const initTraits = getInitCustomerTraits(init)
const traits = {
...initTraits,
...checkoutTraits,
}
const userId = traits.id || undefined
await cdpIdentify(userId, traits)
const props = {
checkout_id: checkout.token,
step: 1,
}
await cdpTrack('Checkout Step Completed', props)
})
analytics.subscribe('checkout_address_info_submitted', async (event) => {
// postMessage (sync, fire-and-forget)
forward('track', 'Checkout Step Completed', {
checkout_id: event.data.checkout.token,
step: 2,
step_name: 'Shipping Address',
})
// Direct CDP
const props = {
checkout_id: event.data.checkout.token,
step: 2,
}
await cdpTrack('Checkout Step Completed', props)
})
analytics.subscribe('checkout_shipping_info_submitted', async (event) => {
// postMessage (sync, fire-and-forget)
forward('track', 'Checkout Step Completed', {
checkout_id: event.data.checkout.token,
step: 3,
step_name: 'Shipping Method',
})
// Direct CDP
const props = {
checkout_id: event.data.checkout.token,
step: 3,
}
await cdpTrack('Checkout Step Completed', props)
})
analytics.subscribe('payment_info_submitted', async (event) => {
const { checkout } = event.data
// postMessage (sync, fire-and-forget)
forward('track', 'Payment Info Entered', {
checkout_id: checkout.token,
step: 4,
currency: checkout.currencyCode,
value: checkout.subtotalPrice.amount,
products: checkout.lineItems.map(formatLineItem),
})
// Direct CDP
const coupon = getCoupon(checkout)
const props = {
checkout_id: checkout.token,
coupon,
currency: checkout.currencyCode,
products: checkout.lineItems.map(getLineItem),
step: 4,
value: checkout.subtotalPrice.amount,
}
await cdpTrack('Payment Info Entered', props)
})
analytics.subscribe('checkout_completed', async (event) => {
const { checkout } = event.data
// postMessage (sync, fire-and-forget)
const userId = checkout.order?.customer?.id || undefined
forward('identify', userId, {
email: checkout.email,
phone: checkout.phone,
firstName: checkout.shippingAddress?.firstName,
lastName: checkout.shippingAddress?.lastName,
})
forward(
'track',
'Order Completed',
formatOrderCompleted(checkout, event.timestamp)
)
// Direct CDP
const coupon = getCoupon(checkout)
const discount = getDiscount(checkout)
const orderProps = {
checkout_id: checkout.token,
checkout_type: 'Shopify',
coupon,
currency: checkout.currencyCode,
discount,
email: checkout.email || undefined,
first_name: checkout.shippingAddress?.firstName || undefined,
last_name: checkout.shippingAddress?.lastName || undefined,
order_date: event.timestamp,
order_id: checkout.order?.id,
phone: checkout.phone || undefined,
products: checkout.lineItems.map((item, i) => {
const price = item.variant?.price.amount
const itemDiscount = item.discountAllocations.reduce(
(acc, { amount }) => acc + (amount.amount || 0),
0
)
const revenue = price ? (item.quantity || 1) * price - itemDiscount : 0
return {
...getLineItem(item, i),
revenue,
}
}),
revenue: checkout.subtotalPrice.amount,
shipping: checkout.shippingLine?.price.amount,
shipping_city: checkout.shippingAddress?.city || undefined,
shipping_country: checkout.shippingAddress?.country || undefined,
shipping_state: checkout.shippingAddress?.province || undefined,
shipping_zipcode: checkout.shippingAddress?.zip || undefined,
subtotal: checkout.subtotalPrice.amount,
tax: checkout.totalTax.amount,
total: checkout.totalPrice.amount,
}
const checkoutTraits = getCheckoutTraits(checkout)
const initTraits = getInitCustomerTraits(init)
const traits = {
...initTraits,
...checkoutTraits,
}
const cdpUserId = traits.id || undefined
await cdpIdentify(cdpUserId, traits)
await cdpTrack('Order Completed', orderProps)
})
function forward(type, ...args) {
try {
const target =
typeof window !== 'undefined' && window.top ? window.top : self
target.postMessage(
{
source: 'chord-web-pixel',
type,
payload: args,
},
'*'
)
} catch (err) {
// Silently fail if postMessage is not available
}
}
// ============================================================================
// Lightweight Data Formatters (for postMessage forwarding)
// ============================================================================
function formatProduct(variant) {
const product = variant?.product
return {
product_id: product?.id,
sku: variant?.sku,
name: product?.title || product?.untranslatedTitle,
price: variant?.price?.amount,
currency: variant?.price?.currencyCode,
category: product?.type,
brand: product?.vendor,
variant: variant?.title || variant?.untranslatedTitle,
variant_id: variant?.id,
image_url: variant?.image?.src,
url: product?.url,
}
}
function formatCartLine(cartLine) {
const variant = cartLine.merchandise
const product = variant?.product
return {
product_id: product?.id,
sku: variant?.sku,
name: product?.title || product?.untranslatedTitle,
price: variant?.price?.amount,
currency: cartLine.cost.totalAmount.currencyCode,
quantity: cartLine.quantity,
category: product?.type,
brand: product?.vendor,
variant: variant?.title || variant?.untranslatedTitle,
variant_id: variant?.id,
image_url: variant?.image?.src,
url: product?.url,
value: cartLine.cost.totalAmount.amount,
}
}
function formatLineItem(item, index) {
const variant = item.variant
const product = variant?.product
return {
product_id: product?.id,
sku: variant?.sku,
name: product?.title || product?.untranslatedTitle,
price: variant?.price?.amount,
quantity: item.quantity,
category: product?.type,
brand: product?.vendor,
variant: variant?.title || variant?.untranslatedTitle,
variant_id: variant?.id,
image_url: variant?.image?.src,
url: product?.url,
position: typeof index === 'number' ? index + 1 : undefined,
}
}
function formatCheckout(checkout) {
const discount = checkout.lineItems.reduce((acc, item) => {
return (
acc +
item.discountAllocations.reduce(
(sum, d) => sum + (d.amount.amount || 0),
0
)
)
}, 0)
const coupon = checkout.discountApplications.find((d) => {
if ('amount' in d.value) return d.value.amount > 0
return d.value.percentage > 0
})?.title
return {
checkout_id: checkout.token,
currency: checkout.currencyCode,
value: checkout.subtotalPrice.amount,
revenue: checkout.subtotalPrice.amount,
shipping: checkout.shippingLine?.price?.amount,
tax: checkout.totalTax?.amount,
discount,
coupon,
products: checkout.lineItems.map(formatLineItem),
}
}
function formatOrderCompleted(checkout, timestamp) {
const base = formatCheckout(checkout)
return {
...base,
order_id: checkout.order?.id,
order_date: timestamp,
total: checkout.totalPrice.amount,
subtotal: checkout.subtotalPrice.amount,
email: checkout.email,
phone: checkout.phone,
first_name: checkout.shippingAddress?.firstName,
last_name: checkout.shippingAddress?.lastName,
shipping_city: checkout.shippingAddress?.city,
shipping_state: checkout.shippingAddress?.province,
shipping_country: checkout.shippingAddress?.country,
shipping_zipcode: checkout.shippingAddress?.zip,
}
}
// ============================================================================
// Direct CDP Methods (for checkout events in the sandbox)
// ============================================================================
function cdpEnabled() {
if (window.chord) return true
if (!CDP_WRITE_KEY || !CDP_DOMAIN) return false
initCdp()
return !!window.chord
}
async function cdpIdentify(userId, traits) {
if (!cdpEnabled()) return
const options = await getOptions()
const anonymousId = getStorefrontAnonymousId()
await window.chord.identify(userId, traits, {
...options,
...(anonymousId && {
anonymousId,
}),
context: {
page: options.page,
},
})
}
async function cdpTrack(event, props) {
if (!cdpEnabled()) return
const options = await getOptions()
const meta = await getMeta()
const anonymousId = getStorefrontAnonymousId()
await window.chord.track(
event,
{
...props,
meta,
context: {
page: options.page,
},
},
{
...options,
...(anonymousId && {
anonymousId,
}),
}
)
}
// ============================================================================
// CDP Options & Metadata
// ============================================================================
async function getCategoryPreferences() {
return {
analytics: customerPrivacyStatus?.analyticsProcessingAllowed,
marketing: customerPrivacyStatus?.marketingAllowed,
preferences: customerPrivacyStatus?.preferencesProcessingAllowed,
sale_of_data: customerPrivacyStatus?.saleOfDataAllowed,
}
}
async function getOptions() {
const categoryPreferences = (await getCategoryPreferences()) || {}
return {
consent: {
categoryPreferences,
},
page: {
path: init.context.document.location.pathname,
url: init.context.document.location.href,
},
}
}
async function getMeta() {
if (!currency) {
currency = await browser.cookie.get('cart_currency')
}
return {
i18n: {
currency,
locale,
},
ownership: {
oms_id: CHORD_OMS_ID,
store_id: CHORD_STORE_ID,
tenant_id: CHORD_TENANT_ID,
},
platform: {
name: 'Shopify',
type: 'web',
},
store: {
domain: STORE_DOMAIN,
},
version: {
major: 3,
minor: 0,
patch: 0,
},
}
}
// ============================================================================
// Rich Data Formatters (for direct CDP — adapted from legacy pixel)
// ============================================================================
function getLineItem(item, position) {
const variant = 'variant' in item ? item.variant : item.merchandise
const product = variant?.product
const discountAllocations =
'discountAllocations' in item ? item.discountAllocations : undefined
const coupon = discountAllocations?.find((al) => al?.amount?.amount > 0)
?.discountApplication.title
return {
brand: product?.vendor,
category: product?.type || undefined,
coupon,
image_url: variant?.image?.src,
line_item_id: 'id' in item ? item.id : undefined,
name: product?.title || product?.untranslatedTitle,
position: typeof position === 'number' ? position + 1 : undefined,
price: variant?.price.amount,
product_id: product?.id,
quantity: ('quantity' in item && item.quantity) || undefined,
sku: variant?.sku || undefined,
url: product?.url,
variant: variant?.title || variant?.untranslatedTitle || undefined,
variant_id: variant?.id,
}
}
function getDiscount(checkout) {
return checkout.lineItems.reduce((acc, item) => {
const { discountAllocations } = item
const discount = discountAllocations.reduce((innerAcc, { amount }) => {
return innerAcc + (amount.amount || 0)
}, 0)
return acc + discount
}, 0)
}
function getCoupon(checkout) {
return checkout.discountApplications.find(({ value }) => {
if ('amount' in value) return value.amount > 0
return value.percentage > 0
})?.title
}
function getCheckoutTraits(checkout) {
const traits = {}
if (checkout.email) traits.email = checkout.email
if (checkout.phone) traits.phone = checkout.phone
const customerId = checkout.order?.customer?.id
if (customerId) traits.id = customerId
return traits
}
function getInitCustomerTraits(init) {
const { customer } = init.data
const traits = {}
if (customer?.email) traits.email = customer.email
if (customer?.firstName) traits.firstName = customer.firstName
if (customer?.id) traits.id = customer.id
if (customer?.lastName) traits.lastName = customer.lastName
if (customer?.ordersCount) traits.ordersCount = customer.ordersCount
if (customer?.phone) traits.phone = customer.phone
return traits
}