Hi
Product
Features
Security
Difference
Limits
FAQ
Pricing
Doc & Api
Get started free
NEXT-GEN LINK INFRASTRUCTURE

Stop guessing.
Start improving.

Deploy custom domains, ISO country routing, A/B split testing, PathLock™ PIN gates, and deterministic revenue attribution in under two minutes.

Get startedAccess our plans
Platform
  • Country & Device Routing
  • Weighted A/B Testing
  • Revenue Attribution (EPC)
  • Dynamic QR Studio
Security & Access
  • PathLock™ PIN Protection
  • Branded URL Masking
  • Click Limits & Expiration
  • Custom OpenGraph Cards
Developers
  • REST API & SDK Quickstart
  • Real-Time Click Telemetry
  • Signed Webhooks & Events
  • Full Documentation Hub
Legal & Trust
  • Privacy Policy (GDPR)
  • Terms of Service
  • No-Refund Policy
  • Dashboard Overview
LShorter Logo
LShorter© 2026 LShorter Inc.
Built on Cloudflare Edge Network
All 310+ Cloudflare Edge PoPs Operational
Privacy•Terms•Refunds
LShorter Logo
LShorter
HiHiProductProductFeaturesFeaturesSecuritySecurityDifferenceDifferenceLimitsLimitsFAQFAQPricingPricing
Doc & ApiDoc & Api
SDK & API Reference
Endpoints REST, clés Bearer et SDK TypeScript
Routage intelligent & Géo
Ciblage pays, OS mobile et redirection Edge
PathLock™ & Sécurité
Protection par code PIN, expiration et masquage
Télémétrie & Analytics
Flux de clics en direct, attribution et webhooks
RESSOURCESDocumentation

Documentation

Get started
LShorter Logo
LShorter
All guides & documentation

Conversion Tracking & Financial Metrics (Amount & Count)

Complete end-to-end guide to attributing monetary revenue (amount), calculating EPC and ROI, capturing clickId across checkout funnels, and handling multi-currency purchases and refunds.

6 min read•Official Edge Documentation
Conversion Tracking & Financial Metrics (Amount & Count)

Overview & Architecture

The LShorter Financial Conversion Engine provides exact, end-to-end monetary attribution by linking e-commerce revenue (amount) directly to the originating short link click (clickId). Whether you sell via Stripe, Shopify, LemonSqueezy, WooCommerce, or a custom checkout funnel, every dollar, euro, or pound is mathematically tied to its acquisition channel.

On the telemetry side, click counters (clicksCount) and conversions (conversionsCount) are incremented lock-free at Cloudflare's Edge. The platform continuously computes critical financial efficiency KPIs: Earnings Per Click (EPC = trackedRevenue / totalClicks), Average Order Value (AOV = trackedRevenue / conversionsCount), and Conversion Rate (CR = conversionsCount / totalClicks * 100).

The system natively handles multi-currency normalization (USD, EUR, GBP, CAD, JPY), partial or full refund deductions (POST /api/v1/track/refund) to keep financial reports clean, and delayed asynchronous payments.

Key Principles

Core architectural pillars ensuring high performance, isolation, and security.

01Automated Click Attribution (clickId)
When a visitor clicks an LShorter link, our Edge Worker automatically appends an ephemeral ref=clk_... token to the destination URL. This token is stored in the browser and passed to checkout.
02Monetary Revenue Ingestion (amount & currency)
Pass exact transaction amounts (e.g., amount: 49.00, currency: 'USD'). The API updates trackedRevenue atomically with zero lock contention even during viral flash sales.
03Real-Time EPC & Profitability Formulas
Earnings Per Click (EPC = trackedRevenue / totalClicks) lets you compare earnings directly against ad Cost Per Click (CPC). When EPC > CPC, your campaign is profitable.
04Refund & Chargeback Adjustments
Maintain pristine financial accounting: refund endpoints automatically deduct refunded amounts from trackedRevenue and update ROI calculations.
05Cookieless Edge Deduplication
GDPR and PECR compliant visitor tracking that accurately counts unique buyers and conversion events without violating user privacy laws.
SDK Integration

Implementation & SDK Examples

Ready-to-use TypeScript code snippets and configuration examples for your integrations.

1. Universal Client-Side Tracking Pixel (HTML / Webflow / Shopify / WordPress)

Add this lightweight snippet to your website's <head> or footer to automatically capture the clickId from the URL and persist it in a 30-day cookie and localStorage:

lshorter-pixel.js
javascript
12345678910111213141516171819
<!-- LShorter Universal Attribution Pixel -->
<script>
(function() {
// 1. Extract clickId parameter (?ref=clk_... or ?clickId=clk_...)
const params = new URLSearchParams(window.location.search);
const clickId = params.get("ref") || params.get("clickId");
if (clickId) {
// 2. Persist in sessionStorage and a 30-day first-party cookie
sessionStorage.setItem("lshorter_click_id", clickId);
document.cookie = "lshorter_click_id=" + encodeURIComponent(clickId) + "; path=/; max-age=" + (30 * 86400) + "; SameSite=Lax";
// 3. Automatically populate any hidden input fields in forms
document.querySelectorAll('input[name="clickId"], input[name="lshorter_click_id"]').forEach(function(el) {
el.value = clickId;
});
}
})();
</script>

2. Next.js (App Router) & React Attribution Hook

React hook to capture the clickId from Next.js useSearchParams and persist it across client routing:

hooks/use-lshorter-attribution.ts
typescript
123456789101112131415161718192021222324
"use client";
import { useEffect, useState } from "react";
import { useSearchParams } from "next/navigation";
export function useLShorterAttribution() {
const searchParams = useSearchParams();
const [clickId, setClickId] = useState<string | null>(null);
useEffect(() => {
// Check URL search params first, fallback to session storage
const paramClickId = searchParams.get("ref") || searchParams.get("clickId");
if (paramClickId) {
sessionStorage.setItem("lshorter_click_id", paramClickId);
setClickId(paramClickId);
} else {
const stored = sessionStorage.getItem("lshorter_click_id");
if (stored) setClickId(stored);
}
}, [searchParams]);
return { clickId };
}

3. Stripe Checkout Session & Webhook Ingestion

Pass clickId and customer details into Stripe Checkout Session metadata, then attribute revenue upon payment in your webhook handler:

app/api/webhooks/stripe/route.ts
typescript
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
import { NextRequest, NextResponse } from "next/server";
import { lshorter } from "@/lib/lshorter";
import Stripe from "stripe";
import crypto from "node:crypto";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: "2023-10-16" });
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET!;
// Helper: Gravatar from email
function getGravatarUrl(email?: string): string | undefined {
if (!email) return undefined;
const hash = crypto.createHash("md5").update(email.trim().toLowerCase()).digest("hex");
return `https://www.gravatar.com/avatar/${hash}?s=200&d=identicon`;
}
export async function POST(req: NextRequest) {
const rawBody = await req.text();
const sig = req.headers.get("stripe-signature")!;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(rawBody, sig, endpointSecret);
} catch (err: any) {
return NextResponse.json({ error: `Webhook Error: ${err.message}` }, { status: 400 });
}
// Handle successful checkout payment
if (event.type === "checkout.session.completed") {
const session = event.data.object as Stripe.Checkout.Session;
// 1. Extract clickId passed in session metadata or client_reference_id
const clickId = session.metadata?.clickId || session.client_reference_id || undefined;
const amountPaid = (session.amount_total || 0) / 100; // Convert cents to decimal dollars/euros
const customerEmail = session.customer_details?.email || undefined;
const customerName = session.customer_details?.name || undefined;
const customerAvatar = session.metadata?.avatarUrl || getGravatarUrl(customerEmail);
// 2. Report monetary conversion to LShorter API with customer avatar
await lshorter.track.conversion({
eventName: "purchase",
amount: amountPaid, // e.g. 49.00
currency: (session.currency || "USD").toUpperCase(),
clickId: clickId,
orderId: session.id,
customer: {
email: customerEmail,
name: customerName,
avatarUrl: customerAvatar, // Customer profile picture displayed in dashboard
},
customerAvatar: customerAvatar, // Top-level alias also supported
metadata: {
paymentIntent: session.payment_intent as string,
customerCountry: session.customer_details?.address?.country,
}
});
console.log(`Successfully attributed ${amountPaid} ${session.currency} to clickId ${clickId}`);
}
return NextResponse.json({ received: true });
}

4. Shopify / E-Commerce Webhook Ingestion with Customer Avatar

Attributing orders created in Shopify store back to LShorter short links with customer avatar:

app/api/webhooks/shopify/route.ts
typescript
12345678910111213141516171819202122232425262728293031
import { NextRequest, NextResponse } from "next/server";
import { lshorter } from "@/lib/lshorter";
export async function POST(req: NextRequest) {
const order = await req.json();
// Find clickId in Shopify cart note attributes
const clickIdAttr = order.note_attributes?.find(
(attr: { name: string; value: string }) => attr.name === "clickId" || attr.name === "lshorter_click_id"
);
const clickId = clickIdAttr ? clickIdAttr.value : undefined;
const totalAmount = parseFloat(order.total_price); // e.g. 89.50
const customerEmail = order.email;
const customerAvatar = order.customer?.avatar_url || `https://www.gravatar.com/avatar/${customerEmail}?d=identicon`;
await lshorter.track.conversion({
eventName: "shopify_order",
amount: totalAmount,
currency: order.currency || "USD",
clickId: clickId,
orderId: String(order.id),
customer: {
email: customerEmail,
name: order.customer ? `${order.customer.first_name} ${order.customer.last_name}` : undefined,
avatarUrl: customerAvatar,
},
customerAvatar: customerAvatar,
});
return NextResponse.json({ status: "success" });
}

5. Customer Avatar & Identity Resolution (customerAvatar & avatarUrl)

Extract customer profile images from Google/GitHub OAuth, NextAuth, Clerk, or Gravatar and supply them to LShorter:

resolve-customer-avatar.ts
typescript
12345678910111213141516171819202122232425262728293031
import { lshorter } from "@/lib/lshorter";
import crypto from "node:crypto";
// 1. Helper function: Compute Gravatar URL from email
function getGravatar(email: string): string {
const hash = crypto.createHash("md5").update(email.trim().toLowerCase()).digest("hex");
return `https://www.gravatar.com/avatar/${hash}?s=200&d=identicon`;
}
// 2. Resolve avatar from OAuth session (NextAuth, Clerk, Supabase) or Gravatar
const userEmail = "alex.johnson@example.com";
const userAvatarUrl = session?.user?.image // NextAuth Google/GitHub profile picture
|| user?.imageUrl // Clerk Auth
|| getGravatar(userEmail); // Gravatar fallback
// 3. Track conversion with customer avatar
await lshorter.track.conversion({
eventName: "purchase",
amount: 49.00,
currency: "USD",
clickId: "clk_9921ab01",
orderId: "ord_992812",
customer: {
name: "Alex Johnson",
email: userEmail,
avatarUrl: userAvatarUrl, // Photo displayed in real-time revenue telemetry
},
customerAvatar: userAvatarUrl, // Top-level alias
});
console.log("Conversion tracked with customer avatar:", userAvatarUrl);

6. Processing Partial & Full Refunds (track.refund)

Deduct refunded amounts from cumulative link revenue to maintain accurate financial telemetry:

handle-refund.ts
typescript
12345678910111213
import { lshorter } from "@/lib/lshorter";
// Deduct a $49.00 customer refund
const refundResult = await lshorter.track.refund({
orderId: "ch_9921823ab", // Same orderId passed during conversion
amount: 49.00,
currency: "USD",
reason: "customer_requested_return",
refundId: "re_3Nq..."
});
console.log("Updated tracked revenue:", refundResult.newTrackedRevenue, "USD");
console.log("Deducted amount:", refundResult.amountRefunded, "USD");

7. Querying Real-Time Financial Telemetry & EPC via SDK

Read atomic click counters, cumulative revenue, AOV, and calculate campaign ROI:

get-roi-metrics.ts
typescript
123456789101112131415161718192021222324
import { lshorter } from "@/lib/lshorter";
// Query financial telemetry for a marketing campaign
const roi = await lshorter.analytics.getRevenue("summer-sale-2026", {
period: "30d",
currency: "USD",
});
console.log("Total Clicks:", roi.totalClicks);
console.log("Unique Visitors:", roi.uniqueClicks);
console.log("Conversions Count:", roi.conversionsCount);
console.log("Conversion Rate:", roi.conversionRate.toFixed(2) + "%");
console.log("Tracked Revenue (Amount):", roi.trackedRevenue.toFixed(2), "USD");
console.log("Average Order Value (AOV):", roi.averageOrderValue.toFixed(2), "USD");
console.log("Earnings Per Click (EPC):", roi.epc.toFixed(2), "USD / click");
// Calculate Net Profit assuming $0.50 CPC advertising spend
const adSpend = roi.totalClicks * 0.50; // $1,250 ad spend
const netProfit = roi.trackedRevenue - adSpend; // $3,332 - $1,250 = $2,082
const roas = (roi.trackedRevenue / adSpend) * 100; // 266.5% Return on Ad Spend
console.log("Ad Spend:", adSpend.toFixed(2), "USD");
console.log("Net Profit:", netProfit.toFixed(2), "USD");
console.log("ROAS:", roas.toFixed(1) + "%");
REST API Specification

API Endpoints & Reference

Direct HTTP request methods, parameters schema, authentication, and responses.

