Overview
Conversion tracking allows you to measure the real business impact of your short links by tracking leads, sales, revenue, and customer journeys. Understand which links drive the most valuable actions and optimize your marketing accordingly.
Conversion tracking is available on Business plans and above.
Enabling Conversion Tracking
For New Links
Create Link
Open the link creation modal.
Toggle Conversion Tracking
Enable the “Conversion Tracking” toggle in the link builder.
Save Link
Create the link with conversion tracking enabled.
The conversion tracking toggle appears in the link builder with a “New” badge. Click to enable. Keyboard shortcut: Press C to quickly toggle conversion tracking.
const link = await fetch ( '/api/links' , {
method: 'POST' ,
body: JSON . stringify ({
domain: 'dub.sh' ,
key: 'product' ,
url: 'https://example.com/product' ,
trackConversion: true
})
});
For Existing Links
Enable conversion tracking on existing links:
Single Link
Bulk Update
API
Open the link editor
Toggle “Conversion Tracking” on
Save changes
Select multiple links
Click “Bulk Edit”
Choose “Add conversion tracking”
Apply to selected links
// Update single link
await fetch ( '/api/links/link_123' , {
method: 'PATCH' ,
body: JSON . stringify ({ trackConversion: true })
});
// Bulk update
await fetch ( '/api/links/bulk?workspaceId=ws_123' , {
method: 'PATCH' ,
body: JSON . stringify ({
linkIds: [ 'link_123' , 'link_456' ],
data: { trackConversion: true }
})
});
Conversion tracking requires Business plan or above. Links with A/B testing enabled must have conversion tracking on.
Conversion Events
Event Types
Dub tracks three types of conversion events:
Leads A user signs up, submits a form, or becomes a qualified lead
Sales A conversion results in a purchase or paid transaction
Conversions When a lead converts to a customer (lead → sale)
Recording Lead Events
Track when users become leads:
// Client-side tracking
await fetch ( 'https://api.dub.co/track/lead' , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({
clickId: 'click_abc123' , // From dub.co/click query parameter
eventName: 'Sign Up' ,
customerId: 'user_456' ,
customerName: 'John Doe' ,
customerEmail: '[email protected] ' ,
customerAvatar: 'https://example.com/avatar.jpg' ,
metadata: {
plan: 'pro' ,
source: 'organic'
}
})
});
The clickId comes from the URL query parameter when users click your short link.
Recording Sale Events
Track purchases and revenue:
// Server-side tracking (recommended for security)
await fetch ( 'https://api.dub.co/track/sale' , {
method: 'POST' ,
headers: {
'Authorization' : 'Bearer YOUR_API_KEY' ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
customerId: 'user_456' ,
amount: 9900 , // Amount in cents ($99.00)
currency: 'usd' ,
paymentProcessor: 'stripe' ,
invoiceId: 'inv_abc123' ,
metadata: {
product: 'Pro Plan' ,
billingCycle: 'monthly'
}
})
});
Always record sales server-side to prevent fraud. Never expose API keys in client-side code.
Customer Attribution
First-Touch Attribution
Dub automatically attributes conversions to the first link a customer clicked:
User clicks your short link → Click ID stored
User signs up → Lead event with Click ID
User purchases → Sale attributed to original link
// Lead event stores the attribution
await fetch ( 'https://api.dub.co/track/lead' , {
method: 'POST' ,
body: JSON . stringify ({
clickId: 'click_abc123' ,
customerId: 'user_456'
})
});
// Later: Sale automatically attributed
await fetch ( 'https://api.dub.co/track/sale' , {
method: 'POST' ,
body: JSON . stringify ({
customerId: 'user_456' , // Links to the original click
amount: 9900
})
});
Multi-Touch Attribution
Track the full customer journey:
// First touch - awareness
Click link A → clickId : click_001
// Second touch - consideration
Click link B → clickId : click_002
// Third touch - conversion
Click link C → clickId : click_003 ( used for lead event )
// All touches are tracked and visible in analytics
The lead event determines attribution, but all clicks from a customer are tracked.
Conversion Analytics
View Conversion Metrics
Access conversion data in analytics:
// Get lead events
const leads = await fetch (
'/api/analytics?event=leads&interval=30d'
);
// Get sales
const sales = await fetch (
'/api/analytics?event=sales&interval=30d'
);
// Get conversion rate
const conversions = await fetch (
'/api/analytics?event=composite&interval=30d'
);
// Returns: clicks, leads, sales, saleAmount
Each link tracks conversion statistics:
const link = await fetch ( '/api/links/link_123' );
const data = await link . json ();
console . log ({
clicks: data . clicks ,
leads: data . leads ,
conversions: data . conversions ,
sales: data . sales ,
saleAmount: data . saleAmount , // in cents
lastLeadAt: data . lastLeadAt ,
lastConversionAt: data . lastConversionAt
});
Time Series Analysis
Track conversions over time:
// Daily conversion timeline
const timeline = await fetch (
'/api/analytics?' +
'event=sales&' +
'groupBy=timeseries&' +
'interval=30d&' +
'granularity=day'
);
// Compare clicks vs conversions
const funnel = await fetch (
'/api/analytics?' +
'event=composite&' +
'groupBy=timeseries&' +
'interval=7d'
);
Revenue Tracking
Sale Amount Tracking
Track total revenue generated:
// Get total revenue for a link
const revenue = await fetch (
'/api/analytics?' +
'event=sales&' +
'linkId=link_123&' +
'interval=all'
);
const { saleAmount } = await revenue . json ();
console . log ( 'Total revenue:' , saleAmount / 100 ); // Convert cents to dollars
New vs Recurring Sales
Differentiate between new and recurring revenue:
// Track new customer sales
const newSales = await fetch (
'/api/analytics?event=sales&saleType=new&interval=30d'
);
// Track recurring revenue
const recurring = await fetch (
'/api/analytics?event=sales&saleType=recurring&interval=30d'
);
A sale is “new” if it’s the customer’s first purchase. Subsequent purchases are “recurring”.
Customer Data
Store rich customer data with conversions:
await fetch ( 'https://api.dub.co/track/lead' , {
method: 'POST' ,
body: JSON . stringify ({
clickId: 'click_abc123' ,
customerId: 'user_456' ,
customerName: 'Sarah Smith' ,
customerEmail: '[email protected] ' ,
customerAvatar: 'https://example.com/avatars/sarah.jpg' ,
metadata: {
company: 'Acme Corp' ,
industry: 'SaaS' ,
employees: '50-100' ,
source: 'linkedin' ,
campaign: 'Q1-2024'
}
})
});
Store custom metadata for detailed analysis:
// Flexible metadata structure
metadata : {
// Campaign tracking
utm_source : 'twitter' ,
utm_campaign : 'launch' ,
// Product info
product_id : 'prod_123' ,
plan : 'business' ,
// Qualification data
lead_score : 85 ,
industry : 'technology' ,
// Custom fields
referral_code : 'REF123' ,
promo_code : 'SAVE20'
}
Query Customer Events
Search conversions by metadata:
// Find leads from specific campaign
const campaignLeads = await fetch (
"/api/events?" +
"event=leads&" +
"query=metadata['utm_campaign']:'launch'"
);
// Find high-value sales
const highValue = await fetch (
"/api/events?" +
"event=sales&" +
"query=metadata['plan']:'enterprise'"
);
Integration Examples
Next.js Integration
// app/api/signup/route.ts
export async function POST ( req : Request ) {
const { email , name } = await req . json ();
// Get click ID from session/cookie
const clickId = req . cookies . get ( 'dub.co/click' )?. value ;
// Create user in your database
const user = await createUser ({ email , name });
// Track lead event
if ( clickId ) {
await fetch ( 'https://api.dub.co/track/lead' , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({
clickId ,
customerId: user . id ,
customerEmail: email ,
customerName: name
})
});
}
return Response . json ({ success: true });
}
Stripe Integration
// Webhook handler for Stripe payments
import Stripe from 'stripe' ;
const stripe = new Stripe ( process . env . STRIPE_SECRET_KEY );
export async function POST ( req : Request ) {
const event = await stripe . webhooks . constructEvent (
await req . text (),
req . headers . get ( 'stripe-signature' ),
process . env . STRIPE_WEBHOOK_SECRET
);
if ( event . type === 'checkout.session.completed' ) {
const session = event . data . object ;
// Track sale event
await fetch ( 'https://api.dub.co/track/sale' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ process . env . DUB_API_KEY } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
customerId: session . client_reference_id ,
amount: session . amount_total ,
currency: session . currency ,
paymentProcessor: 'stripe' ,
invoiceId: session . invoice ,
metadata: {
plan: session . metadata . plan ,
interval: session . metadata . interval
}
})
});
}
return Response . json ({ received: true });
}
Conversion Funnel Analysis
Analyze your conversion funnel:
// Get funnel metrics
const funnel = await fetch (
'/api/analytics?event=composite&linkId=link_123&interval=30d'
);
const { clicks , leads , sales } = await funnel . json ();
const clickToLeadRate = ( leads / clicks ) * 100 ;
const leadToSaleRate = ( sales / leads ) * 100 ;
const clickToSaleRate = ( sales / clicks ) * 100 ;
console . log ({
clickToLeadRate: ` ${ clickToLeadRate . toFixed ( 2 ) } %` ,
leadToSaleRate: ` ${ leadToSaleRate . toFixed ( 2 ) } %` ,
clickToSaleRate: ` ${ clickToSaleRate . toFixed ( 2 ) } %`
});
Best Practices
Enable Workspace-Wide Turn on conversion tracking by default for all new links in your workspace settings
Track Server-Side Always record sale events server-side to ensure accuracy and prevent fraud
Use Unique Customer IDs Maintain consistent customer IDs across lead and sale events for accurate attribution
Include Rich Metadata Add detailed metadata to enable powerful filtering and analysis
Monitor Conversion Rates Regularly review click-to-lead and lead-to-sale rates to optimize performance
Test Attribution Test the full flow in development to ensure proper event tracking
Troubleshooting
Conversions Not Tracking
Verify Tracking Enabled
Confirm trackConversion: true on the link.
Check Click ID
Ensure the clickId is being captured from the URL and passed to lead events.
Validate Customer ID
Use the same customerId for both lead and sale events.
Review API Responses
Check for error responses when recording events.
API Reference
For detailed API documentation: