Programmatic Tracking API
Overview
In the vast majority of cases, you do not need to use the Programmatic Tracking API. You can track page views, button clicks, form submissions, and e-commerce events out of the box via assigning events to particular URLs or via tagging interactive elements via classes without any developer involvement. This API is designed for advanced use cases that cannot be handled by standard tracking.
When the Programmatic API Makes Sense
Consider using the Programmatic Tracking API only when:
Granular Event Control Required – You need precise control over when events are sent based on complex business logic, conditional workflows, or specific application states that automatic tracking cannot detect.
Application-Level Data Access – You have customer or product data available in your application's JavaScript context (user objects, cart state, session variables) that you want to send with events without rendering them to the DOM and assigning CSS classes to appropriate elements.
Using this API requires developer involvement and JavaScript knowledge. If your tracking needs can be satisfied by PixelFlow's standard functionality, we recommend using those methods instead.
What This API Provides
PixelFlow's Programmatic Tracking API exposes the trackEvent function globally via window.pixelFlow.trackEvent(), along with utility functions for data normalization to ensure your data meets Meta's Conversions API requirements.
Getting Started
Wait Until PixelFlow Initializes
Prepare your payload (custom data) and normalized user data, then call the trackEvent function:
// Wait for PixelFlow to initialize, then track event
const trackPurchase = async () => {
if (
window.pixelFlow?.trackEvent &&
window.pixelFlow?.utils?.normalizeCustomerData
) {
try {
const normalizedCustomerData =
await window.pixelFlow.utils.normalizeCustomerData(customerData); // normalizes data
window.pixelFlow.trackEvent("Purchase", payload, normalizedCustomerData);
} catch (error) {
console.error("PixelFlow: Error tracking Purchase event", error);
}
} else {
console.warn("PixelFlow: API not ready, retrying...");
// Retry after 1 second if not ready
setTimeout(trackPurchase, 1000);
}
};The trackEvent Function
Function Signature
trackEvent(
eventName: string,
customData: CustomData,
userData: UserData
): Promise<boolean>Do not send these fields
trackEvent takes exactly three arguments: the event name, the custom data, and the user data. There is no fourth argument and no eventID option.
PixelFlow sets the fields below itself. Do not put them in customData, userData, or anywhere else in the call:
event_idandeventIDevent_timeandeventTimeaction_sourceandactionSourcefbpandfbcexternal_idclient_ip_address
event_id is generated on each call and shared by the Meta pixel and the server event so Meta can deduplicate them. If client code adds its own event_id inside custom data, the API returns HTTP 400 and nothing is recorded.
Parameters
1. eventName (string, required)
The name of the event you're tracking. Use Meta's standard event names.
Only standard Meta events are supported (e.g., "Purchase", "Lead", "AddToCart"). Custom event names are not supported at this time.
2. customData (CustomData, required)
Object containing event-specific data like product information and pricing. customData accepts only the fields listed below.
Accepted fields:
{
value?: number; // Monetary value (required for Purchase, recommended for value optimization)
currency?: string; // ISO 4217 currency code (e.g., "USD", "EUR", "GBP")
content_ids?: string[]; // Product SKUs or identifiers
content_name?: string; // Product or content name
content_type?: 'product' | 'product_group';
contents?: ContentItem[]; // Detailed product information
num_items?: number; // Quantity of items
searchStr?: string; // Search string (Search events only)
}On each contents item, delivery_category may only be "in_store", "curbside", or "home_delivery".
Any other property is not ignored. The event endpoint returns HTTP 400 and the event is not stored. This includes content_category: the server does not accept it, and if it is sent, the whole event is rejected.
Pass an empty object {} if no custom data is needed, but never pass null or undefined.
3. userData (UserData, required)
Object containing customer information for better ad targeting and attribution.
Common Properties:
{
em: string; // Email address (normalization required)
ph: string; // Phone number (normalization required)
fn: string; // First name (normalization required)
ln: string; // Last name (normalization required)
}Return Value
Returns a Promise<boolean>:
true- Event successfully queued and sent to Metafalse- Event failed to send (check console for errors)
The promise resolves after the event is queued, not after confirmation from Meta's servers. Check Meta Events Manager for actual event delivery status.
Available Utility Functions
PixelFlow exposes utility functions at window.pixelFlow.utils for data preparation:
Data Normalization Functions
Use these functions to prepare customer data before passing to trackEvent:
// Normalize entire customer data object
const normalizedData = await window.pixelFlow.utils.normalizeCustomerData({
em: "[email protected]",
fn: " John ",
ln: "Doe",
ph: "(555) 123-4567",
});
// Returns normalized and ready-to-use object
// Email normalization (lowercase, trim)
const normalizedEmail =
window.pixelFlow.utils.normalizeEmail("[email protected]");
// Returns: '[email protected]'
// Name normalization (lowercase, trim)
const normalizedName = window.pixelFlow.utils.normalizeName(" John ");
// Returns: 'john'
// Phone normalization (digits only, add country code)
const normalizedPhone = window.pixelFlow.utils.normalizePhone(
"+1 (555) 123-4567",
"US"
);
// Returns: '15551234567'
// Generic alphanumeric lowercase (for city, state, etc.)
const normalizedCity = window.pixelFlow.utils.normalizeAlnumLower("New York");
// Returns: 'newyork'
// Postal code normalization
const normalizedZip = window.pixelFlow.utils.normalizePostal("94103-1234");
// Returns: '94103'
// Country code normalization
const normalizedCountry =
window.pixelFlow.utils.normalizeCountry("United States");
// Returns: 'us'TypeScript Type Definitions:
/**
* Content item for product details
*/
export type ContentItem = {
/** Product ID or SKU */
id: string;
/** Quantity of the product */
quantity: number;
/** Price of the individual item */
item_price?: number;
/** "in_store", "curbside", or "home_delivery" */
delivery_category?: 'in_store' | 'curbside' | 'home_delivery';
};
/**
* CustomData type for Meta Conversions API
* Contains event-specific data like product information and pricing.
* Only the fields listed below are accepted. Any other property causes
* the event endpoint to return HTTP 400 and the event is not stored.
*/
export type CustomData = {
/** Monetary value associated with the event (required for Purchase) */
value?: number;
/** ISO 4217 currency code (e.g., "USD", "EUR", "GBP") */
currency?: string;
/** Product SKUs or identifiers */
content_ids?: string[];
/** Product or content name */
content_name?: string;
/** "product" or "product_group" */
content_type?: 'product' | 'product_group';
/** Array of product items with details */
contents?: ContentItem[];
/** Total number of items */
num_items?: number;
/** Search string (Search events only) */
searchStr?: string;
};
/**
* UserData type for Meta Conversions API
* Customer information for better ad targeting and attribution
* IMPORTANT: Data must be normalized before passing to trackEvent (use utils)
* Hashing is handled automatically by the script
*/
export type UserData = {
/**
* Email - must be normalized (lowercase, trimmed)
* Use window.pixelFlow.utils.normalizeCustomerData()
* @example "[email protected]" (normalized)
*/
em?: string;
/**
* Phone - must be normalized (digits only, with country code)
* Use window.pixelFlow.utils.normalizeCustomerData()
* @example "16505551212" (normalized)
*/
ph?: string;
/**
* First name - must be normalized (lowercase, trimmed)
* Use window.pixelFlow.utils.normalizeCustomerData()
* @example "john" (normalized)
*/
fn?: string;
/**
* Last name - must be normalized (lowercase, trimmed)
* Use window.pixelFlow.utils.normalizeCustomerData()
* @example "smith" (normalized)
*/
ln?: string;
/**
* Client User Agent
* Automatically captured by PixelFlow if not provided
*/
client_user_agent?: string;
};
/**
* PixelFlow global type for window.pixelFlow
*/
export type PixelFlow = {
version: string;
initialized: boolean;
trackEvent: (eventName: string, customData: CustomData, userData: UserData) => Promise<boolean>;
utils: {
/** Normalize entire customer data object (required before trackEvent, hashing automatic) */
normalizeCustomerData: (data: Partial<UserData>) => Promise<Partial<UserData>>;
/** Normalize email: lowercase, trim whitespace */
normalizeEmail: (email: string) => string;
/** Normalize name: lowercase, trim whitespace */
normalizeName: (name: string) => string;
/** Normalize phone: digits only, add country code if needed */
normalizePhone: (phone: string, countryCode?: string) => string;
/** Normalize to alphanumeric lowercase */
normalizeAlnumLower: (value: string) => string;
/** Normalize postal code: remove spaces and dashes */
normalizePostal: (postal: string) => string;
/** Normalize country: 2-letter ISO code lowercase */
normalizeCountry: (country: string) => string;
};
};
declare global {
interface Window {
pixelFlow?: PixelFlow;
}
}Real-World Examples
Example 1: Track Custom Form Submission
Scenario: Multi-step lead form where you want to track completion only after backend validation.
// After form validation succeeds on server
async function onFormValidationSuccess(formData) {
// Prepare user data from form
const userData = await window.pixelFlow.utils.normalizeCustomerData({
em: formData.email,
fn: formData.firstName,
ln: formData.lastName,
ph: formData.phone,
});
// Prepare custom data
const customData = {
value: 500, // Estimated lead value
currency: "USD",
};
// Track the lead event
const success = await window.pixelFlow.trackEvent(
"Lead",
customData,
userData
);
if (success) {
console.log("Lead tracked successfully");
} else {
console.error("Failed to track lead");
}
}Example 2: Stripe Purchases
For Stripe Checkout, do not send Purchase through trackEvent. In the PixelFlow dashboard, add a Purchase trigger and choose Stripe Checkout. PixelFlow receives the payment from Stripe, including email, phone, and name, then matches the Stripe Checkout session id on the success-page URL. That success URL must already include the session id.
Do not also fire Purchase from your own code, or the same order can be sent twice.
Test in PixelFlow Dashboard Events Page
Always verify events appear correctly in PixelFlow Events log:
Send an event using the API
Find your event, expand the event row and verify the event payload sent to Meta contains all the expected data
Events tracked programmatically will appear alongside your automatic events in the dashboard, making it easy to monitor and debug your implementation.
If a page view appears but the expected Lead, AddToCart, or Purchase does not, check the browser network tab for the event POST. An HTTP 400 response means the payload contains a field PixelFlow does not accept. Remove that field. Do not add more Meta fields to try to fix it.