POST

Record Conversion & Monetary Amount

Bearer <API_KEY>60 to 1,400 req/min (by plan)
https://lsho.cc/api/v1/track/conversion

Records a completed purchase, subscription, or conversion event with revenue amount, currency, and customer details attributed to a specific clickId.

Headers

ParameterTypeRequiredDescriptionExample
AuthorizationstringRequiredBearer token containing your API key.Bearer lsho_live_89f023ab912
Content-TypestringRequiredMust be set to application/json.application/json

Request Body Parameters

ParameterTypeRequiredDescriptionExample
eventNamestringRequiredName of the conversion event (e.g. purchase, subscription, upsell).purchase
amountnumberRequiredDecimal transaction revenue amount collected.49.00
currencystring (ISO 4217)RequiredThree-letter uppercase currency code (USD, EUR, GBP, CAD, JPY).USD
clickIdstringOptionalOriginating clickId token passed by the short link redirection.clk_9921ab01
linkIdstringOptionalShort link ID or slug alias if clickId is not available.link_89f023ab912
orderIdstringOptionalYour internal transaction or invoice ID for deduplication.ch_9921823ab
customerobjectOptionalCustomer metadata.{"email": "alex@example.com", "name": "Alex Johnson", "avatarUrl": "https://example.com/photos/alex.jpg"}
customer.emailstring (email)OptionalCustomer email address for LTV attribution.alex@example.com
customer.namestringOptionalCustomer full name.Alex Johnson
customer.avatarUrlstring (url)OptionalAvatar image URL displayed in telemetry dashboards.https://example.com/photos/alex.jpg
customerAvatarstring (url)OptionalTop-level alias for customer avatar image URL (Gravatar, Google, Discord, GitHub).https://example.com/photos/alex.jpg
metadataobjectOptionalArbitrary JSON key-value metadata for downstream reporting.{"plan": "pro_annual", "coupon": "SUMMER30"}

Request Examples

bash
12345678910111213141516171819
curl -X POST "https://lsho.cc/api/v1/track/conversion" \
-H "Authorization: Bearer $LSHORTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"eventName": "purchase",
"amount": 49.00,
"currency": "USD",
"clickId": "clk_9921ab01",
"orderId": "ch_9921823ab",
"customer": {
"email": "customer@example.com",
"name": "Alex Johnson",
"avatarUrl": "https://example.com/photos/alex.jpg"
},
"metadata": {
"plan": "pro_annual",
"coupon": "SUMMER30"
}
}'

Response Payload

201 CreatedConversion successfully recorded and financial metrics updated in real time.
response-201.json
json
123456789101112
{
"conversionId": "conv_77192",
"clickId": "clk_9921ab01",
"linkId": "link_89f023ab912",
"slug": "summer-sale-2026",
"amount": 49.00,
"currency": "USD",
"status": "attributed",
"cumulativeTrackedRevenue": 3332.00,
"updatedEpc": 1.33,
"createdAt": "2026-09-17T12:40:00.000Z"
}

Possible Error Responses

HTTP StatusError CodeDescription
400INVALID_AMOUNTThe amount parameter must be a positive decimal number.
400INVALID_CURRENCYCurrency must be a valid 3-letter ISO 4217 uppercase code (e.g. USD, EUR, GBP).
401UNAUTHORIZEDMissing or invalid Bearer API key.
404CLICK_OR_LINK_NOT_FOUNDNeither clickId nor linkId matched an active record.
409DUPLICATE_ORDER_IDA conversion with this orderId has already been recorded (idempotency protection).
POST

Process Refund or Chargeback

Bearer <API_KEY>60 to 1,400 req/min (by plan)
https://lsho.cc/api/v1/track/refund

Deducts a full or partial refund amount from the link's cumulative revenue, adjusting EPC and AOV atomically.

Request Body Parameters

ParameterTypeRequiredDescriptionExample
orderIdstringRequiredThe original orderId passed during conversion recording.ch_9921823ab
amountnumberRequiredRefunded monetary amount.49.00
currencystring (ISO 4217)OptionalCurrency code (defaults to original order currency).USD
reasonstringOptionalReason for refund (e.g. customer_return, fraud, cancelled).customer_requested_return
refundIdstringOptionalYour payment gateway refund identifier.re_3Nq2182

Request Examples

bash
12345678
curl -X POST "https://lsho.cc/api/v1/track/refund" \
-H "Authorization: Bearer $LSHORTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"orderId": "ch_9921823ab",
"amount": 49.00,
"reason": "customer_requested_return"
}'

