When it comes to conversion tracking, a sale event happens when a user purchases your product or service. Examples include:
Subscribing to a paid plan
Usage expansion (upgrading from one plan to another)
Purchasing a product from your online store
In this guide, we will be focusing on tracking sales conversion events without a prior lead event. This is useful when you want to attribute a sale directly to a click, bypassing the lead tracking step.
First, you’ll need to enable conversion tracking for your Dub links to be able to start tracking conversions:
If you’re using Dub Partners, you can skip this
step since partner links will have conversion tracking enabled by default.
Option 1: On a workspace-level
To enable conversion tracking for all future links in a workspace, you can do the following:
To enable conversion tracking for all future links in a workspace, you can do the following:
Toggle the Workspace-level Conversion Tracking switch to enable conversion tracking for the workspace.
This option will enable conversion tracking in the Dub Link Builder for all future links.
Option 2: On a link-level
If you don’t want to enable conversion tracking for all your links in a workspace, you can also opt to enable it on a link-level.To enable conversion tracking for a specific link, open the Dub Link Builder for a link and toggle the Conversion Tracking switch.
You can also use the C keyboard shortcut when inside the link builder to
quickly enable conversion tracking for a given link.
Option 3: Via the API
Alternatively, you can also enable conversion tracking programmatically via the Dub API. All you need to do is pass trackConversion: true when creating or updating a link:
const link = await dub.links.create({ url: "https://dub.co", trackConversion: true,});
link = d.links.create(url="https://dub.co", track_conversion=True)
Then, you’ll need to install the Dub Analytics script and set up the necessary configuration for client-side conversion tracking:
1
Allowlist your site's domain
Then, you’ll need to allowlist your site’s domain to allow the client-side conversion events to be ingested by Dub.To do that, navigate to your workspace’s Tracking settings page and add your site’s domain to the Allowed Hostnames list.This provides an additional layer of security by ensuring only authorized domains can track conversions using your publishable key.
You can group your hostnames when adding them to the allow list:
example.com: Tracks traffic only from example.com.
*.example.com: Tracks traffic from all subdomains of example.com, but not from example.com itself.
When testing things out locally, you can add localhost to the Allowed
Hostnames list temporarily. This will allow local events to be ingested by
Dub. Don’t forget to remove it once you’re ready to go live!
2
Generate your publishable key
Before you can track conversions on the client-side, you need to generate a publishable key from your Dub workspace.To do that, navigate to your workspace’s Tracking settings page and generate a new publishable key under the Publishable Key section.
3
Install Dub Analytics script
Next, install the Dub Analytics script on your website/web application.You can install the Dub Analytics script in several different ways:
React
Manual installation
Framer
Shopify
WordPress
Webflow
Google Tag Manager
You must configure the publishable key you generated in step 1 when
installing the analytics script. Without this key, client-side conversion
tracking will not work.
To track a direct sale, you need to pass the clickId parameter along with customer information when tracking the sale event. The clickId can be read from the dub_id cookie that’s automatically set when a user clicks on your Dub link.
If you redirect users to a confirmation page after a successful purchase, you can track direct sales by reading query parameters from the URL and the dub_id cookie.
import { useAnalytics } from "@dub/analytics/react";import { useEffect } from "react";// Helper function to read cookiefunction getCookie(name: string) { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop()?.split(";").shift();}export function OrderConfirmationPage() { const { trackSale } = useAnalytics(); useEffect(() => { // Get query parameters from URL const params = new URLSearchParams(window.location.search); const customerId = params.get("customer_id"); const amount = params.get("amount"); const invoiceId = params.get("invoice_id"); // Get click ID from cookie const clickId = getCookie("dub_id"); if (customerId && amount && clickId) { // Track the direct sale event trackSale({ eventName: "Purchase", customerExternalId: customerId, amount: parseInt(amount), // Amount in cents invoiceId: invoiceId || undefined, // Required for direct sale tracking: clickId: clickId, customerName: "John Doe", // Optional: customer name customerEmail: "john@example.com", // Optional: customer email customerAvatar: "https://example.com/avatar.jpg", // Optional: avatar URL }); } }, [trackSale]); return <div>Thank you for your purchase!</div>;}
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Order Confirmation</title> </head> <body> <div>Thank you for your purchase!</div> <script> // Helper function to read cookie function getCookie(name) { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop().split(";").shift(); } // Get query parameters from URL const params = new URLSearchParams(window.location.search); const customerId = params.get("customer_id"); const amount = params.get("amount"); const invoiceId = params.get("invoice_id"); // Get click ID from cookie const clickId = getCookie("dub_id"); if (customerId && amount && clickId) { // Track the direct sale event dubAnalytics.trackSale({ eventName: "Purchase", customerExternalId: customerId, amount: parseInt(amount), // Amount in cents invoiceId: invoiceId || undefined, // Required for direct sale tracking: clickId: clickId, customerName: "John Doe", // Optional: customer name customerEmail: "john@example.com", // Optional: customer email customerAvatar: "https://example.com/avatar.jpg", // Optional: avatar URL currency: "usd", paymentProcessor: "stripe", metadata: { plan: "pro" }, }); } </script> </body></html>
You can also track direct sales from your backend by passing the clickId parameter when calling the track sale API:
import { Dub } from "dub";const dub = new Dub();await dub.track.sale({ customerExternalId: "cus_RBfbD57HDzPKpduI8elr5qHA", amount: 5000, paymentProcessor: "stripe", eventName: "Purchase", invoiceId: "in_1MtHbELkdIwH", currency: "usd", // Required for direct sale tracking: clickId: "cm3w...", // Pass the click ID from your frontend customerName: "John Doe", customerEmail: "john@example.com", customerAvatar: "https://example.com/avatar.jpg",});
from dub import Dubimport osdub = Dub(token=os.environ['DUB_API_KEY'])dub.track.sale({ 'external_id': 'cus_RBfbD57HDzPKpduI8elr5qHA', 'amount': 5000, 'payment_processor': 'stripe', 'event_name': 'Purchase', 'invoice_id': 'in_1MtHbELkdIwH', 'currency': 'usd', # Required for direct sale tracking: 'click_id': 'cm3w...', # Pass the click ID from your frontend 'customer_name': 'John Doe', 'customer_email': 'john@example.com', 'customer_avatar': 'https://example.com/avatar.jpg'})
package mainimport ( "context" dub "github.com/dubinc/dub-go")d := dub.New( dub.WithSecurity(os.Getenv("DUB_API_KEY")),)_, err := d.Track.Sale(context.Background(), &operations.TrackSaleRequest{ CustomerExternalId: "cus_RBfbD57HDzPKpduI8elr5qHA", Amount: 5000, PaymentProcessor: "stripe", EventName: "Purchase", InvoiceId: "in_1MtHbELkdIwH", Currency: "usd", // Required for direct sale tracking: ClickId: "cm3w...", // Pass the click ID from your frontend CustomerName: "John Doe", CustomerEmail: "john@example.com", CustomerAvatar: "https://example.com/avatar.jpg",})
require 'dub'dub = ::OpenApiSDK::Dub.newdub.config_security( ::OpenApiSDK::Shared::Security.new( token: ENV['DUB_API_KEY'] ))req = ::OpenApiSDK::Operations::TrackSaleRequest.new( external_id: 'cus_RBfbD57HDzPKpduI8elr5qHA', amount: 5000, payment_processor: 'stripe', event_name: 'Purchase', invoice_id: 'in_1MtHbELkdIwH', currency: 'usd', # Required for direct sale tracking: click_id: 'cm3w...', # Pass the click ID from your frontend customer_name: 'John Doe', customer_email: 'john@example.com', customer_avatar: 'https://example.com/avatar.jpg')dub.track.sale(req)
<?phprequire 'vendor/autoload.php';use Dub\Dub;use Dub\Models\Operations;$dub = Dub::builder()->setSecurity($_ENV["DUB_API_KEY"])->build();$request = new Operations\TrackSaleRequest();$request->customerExternalId = 'cus_RBfbD57HDzPKpduI8elr5qHA';$request->amount = 5000;$request->paymentProcessor = 'stripe';$request->eventName = 'Purchase';$request->invoiceId = 'in_1MtHbELkdIwH';$request->currency = 'usd';// Required for direct sale tracking:$request->clickId = 'cm3w...'; // Pass the click ID from your frontend$request->customerName = 'John Doe';$request->customerEmail = 'john@example.com';$request->customerAvatar = 'https://example.com/avatar.jpg';$dub->track->sale($request);
Here are the properties you can include when sending a sale event:
Property
Required
Description
customerExternalId
Yes
The unique ID of the customer in your system. Will be used to identify and attribute all future events to this customer.
amount
Yes
The amount of the sale in cents.
paymentProcessor
No
The payment processor that processed the sale (e.g. Stripe, Shopify). Defaults to “custom”.
eventName
No
The name of the event. Defaults to “Purchase”.
invoiceId
No
The invoice ID of the sale. Can be used as a idempotency key – only one sale event can be recorded for a given invoice ID.
currency
No
The currency of the sale. Defaults to “usd”.
metadata
No
An object containing additional information about the sale.
clickId
No
[For direct sale tracking]: The unique ID of the click that the sale conversion event is attributed to. You can read this value from dub_id cookie.
customerName
No
[For direct sale tracking]: The name of the customer. If not passed, a random name will be generated.
customerEmail
No
[For direct sale tracking]: The email address of the customer.
customerAvatar
No
[For direct sale tracking]: The avatar URL of the customer.
When to track sale: Track sale events only after a user successfully
completes a purchase or payment-related action. Ensure the event is triggered
only after the backend confirms the payment was successful.
And that’s it – you’re all set! You can now sit back, relax, and watch your conversion revenue grow. We provide 3 different views to help you understand your conversions:
Time-series: A time-series view of the number clicks, leads and sales.
Funnel chart: A funnel chart view visualizing the conversion & dropoff rates across the different steps in the conversion funnel (clicks → leads → sales).
Real-time events stream: A real-time events stream of every single conversion event that occurs across all your links in your workspace.