Where Lemon Squeezy leaves recovery on the table
Lemon Squeezy is a merchant of record built for digital products, and it does the hard parts well: global tax, checkout, licence keys, affiliate payouts. Its dunning, however, is deliberately simple — a small number of retry attempts paired with a templated email from the store, on a fixed cadence, in one language, on one channel.
For a store doing $30k MRR, a 7% monthly failure rate is roughly $2,100 of at-risk revenue every cycle. Simple dunning typically recovers a portion of it; a decline-aware, multi-channel sequence routinely recovers a substantially larger share of the same pool without touching acquisition spend.
The point is not that the platform is deficient. It is that recovery is a specialised conversation problem, and a billing platform optimises for collecting money rather than for persuading a distracted human to spend two minutes updating a card.
The webhook contract
Lemon Squeezy sends webhooks with an `X-Signature` header containing an HMAC-SHA256 of the raw request body, keyed on the signing secret you set when creating the webhook. Verification rules are identical to every other provider: hash the raw bytes, compare in constant time, reject before persisting.
The events that matter are `subscription_payment_failed` for the failure itself, `subscription_payment_success` for the confirmation of recovery, `subscription_updated` for state transitions into and out of past-due, and `subscription_expired` for the terminal loss.
Each payload carries the store, the customer, the order and subscription identifiers, the amount in cents, the currency, and the card brand and last four digits — enough to write a message that names the exact card without ever storing a credential.
Idempotency and ordering on a busy store
Webhook deliveries are retried and can arrive out of order, which matters when a customer's card recovers between your queueing a message and your sending it. Nothing damages trust faster than a 'your payment failed' WhatsApp arriving twenty minutes after the receipt.
Guard with two rules. Deduplicate on the provider event identifier with a unique constraint so replays are no-ops. And re-check subscription state immediately before dispatch: if the subscription is active again, cancel the queued touch and log it as a self-recovery rather than a send.
Return 200 fast and do the work asynchronously. A handler that waits on copy generation or an outbound API will eventually time out and trigger a retry storm that your deduplication then has to absorb.
Routing the four codes that matter
Lemon Squeezy surfaces a normalised failure reason. Map it into the standard buckets and give each its own timing and channel.
Short balance: retry at roughly 48 hours, message on email plus WhatsApp, state the retry date plainly. Recovery in the 55-70% range is typical because the underlying condition is temporary.
Expired or replaced card: no useful retry exists. Send the customer-portal update link immediately, lead with the card brand and last four so the customer knows which card to replace, and expect the highest recovery of any code at 70-85%.
Issuer decline with no reason: retry at about 36 hours and ask for a different card in the first message. Recovery here is the weakest of the four, commonly 30-45%, so do not spend your whole cadence on it.
Authentication required: send within minutes on WhatsApp with nothing in the message but the confirmation link, and retry at 24 hours before the challenge goes stale.
Using the hosted customer portal correctly
Lemon Squeezy provides signed customer-portal URLs where a subscriber can update their payment method. Always deep-link to that page rather than building any form of your own — it keeps card handling entirely inside the platform and your integration entirely outside PCI scope.
Generate the portal link at send time, not at ingestion time, because signed URLs expire. A dead link in a dunning message is a recovery you have thrown away, and it is the most common preventable bug in this entire pipeline.
Put the link in the first third of the message on both channels. On WhatsApp, a single call-to-action button outperforms a paragraph containing a URL, and Meta's utility template rules encourage exactly that shape.
Template approval and message design on WhatsApp
Sending on WhatsApp Cloud API requires Meta-approved utility templates before the first send. Write them as transactional notices with variable slots for product name, amount, card last four and retry date. Approval typically takes hours, not days, when the template avoids promotional language.
Keep one template per decline bucket. A single generic template forces vague copy, and vague copy is why most dunning underperforms. The expired-card template should not mention retry dates; the insufficient-funds template should not mention updating a card as the primary action.
Because Meta bills conversations to your own WhatsApp Business account, the marginal cost per touch is visible and predictable, and it is trivially covered by a single recovered subscription in almost any price band.
How much of your involuntary churn is recoverable?
Compares a 40% single-channel baseline against the 63.8% RRLabs platform average.
- At risk / month
- $5,600
- Extra recovered / month
- $1,333
- Annualised, less $3,000 plan
- $12,994
RRLabs vs legacy dunning tools
Churn Buster, Baremetrics Recover, Stripe native dunning and Gravy compared with Revenue Recovery Labs.
| Feature | Legacy tools | Revenue Recovery Labs |
|---|---|---|
| Recovery channels | Email and basic SMS only | Native Meta WhatsApp Cloud API (your number) + email |
| Pricing architecture | $129–$629+/mo, or a percentage cut of recovered cash | Flat $100 / $250 / $500 per month — you keep 100% |
| Retry logic | Fixed calendar dunning (day 1, 3, 7) | Retry window derived per decline code, persisted at ingestion |
| Copy engine | Static templates you edit by hand | 4-tier cascade: cache → primary model → fallback model → deterministic templates |
| White-label | Unavailable or enterprise-only | Flat +$300/mo with custom domain and no vendor watermark |
| Audit trail | Send logs at best | Tier, model, latency and recovery score on every message |
Measuring what the store actually recovered
Define at-risk revenue as the sum of failed renewal amounts in a cohort window, and recovered revenue as the subset whose subscriptions returned to active within fourteen days. Report the ratio, not the raw count of recovered subscriptions, because a store's failures are rarely uniform in value.
Break the ratio down by decline bucket. If expired cards recover below 60% your update link or its placement is broken; if short-balance recoveries are low your retry timing is probably fighting the customer's pay cycle rather than following it.
Track cost per recovered dollar as a first-class metric. Flat-fee tooling should keep it under a couple of cents at any real volume, which is the argument against percentage-of-recovery pricing in one number.
A two-week rollout
Week one: create the webhook, verify signatures, store events idempotently, and run in shadow mode to establish a baseline against the platform's own dunning.
Week two: enable branded email for all four buckets, submit two WhatsApp templates for approval, then enable WhatsApp for the funds and authentication buckets only.
From there, iterate on one variable at a time — timing, then channel order, then copy — and compare cohorts across full billing cycles rather than across weeks, because renewal dates cluster and a seven-day comparison will lie to you.
Store-level deliverability
Digital-product stores frequently send from a shared or platform domain, which caps email open rates well below the 18-25% baseline. Authenticate your own sending domain with SPF, DKIM and a DMARC policy, and keep billing mail on a subdomain separate from marketing.
Warm the domain before you rely on it for dunning. A cold domain sending a burst of payment-failure notices to a broad list is the exact pattern spam filters are tuned to catch, and the resulting reputation damage takes weeks to undo.
Send billing mail as plain, narrow, text-forward HTML. Heavy templates with hero images and tracking pixels are filtered more aggressively and render worse on the mobile clients where most of these are read.
Licence keys, access and the revocation decision
Many Lemon Squeezy stores gate software licences or downloads. Deciding when a failed payment revokes a licence is a recovery decision, not just a policy one: revoke too fast and you convert a temporary shortfall into an angry cancellation, revoke too slowly and the deadline stops motivating anyone.
Five to seven days of grace, stated explicitly in the second message, is the common balance. Automate the revocation so it actually happens on the stated date — a deadline that passes without consequence teaches customers to ignore the next one.
Restore access instantly on recovery. A customer who pays and then waits an hour for their licence to reactivate writes the support ticket you were trying to avoid.
Where RRLabs fits
Revenue Recovery Labs connects to a Lemon Squeezy store through signed webhooks, classifies each failure, generates copy through a four-tier cascade with deterministic fallback, and dispatches on your own email domain and your own WhatsApp Cloud API number.
Every message is logged with the tier used, latency and recovery score, and every recovery is attributed back to the original at-risk amount so the reported rate is honest rather than flattering.
Pricing is flat — $100, $250 or $500 per month, plus $300 for white-label — with no percentage taken from anything recovered.