hey @cranetrain @sebizox apologies for delay, but this is fully available within Shopify pixels and Builder! It is a newer paradigm so we are in the process of updating our docs but you can see a short loom explanation here:
And an example snippet below:
UPDATE: As outlined in this doc: Builder vs. Shopify Conversions - Builder.io it is possible that sometimes events from Shopify or some other event tracker may differ, and so we have updated the code snippet below to add a more robust mechanism to capture and retry any events that may be dropped by poor network connection or unsuccessful event capture
const BUILDER_STORAGE_KEY = 'builderPendingEvents';
const API_KEY = 'Your API Key';
const MAX_RETRIES = 3;
const RETRY_DELAY = 1000;
// Helper function to safely get storage data
const getPendingEvents = () => {
try {
const stored = localStorage.getItem(BUILDER_STORAGE_KEY);
return stored ? JSON.parse(stored) : [];
} catch (e) {
console.error('Error reading pending events:', e);
return [];
}
};
// Helper function to safely store data
const storePendingEvent = (eventData) => {
try {
const events = getPendingEvents();
events.push(eventData);
localStorage.setItem(BUILDER_STORAGE_KEY, JSON.stringify(events));
} catch (e) {
console.error('Error storing pending event:', e);
}
};
// Helper function to remove stored events
const removePendingEvents = () => {
try {
localStorage.removeItem(BUILDER_STORAGE_KEY);
} catch (e) {
console.error('Error removing pending events:', e);
}
};
// Function to send tracking data to Builder.io
const sendToBuilder = async (eventData, retryCount = 0) => {
try {
const response = await fetch(`https://cdn.builder.io/api/v1/track?apiKey=${API_KEY}`, {
method: 'POST',
body: JSON.stringify({ events: [eventData] }),
headers: {
'content-type': 'application/json',
},
mode: 'cors',
keepalive: true,
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return true;
} catch (error) {
console.error('Tracking error:', error);
if (retryCount < MAX_RETRIES) {
await new Promise(resolve => setTimeout(resolve, RETRY_DELAY * (retryCount + 1)));
return sendToBuilder(eventData, retryCount + 1);
}
// If all retries failed, store the event for later
storePendingEvent(eventData);
return false;
}
};
// Main tracking function
const trackConversion = ({ checkout, amountInfo, meta = {} }) => {
if (!checkout || !checkout.order || !checkout.order.id) {
console.error('Invalid checkout data received');
return;
}
const sessionId = document.cookie.split('; ')
.find(row => row.startsWith('builderSessionId='))
?.split('=')[1] || null;
const visitorId = localStorage.getItem('builderVisitorId') || null;
if (!sessionId || !visitorId) {
console.warn('Missing sessionId or visitorId');
}
const eventData = {
type: 'conversion',
data: {
amount: amountInfo.amount,
metadata: {
sdkVersion: '3.2.10',
url: location.href,
orderId: checkout.order.id,
currency: checkout.currencyCode,
timestamp: new Date().toISOString(),
...meta,
},
ownerId: API_KEY,
userAttributes: {
sessionId,
visitorId,
},
sessionId,
visitorId,
},
};
return sendToBuilder(eventData);
};
// Setup main event listener
analytics.subscribe('checkout_completed', (event) => {
trackConversion({
checkout: event.data.checkout,
amountInfo: event.data.checkout.totalPrice,
meta: {
additionalData: 'Conversion Recorded',
eventId: event.id,
clientId: event.clientId
},
});
});
// Try to send pending events on page load
window.addEventListener('load', async () => {
const pendingEvents = getPendingEvents();
if (pendingEvents.length > 0) {
const results = await Promise.all(
pendingEvents.map(event => sendToBuilder(event))
);
if (results.every(Boolean)) {
removePendingEvents();
}
}
});
// Backup tracking before page unload
window.addEventListener('beforeunload', () => {
const pendingEvents = getPendingEvents();
if (pendingEvents.length > 0) {
// Use synchronous localStorage to ensure data is saved
try {
navigator.sendBeacon(
`https://cdn.builder.io/api/v1/track?apiKey=${API_KEY}`,
JSON.stringify({ events: pendingEvents })
);
removePendingEvents();
} catch (e) {
console.error('Error sending beacon:', e);
}
}
});