Why Paddle matters for global SaaS
Paddle is useful in a tracking architecture for the same reason it is useful commercially: it sits at the legally meaningful point of sale. Because Paddle acts as merchant of record, it handles tax calculation, collection, invoicing, and payment processing across jurisdictions that would otherwise turn global SaaS into a compliance project. That means the billing payload already knows whether a customer paid, in what currency they paid, how much of the total was tax, whether a subscription is trialing or active, and whether a later adjustment reversed part of the revenue.
For TrackLayer, that is the difference between inference and truth. Browser tracking can tell you a checkout probably happened. Paddle can tell you whether the commercial event actually settled, what the financially recognized amount was, and whether that value later changed. For global SaaS teams sending conversion signals to Meta, Google, or warehouse models, that makes Paddle one of the best sources for post-checkout attribution because it combines subscription state, tax detail, and international currency context in one durable system of record.
Prerequisites
A Paddle Billing account with access to developer settings, notification destinations, and the subscription workflows you want to track.
A TrackLayer workspace with API credentials and an inbound webhook endpoint ready for production event ingestion.
Durable identifiers carried across checkout and product access, such as customer email, customer ID, subscription ID, transaction ID, and your own account or workspace ID.
A written event policy for what counts as `trial_started`, `subscription_started`, `subscription_renewed`, `payment_failed`, `refund`, and `subscription_cancelled` inside your business.
Setup
Create a Paddle notification destination
In Paddle Billing, add a webhook destination that points to your TrackLayer inbound endpoint. Subscribe only to the billing and lifecycle events you intend to operationalize. A narrow event set is easier to test, reason about, and deduplicate than a full firehose of every entity mutation.
Paddle → Developer tools → Notifications → New destination
Destination URL
https://edge.tracklayer.io/v1/webhooks/paddle
Recommended initial events
subscription.created
subscription.activated
subscription.updated
subscription.paused
subscription.canceled
transaction.paid
transaction.updated
adjustment.updatedStore the signing secret in TrackLayer
Copy the Paddle endpoint secret into TrackLayer so inbound signatures can be validated before any event is normalized or forwarded. This protects the attribution layer from forged requests and keeps retries safe because TrackLayer can trust the source event ID when deduplicating deliveries.
TrackLayer → Sources → Paddle
TRACKLAYER_PADDLE_ENDPOINT_SECRET=pdl_ntfset_xxxxxxxxx
TRACKLAYER_API_KEY=tl_live_xxxxxxxxx
Verification status
source: paddle
signature: valid
mode: liveMap Paddle events into canonical names
Treat Paddle webhook names as transport detail, not as your company-wide reporting language. Build a small mapping layer so `subscription.activated` becomes `subscription_started`, a renewal-side `transaction.paid` becomes `subscription_renewed`, and a failed collection surfaced through `transaction.updated` with `past_due` status becomes `payment_failed`. This is where TrackLayer earns its keep.
subscription.created → trial_started or subscription_created
subscription.activated → subscription_started
transaction.paid → subscription_started or subscription_renewed
subscription.updated → subscription_updated
transaction.updated → payment_failed when status = past_due
subscription.paused → subscription_paused
subscription.canceled → subscription_cancelled
adjustment.updated → refund when approvedAdd field-level value logic
Use Paddle transaction totals as the financial source of truth, but be explicit about which fields TrackLayer should promote to optimization value. Paddle transactions include tax, discounts, currency conversion, and line-item detail. Decide whether TrackLayer emits gross billed revenue, net software revenue, or both, then keep that choice stable per event type.
Suggested normalized fields
customer_id
subscription_id
transaction_id
event_name
event_timestamp
currency_code
gross_value
net_value
tax_amount
billing_period_start
billing_period_end
source = paddleSimulate the lifecycle before going live
Use Paddle's test webhooks and scenario tools to walk through creation, activation, failed payment, cancellation, and refund flows. Verify not just receipt but meaning: the event should land once, map correctly, preserve currency, and carry the intended value field into downstream destinations. Test renewals and refunds especially, because that is where subscription attribution usually breaks.
Expected TrackLayer output
{
"source": "paddle",
"event": "subscription_renewed",
"paddle_event": "transaction.paid",
"subscription_id": "sub_01...",
"transaction_id": "txn_01...",
"gross_value": 129,
"net_value": 107.5,
"currency": "EUR",
"status": "forwarded"
}Event mapping
The important design choice is to keep Paddle-specific webhook names at the ingestion edge and preserve canonical TrackLayer names everywhere else. That way billing can evolve without forcing your ad destinations, internal dashboards, and RevOps workflows to relearn the vocabulary every time Paddle changes a lifecycle detail.
| Paddle event | TrackLayer canonical | Mapping intent |
|---|---|---|
| subscription.created | trial_started or subscription_created | Create a trial event when the subscription status is `trialing`; otherwise store the creation record without optimizing on it yet. |
| subscription.activated | subscription_started | Best signal for a newly paying subscription after checkout or trial activation. |
| transaction.paid | subscription_started or subscription_renewed | Use billing period and prior subscription state to distinguish first paid billing from renewals. |
| subscription.updated | subscription_updated | Capture plan, quantity, scheduled change, or billing-date mutations without inventing revenue. |
| transaction.updated | payment_failed | Map only when the transaction status moves to `past_due` or another payment-recovery state you treat as dunning risk. |
| subscription.paused | subscription_paused | Pause is operationally different from cancellation and should stay distinct in audiences and reporting. |
| subscription.canceled | subscription_cancelled | Use the effective cancellation moment, not merely the scheduled change creation, as the final churn event. |
| adjustment.updated | refund | Create a refund or negative-value correction when the adjustment is approved. |
Tax-inclusive revenue
The merchant-of-record benefit creates a measurement decision you should make deliberately: when Paddle bills 129 EUR and part of that total is VAT or sales tax, what value should the media platform learn from? In most SaaS cases, the safer optimization signal is net software revenue, not gross tax-inclusive revenue, because tax is not economic value created by the campaign. If you pass tax-inclusive totals into Meta, Google, or another bidder, you may teach the model that jurisdictions with higher indirect tax rates are intrinsically more valuable customers, which is not usually what finance or growth actually means.
The pragmatic pattern is to keep both fields. Let TrackLayer store `gross_value`, `tax_amount`, and `net_value`, then choose one stable primary `value` field per destination. For Meta purchase optimization, many teams send net value and keep gross for reporting only. If your internal revenue model truly evaluates on gross billed amounts, you can pass gross instead, but make that a conscious policy and use it consistently across first purchase, renewal, and refund adjustments.
Customer currency
Paddle supports multi-currency billing, which is useful for conversion rates but dangerous if your downstream systems silently collapse everything into one home-currency number. TrackLayer should preserve the customer-facing transaction currency exactly as billed, along with any normalized finance currency your warehouse uses later. Ad platforms generally expect a value plus currency pair. Give them the real billed currency from Paddle so the signal remains faithful to what the customer actually paid.
The mistake to avoid is mixing currencies at the event layer. Do not send some subscriptions in local currency and others after ad hoc FX conversion unless that rule is universal and documented. Keep event delivery in the original customer currency, perform consolidated FX analysis downstream, and use consistent refund handling in that same currency so optimization and reporting stay internally coherent.
Common questions
Paddle Classic or Paddle Billing?
Build the new integration around Paddle Billing. Paddle Billing has the modern API, webhook model, and notification tooling you want for canonical lifecycle tracking. If you still run Paddle Classic, treat it as a migration case rather than the target architecture and keep the mapping logic isolated so you can swap sources later.
How should refunds be signaled?
Use Paddle adjustments as the correction layer. When an `adjustment.updated` webhook indicates an approved refund, TrackLayer should emit a `refund` event or a negative-value correction tied to the original customer, subscription, and transaction references. Otherwise your media platforms keep credit for revenue that no longer exists.
What about dunning and failed payments?
Do not treat every payment hiccup as churn. A failed renewal should usually map to `payment_failed`, remain separate from `subscription_cancelled`, and feed suppression or win-back logic while Paddle continues recovery attempts. Only emit cancellation when the subscription is actually canceled or your own access policy considers the account lost.
How do usage-billed or metered plans fit?
Metered plans usually still resolve into transactions, which means TrackLayer can use paid transaction value as the optimization truth. The useful pattern is to forward the revenue when Paddle says the usage-backed charge is paid, not when the product internally increments a usage counter.
Is Lemon Squeezy the same pattern?
Architecturally, yes. It is another merchant-of-record style billing source that should map into the same canonical TrackLayer lifecycle schema. The exact webhook names differ, but the operating model should not. Keep source-specific adapters thin and preserve one destination-neutral event vocabulary.
Related implementation guides
Subscription lifecycle events
Define the canonical event vocabulary that Paddle should feed.
Read guide →Stripe webhook bridge
Compare another billing-source integration built around post-checkout truth.
Read guide →Webhook delivery
Harden retries, signatures, ordering expectations, and delivery observability.
Read guide →