Response Payload

200 OKRefund applied and revenue metrics adjusted.
response-200.json
json
12345678910
{
"refundId": "ref_992812",
"orderId": "ch_9921823ab",
"amountRefunded": 49.00,
"currency": "USD",
"newTrackedRevenue": 3283.00,
"updatedEpc": 1.31,
"status": "refunded",
"updatedAt": "2026-09-17T13:00:00.000Z"
}
GET

Get Financial & Revenue Telemetry

Bearer <API_KEY>60 to 1,400 req/min (by plan)
https://lsho.cc/api/v1/analytics/:slug/revenue

Retrieves monetary revenue totals, conversion counts, Average Order Value (AOV), and Earnings Per Click (EPC) for a short link.

Path Parameters

ParameterTypeRequiredDescriptionExample
slugstringRequiredShort link slug alias.summer-sale-2026

Query Parameters

ParameterTypeRequiredDescriptionExample
periodstring (24h | 7d | 30d | 90d | all)OptionalTimeframe window for financial metrics.Default: 30d30d
currencystringOptionalTarget currency for display conversion.Default: USDUSD

Request Examples

bash
12
curl -X GET "https://lsho.cc/api/v1/analytics/summer-sale-2026/revenue?period=30d" \
-H "Authorization: Bearer $LSHORTER_API_KEY"

Response Payload

200 OKComprehensive financial telemetry payload.
response-200.json
json
12345678910111213141516171819202122232425262728293031
{
"slug": "summer-sale-2026",
"period": "30d",
"currency": "USD",
"counts": {
"totalClicks": 2500,
"uniqueClicks": 2100,
"conversionsCount": 68,
"conversionRate": 2.72
},
"financials": {
"trackedRevenue": 3332.00,
"currency": "USD",
"averageOrderValue": 49.00,
"earningsPerClick": 1.33
},
"recentTransactions": [
{
"conversionId": "conv_77192",
"orderId": "ch_9921823ab",
"amount": 49.00,
"currency": "USD",
"customer": {
"email": "customer@example.com",
"name": "Alex Johnson",
"avatarUrl": "https://example.com/photos/alex.jpg"
},
"timestamp": "2026-09-17T12:40:00.000Z"
}
]
}

Full Conversion & Financial Payload Example

Structured JSON response returning atomic visit counts, financial revenue sums, EPC, and customer transactions:

response.json
json
12345678910111213141516171819202122232425262728293031323334
{
"slug": "summer-sale-2026",
"linkId": "link_89f023ab912",
"currency": "USD",
"counts": {
"clicksCount": 2500,
"uniqueClicks": 2100,
"conversionsCount": 68,
"maxClicks": 5000,
"remainingClicks": 2500
},
"financials": {
"trackedRevenue": 3332.00,
"currency": "USD",
"conversionRate": 2.72,
"averageOrderValue": 49.00,
"earningsPerClick": 1.33
},
"recentConversions": [
{
"conversionId": "conv_77192",
"clickId": "clk_9921ab01",
"orderId": "ch_9921823ab",
"amount": 49.00,
"currency": "USD",
"customer": {
"name": "Alex Johnson",
"email": "customer@example.com",
"avatarUrl": "https://example.com/photos/alex.jpg"
},
"timestamp": "2026-09-17T12:40:00.000Z"
}
]
}

Recommended Best Practices

Expert tips to maximize performance, resilience, and link security.

Recommendation 01

Always store clickId in payment metadata (e.g. Stripe Session Metadata, Shopify Cart Note) so async webhooks and retries never lose attribution.

Recommendation 02

Pass numeric decimal amounts (e.g. 49.00 rather than 4900 cents) and standard 3-letter ISO uppercase currencies (USD, EUR, GBP).

Recommendation 03

Subscribe to Stripe charge.refunded or Shopify refunds/create webhooks to call POST /api/v1/track/refund and maintain pristine financial accounting.

Recommendation 04

Compare your EPC (Earnings Per Click) directly against your CPC (Cost Per Click) to automatically calculate Return on Ad Spend (ROAS).

Previous guideWebhooks & Real-Time Events APINext guidePercentage Routing & A/B Testing API

Related Guides & APIs

Quickstart & REST API Reference
Quickstart & REST API Reference
Integrate intelligent edge link shortening, real-time analytics, and conversion tracking with our high-performance REST API and TypeScript SDK.
Read guide & API
Dynamic Routing & Edge Geolocation
Dynamic Routing & Edge Geolocation
Redirect visitors to contextual URLs based on their origin country or continent with ultra-low latency.
Read guide & API