Revenue Attribution
Attribute revenue to traffic sources with first-touch and last-touch models.
Revenue Attribution
EngageTrack connects your revenue data to traffic sources, giving you a clear picture of which channels drive actual business results.
How It Works
- Visitor Identity — The SDK generates a persistent visitor ID (
localStorage) and a session ID (sessionStorage) to track visitor sessions. Access them viawindow.engagetrack.getVisitorId()andwindow.engagetrack.getSessionId(). - Source Tracking — Every session records the traffic source (UTM parameters, referrer, direct)
- Revenue Events — When a purchase occurs, EngageTrack receives a webhook from your payment provider and links the payment to the originating session
- Attribution — Revenue is attributed to the original traffic source using a four-level priority chain (see Attribution Models below)
Payment Provider Webhooks
EngageTrack receives payment events directly from your provider via webhooks. Connect your provider in Site Settings → Revenue:
| Provider | Connection Method |
|---|---|
| Stripe | API Key |
| Lemon Squeezy | API Key + auto-webhook |
| Paddle | API Key + auto-webhook |
| Polar | API Key + auto-webhook |
EngageTrack automatically registers the webhook on your provider during connection. No manual webhook setup is required.
Manual Revenue Tracking
For custom payment flows, use the JavaScript API:
engagetrack.trackPurchase("ORD-789", 149.99, "USD");
// signature: trackPurchase(orderId, revenue, currency)Client-side trackPurchase runs in the browser — it can fail if the user
closes the tab before the success page loads, if an ad-blocker removes the
SDK, or if someone calls it manually from the console. For verified revenue
use the server-side flow below.
Server-Side Revenue Tracking
When your payment processor is not natively supported (for example Netopia, PayU, or any custom checkout), you can record revenue from your backend after verifying the payment notification signature yourself. This is the recommended approach for producers that deliver a webhook / IPN to your server on a confirmed payment.
The same POST /api/v1/event endpoint that powers the tracking script accepts
server-side calls. No API key is required — the site is identified by its
public ID.
Recommended Flow
-
On your checkout page, capture the visitor and session IDs and the current URL (including any
utm_*query parameters) before the redirect to the payment processor:// On the checkout / pay-now page, BEFORE redirecting to the processor: const payload = { visitor_id: window.engagetrack.getVisitorId(), session_id: window.engagetrack.getSessionId(), checkout_url: location.href, // preserves utm_source, utm_campaign, etc. }; await fetch("/api/checkout", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...payload, cart }), }); -
Persist
visitor_id,session_id, andcheckout_urlagainst the order record in your database. -
After the processor confirms payment (verify their signature / IPN as you normally would), call the EngageTrack ingest endpoint from your backend:
curl -X POST https://api.engagetrack.net/api/v1/event \ -H "Content-Type: application/json" \ -d '{ "site_id": "YOUR_SITE_PUBLIC_ID", "event_type": "purchase", "url": "https://yourdomain.com/checkout?utm_source=facebook&utm_campaign=spring", "visitor_uid": "<value you stored in step 2>", "session_id": "<value you stored in step 2>", "revenue": 249.99, "currency": "RON", "order_id": "ORD-12345" }'A
202 Acceptedresponse means the event was accepted into the ingest queue.
Field Reference
| Field | Required | Notes |
|---|---|---|
site_id | Yes | The site's public ID (find it in Site Settings → Tracking). |
event_type | Yes | Must be "purchase" for revenue events. |
url | Yes | Must match your registered site domain. Include the original utm_* params for source attribution. |
visitor_uid | Strongly recommended | The value from window.engagetrack.getVisitorId() captured on the checkout page. Without it the event is stored but not linked to the visitor. |
session_id | Recommended | The value from window.engagetrack.getSessionId(). |
revenue | Yes | Numeric amount (not cents — e.g. 249.99). |
currency | Yes | ISO 4217 code, e.g. RON, EUR, USD. |
order_id | Yes | Your order identifier. Used for deduplication on your side (see below). |
Differences vs. Native Integrations
The native Stripe / LemonSqueezy / Paddle / Polar integrations do some extra work that the server-side ingest endpoint does not. Be explicit about these when you build:
- Idempotency is your responsibility. Native webhooks are deduplicated
by a
(site_id, order_id)unique constraint. The ingest endpoint accepts the event every time it is called. If your processor retries the IPN, guard against double-counting in your own handler (e.g. anorder_id → sent_attable with a unique index). - Attribution comes from the
urlyou send. Native webhooks copy the UTM, referrer, and country fields from the visitor's most recent session event. The ingest endpoint reads UTM parameters only from theurlquery string of the current request. Always forward the original checkout URL (with itsutm_*params) to preserve the channel breakdown in the Revenue dashboard. - No auto-created Revenue goal. Native integrations auto-create a
Stripe Revenue/Polar Revenuegoal so purchases appear under Goals. For custom processors create your own Goal manually (Goal type: Custom Event, Event name:purchase) if you want goal-based reporting. - Currency normalization runs only when an active native Revenue
integration exists for the site. Without one, the raw
revenue+currencyare stored and the dashboard sums them as-is. If you invoice in a single currency this does not affect your totals.
Webhook Integration
For server-side revenue tracking (subscriptions, renewals), configure a webhook integration:
- Go to Site Settings → Revenue
- Select your payment provider (Stripe, Lemon Squeezy, Paddle, or Polar)
- Copy the webhook URL and add it to your provider's dashboard
- EngageTrack will match webhook events to visitors using the
engagetrack_visitor_idin Stripe metadata
// Server-side: Add visitor ID to Stripe checkout
const session = await stripe.checkout.sessions.create({
metadata: {
engagetrack_visitor_id: visitorId, // from engagetrack.getVisitorId()
engagetrack_session_id: sessionId, // from engagetrack.getSessionId()
},
// ...other options
});Attribution Model
EngageTrack uses last-touch attribution based on the session data available at webhook time. When a payment webhook is received, the revenue is attributed to the traffic source of the most recent session associated with the visitor.
To ensure accurate attribution, pass the engagetrack_visitor_id and engagetrack_session_id as metadata in your payment provider's checkout flow (see the Webhook Integration section above). This allows EngageTrack to link the payment directly to the originating session and its traffic source.