What is coupon abuse?
Coupon abuse, also called coupon fraud or discount code abuse, is the repeated redemption of a discount that was meant to be claimed once — one shopper applying a one-per-customer code through several accounts or browsers, many people redeeming a single-use code that leaked or was shared, or one buyer stacking codes across repeated attempts. The orders look like distinct customers, but the redemptions trace back to one machine or one network, or to a code that has already been spent.How ShieldLabs surfaces it
Your coupon code, cart, and order stay in your commerce stack. What ShieldLabs identifies is the session applying the code: the per-claim RequestID, the cookie-scoped VisitorID, your hashed UserHID if the shopper is signed in, and the durable, server-derived DeviceID that survives a cleared cookie, an incognito window, and a rotated IP. A shopper who clears cookies and switches accounts between redemptions reads as a brand-new customer to a cookie or public-IP check, but the DeviceID holds steady — so your code can tell that ten “different” buyers applying a code came off one machine. ShieldLabs also returns a per-request Risk Score (0–100); when the connection is masked with a VPN, proxy, Tor, or a privacy relay, the anonymity signals fire and the real network IP (local_ip) can expose the network behind a rotated public public_ip. Over time the dashboard grades the relationship with the Many Accounts on One Device and Many Accounts on One Local IP patterns.
The split is the whole point: ShieldLabs tells you who the session is and how hidden it is; your redemption code counts how many times this code (or any code) has been spent from one DeviceID and one local_ip.ip, and enforces your one-per-customer rule. Neither half works alone — the identity makes the count durable, the count makes the identity actionable.
Prevent coupon abuse
The rule your code applies, wired up in## Build it below: at the apply-code step, resolve the session to its DeviceID, read the score and signals for masking, then in your own store count how many times this exact code has been redeemed from that device or local network, and how many distinct accounts have spent any one-per-customer code off it. Honor the discount when the device is fresh and the session is clean; require verification or refuse the discount when the per-customer cap is crossed, when a single-use code is being applied a second time from any device, or when a masked session pushes the count past your tolerance. Weigh masked sessions heavier — a shopper hiding behind a VPN to look like a new buyer is exactly the case the score is for. ShieldLabs surfaces the session and the score; your code owns the limit and the verdict, so a forum-leaked code collapses to one device-and-network footprint and holds for review, while a genuine first-time shopper checks out clean.
Build it
Create a ShieldLabs account and get your keys
Sign up for free and get 5,000 identifications, or log in if you already have an account. Register the storefront domain you want to identify shoppers on, then open the Keys page. Use the Public Key to initialize the snippet in the browser, and keep your server-side credentials on your backend: the Private API Key authenticates the History API, and each webhook endpoint has its own
whsec_… signing secret. See Keys.Identify the session at the apply-code step
Add the snippet to the cart or checkout page where the coupon is entered, and re-identify on the apply-code action itself so you score the session that is actually redeeming, not a stale page load. Use
checkAuthenticatedUser with the shopper’s hashed id when they are signed in, or checkAnonymous for guest checkout. Pass a hash, never a raw email.cart.html
Read the score and gate on masking
The score arrives on the webhook — verify the A high score is not a fraud verdict — a real shopper on a corporate VPN or a privacy browser can land in the High band. Branch on the score band and the named
X-Shield-Signature HMAC, then cache it by request_id. Your apply-coupon endpoint reads it back with the shared waitForScore helper from the Use Case Tutorials, or falls back to a History API read by request_id. Validate the code with your own commerce checks first, then hold a masked session for verification before moving on to the redemption count.api/apply-coupon.js
detection_flags, and treat masking as one input to combine with the count, never as proof on its own.Count redemptions per device and local network
The score says whether one session looks masked. It does not say how many times this code, or any one-per-customer code, has already been spent from the device or the local network — and that count is what catches both a duplicate-account shopper and a leaked code making the rounds. The durable DeviceID is the anchor (it survives a cookie clear, incognito, and IP rotation), and the real network IP (A shopper using two genuinely separate browsers shows up as two DeviceIDs, since the DeviceID is browser-bound. The local-IP count closes that gap: the same code applied repeatedly through one
local_ip.ip) catches a household or office sitting behind one router even when each session shows a fresh cookie and a rotated public IP. Read the per-identifier history live and count:Read a device's redemption history
Hold the discount to your per-customer limit
History reads through
account.shieldlabs.ai do not consume request balance. For busy checkouts, keep your own redemption-per-device counter (incremented when you honor a discount) as the fast path and reserve live device_id / local_ip.ip reads for borderline carts.local_ip.ip is a strong shape even when each session reports a different device. Weigh both alongside your own per-code redemption caps.Corroborate with the dashboard patterns
The live counts decide the cart in the moment. Over time, two dashboard Patterns grade the same relationship Suspicious, then Dangerous, as it crosses a threshold in a rolling window — useful for building a denylist and for catching slow, patient abuse the per-cart count misses:
- Many Accounts on One Device — one DeviceID tied to many accounts, the duplicate-account coupon shape.
- Many Accounts on One Local IP — many accounts redeeming through one local network (
local_ip.ip), the leaked-code-shared-around-a-network shape.
Tune to your offers
A one-per-customer welcome code wants a hard limit of 1; a stackable site-wide sale tolerates more. Your commerce rules own stacking policy — which codes combine in a cart — while ShieldLabs ties a burst of apply-code attempts back to one DeviceID, so a shopper stacking codes from one machine still surfaces. Start in logging-only mode, watch how real redemptions distribute across devices and networks, then set the per-device and per-network caps that match each campaign.
Test it
You do not need a real abuse ring to see this work. Apply a one-per-customer code once in your normal browser and note thedevice_id on the webhook. Then play the abuser: clear cookies, open an incognito window, or switch to a second browser profile, sign in as a different account, and apply the same code again. The cookie_id and visitor_id change every time, but the same device_id returns, and your redemption count off that device climbs with each attempt — exactly the count your handler holds the discount on. Toggle a VPN and the anonymity signals light up on the redeeming session without changing the DeviceID, so a masked retry reads as the same machine, more hidden.
Recommended starting thresholds
The four bands are defined in Risk Scoring, and the per-band playbook lives in Acting on the Risk Score. Mapped to the apply-code gate, with your redemption count layered on top:| Signal at apply-code | Suggested action |
|---|---|
| Clean / Low score, redemption count under your cap | Honor the discount |
| Medium score, under your cap | Honor, but log and watch the device |
| High score | Require verification before honoring |
| Per-device or per-network redemption count at your cap | Refuse the discount, regardless of session score |
| Single-use code applied a second time (any device) | Refuse outright |
| Device or local IP flagged Suspicious / Dangerous | Require verification, route to review |
| All-zero / missing DeviceID (snippet blocked or JS disabled, score 90+) | Require verification before honoring |
Next: Acting on the Risk Score
The full per-band decision playbook, including signal-aware decisioning and how to combine the score with specific signals.