Shopify Pixel integration
How the Apex Shopify Web Pixel maps Shopify customer events, dedupes event IDs, and buffers attribution.
The Apex Shopify Web Pixel runs inside Shopify's sandboxed pixel iframe. It subscribes to standard Shopify customer events, maps them to Apex events, and posts them to the Worker event pipeline.
Settings
The pixel receives settings from Shopify's webPixelCreate configuration:
| Setting | Purpose |
|---|---|
shopId | Public Apex shop ID on outgoing events. |
endpoint | Worker base URL; the pixel posts to {endpoint}/events. |
ingestShopId | Shop ID covered by the signed ingest token. |
ingestSignature | Signed pixel ingest token sent as _signature. |
enabledEvents | Shopify event names Apex should forward. |
trackPageViewed | Controls whether page_viewed is forwarded. |
queueUnattributedMs | Time to buffer events waiting for SDK assignment sync; default 3000 ms. |
maxBufferedEvents | Maximum buffered unattributed events; default 20. |
Assignment sync
The storefront SDK publishes assignment state through the Shopify analytics bridge:
Shopify.analytics.publish("drip_assignment_sync", {
visitorId: "visitor_demo_123",
sessionId: "session_demo_123",
assignments: [
{
experimentId: "22222222-2222-4222-8222-222222222222",
variationId: "33333333-3333-4333-8333-333333333333"
}
],
qaMode: false
});The pixel normalizes that payload, stores it in memory, and persists it best-effort to Shopify pixel local storage and a cookie named drip_assignment_sync_v1 for two hours.
Event mapping
| Shopify event | Apex event type | Key fields |
|---|---|---|
checkout_completed | goal | goalId: "__revenue__", funnelStep: "purchase_completed", revenue, currency, order ID, line items |
checkout_started | goal | goalId: "__checkout_started__", checkout value fields |
checkout_contact_info_submitted | goal | goalId: "__checkout_contact_info_submitted__" |
checkout_address_info_submitted | goal | goalId: "__checkout_address_info_submitted__" |
checkout_shipping_info_submitted | goal | goalId: "__checkout_shipping_info_submitted__" |
payment_info_submitted | goal | goalId: "__payment_info_submitted__" |
product_added_to_cart | goal | goalId: "__add_to_cart__", product, variant, price, quantity |
product_removed_from_cart | product_removed_from_cart | Product, variant, price, quantity |
cart_viewed | cart_viewed | Cart total and item count |
product_viewed | product_viewed | Product ID, title, type, vendor, price |
collection_viewed | collection_viewed | Collection ID and title |
search_submitted | search_submitted | Query and result count |
page_viewed | pageview | Page title and URL, only when trackPageViewed is true |
alert_displayed | alert_displayed | Message, alert type, area, target field, discount code when present |
ui_extension_errored | ui_extension_errored | Error and extension metadata |
drip_checkout_experiment_viewed | experiment_viewed | Checkout-block experiment and variation IDs |
drip_checkout_message_rendered | checkout_message_rendered | Checkout message shown for a test that also changes the storefront: experiment and variation IDs, assignment_epoch, extension_version; not an exposure, so results never count it |
Checkout line items list a bundle per component: each product in the bundle becomes its own line with its own price, and carries bundleParentLineId, bundleParentVariantId and bundleParentProductId so the lines can be grouped back into the bundle. A bundle whose component prices do not add up to the bundle line price stays as one line flagged bundleExpansion: "unreconciled". Each line also carries its own discountAllocations (amount, title and type) when Shopify allocated a discount to it.
Most mapped events add source: "shopify_pixel" in the event data before sending. Both checkout-block events use source: "shopify_checkout_block" so checkout extension exposures stay distinguishable.
Event IDs and dedupe
The pixel creates stable IDs when Shopify or commerce data provides one:
- Shopify event
id - Shopify event
eventId - Shopify event
clientId - Mapped
orderId - Mapped
order_id - Mapped
checkoutToken
Stable IDs use this shape:
shopify_pixel:{event_type}:{source_id}If no stable source identifier exists, the pixel generates a random event ID. The Worker performs bounded event ID dedupe before publishing to Tinybird.
Unattributed buffer
When no assignment sync is available yet, the pixel builds an unattributed event with visitorId: "shopify_anonymous" and needs_attribution: true.
If buffering is enabled, the event is kept in memory until assignment data arrives or queueUnattributedMs expires. When assignments arrive, the buffer replays each queued event with needs_attribution: false and one event per active assignment. If the timer expires, the buffer drains as unattributed.
Assignment events always enable the pixel's attribution buffer before replaying queued events.