Install RecartIQ

Four steps. Traffic and friction come from the snippet; orders you send, because no script can see them on its own. Pick your platform.

1.Add the snippet to your global header template

<script>window.riq=window.riq||function(){(riq.q=riq.q||[]).push(arguments)};</script>
<script async src="https://cdn.recartiq.com/r.js" data-key="pk_..."></script>

Both lines, in that order. The first is a queue: the snippet loads asynchronously so it does not slow your store down, and without the queue a call made before it finishes throws and the event is lost. Your key is on the Install page of the store and in Settings → API keys.

That alone gives you page views, sessions, device, country and UTM attribution. Single-page apps get a page view on every route change. Empty searches and checkout or coupon errors are picked up too, where your pages expose them: a path containing cart or checkout, and the error rendered in an element with role="alert".

Out-of-stock views, size-guide opens and product views are not detected on a custom site. Those need theme markup we recognise, so send them with the calls in step 2.

2.Send the commerce events

Call these wherever the thing happens: the product page, the add-to-cart handler, the checkout button.

riq("track", "view_product",   { product_id: "SKU-1", name: "Trail Jacket", price: 129, currency: "USD" });
riq("track", "add_to_cart",    { product_id: "SKU-1", price: 129, currency: "USD", quantity: 1, cart_value: 129 });
riq("track", "begin_checkout", { cart_value: 129, currency: "USD" });
riq("identify", "customer-123", { email: "c@example.com" });

No JavaScript to hand? Annotate the button instead:

<button data-riq-event="add_to_cart" data-riq-product-id="SKU-1" data-riq-price="129" data-riq-currency="USD">Add to cart</button>

3.Send the purchase

You own the checkout, so call it on your order confirmation page like any other event. It carries the revenue behind conversion rate, AOV and the value of every product leak, and nothing detects it for you.

riq("track", "purchase", { order_id: "1001", revenue: 137.9, currency: "USD", shipping: 8.9,
                           items: [{ product_id: "SKU-1", price: 129, quantity: 1 }] });

If the confirmation page can be reloaded, send it from your backend with the HTTP API below instead, so one order is counted once.

4.Check it arrived

The Install page of each store watches for the first event and lists which standard events it has seen, so a half-finished install is obvious. Live view shows events landing as they happen. An event missing a required property is rejected and named in the response.

Sending from a backend instead? The HTTP API below takes the same events, against recartiq.com.

Friction events

These turn "checkout conversion fell" into a reason. Send the ones your theme does not expose:

riq("track", "shipping_cost_viewed", { amount: 8.9, currency: "USD", cart_value: 129 });
riq("track", "coupon_rejected",      { code: "FALL10", reason: "expired" });
riq("track", "payment_failed",       { method: "card", reason: "declined" });

Event reference

EventRequired properties
page_viewnone
view_productproduct_id, price
add_to_cartproduct_id, price
remove_from_cartproduct_id, price
begin_checkoutcart_value
purchaseorder_id, revenue
refundorder_id, revenue
identifynone (call riq identify)
checkout_errornone
coupon_rejectedcode
payment_failednone
out_of_stock_viewproduct_id
variant_unavailableproduct_id, variant
search_no_resultsquery
shipping_cost_viewedamount
size_guide_openedproduct_id

Every amount is tracked in the store's own currency, set in Settings. currency is optional; if you send a different code the amount is counted at face value and the code you sent is kept as original_currency. remove_from_cart is stored and available in the Events explorer and custom funnels, but no built-in report uses it. quantity defaults to 1 where it applies. Custom events: any name matching ^[a-z][a-z0-9_]{0,63}$ with free-form properties. Keys named password, card_number, cardnumber, cvv, cvc or ssn are dropped on arrival and never stored.

Server-side (HTTP API)

POST https://recartiq.com/api/v1/track
Authorization: Bearer pk_...
Content-Type: application/json

{ "batch": [
  { "event": "purchase", "insert_id": "<uuid>", "timestamp": "2026-09-16T10:12:03Z",
 "distinct_id": "customer-123",
 "properties": { "order_id": "1001", "revenue": 137.9, "currency": "USD" } }
] }

Up to 100 events and 512 KB per request. The key also works as ?k= or an X-RecartIQ-Key header. Always send insert_id and timestamp so retries deduplicate. Timestamps more than 7 days old, or more than 5 minutes ahead, are clamped.

Privacy

The snippet respects Do Not Track and Global Privacy Control by default, sets first-party cookies only, and never sends IP addresses or raw user agents to storage. Delete a customer's data with DELETE /api/v1/persons/:distinct_id or from Settings → Data.