Overview
Conversion tracking links purchases back to the search queries and recommendations that drove them. This data powers revenue attribution reporting and improves recommendation models.
Tracking Purchases
Call trackPurchase when an order is completed:
refine.events.trackPurchase({
orderId: 'order_12345',
value: 249.97,
currency: 'USD',
items: [
{ itemId: 'sku_001', quantity: 1, unitPrice: 149.99 },
{ itemId: 'sku_002', quantity: 2, unitPrice: 49.99 }
]
});
Purchase Parameters
Unique identifier for the order. Used for deduplication.
Total order value (sum of all items).
ISO 4217 currency code (e.g., ‘USD’, ‘EUR’, ‘GBP’).
Array of purchased items.
PurchaseItem
Product ID matching your catalog.
Number of units purchased.
Price per unit at time of purchase.
Attribution
Refine automatically attributes purchases to previous interactions within the attribution window (default: 7 days). The flow:
- User searches for “running shoes”
- User clicks on product SKU_001
- User adds SKU_001 to cart
- User completes purchase with SKU_001
The purchase is attributed to the original search.
Attribution works automatically using the visitor/user ID. You don’t need to pass the original serve ID to trackPurchase.
Implementation Examples
Order Confirmation Page
// On order confirmation page
async function trackOrderComplete(order: Order) {
refine.events.trackPurchase({
orderId: order.id,
value: order.total,
currency: order.currency,
items: order.lineItems.map(item => ({
itemId: item.productId,
quantity: item.quantity,
unitPrice: item.price
}))
});
// Ensure event is sent before user navigates away
await refine.events.flush();
}
Shopify
WooCommerce
Custom Backend
// In your Shopify checkout extension or theme
document.addEventListener('DOMContentLoaded', () => {
// Check if on thank you page
if (window.Shopify?.checkout?.order_id) {
const checkout = window.Shopify.checkout;
refine.events.trackPurchase({
orderId: checkout.order_id.toString(),
value: parseFloat(checkout.total_price),
currency: checkout.currency,
items: checkout.line_items.map(item => ({
itemId: item.sku || item.variant_id.toString(),
quantity: item.quantity,
unitPrice: parseFloat(item.price)
}))
});
}
});
Handling Edge Cases
Duplicate Prevention
Refine deduplicates purchases by orderId. If the same order is tracked multiple times, only the first is recorded.
// Safe to call multiple times with same orderId
refine.events.trackPurchase({ orderId: 'order_123', ... });
refine.events.trackPurchase({ orderId: 'order_123', ... }); // Ignored
Partial Orders
Track only the items that were actually fulfilled:
// Original order had 3 items, but 1 was out of stock
refine.events.trackPurchase({
orderId: 'order_123',
value: 199.98, // Adjusted total
currency: 'USD',
items: [
{ itemId: 'sku_001', quantity: 1, unitPrice: 149.99 },
{ itemId: 'sku_002', quantity: 1, unitPrice: 49.99 }
// sku_003 excluded - out of stock
]
});
Refunds
For full refunds, no action needed (attribution is preserved for analytics).
For partial refunds, you may optionally track a negative adjustment:
// Optional: Track refund for analytics
refine.events.trackPurchase({
orderId: 'refund_order_123',
value: -49.99,
currency: 'USD',
items: [
{ itemId: 'sku_002', quantity: -1, unitPrice: 49.99 }
]
});
Guest Checkout
For guest checkout, the visitor ID automatically links the purchase to previous interactions:
// No identify() needed - visitor ID handles attribution
refine.events.trackPurchase({
orderId: 'order_guest_456',
value: 99.99,
currency: 'USD',
items: [{ itemId: 'sku_001', quantity: 1, unitPrice: 99.99 }]
});
Post-Purchase Identification
If the user creates an account after checkout, call identify to link:
// User completes purchase as guest
refine.events.trackPurchase({ orderId: 'order_123', ... });
// Later, user creates account
refine.identify('new_user_789');
// Future purchases and browsing now linked to user account
Ensuring Data Delivery
Always flush events after tracking purchases:
async function onOrderComplete(order: Order) {
refine.events.trackPurchase({
orderId: order.id,
value: order.total,
currency: order.currency,
items: order.items
});
// Wait for event to be sent
await refine.events.flush();
// Now safe to redirect
window.location.href = '/order-confirmation';
}
Beacon API Fallback
For page unloads, the SDK uses the Beacon API when available:
window.addEventListener('beforeunload', () => {
// SDK handles Beacon API internally during flush
refine.events.flush();
});
Testing Conversions
Enable debug mode to verify purchase tracking:
const refine = new Refine({
apiKey: process.env.REFINE_API_KEY,
organizationId: 'org_abc123',
catalogId: 'cat_xyz789',
debug: true // Logs all events to console
});
// Track a test purchase
refine.events.trackPurchase({
orderId: 'test_order_001',
value: 99.99,
currency: 'USD',
items: [{ itemId: 'sku_test', quantity: 1, unitPrice: 99.99 }]
});
// Check console for:
// [Refine] trackPurchase { orderId: 'test_order_001', ... }
Next Steps