Country and the anonymity signals on the visit, then lets your pricing code decide whether the claimed region is trustworthy enough to honor.
ShieldLabs scores. Your code decides. ShieldLabs never picks a price or rejects a buyer. It returns a Risk Score (0 to 100), the anonymity signals behind it, and the two-letter ISO Country derived from the IP. Whether to show the regional price, ask for verification, or fall back to the standard price is logic you write in your checkout flow.
The pattern
Identify when the region is set
Load the snippet on the checkout or plan-selection page. When the buyer selects or confirms a region, call
forceCheckAuthenticatedUser so you score the session as it is at the moment of the price decision, not a stale read from page load.Receive the score, country, and signals
Correlate the browser
requestID with the webhook your server receives, with a short timeout fallback to the History API. The body carries Country, the Score, and the Details.Compare the claimed region to the evidence
If the session carries anonymity signals, or the network
Country does not match the region the buyer claims, treat the claimed region as unverified.Step 1: identify when the region is set
On a pricing page you may already runcheckAnonymous for analytics. When the buyer selects a region or reaches checkout with a regional price applied, force a current read with the forceCheck* variant. It clears the session and runs a new identify call immediately, so the country and signals reflect the session at decision time.
Always pass a hashed or pseudonymous user id to the authenticated call, never a raw email or account id.
pricing.html
Step 2: receive the webhook and read Country plus signals
Within about 1 second of the check, ShieldLabs POSTs a{ Data, Assing } envelope to your callback URL. Verify the Assing HMAC first, then cache the result keyed by RequestID so the price request can look it up. This is the same verify, respond fast, store, expire handler every recipe shares, so the Cookbook defines it once as the scoreCache and waitForScore helper. That helper already stores Country alongside the score, so nothing extra is needed here.
A geo-pricing body looks like this. The Country field is the two-letter ISO code resolved from the IP, and the anonymity signals ride in Details:
Country resolves to US while a VPN signal is present, so a session claiming a Brazil price is masking its location. The Risk Score on its own is only Low (15), which is exactly why the country mismatch matters: a low score is not a clean signal for a regional-price claim.
Description is a human-readable label for visibility and logging. Branch on whether an anonymity signal is present in Details and on the Country value, not on the exact label text, which can change.Step 3: gate the price on country and anonymity
Wait briefly for the score, then compare three things: is the session anonymized, does the network country match the claimed region, and how strong is the Score. The presence of any anonymity signal, or a country that does not line up with the claim, is enough to stop honoring the discount and fall back to your standard price.price.js
waitForScore is the shared webhook-cache read (poll the cache, then fall back to a History API read by request_id); the Cookbook defines it once alongside the handler that fills the cache.
The fallback, hold, and verification steps above run in your application. ShieldLabs returns the
Country, the Score, and Details. It does not block buyers, decline a price, or pick a region for you.Reading the signals for a price decision
For a checkout decision you weigh the Score and its bands. For a regional-price claim the country comparison carries most of the weight, because a masked session can score Low yet still be hiding its true location. The four bands and their ranges are defined in Risk Scoring; here is how to read the two inputs together.Network Country vs claimed region | Anonymity signal in Details | Reasonable action |
|---|---|---|
| Match | None | Honor the regional price |
| Match | VPN / Proxy / Privacy Relay present | Verify before discount; a corporate VPN can match by coincidence |
| Mismatch | None | Standard price, ask for verification |
| Mismatch | Any present | Standard price, or hold for review |
| Unknown (no webhook yet) | Unknown | Standard price until verified |
Signals that make a region unverifiable
TheCountry tells you where the network exits. The anonymity signals tell you whether that exit can be trusted as the buyer’s real location. The names below are the human-readable labels that appear in Details for visibility and logging; they can change, so match on presence rather than exact text.
Signal in Details | Why it breaks a region claim |
|---|---|
| Tor | Connection exits through the Tor network, so the country is the exit node, not the buyer. |
| VPN | Traffic routes through a VPN, so the exit country is chosen, not where the buyer sits. |
| Proxy | IP flagged as a proxy. The geolocated country reflects the proxy, not the person. |
| Privacy Relay | iCloud Private Relay relays the connection, so the visible country can differ from the real one. |
| Datacenter IP | IP is in a hosting range. A real shopper on a personal device rarely exits from a datacenter. |
Next steps
Signals
Every anonymity signal that can appear in
Details, in plain language, with its weight.The Risk Score
How the 0 to 100 score is built, what
Details carries, and the band definitions.Checkout protection
The fresh-check pattern at the payment step, where the same signals gate the charge.
Acting on the Risk Score
Turn the Score,
Country, and Details into allow, verify, and hold logic in your app.