Stop discount loss from day one
You installed PromoLock because money is leaking somewhere. Don’t guess where — the whole point of the loss report is that it names your leaks. This playbook is the loop: build the report, fix sale-item loss by replacing the codes causing it, then fix stacking loss with the Controller.
Step 1: initialize Analytics
Section titled “Step 1: initialize Analytics”Open Analytics and hit Initialize report. It scans up to a year of your order history — minutes for most stores, up to a couple of hours for high-volume ones. You can leave the page while it builds.
That’s the whole first step. Resist the urge to flip switches before the data is in — the report is about to tell you exactly which fixes are worth making.
Step 2: fix sale-item loss — replace the offenders
Section titled “Step 2: fix sale-item loss — replace the offenders”Open the Sale-item discounts report on Last 90 days. The Lost on sale items tile is what codes landing on marked-down products cost you; Top discounts by loss names the codes responsible. Work down that list — the fix depends on what each row is:
A recurring campaign code — welcome offer, abandoned checkout, win-back
Section titled “A recurring campaign code — welcome offer, abandoned checkout, win-back”A shared, publicly-known code (WELCOME15 in every inbox) is the most farmed thing in commerce — Shopify counts “one per customer” per account, and accounts are free.
The fix is structural: replace the shared code with a bulk set. Every recipient gets their own single-use code, and the offer behind them carries the guardrails the old code lacked — Skip on-sale items is on by default, caps bound the total, and the set’s Fraud protection guard makes it one code per person, alias emails and fresh accounts included. Export the codes as CSV into your email tool’s per-recipient field, then retire the shared code: deactivate it, or block it in the Controller.
A normal code or automatic discount with no guardrails
Section titled “A normal code or automatic discount with no guardrails”An old 20%-off that applies to anything, marked-down or not. Rather than editing around it: recreate it in Discounts as the same offer, but guarded — same rate, same targeting, with Skip on-sale items doing its default job and Cap total discount amount set. Publish the new one, kill the old one. Customers see the same deal; sale items stop taking it.
A Campaign row from another app — REF-*, *10OFF
Section titled “A Campaign row from another app — REF-*, *10OFF”A referral, loyalty, or SMS app minting one-time codes you can’t rebuild. Judge the economics — is the app’s revenue worth its loss line? — and if its codes shouldn’t work on sale items at all, the Controller’s sale-items lock polices every code at checkout, that app’s included.
The Single-use rollup row
Section titled “The Single-use rollup row”Diffuse one-off codes matching no pattern. Usually tolerable — revisit only if the line grows.
Step 3: fix stacking loss — the Controller’s job
Section titled “Step 3: fix stacking loss — the Controller’s job”Switch the report to Stacked discounts. Every dollar here is an extra code that piled on top of the one you meant — and unlike sale loss, this one is fixed with a switch rather than replacements. Open the Controller and pick your policy:
No stacking should ever happen (most stores): turn on the Discount Stacking Lock, leave Stacking exceptions empty, save. Done — one code per order, the first one wins, everything else is rejected with your message.
Some stacking is intended — say, a free-shipping code that may ride along with any discount: turn the lock on and put that code (or its pattern, like SHIP#) in Stacking exceptions. Exception codes always apply and don’t count toward the limit, so the combinations you designed keep working while every unplanned combination dies.
Also glance at your own discounts’ Combines with flags — a PromoLock discount with all three off can never be part of a stack, which makes the lock’s job trivial.
Step 4: re-measure, repeat
Section titled “Step 4: re-measure, repeat”A week later, set both reports to Last 7 days and compare per-day loss against the 90-day baseline — the sale-loss lines you replaced and the stacking tile should have collapsed. From there the loop is monthly: open Analytics, check the top rows, tighten one thing. One target per week is the sustainable pace.
Pitfalls
Section titled “Pitfalls”- Don’t start with “Block all codes”. It stops leaks and campaigns alike; the report exists so you can tell them apart.
- Don’t fix by feel while the report builds. Wait for the data — the top offenders are almost never what you’d have guessed.
- Don’t delete a bulk set to retire a campaign — that kills distributed codes mid-flight. Deactivate or set an end date instead.
- Don’t forget the replaced code’s funnel. If the old shared code is printed in a running email flow, update the flow to the new per-recipient codes before blocking the old one.
