Programmatic API
Use the Programmatic Tracking API when you need to send events from third-party payment integrations, cross-page flows, or after backend validation. Watch the Event Monitor panel to see events fire in real-time. View API Documentation →
Payment Provider SDK Callback
InitiateCheckout
Purchase
Track events when third-party payment widgets emit client-side events (Paddle, Gumroad, Lemon Squeezy, etc.)
Try the Payment Flow
Click the button below to open a simulated third-party payment modal. Watch the Event Monitor panel to see events fire in real-time.
Order Details
/**
* Payment Provider SDK Integration
*
* Handle client-side events from embedded payment widgets/SDKs.
* Replace 'paymentProvider' with your SDK instance (Paddle, Gumroad, etc.)
*/
// Helper function with retry logic for PixelFlow availability
async function trackPurchaseEvent(eventData, retryCount = 0) {
const maxRetries = 3;
// Wait for PixelFlow to initialize
if (
!window.pixelFlow?.trackEvent ||
!window.pixelFlow?.utils?.normalizeCustomerData
) {
if (retryCount < maxRetries) {
await new Promise((resolve) => setTimeout(resolve, 1000));
return trackPurchaseEvent(eventData, retryCount + 1);
}
console.error("PixelFlow not available after retries");
return false;
}
try {
// Build rich customData with all available fields
const customData = {
value: eventData.amount / 100, // Monetary value (required for Purchase)
currency: eventData.currency.toUpperCase(), // ISO 4217 code: "USD", "EUR", "GBP"
content_type: "product", // "product" or "product_group"
content_name: eventData.productName, // Product or content name
content_ids: eventData.items.map(item => item.sku), // Product SKUs
num_items: eventData.items.reduce((sum, item) => sum + item.quantity, 0),
contents: eventData.items.map(item => ({
id: item.sku, // Product ID or SKU
quantity: item.quantity, // Quantity purchased
item_price: item.price / 100 // Price per item
}))
};
// Normalize and prepare user data (hashing is automatic)
const userData = await window.pixelFlow.utils.normalizeCustomerData({
em: eventData.customer?.email, // Email (will be normalized & hashed)
fn: eventData.customer?.firstName, // First name (will be normalized & hashed)
ln: eventData.customer?.lastName, // Last name (will be normalized & hashed)
ph: eventData.customer?.phone // Phone (will be normalized & hashed)
});
const success = await window.pixelFlow.trackEvent("Purchase", customData, userData);
if (success) {
console.log("Purchase event tracked successfully");
}
return success;
} catch (error) {
console.error("Error tracking purchase:", error);
return false;
}
}
// Listen for payment provider's completion event
paymentProvider.on("purchase.complete", async (event) => {
try {
await trackPurchaseEvent({
amount: event.data?.amount, // Amount in cents (e.g., 14999 = $149.99)
currency: event.data?.currency, // Currency code
productName: event.data?.productName,
items: event.data?.items || [], // Array of purchased items
customer: {
email: event.data?.customer?.email,
firstName: event.data?.customer?.firstName,
lastName: event.data?.customer?.lastName,
phone: event.data?.customer?.phone
}
});
} catch (error) {
console.error("Error in purchase.complete handler:", error);
}
});
// Track checkout initiation when modal opens
paymentProvider.on("checkout.open", async (event) => {
if (window.pixelFlow?.trackEvent) {
// InitiateCheckout can include cart preview data
await window.pixelFlow.trackEvent("InitiateCheckout", {
value: event.data?.cartTotal / 100,
currency: event.data?.currency || "USD",
num_items: event.data?.itemCount || 1,
content_type: "product"
}, {});
}
});
Cross-Page Data: localStorage
Purchase
Save checkout data to localStorage, then send Purchase event on the thank-you page
Step 1: Checkout Page — Save Data
This simulates saving order data before redirecting to the thank-you page.
Step 2: Thank-You Page — Send Event
After redirect, retrieve the data from localStorage and send the Purchase event. Watch the Event Monitor panel to see the event fire.
/**
* localStorage Cross-Page Data Flow
* Use when checkout and thank-you pages are on the same domain
*/
// ═══════════════════════════════════════════════════════════
// CHECKOUT PAGE: Save complete order data before redirect
// ═══════════════════════════════════════════════════════════
function saveCheckoutData(orderData) {
const checkoutData = {
// Order details
orderId: orderData.orderId,
value: orderData.total, // Total order value
currency: orderData.currency, // ISO 4217: "USD", "EUR", etc.
// Product information for contents array
items: orderData.items.map(item => ({
id: item.sku, // Product SKU
name: item.name, // Product name
quantity: item.quantity, // Quantity
item_price: item.price // Unit price
})),
// Customer information (will be normalized & hashed)
customer: {
email: orderData.customer.email, // Required for matching
firstName: orderData.customer.firstName,
lastName: orderData.customer.lastName,
phone: orderData.customer.phone
},
timestamp: Date.now()
};
localStorage.setItem("pixelflow_checkout_data", JSON.stringify(checkoutData));
window.location.href = "/thank-you";
}
// ═══════════════════════════════════════════════════════════
// THANK-YOU PAGE: Read data and send Purchase event
// ═══════════════════════════════════════════════════════════
async function sendPurchaseFromStorage(retryCount = 0) {
const maxRetries = 3;
// Wait for PixelFlow with retry logic
if (
!window.pixelFlow?.trackEvent ||
!window.pixelFlow?.utils?.normalizeCustomerData
) {
if (retryCount < maxRetries) {
await new Promise((resolve) => setTimeout(resolve, 1000));
return sendPurchaseFromStorage(retryCount + 1);
}
console.error("PixelFlow not available");
return false;
}
const stored = localStorage.getItem("pixelflow_checkout_data");
if (!stored) {
console.warn("No checkout data found");
return false;
}
try {
const data = JSON.parse(stored);
// Build comprehensive customData
const customData = {
value: data.value, // Total monetary value (required)
currency: data.currency, // ISO 4217 currency code
content_type: "product", // "product" or "product_group"
content_ids: data.items.map(item => item.id), // Array of SKUs
num_items: data.items.reduce((sum, item) => sum + item.quantity, 0),
contents: data.items.map(item => ({
id: item.id, // Product ID/SKU
quantity: item.quantity, // Quantity
item_price: item.item_price // Unit price
}))
};
// Normalize user data (hashing handled automatically)
const userData = await window.pixelFlow.utils.normalizeCustomerData({
em: data.customer.email, // Email → normalized → hashed
fn: data.customer.firstName, // First name → normalized → hashed
ln: data.customer.lastName, // Last name → normalized → hashed
ph: data.customer.phone // Phone → normalized → hashed
});
const success = await window.pixelFlow.trackEvent("Purchase", customData, userData);
if (success) {
// Clear localStorage to prevent duplicate events
localStorage.removeItem("pixelflow_checkout_data");
console.log("Purchase tracked successfully");
}
return success;
} catch (error) {
console.error("Error sending purchase:", error);
return false;
}
}
// Auto-run when thank-you page loads
sendPurchaseFromStorage();
Cross-Page Data: API Fetch
Purchase
Fetch order data from your backend API on the thank-you page
Thank-You Page — Fetch & Send
The order ID is passed via URL. Your page fetches the complete order data from your backend API. Watch the Event Monitor to see the Purchase event fire.
/thank-you?order_id=ORD-2024-002
/**
* API Fetch Cross-Page Data Flow
* Use for cross-domain flows or when order data is stored server-side
*/
async function sendPurchaseFromAPI(retryCount = 0) {
const maxRetries = 3;
// Wait for PixelFlow with retry logic
if (
!window.pixelFlow?.trackEvent ||
!window.pixelFlow?.utils?.normalizeCustomerData
) {
if (retryCount < maxRetries) {
await new Promise((resolve) => setTimeout(resolve, 1000));
return sendPurchaseFromAPI(retryCount + 1);
}
console.error("PixelFlow not available");
return false;
}
// Get order ID from URL parameters
const urlParams = new URLSearchParams(window.location.search);
const orderId = urlParams.get("order_id");
if (!orderId) {
console.error("No order_id in URL");
return false;
}
try {
// Fetch complete order details from your backend
const response = await fetch(`/api/orders/${orderId}`);
if (!response.ok) throw new Error("Order not found");
const order = await response.json();
// Build comprehensive customData with all fields
const customData = {
value: order.total, // Total order value (required)
currency: order.currency, // ISO 4217: "USD", "EUR", "GBP"
content_type: "product", // "product" or "product_group"
content_name: order.items[0]?.name, // Primary product name
content_ids: order.items.map(item => item.sku), // All product SKUs
num_items: order.items.reduce((sum, item) => sum + item.quantity, 0),
contents: order.items.map(item => ({
id: item.sku, // Product ID or SKU
quantity: item.quantity, // Quantity purchased
item_price: item.price // Unit price
}))
};
// Normalize user data - PixelFlow handles hashing automatically
const userData = await window.pixelFlow.utils.normalizeCustomerData({
em: order.customer.email,
fn: order.customer.firstName,
ln: order.customer.lastName,
ph: order.customer.phone
});
const success = await window.pixelFlow.trackEvent("Purchase", customData, userData);
console.log(success ? "Purchase tracked" : "Tracking failed");
return success;
} catch (error) {
console.error("Error:", error);
return false;
}
}
// Auto-run when thank-you page loads
sendPurchaseFromAPI();
Conditional Lead Tracking
Lead
Send Lead event only after backend validation confirms lead quality
Lead Form with Backend Validation
The form submits to your backend for validation. Only when the backend returns a success response will the Lead event be sent to PixelFlow. Watch the Event Monitor to see the event fire.
/**
* Conditional Lead Tracking
* Only send Lead event after backend validation passes
*
* Use case: Validate email deliverability, check for spam,
* verify business domain, etc. before tracking as a qualified lead
*/
async function submitLeadForm(formData) {
const statusEl = document.getElementById("validation-status");
statusEl.textContent = "Validating...";
/* Expected formData structure:
{
email: "alex@techcorp.com",
firstName: "Alex",
lastName: "Johnson",
phone: "+1 555 987 6543",
company: "TechCorp Inc",
estimatedValue: 5000, // Estimated deal value
leadSource: "Website Demo Request"
}
*/
try {
// Step 1: Validate with your backend (email verification, spam check, etc.)
const validation = await fetch("/api/validate-lead", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: formData.email,
company: formData.company
})
});
const result = await validation.json();
if (!result.valid) {
statusEl.textContent = `Validation failed: ${result.reason}`;
return false;
}
statusEl.textContent = "Validation passed, tracking lead...";
// Step 2: Track Lead event only after validation passes
return await trackValidatedLead(formData);
} catch (error) {
statusEl.textContent = `Error: ${error.message}`;
return false;
}
}
async function trackValidatedLead(formData, retryCount = 0) {
const maxRetries = 3;
if (
!window.pixelFlow?.trackEvent ||
!window.pixelFlow?.utils?.normalizeCustomerData
) {
if (retryCount < maxRetries) {
await new Promise((resolve) => setTimeout(resolve, 1000));
return trackValidatedLead(formData, retryCount + 1);
}
return false;
}
try {
// Build customData for Lead event
const customData = {
value: formData.estimatedValue, // Estimated lead/deal value
currency: "USD", // ISO 4217 currency code
content_name: formData.leadSource, // Lead source or form name
content_category: formData.company // Can use for company/industry
};
// Normalize user data - all PII is hashed automatically
const userData = await window.pixelFlow.utils.normalizeCustomerData({
em: formData.email, // Email → normalized → SHA256 hashed
fn: formData.firstName, // First name → normalized → hashed
ln: formData.lastName, // Last name → normalized → hashed
ph: formData.phone // Phone → digits only → hashed
});
const success = await window.pixelFlow.trackEvent("Lead", customData, userData);
document.getElementById("validation-status").textContent = success
? "Lead tracked successfully!"
: "Tracking failed";
return success;
} catch (error) {
console.error("Tracking error:", error);
return false;
}
}
API Quick Reference
Core Functions
| Method | Returns | Description |
|---|---|---|
trackEvent(eventName, customData, userData)
|
Promise<boolean> |
Send an event to Meta both Client Side and Server Side via Conversions API |
utils.normalizeCustomerData(userData) |
Promise<UserData> |
Normalize user data (hashing is automatic) |
customData Properties
| Property | Type | Description |
|---|---|---|
value |
number | Monetary value (required for Purchase) |
currency |
string | ISO 4217 code: "USD", "EUR", "GBP" |
content_type |
string | "product" or "product_group" |
content_name |
string | Product or content name |
content_ids |
string[] | Array of product SKUs |
contents |
array | [{ id, quantity, item_price }] |
num_items |
number | Total quantity of items |
userData Properties
| Property | Description | Normalization |
|---|---|---|
em |
Email address | lowercase, trimmed |
fn |
First name | lowercase, trimmed |
ln |
Last name | lowercase, trimmed |
ph |
Phone number | digits only |