# ReTarget.gg — Full corpus ReTarget.gg is a Toronto-based geo targeting marketing platform. Publishers monetize blocked, declined, and out-of-region traffic with one script tag. Advertisers buy geo-matched CPC, CPA, or eCPM demand only where their offers are licensed and eligible. Source: https://retarget.gg Generated: 2026-10-19 --- ## Documentation (26 articles) ### Geo Location Targeting Quickstart URL: https://retarget.gg/docs/quickstart Category: start Audience: both Last reviewed: 2026-07-15 Keywords: ReTarget.gg quickstart, install ReTarget.gg in 2 minutes, geo-blocking monetization setup, publisher script install, turn blocked traffic into revenue This guide takes you from signup to live offers in **two minutes**. We'll skip the "what is this" parts; for context read [How ReTarget.gg works](/docs/how-it-works) after you're live. Anyone with a website and a `` (or a theme hook) they can paste one script tag into. That includes Next.js, Cloudflare Pages, any static host, custom stacks, and anything fronted by Google Tag Manager or Cloudflare Zaraz. ## Prerequisites - An ReTarget.gg account (sign up at [app.retarget.gg](https://app.retarget.gg)). - A website you control: including the ability to add a ``} {`// app/layout.tsx import Script from "next/script"; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} `} If you're reading this while signed into the dashboard, the snippets above already contain your real keys: click copy and paste. ## 3. Configure your geo rule Back in the dashboard → website detail → **Geo rules**, pick a mode: - **Allowlist**: you list the countries where your product _is_ available. Everywhere else sees offers. - **Blocklist**: you list the countries where your product _isn't_ available. Those see offers. If you're on allowlist mode, list only your licensed / supported countries to start. It's easier to widen later than to debug why offers aren't showing. ## 4. Verify from a blocked country Anywhere not on your allowlist (or on your blocklist) works: common picks for testing are Germany, Sweden, Australia, Canada. The script runs, fetches a decision from `api-v2.retarget.gg`, and renders a full-screen overlay of offers. Confirm it opens the advertiser's landing page. Back in the dashboard, the website's **Analytics** tab shows the impression and click after your events flush. ## Nothing's showing: now what? The most common install failures and how to fix each in under a minute. Every `data-*` attribute, endpoint, and error message the script can produce. ## Where to go next The system diagram: publishers, widgets, decisions, offers, billing: without implementation noise. Keys, decisions, impressions, clicks, CPCs, budgets. A 4-minute read that pays off every later guide. Allowlist vs blocklist, region-level targeting, and custom decline messaging. App Router install with env-scoped keys and CSP-friendly script loading. --- ### Create Your ReTarget.gg Account URL: https://retarget.gg/docs/getting-started Category: start Audience: operator Last reviewed: 2026-07-15 Keywords: ReTarget.gg getting started, ReTarget.gg sign up, geo-gated affiliate setup, monetize blocked traffic getting started, ReTarget.gg onboarding This guide covers account creation, onboarding, and the first things to set up before you touch the widget. If you already have an account and just want to install, jump straight to the [two-minute Quickstart](/docs/quickstart). ## Create your account Visit [app.retarget.gg](https://app.retarget.gg) and sign in with your email. A magic link arrives within a few seconds. ## Complete onboarding Onboarding collects a handful of details so we can set up your workspace correctly: - **Name**: Your first and last name, used on invoices and the team directory. - **Phone verification**: We text an SMS code to verify your number. This is also the recovery channel for your account. - **Company details**: Your company or legal name and your title. This appears on reports, invoices, and your team workspace. - **Terms & privacy**: Review and accept the [ReTarget.gg Terms](/legal/terms) and [Privacy policy](/legal/privacy). If you have a **promo code** from a sales conversation, paste it during onboarding: bonus credits are applied to your advertiser balance on sign-up. If a partner's **referral link** brought you here, they're credited automatically; no coupon needed. ## Choose your path ReTarget.gg supports two primary roles: - **Publisher**: You own or manage websites and want to monetize blocked traffic by showing sponsored offers to visitors outside your service area. - **Advertiser**: You want to reach high-intent, in-market visitors on publisher sites on a CPC basis. One organization can be both. Most of our users start as publishers and add advertiser offers later. ## What happens after onboarding Your **organization** is created: your central workspace for websites, offers, billing, team members, and analytics. You also receive a unique **affiliate referral link** (see [Affiliate program](/docs/affiliate-program)) that pays commissions on every organization you bring. From here, you can: Register a domain, pick a display mode, and grab your integration keys. Paste the script, configure geo, and verify from a VPN. Apply, fund your balance, and launch your first CPC offer. Roles and shared workspaces for larger publishing groups. ## Next steps Keys, decisions, impressions, clicks, CPCs, budgets: the vocabulary you'll see everywhere. The system diagram: publishers, widgets, decisions, offers, billing. --- ### How Geo Targeting Works URL: https://retarget.gg/docs/how-it-works Category: start Audience: both Last reviewed: 2026-07-15 Keywords: ReTarget.gg architecture, geo-gated monetization flow, publisher widget how it works, ReTarget.gg system overview, affiliate marketplace architecture ReTarget.gg connects **publishers** who have geo-restricted audiences with **advertisers** who pay per click to reach those visitors. One embed, three public surfaces, money moves automatically. ## The three roles | Role | What they do | | --- | --- | | **Publisher** | Owns websites, defines where their product is legally available (geo rules), and embeds the ReTarget.gg script. | | **Advertiser** | Funds a balance, creates CPC offers, and pays when eligible visitors click through to their landing pages. | | **ReTarget.gg** | Matches offers to traffic, enforces geo and quality rules, tracks impressions and clicks, and moves money on your contract. | You can be a publisher, an advertiser, or both inside one organization. ## The three public surfaces Where you configure websites, geo rules, offers, budgets, analytics, billing, and team access. Three embeds (geo-blocking, section, Trigger Widget) plus an iframe fallback. One script tag per site. REST endpoints the widgets speak: `/v1/decision`, `/v1/offers`, `/v1/events/*`, `/v1/r` redirects. ## End-to-end flow (browser) The page loads your content plus the ReTarget.gg script from `cdn.retarget.gg/widget.js` (or the section / Trigger Widget variant). The script is small and async. It never blocks first paint. The script issues a `GET /v1/decision` call carrying your **public key**, **website key**, and the visitor's server-resolved country. The API decides: **allowed** or **blocked**: against the geo rule you set on the website. If the visitor is on the allowed side, nothing else happens. Your site behaves exactly as it would without ReTarget.gg. The script does post a small `snippet-ping` so the dashboard knows the widget is alive. On the monetization path the widget calls `GET /v1/offers` to get a ranked list of eligible sponsored offers for that country, then renders them in either a full-screen overlay (geo-blocking), an inline container (section), or triggered overlay (Trigger Widget). The widget posts **impressions** and, when visitors click, a **click event** plus a hop through `GET /v1/r` so attribution lands before the advertiser's landing page. The advertiser's balance is debited; your publisher earnings are credited. Payouts per your contract and the [billing guide](/docs/billing-and-payouts). ## The server-triggered path (optional) Some teams want to show alternative offers only after **their own** eligibility or risk checks fail. Think failed KYC, declined deposit, or "we can't serve you here." For that flow you: Websites → [Your site] → **Site details** → toggle **Trigger Widget**. This reveals your **integration secret**, a server-only credential. Never ship it to the browser. The script creates a `sessionId` and starts polling. Your backend captures the id (via the `retarget:decline-session` DOM event or `window.__RETARGET_DECLINE_SESSION_ID`, then posts it to your API). When your rules say "show offers now," call `POST /v1/widget-decline/trigger` with `Authorization: Bearer ` and the `sessionId`. The next poll sees `ready` and the overlay opens with the same offer UI as geo-blocking. See [Trigger Widget](/docs/widget-decline) for the full server-side example. ## The dashboard and the API The public API lives at `api-v2.retarget.gg`: that's what the widget calls from every page view. The dashboard at `app.retarget.gg` is where you configure websites, geo rules, offers, budgets, analytics, billing, and team access. Configuration changes you make in the dashboard (new geo rules, a paused offer, updated budget) take effect immediately on the next decision call. No redeploy of the embed needed. ## Security posture (the short version) - **Origin check on every API call.** The browser's `Origin` must match the **domain** registered on the website. Unknown origins get `403`. - **Keys are scoped per website.** If a key leaks, you rotate only that site: not your whole account. - **Integration secret stays server-side.** It's the only credential that can arm a Trigger Widget session. - **Rate limits on widget and event endpoints** to keep noisy neighbors from impacting your analytics. - **Minimal client data.** The widget attaches a first-party visitor/session id (so we can deduplicate real visitors), a coarse device bucket (mobile/tablet/desktop), and the page's country from IP. No URLs, referrers, UTMs, or user-agent beyond the standard HTTP header are stored with each event. For the SOC 2 posture and subprocessor list, see the [Trust center](/security). ## Funding and earnings (conceptual) Advertisers top up via card (Stripe) or crypto (NowPayments), set a CPC and budgets per offer, and target countries that match their product. Publishers earn a share of every billable click: you see both sides in real time in **Analytics** and **Billing**. Exact rates, holds, and payout schedules live in your agreement and in the in-product billing screens. ## Where to go next Two-minute install: add a website, paste the script, verify from a VPN. Keys, decisions, impressions, clicks, CPCs, budgets: the vocabulary used everywhere else. Every `data-*` attribute and widget behavior, cross-linked to the relevant API. Public `/v1/*` endpoints with request/response shapes and curl examples. --- ### Core Concepts for ReTarget.gg URL: https://retarget.gg/docs/core-concepts Category: start Audience: both Last reviewed: 2026-07-15 Keywords: ReTarget.gg concepts, publisher key vs website key, decision impression click, CPC affiliate glossary, geo-blocking terminology Every docs page that follows assumes you know this handful of terms. Four minutes, no jargon. ## Organization Your top-level workspace. Everything: websites, offers, balance, team members, API keys: belongs to one organization. A single ReTarget.gg user can have multiple organizations via invitations. ## Website A single domain you've registered in the dashboard. Each website has its own: - **Integration keys** (public key + website key). - **Geo rules** (your allowlist of allowed countries / regions). - **Display mode** (geo-blocking overlay, inline section, or Trigger Widget, picked per site). - **Analytics** scope (impressions, clicks, earnings). Sites can be **personal** (only you see them) or **team** (shared with your organization). ## Integration keys The values you paste into the script tag: each website has its own set: | Key | Prefix | Role | | --- | --- | --- | | **Public key** | `pk_…` | Per-website. Safe for client-side HTML. | | **Website key** | `web_…` | Per-website. Safe for client-side HTML. | | **Integration secret** | `its_…` | Per-website, server-only. Used only by the Trigger Widget flow to arm a session. **Never** ship this to the browser. | Keys are scoped: if a public key leaks (e.g. indexed in a public repo), the damage is confined because the API validates the `Origin` against your registered domain. You can rotate a key from the website detail page in the dashboard at any time. ## Decision One call to `GET /v1/decision` for every page view. Returns `{ allowed, requestId, country, region }`. Everything downstream (offers, impressions, clicks) carries the `requestId` so attribution is watertight. - **Allowed** → the visitor is on the allowed side of your geo rule. The widget stays silent and your page renders normally. - **Not allowed** → the widget fetches offers and renders the monetization surface. ## Offer A creative + landing page + payout model + budget + targeting bundle that advertisers configure in the dashboard. Each offer has: - A **vertical** (iGaming, finance, sports, crypto, e-commerce, streaming). - A **payment model**: CPC (per click) or CPA (per conversion). eCPM is on the roadmap. - Per-country **rates**, with platform minimums for quality. - **Budgets** (daily / weekly / monthly / total). When any cap exhausts, the offer auto-pauses. - **Creative** (brand, headline, image, CTA). - An **affiliate URL** with optional dynamic placeholders: `{clickId}`, `{websiteKey}`, `{country}`, `{region}`. Some CPC offers are promoted as **Premium placements**: they appear earlier in the offer grid. Advertisers request premium status from the **Advertiser** section of the dashboard. ## Impression Counted when an offer card is rendered. Impressions are free to publishers: no earnings: but they inform ranking and marketplace balance. ## Click Counted when a visitor taps an offer. The widget posts a click event, then the browser follows `GET /v1/r?t=` so ReTarget.gg can attribute before the advertiser's landing. - **Billable clicks** earn the publisher a share of the CPC. - **Filtered clicks** (duplicates in a window, known bots, self-clicks, wrong origin) don't charge or earn. They're visible in analytics for transparency. ## Display mode How the widget shows offers when a decision is **not allowed**: - **Geo-blocking overlay** (`widget.js`): full-screen card layout. Typical for regulated verticals. - **Section** (`widget-section.js`): inline container you place in your layout (sidebar, below article, etc.). Used when you want to keep your own experience and just add a "relevant offers" strip. - **Trigger Widget** (`widget-decline.js`): same overlay UI as geo-blocking, but opens only after your server triggers it. Used for post-KYC / post-signup ineligible-user flows. ## Balance, earnings, payouts - **Advertiser balance**: funds available to spend. Top up via Stripe or NowPayments. Payouts are debited per billable event. - **Publisher earnings**: credits earned per billable event. Immediately available on the Billing page (subject to compliance / dispute holds). - **Payouts**: manual withdrawals via PayPal, Skrill, or crypto. Minimum $50. ## Session (Trigger Widget flow only) A short-lived record opened by `widget-decline.js` when the page loads. It pairs a browser's polling connection with your server's readiness to show offers. 1. Browser `POST`s to `/v1/widget-decline/session` and receives a `sessionId`. 2. Your server `POST`s `/v1/widget-decline/trigger` with `{ sessionId }` and your integration secret when you're ready. 3. Browser's next poll sees `ready` and shows offers. If you never trigger, the session expires quietly. ## Vertical A high-level category (iGaming, finance, sports, crypto, e-commerce, streaming). Matched between advertiser offers and publisher websites so a casino offer lands on a casino blog, not a recipe site. You set your website's vertical during onboarding; you can change it anytime. ## CPC / CPA **Cost per click** / **cost per action**: the two main payment models. Advertisers choose one per offer and set per-country rates. Publishers earn a configured share on each billable event. ## Where to go next Install the script and verify from a VPN in two minutes. Every `data-*` attribute and widget behavior. Strategy, overlapping rules, regional targeting. Full `/v1/*` spec with curl examples. --- ### Add Your First Website for Geo Targeting URL: https://retarget.gg/docs/adding-your-first-website Category: integrate Audience: operator Last reviewed: 2026-07-15 Keywords: ReTarget.gg add website, publisher website setup, ReTarget.gg widget setup, geo-gated website integration, integration keys Every widget install starts with one step: registering your website in the dashboard. This gives you the two keys the script tag needs and anchors all of your analytics and billing to a single domain. ## Add a website From the **Overview** dashboard or the **Websites** section in the sidebar, click **Add website**. You'll fill in: - **Website name**: A label you'll recognize later (e.g. "My Casino Blog"). Only visible in your dashboard. - **Domain**: The root hostname where the script will run (e.g. `example.com`). No protocol, no path. The API enforces this at request time: `Origin` must match. - **Vertical**: Your industry (iGaming, finance, sports, crypto, e-commerce, streaming). Drives offer matching. - **Display mode**: Pick one of: - **Geo-blocking**: Full-screen overlay for visitors on the blocked side of your geo rule. The default for regulated verticals. - **Section**: Inline sponsored-offers container placed in your layout. Better for content sites. - **Ownership**: **Personal** (only you see it) or **Team** (shared with your organization's other members). You can switch modes later without redeploying: swap `widget.js` for `widget-section.js` (or vice versa) on your site. Both scripts use the same public key and website key. Pick whichever matches your first use case. ## Your two keys After saving, the website detail page shows: | Key | Prefix | Scope | | --- | --- | --- | | **Public key** | `pk_…` | Per-website: each website you register gets its own. | | **Website key** | `web_…` | Per-website: identifies this specific site. | Both are safe in client-side HTML: the API checks the request's `Origin` against the registered domain, so a leaked key is still scoped to one site. ## Staging, preview, or localhost Each website is pinned to a single domain. For parallel environments you have two clean options: - **Add a second website**: if you deploy to `staging.example.com` or `my-app.vercel.app`, create a separate website in the dashboard with that hostname as the domain. Use its own `pk_` / `web_` pair in staging code. Production analytics stay untouched. - **Test from localhost**: while developing on `http://localhost:3000` the widget treats the host as "localhost" and honors `data-dev-country` / `data-dev-region` for simulating blocked traffic. No staging domain needed for dev: ```html ``` Don't run production keys on staging. Real impressions and clicks from your team's testing will count against your dashboard analytics. ## Next steps Paste the script and verify from a VPN in two minutes. Choose which countries and regions see sponsored offers. Every `data-*` attribute and widget behavior. First place to go when offers don't show or analytics look wrong. --- ### ReTarget.gg Setup Guide URL: https://retarget.gg/docs/widget-installation Category: integrate Audience: developer Last reviewed: 2026-07-15 Keywords: ReTarget.gg widget installation, ReTarget.gg script embed, geo-gated widget setup, affiliate widget integration, ReTarget.gg install ReTarget.gg ships **three scripts and one iframe fallback**. You use exactly one per page. They all share the same keys, the same API, and the same analytics: only the surface differs. ## Pick an embed | Script | Doc | Best for | | --- | --- | --- | | `widget.js` | [Geo-blocking widget](/docs/widget-geo-blocking) | Full-page overlay when visitors are outside your licensed regions. | | `widget-section.js` | [Section widget](/docs/widget-section) | Sponsored offers in a box inside your layout (sidebar, article footer). | | `widget-decline.js` | [Trigger Widget](/docs/widget-decline) | After **your** server declines a user, show alternative offers (server-triggered). | | `iframe` | [Iframe fallback](/docs/widget-iframe) | Popup builders (GeoTargetly, OptinMonster) where you can't drop a script tag. | ## Which one do I want? Use the [Geo-blocking widget](/docs/widget-geo-blocking). It renders a full-screen overlay only when the visitor is outside your license: visitors who should see your product never notice it's there. Use the [Section widget](/docs/widget-section). Offers render inline inside a `
` you place in the page: works like any other ad block. Use the [Trigger Widget](/docs/widget-decline). Your server triggers the overlay **only** for users you've already declined: perfect fallback for failed signups. Use the [Iframe fallback](/docs/widget-iframe). Two query params, any popup builder that accepts custom HTML. ## Before you start [Add a website](/docs/adding-your-first-website) in the dashboard and copy the **public key** and **website key**. Dashboard → **Websites** → your site → **Geo rules**. Add the countries where your primary product is available. Everyone else becomes eligible for offers. Grab a VPN or have a friend in a blocked country reload the page after install. Without a real blocked test, you'll never know the install worked. ## Common attributes Every script tag takes the same three minimum attributes: | Attribute | Required | Purpose | | --- | --- | --- | | `src` | yes | URL of the script (`widget.js`, `widget-section.js`, or `widget-decline.js`). | | `data-pub` | yes | Your public key (`pk_…`). | | `data-website` | yes | Your website key (`web_…`). | Optional, shared across all three: | Attribute | Purpose | | --- | --- | | `data-api` | Override API base URL (defaults to `https://api-v2.retarget.gg`). Use for a staging API. | | `data-comments="true"` | Turn on verbose console logging for local debugging. | | `data-dev-country` / `data-dev-region` | **Localhost only.** Force a simulated country (e.g. `DE`, `BY`). Ignored on non-localhost hosts unless `data-allow-localhost="true"` is set. | Section mode also accepts a `data-container=""` attribute to pick which `
` to mount into, plus `data-max-ads=""` (1 to 24, default 6). ## Verifying installation After you install any embed, test from a VPN or location that matches your [geo rules](/docs/geo-rules). On the website detail page in the dashboard, the **Visitors** panel shows a green **Live** pill within a minute of your first page view, along with country and device breakdowns as traffic builds. See [Debugging](/docs/debugging) when the overlay doesn't render or clicks don't show up in analytics. ## Performance All three scripts are: - **Async by default**: they never block first paint or hurt Core Web Vitals. `widget.js` and `widget-section.js` issue one decision request and, only when blocked, one offers request. - **Lazy**: offer fetch only runs after a blocked decision, so allowed visitors incur one tiny fetch and nothing else. ## Next steps Full-page overlay install with attributes, iframe setups, and verification. Inline section embed with layout tips and container options. Server-triggered overlay for KYC / deposit decline flows. No-script path for popup builders like GeoTargetly and OptinMonster. End-to-end reference for every attribute, API URL, and behavior. REST endpoints behind the widgets. --- ### Geo Blocking Widget URL: https://retarget.gg/docs/widget-geo-blocking Category: integrate Audience: developer Last reviewed: 2026-07-15 Keywords: ReTarget.gg widget.js, geo-blocking overlay, full page affiliate widget, affiliate overlay embed, geo-gated popup The geo-blocking widget renders a **full-screen** overlay of sponsored offers only to visitors whose country or region falls outside the rule you set on your website. It's the right fit when your core product is regulated or region-locked (iGaming, finance, streaming catalogues) and you want to turn otherwise-declined traffic into revenue. You'll need a **public key** (`pk_…`) and a **website key** (`web_…`) from the dashboard: **Websites → [Your site]**. Both are per-website and safe to expose in client-side HTML: the widget validates the page's origin against the domain you registered. ## How it works The ``} {`// app/layout.tsx import Script from "next/script"; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} `} The widget refuses to render if the page's `Origin` doesn't match the **domain** you registered on the website. If you serve the same site on staging or a preview host, either add the staging hostname as a second website (with its own keys) or use `data-api` to point at a staging API. ## Attributes | Attribute | Required | Purpose | | --- | --- | --- | | `data-pub` | ✅ | The website's public key. Starts with `pk_`. | | `data-website` | ✅ | The website key. Starts with `web_`. | | `data-api` |: | Override the API base URL. Defaults to `https://api-v2.retarget.gg`. Use for staging. | | `data-dev-country` |: | Force a country (e.g. `DE`) regardless of IP. **Only honored on `localhost` or when `data-allow-localhost` is set**; ignored on arbitrary production hosts. | | `data-dev-region` |: | Force a region (e.g. `BY` for Bavaria). Same localhost-only constraint. | | `data-allow-localhost` |: | Treat the current host as "localhost" for dev-override purposes (useful when testing on a tunnelled hostname like `*.ngrok.io`). | | `data-comments="false"` |: | Mute the script's `console.log` diagnostics. Purely a logging toggle. | | `data-debug` |: | Force the widget into verbose debug mode (same effect as `localhost`). | ## Iframe / geo-gate setups If you already use a third-party geo gate (Cloudflare Access, custom middleware, iframe sandboxing) that embeds your page inside an iframe for blocked traffic, point the **blocked URL** to a page on your domain that loads this widget: typically your homepage or a dedicated `/blocked` route. The script behaves identically inside the iframe. `widget.js` and `widget-section.js` share the **same** public key and website key. Embed them on the same page or swap between them without changes in the dashboard. ## Verify the install Nothing should appear. Open DevTools → Network and confirm the `GET /v1/decision` call returns `{ "allowed": true }`. A `POST /v1/events/snippet-ping` will follow it with a `204 No Content`. While developing on `localhost`, add `data-dev-country="DE"` (or any country on your blocked side) to the script tag and reload. The overlay should appear. The website's Analytics tab shows the decision, impression, and click events after they flush. If nothing appears, jump to the [Debugging guide](/docs/debugging). ## Performance - Single async script. Never blocks first paint. - The decision request is tiny (`GET /v1/decision`) and doesn't need any payload beyond the query string. - Allowed visitors: one request out, nothing rendered. Blocked visitors: one decision call, one offers call, then render. - The server responds with `Cache-Control: no-store`: decisions are per-request. Don't put a CDN cache in front of `api-v2.retarget.gg`. ## Related Inline sponsored offers instead of a full-page takeover: same keys, different surface. Server-triggered overlay for failed KYC / deposit decline flows. The REST endpoints the widget calls under the hood: `/v1/decision`, `/v1/offers`, `/v1/events/*`. What to check when the overlay doesn't render or clicks don't attribute. --- ### Embed the Section Widget to Your Website URL: https://retarget.gg/docs/widget-section Category: integrate Audience: developer Last reviewed: 2026-07-15 Keywords: ReTarget.gg widget-section.js, inline affiliate widget, section mode ReTarget.gg, sidebar offer block, inline sponsored offers The section widget embeds sponsored offers **inside your layout**: sidebar, below an article, a dedicated "Deals" strip: instead of a full-screen takeover. Same keys as the [Geo-blocking widget](/docs/widget-geo-blocking); the API returns section-optimized offer counts. Content sites (news, reviews, recipes, how-to) that want a revenue strip alongside organic content. If your vertical is regulated and you want the "block until they click" UX, use the [Geo-blocking widget](/docs/widget-geo-blocking) instead. ## Install Two pieces: a container where offers render, and the script tag. {`
`}
{`// app/page.tsx (App Router) import Script from "next/script"; export default function Page() { return ( <>
{/* ... your content ... */}
`} ## How it works `widget-section.js` calls `GET /v1/decision?mode=section` so analytics reflect inline placement instead of a takeover. The container stays empty. This is expected: allowed visitors use your normal site. The widget fills the container with an offer card grid (up to `data-max-ads`, default 6). The container's width decides the layout. Clicks route through `/v1/r?t=…` for attribution before opening the advertiser's landing page. ## Attributes | Attribute | Required | Purpose | | --- | --- | --- | | `data-pub` | ✅ | The website's public key. | | `data-website` | ✅ | The website key. | | `data-container` |: | Default `retarget-ads`. Change to target a different element by id. Accepts a **single id** only. | | `data-max-ads` |: | Cap the number of offer cards. Default 6, hard cap 24. | | `data-api` |: | Override API base URL (defaults to `https://api-v2.retarget.gg`). | | `data-comments="false"` |: | Mute `console.log` diagnostics. Purely a logging toggle. | | `data-dev-country`, `data-dev-region` |: | Simulate a location. **Only honored on `localhost` or with `data-allow-localhost`.** | | `data-allow-localhost` |: | Treat the current host as "localhost" for dev-override purposes. | | `data-debug` |: | Force verbose debug mode. | ## Layout tips - **Width**: the widget respects its container: give it `100%` width and it will fill the slot. - **Height**: cards grow with content. Expect ~320 to 480 px per row of three offers. - **Sidebar**: 300 to 400 px wide columns work well; offers stack into one column per row. - **Below article**: a full-width strip of 2 to 3 offers works well on news/blog layouts. - **Gap to content**: add `margin-top` on the container (e.g. `mt-10`) so offers don't feel stapled to your prose. The widget refuses to render if the page's `Origin` doesn't match the **domain** registered on the website. For staging or preview hosts, use a separate website entry (its own keys) or point `data-api` at a dedicated staging API. ## Multiple placements on one page Each script tag mounts into **one** container. If you want offers in two places on the same page, use two separate script tags, each pointing at its own container id: ```html
...
``` Each script tag makes its own decision + offers calls. For most content layouts, a single well-placed block converts better than two and avoids the extra requests. ## Performance Same architecture as the [Geo-blocking widget](/docs/widget-geo-blocking): single async script, no critical-path work. The container is reserved in your layout but empty until a blocked decision lands. For a shift-free experience, give the container a `min-height` (e.g. `320px`) so the layout doesn't jump when offers appear. ## Related Full-page overlay on the same keys: good for regulated verticals. Full attribute list and cross-cutting behavior across all three widgets. The script loaded but the container is empty? Start here. Decide which visitors see the section block. --- ### Monetize KYC Declines with the Trigger Widget URL: https://retarget.gg/docs/widget-decline Category: integrate Audience: developer Last reviewed: 2026-07-15 Keywords: ReTarget.gg widget-decline, decline flow affiliate, ReTarget.gg integration secret, post-KYC monetization, failed signup monetization, server-triggered widget Use the Trigger Widget when **your app** has already decided a user isn't a fit. Think KYC failed, deposit declined, signup rejected, or age check blocked. You want to show alternative sponsored offers instead of an empty "sorry" screen. Same overlay UI as the [Geo-blocking widget](/docs/widget-geo-blocking). Only the trigger differs. **Your server** arms it with an integration secret that must never ship to the browser. The integration secret (`its_…`) is the **only** credential that can arm a Trigger Widget session. Leaking it lets anyone trigger overlays on your domain. Store it in env variables or a secrets manager. Never in `NEXT_PUBLIC_*`, client bundles, or git history. ## Before you install Dashboard → Websites → **[Your site]** → toggle **Trigger Widget**. This reveals the **integration secret** (keep it on the server only). Confirm your **domain** matches the page you'll load the widget on. From the same screen, copy: **public key** (`pk_…`), **website key** (`web_…`), **integration secret** (`its_…`). The first two go in HTML; the secret goes in your server's env. Usually a dedicated page (`/declined`, `/signup-failed`) or a post-decline step in a modal. Must be on the **same domain** you registered. ## How the handshake works The embed POSTs `POST /v1/widget-decline/session` with your public + website keys as query params. On success it receives a `sessionId` and a 15-minute `expiresAt`. It sets `window.__RETARGET_DECLINE_SESSION_ID` and dispatches a DOM event `retarget:decline-session` with `detail: { sessionId }`. Your client code reads either. Send the `sessionId` to your backend (a cookie-auth'd API route, a signed token, or whatever fits your app). Every 2 seconds it calls `GET /v1/widget-decline/session/{sessionId}`. Status stays `pending` until you trigger. `POST /v1/widget-decline/trigger` with `Authorization: Bearer ` and `{ "sessionId": "..." }`. Returns **200** with `{ "ok": true }` (or `{ "ok": true, "alreadyTriggered": true }` if called twice). Within 2 seconds the embed loads offers and renders the same UI as the geo-blocking widget. ## 1. Load widget-decline.js on your ineligible-user page {``} {`// app/declined/page.tsx import Script from "next/script"; export default function DeclinedPage() { return ( <> `} `/api/retarget/decline-arm` should authenticate the caller (session cookie, signed JWT) and pair the `sessionId` with a user id in your database before letting the server trigger. Otherwise a visitor could arm arbitrary sessions on your domain. ## 3. Call trigger from your server Send `Authorization: Bearer ` (or the `x-integration-secret` header) with `{ "sessionId": "..." }`. On success the API returns **200** with `{ "ok": true }`. {`// app/api/retarget/trigger/route.ts import { NextResponse } from "next/server"; const API = process.env.RETARGET_API_URL ?? "https://api-v2.retarget.gg"; export async function POST(req: Request) { const secret = process.env.RETARGET_INTEGRATION_SECRET; if (!secret) { return NextResponse.json( { error: "Missing RETARGET_INTEGRATION_SECRET" }, { status: 500 }, ); } const { sessionId } = (await req.json()) as { sessionId?: string }; if (!sessionId) { return NextResponse.json({ error: "sessionId required" }, { status: 400 }); } const res = await fetch(\`\${API}/v1/widget-decline/trigger\`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: \`Bearer \${secret}\`, }, body: JSON.stringify({ sessionId }), }); if (!res.ok) { return NextResponse.json({ error: await res.text() }, { status: res.status }); } const body = await res.json(); // { ok: true } return NextResponse.json(body, { status: 200 }); }`} {`// server-only util: call this from your "decline" code path const API = process.env.RETARGET_API_URL ?? "https://api-v2.retarget.gg"; export async function triggerDeclineOverlay(sessionId: string) { const secret = process.env.RETARGET_INTEGRATION_SECRET; if (!secret) throw new Error("RETARGET_INTEGRATION_SECRET is not set"); const res = await fetch(\`\${API}/v1/widget-decline/trigger\`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: \`Bearer \${secret}\`, }, body: JSON.stringify({ sessionId }), }); if (!res.ok) { throw new Error(\`ReTarget.gg trigger failed: \${res.status} \${await res.text()}\`); } }`} {`import os import requests API = os.environ.get("RETARGET_API_URL", "https://api-v2.retarget.gg") def trigger_decline_overlay(session_id: str) -> None: secret = os.environ["RETARGET_INTEGRATION_SECRET"] r = requests.post( f"{API}/v1/widget-decline/trigger", headers={ "Content-Type": "application/json", "Authorization": f"Bearer {secret}", }, json={"sessionId": session_id}, timeout=15, ) r.raise_for_status()`} Call `triggerDeclineOverlay(sessionId)` from the exact code path where your product says "not eligible": immediately after your KYC call fails, after you persist a "declined" row, etc. ## Optional attributes | Attribute | Purpose | | --- | --- | | `data-api` | Override API base URL: use for staging or a self-hosted API. | | `data-comments="false"` | Mute the script's `console.log` diagnostics. | | `data-dev-country` / `data-dev-region` | Simulate a visitor's location. Only honored on `localhost`. | ## End-to-end sanity check The ` ``` ## Geo-blocking flow (`widget.js`) Reads its `data-*` attributes and issues `GET /v1/decision?pub=…&website=…`. The API returns `{ allowed, requestId, country, region }`. `allowed=true` → the script emits a `POST /v1/events/snippet-ping` and exits. `allowed=false` → the widget fetches offers. `GET /v1/offers?website=…&requestId=…&country=…` returns a ranked list. The widget creates `#retarget-root` and renders the full-screen overlay. The widget posts `POST /v1/events/impression` for each rendered card and `POST /v1/events/click` before navigating through `GET /v1/r?t=…` for attribution. The page's `Origin` must match the **domain** registered on the website. Unknown origins get a `403` from `/v1/decision` with `reason: "invalid_origin"`. ## Section flow (`widget-section.js`) Identical to geo-blocking with three differences: 1. `GET /v1/decision?mode=section` so analytics differentiate inline from overlay. 2. `allowed=false` renders cards **into your container** instead of creating an overlay. 3. Card density is tuned for inline layouts (up to `data-max-ads` cards, default 6). Same keys, same origin rules, same events. ## Trigger Widget flow (`widget-decline.js`) For "we're not proceeding" flows where you still want alternative offers, with **you** deciding **when** the overlay opens. **One-time setup** in the dashboard: **Websites → [Your site]** → toggle **Trigger Widget** to reveal the **integration secret**. `POST /v1/widget-decline/session?pub=…&website=…` returns a `sessionId` and `expiresAt` (~15 min). The script sets `window.__RETARGET_DECLINE_SESSION_ID` and dispatches `retarget:decline-session` with `detail: { sessionId }`. Send the `sessionId` to your backend via whatever transport fits your app (cookie-auth'd fetch, signed JWT, etc.). When ready, `POST /v1/widget-decline/trigger` with `Authorization: Bearer ` and `{ "sessionId": "..." }`. Returns `200` with `{ "ok": true }` (or `{ "ok": true, "alreadyTriggered": true }` on a second call). The embed is polling `GET /v1/widget-decline/session/{sessionId}` every 2 seconds. On `ready` it fetches offers and renders. The integration secret is the **only** credential that can arm a Trigger Widget session. Keep it in server env vars or a secrets manager. Never in `NEXT_PUBLIC_*`, in git, or in client bundles. ## API endpoints used by the widgets The widgets call these on your behalf. You don't need to call them directly unless you're building custom integrations: but knowing what they do helps debug. Full spec at [`/docs/api`](/docs/api). ## Browser events dispatched Only the Trigger Widget currently dispatches a DOM event. The geo-blocking and section widgets do not emit custom events: if you want to forward widget activity to your own analytics stack, do it from your server via the events you receive in your dashboard, or subscribe to the API's webhook product (contact us). | Event | Widget | `detail` | | --- | --- | --- | | `retarget:decline-session` | `widget-decline.js` | `{ sessionId: string }` | Example: ```javascript window.addEventListener("retarget:decline-session", (ev) => { const { sessionId } = ev.detail; console.log("ReTarget.gg decline session", sessionId); }); ``` ## Origin and CORS The widget's page `Origin` must match the **domain** registered on the website. The API enforces this on `/v1/decision`, `/v1/offers`, and `/v1/widget-decline/*`. Unknown origins get `403 Forbidden` with `reason: "invalid_origin"`. Event endpoints (`/v1/events/*`) accept any origin: they're designed to be `sendBeacon`-friendly and CORS-safelisted (`text/plain` content type, no preflight). ## Publisher app proxy (optional) Instead of loading the script from `https://retarget.gg/`, you can proxy it through your own host (e.g. `/widget.js` mapped to `https://cdn.retarget.gg/widget.js`). When proxied, set the API base either via `data-api` on the script tag or by assigning `window.RETARGET_API` before the script loads (`window.AFFILFINDER_API` is still accepted as an alias). ## Performance - Single async script per surface. - Allowed visitors: one `/v1/decision` call, one `snippet-ping`, nothing rendered. - Blocked visitors: decision + offers + events. - API responses set `Cache-Control: no-store`: decisions are per-request. Don't cache them in a CDN in front of `api-v2.retarget.gg`. - `widget.js` respects standard async loading: it never blocks first paint. ## Related Full-page overlay: attributes, iframe setups, verification. Inline placement with container options and layout tips. Server-triggered overlay for post-KYC and other ineligible-user flows. No-script path for popup builders. `/v1/*` endpoints with request and response shapes. Troubleshoot installs, empty containers, missing events. --- ### Configure Your Geo Targeting Rules URL: https://retarget.gg/docs/geo-rules Category: operate Audience: operator Last reviewed: 2026-07-15 Keywords: ReTarget.gg geo rules, geo-blocking rules setup, country allowlist affiliate, geo-gated offer targeting, regional licensing Geo rules decide who sees your core product and who sees sponsored offers. It's one decision, usually set once, and it's the most important switch in the dashboard. ## How geo rules work When a visitor loads your page, the widget calls `GET /v1/decision` with the visitor's country (and region, when available: resolved server-side from the IP). The API compares that to your rule and returns `{ allowed: true }` or `{ allowed: false }`. Each website has **one** geo rule with a mode: - **Allowlist**: you list the countries / regions where your product _is_ available. **Everyone else** sees offers. - **Blocklist**: you list the countries / regions where your product _isn't_ available. **Those listed** see offers. Both modes support country-level entries (`US`, `DE`, `SE`) and region-level entries (`US-NJ`, `US-MI`). A visitor matches if their country **or** their country + region combination is on the list. ## Set rules for a website Dashboard → **Websites** → click the site you want to configure. Open the **Geo rules** section on the website detail page. **Allowlist** or **Blocklist**: see the decision shortcuts below. Countries use two-letter ISO codes (`US`, `DE`). Regions use `-` (`US-NJ`). For sub-national regulated markets (US states, Canadian provinces), add the region codes directly. The next `/v1/decision` call uses the new rule. No cache, no redeploy. ## Allowlist vs blocklist: which one? - **Use allowlist** when your product is available in a **short, known list** of countries. Example: "licensed only in UK, MT, SE" → put those three on the allowlist. Every other country gets offers. - **Use blocklist** when your product is available **almost everywhere** and you have a short list of exclusions. Example: "available globally except US + CA + FR" → blocklist those three. Everyone else sees your normal product. If in doubt, start with **allowlist**. It's the default assumption for regulated verticals and produces the safer error mode (worst-case you show offers to legitimate traffic; you never accidentally show your unlicensed product to a regulator). ## Common shapes ### Single country, nation-wide Most common for EU / UK operators. Allowlist, add one country (e.g. `SE`). Every visitor outside Sweden sees offers. ### Multiple countries, nation-wide Global brands with licenses in each country. Allowlist, add all allowed countries: - Sportsbooks licensed in UK, Ireland, Spain, Malta. - Crypto exchanges allowed in EU + UK + CH + JP. - Streaming services with regional catalogs. ### Sub-national (US states) Required for US operators. Allowlist, add `US-` for each regulated market: - iGaming: `US-NJ`, `US-PA`, `US-MI`, `US-WV`, `US-CT`. - Sports betting: wider set including `US-VA`, `US-CO`, `US-TN`, `US-AZ`. Leave the parent `US` country **off** the allowlist when you're listing individual states. Adding both makes the broader country entry win, and every US visitor becomes allowed. ### Launching in a new state or country Edit the rule, add the new code, save. The next page view picks it up. Want to roll back? Remove the entry and save again. ## Custom decline messaging You can attach custom copy to the blocked experience: the headline and description visitors see above the offers. Useful for: - Maintaining your brand voice on the blocked path. - Explaining why your product isn't available (regulatory, technical, content rights). - Pointing to a support page or region-specific landing. Look for the **custom title** and **custom subtitle** fields on the geo rules form. Both are optional and sit on top of the offer grid. ## Tips - **Start tight.** On allowlist mode, it's easier to widen rules (add a country) than to debug "why aren't offers showing" (you forgot to remove one). - **Rules are per-website.** If you run multiple sites under the same account, each one has its own rule: copying isn't automatic. This is a feature (allows per-site licensing) but a common source of "I updated and nothing happened" confusion. Check the right site first. - **Use [Analytics](/docs/analytics-and-reporting)** to see where your blocked traffic actually comes from. You might discover monetizable traffic from countries you hadn't considered. - **VPN traffic gets flagged automatically.** Suspect-proxy signals land in the event log; you don't need a second tool. ## Related See traffic by country and fine-tune your rules with data. Register a domain and pick a display mode before setting geo rules. How the rule maps to the `GET /v1/decision` response. "I'm in an allowed country and the overlay still shows": start here. --- ### Affiliate Marketing Performance Dashboard URL: https://retarget.gg/docs/analytics-and-reporting Category: operate Audience: operator Last reviewed: 2026-07-15 Keywords: ReTarget.gg analytics, publisher analytics dashboard, affiliate click reporting, geo-gated traffic analytics, scheduled CSV exports, publisher earnings report Every website has its own analytics: impressions, clicks, earnings, and breakdowns by geo, device, and offer. Scheduled CSV exports ship the same data to your inbox on whatever cadence you want. ## Where to find analytics Dashboard → **Websites** → click the site. Analytics live on the website detail page. There's also an **Overview** dashboard that rolls up across all your sites. ## Date range Pick any date range on the website detail page. All times are in UTC. ## Core metrics | Metric | Meaning | | --- | --- | | **Earnings** | Revenue from billable events: CPC clicks + CPA conversions when advertisers run CPA offers on your site. | | **Impressions** | Offer cards rendered for blocked visitors. | | **Clicks** | Clicks on offer cards (invalid clicks excluded: see below). | | **CTR** | Clicks ÷ impressions. | | **eCPM** | Earnings per 1,000 impressions. Roll-up combining CTR × avg payout. | | **Visitors** | Unique visitors in range (first-party session id). | ## Breakdown tabs On the website detail page you have four breakdown tabs: - **Geo**: top countries by earnings / impressions / clicks. - **Device**: desktop / mobile / tablet split (from user-agent bucket). - **CPC vs CPA**: earnings split by offer payout type. - **Placement**: if you run multiple ReTarget.gg embeds on different pages, their performance side-by-side. ## Exporting data ### One-off CSV Each breakdown tab has an **Export CSV** control. The download uses the currently selected date range and columns. ### Scheduled CSV exports Automate recurring reports to your team or analytics pipeline: Dashboard → **Websites** → click the site → scroll to **Scheduled CSV export**. Pick: breakdown tab (geo / device / offer / placement), cadence (daily / weekly / monthly), and email recipients. Exports are sent on the chosen cadence. You can review, edit, or pause all schedules from **Settings → Notifications & reports** in the sidebar. Exports arrive as CSV email attachments. Teams commonly forward them to Looker Studio, Zapier, or a Slack channel via a Zap: a full data pipeline with zero integration code. ## Advertiser analytics If you're also running CPC or CPA offers, your **Advertiser** dashboard shows the complementary view: - **Offer-level stats**: impressions, clicks, spend, budget usage per offer. - **Spend insights**: recent trend, billable events, balance runway. - **Geo breakdown**: which countries your offers are winning impressions in. ## What counts as billable Not every click or conversion ends up on your earnings line. ReTarget.gg filters events that look like abuse or automation: - **Duplicate clicks** from the same visitor on the same offer within a short window. - **Known bots**: user-agent and network heuristics. - **Self-clicks**: rate-limited per IP. - **Invalid origins**: events from unregistered domains are rejected by the API. Invalid activity is still visible in analytics for transparency but doesn't charge advertisers or earn publishers. ## Related How earnings roll up to available and how to withdraw. Use country breakdowns to fine-tune your allowlist or blocklist. Offer-level analytics for CPC / CPA advertisers. "My analytics look wrong": start here. --- ### Become a Geo Targeting Affiliate Advertiser URL: https://retarget.gg/docs/advertiser-guide Category: operate Audience: operator Last reviewed: 2026-07-15 Keywords: ReTarget.gg advertiser guide, create CPC offer, advertiser affiliate offers, geo-targeted advertising, CPC marketplace As an advertiser on ReTarget.gg, you run **CPC or CPA offers** that render to high-intent visitors on publisher websites. These visitors are outside their host site's primary service area: they've already shown intent by showing up; they just can't convert on the publisher's product. That's your audience. ## Becoming an advertiser Sidebar → **Advertiser** → **Become an advertiser**. Fill the application: company details, primary website, vertical, preferred payment model. Our team reviews applications during business hours. Regulated verticals (iGaming, crypto) may need additional documentation. Once approved, the **Advertiser** section unlocks with offer creation, budgets, deposits, and stats. ## Creating an offer Click **Create offer** in the advertiser dashboard. You'll configure: ### Basics - **Vertical**: the category your offer serves (iGaming, finance, sports, crypto, streaming, e-commerce). - **Creative**: brand name, short description, call-to-action, and a logo / hero image. Shown in the offer card grid. - **Affiliate URL**: your landing page. Supports dynamic placeholders, all substituted at redirect time on `/v1/r`: - `{clickId}`: the unique id for this click. Pair it with your own analytics to close the attribution loop. - `{websiteKey}`: the publisher's website key. - `{country}`: ISO country code from the request IP. - `{region}`: ISO region code when available. ### Targeting - **Countries**: one or more ISO country codes. The offer only shows in these markets. - **Regions**: optional narrowing to specific regions within a country. ### Payment model - **CPC**: you pay per billable click. Set per-country rates; the dashboard shows the minimum for each country based on platform quality floors. - **CPA**: you pay per conversion. Configure flat or tiered payout rules, an attribution window, and (optionally) first-purchase-only. Requires the CPA integration to be enabled on your account. - **Premium placement**: on CPC offers, premium status puts your offer earlier in the grid for eligible visitors. Request it from your advertiser dashboard; availability depends on fit and spend history. ### Budgets Four optional caps. The first one that exhausts pauses the offer: - **Daily budget**: max spend per UTC day. - **Weekly budget**: max spend per UTC week. - **Monthly budget**: max spend per UTC month. - **Total budget**: lifetime cap. Optionally pause at 90% to avoid overshooting. After submission, new offers enter **pending approval**. Once approved, they go live for matching publisher websites. ## Managing offers From the advertiser dashboard you can: - **Pause / reactivate** an offer at any time. - **Edit** creative, rates, targeting, and budgets. - **Duplicate**: clone an offer to test a creative variant or a regional split. - **View stats**: per-offer impressions, clicks, spend, CTR (and CPA conversions when applicable). ## Budget behavior - **Daily cap reached** → offer pauses until 00:00 UTC the next day. - **Weekly / monthly cap reached** → offer resumes at the start of the next period. - **Total cap reached** → offer pauses permanently. Raise the cap to resume. - **Balance hits zero** → all your offers pause simultaneously; deposit more to resume. Monitor **Spend insights** on the advertiser dashboard: recent spend trend, billable events, and balance runway. ## Funding your account See [Advertiser deposits](/docs/advertiser-deposits) for card and crypto top-ups. Keep balance ahead of spend to avoid mid-campaign pauses. ## Optimization checklist - **Start narrow, scale wide**: launch in one country to learn before scaling. - **Match landing to placement**: a section placement on a blog expects a different CTA than a full-page geo overlay. - **Use `{clickId}` in your URL**: lets you close the loop on your own analytics (which ReTarget.gg click created which signup / conversion). - **Iterate on creative**: run a couple of variants and double down on the winner. - **Don't ignore mobile**: a large share of blocked traffic is mobile. Make sure your landing page works on iOS Safari and Android Chrome. ## Related Card and crypto top-ups, promo bonuses, and spend invoicing. Offer-level stats, geo breakdowns, and budget runway. System overview: how offers match with blocked traffic. If you also publish, this is how your own earnings flow. --- ### Fund Your Advertiser Balance URL: https://retarget.gg/docs/advertiser-deposits Category: operate Audience: operator Last reviewed: 2026-07-15 Keywords: ReTarget.gg deposit, advertiser add funds, affiliate advertising payment, crypto deposit affiliate, Stripe top-up, NowPayments crypto CPC and CPA offers need a positive advertiser balance. Top up via card (Stripe) or cryptocurrency (NowPayments). Card deposits hit your balance as soon as Stripe confirms payment; crypto deposits credit once the blockchain confirmation clears. ## Card payments Dashboard sidebar → **Billing**. In the **Advertiser balance** section, click **Add funds**. Enter the amount in USD. You're redirected to Stripe's hosted checkout. Supports Visa, Mastercard, Amex (where supported by your issuer), and Apple / Google Pay on mobile. Once Stripe marks the payment succeeded (almost always within seconds), your balance is credited and a receipt is emailed to the org billing email. Cancelled checkouts don't charge anything; the balance stays the same. ## Cryptocurrency Billing → **Add funds with crypto** → choose from the supported list (USDT TRC-20 / ERC-20, USDC, BTC, ETH, and more: the live list lives in the dashboard). NowPayments generates a unique, single-use payment address. The dashboard shows the exact amount to send and a QR code. Confirmation time depends on the chain (USDT-TRC20 is fast; BTC can take 10 to 30 minutes). The Billing page updates once we see the confirmation. Once confirmed, your balance is credited and the payment is recorded with its blockchain hash for auditability. Crypto deposits are processed through [NowPayments](https://nowpayments.io). If the received amount differs from the invoice, reach out to support: we credit on a best-effort basis. ## Promo bonuses If you entered a **promo code** during onboarding, a bonus may have been applied to your advertiser balance. Promo bonuses: - Show as a separate line item in **Deposit history**. - Spend before your real balance (first-in, first-out). - Can be non-withdrawable (contract-dependent): usage-only credit. - Expire per the promo's terms. ## Deposit history View every card and crypto deposit in **Deposit history** on the Billing page. Each entry shows: - Amount deposited (and currency sent, for crypto). - Payment method. - Date and timestamp. - Invoice reference / blockchain tx hash. - **Download receipt** (card payments). ## Invoicing For custom invoicing (finance teams with PO requirements, VAT handling, or consolidated reporting), contact [support](/contact) with your workflow and we'll wire it up manually. ## How spend works When a visitor clicks one of your CPC offers (or converts on a CPA offer), the payout is **debited from your advertiser balance** as the event is processed. If the balance drops to zero, all your active offers pause. Deposit more to resume. Keep a comfortable buffer relative to your daily spend. Dashboard notifications surface low-balance warnings you can react to. ## Refunds and chargebacks - **Card deposits**: contact [support](/contact) for refund requests on unspent balance. We refund case-by-case subject to terms. - **Crypto deposits**: non-refundable once confirmed (industry standard). - **Spent amounts**: non-refundable; the traffic has been delivered. ## Related Create and manage CPC offers. Monitor spend, CTR, and budget runway. Publisher-side of the same system: earnings and withdrawals. Billing email, low-balance alerts, and notifications. --- ### Billing and Payouts URL: https://retarget.gg/docs/billing-and-payouts Category: operate Audience: operator Last reviewed: 2026-07-15 Keywords: ReTarget.gg payouts, publisher earnings withdrawal, affiliate payout request, publisher billing, PayPal Skrill crypto payout As a publisher, you earn per billable click (and CPA conversion when advertisers run CPA offers on your site). Earnings accrue in real time, show up on your Billing page immediately, and become withdrawable as soon as you have at least $50 available. ## The numbers on the Billing page | Field | Meaning | | --- | --- | | **Total earnings** | Lifetime earnings across every website in your organization. | | **Available** | `Total earnings − Total withdrawn − Pending withdrawals`. What you can request a payout for right now. | | **Pending** | The sum of withdrawal requests you've already submitted that haven't been paid yet. | | **Total withdrawn** | Total of past, completed withdrawals. | Click earnings land in **Available** as soon as the click is counted and the invalid-click filter passes: there's no mandatory settlement delay. The only reasons an earning might be held are a compliance review or advertiser dispute, and we'll tell you in the dashboard when that happens. ## How clicks become earnings Widget records the click; the visitor hops through `GET /v1/r` to the advertiser's landing. Duplicates, known bots, self-clicks, and events from unregistered origins are removed. Invalid events are visible in analytics for transparency but don't earn. Billable clicks earn the offer's per-country payout × your publisher share. Earnings land in **Available** on your Billing page. Click **Request payout** once Available ≥ $50, pick a method, and submit. The amount moves from Available to Pending until the payout is approved. ## Request a payout Dashboard sidebar → **Billing**. Available is the cap on the amount. The $50 minimum is enforced by the API. - **PayPal**: enter the PayPal email to send to. - **Skrill**: enter the Skrill email. - **Cryptocurrency**: pick a network and paste the wallet address. The request moves into **Pending** status while our team reviews and processes it. You'll get a notification when the payout is approved. ## Withdrawal statuses Every payout request progresses through: | Status | Meaning | | --- | --- | | **Pending** | Just submitted, in the review queue. Held against your Available balance. | | **In progress** | Your payout is being processed. Funds are on their way. | | **Approved** | Paid out. Counts toward **Total withdrawn**. | | **Denied** | Rejected (wrong address, failed compliance, etc). The amount returns to **Available** and the denial reason is attached. | Failed payouts are rare. Common causes: invalid PayPal / Skrill email, wrong wallet address or network, or a sanctioned destination. On denial, you can correct the details and request again. ## Withdrawal fees Each payout carries a network / processing fee that's shown on the request form **before** you submit. The net (amount − fee) is what lands in your account; total earnings are debited by the full amount. Fees vary by method and crypto network. ## Currency display The dashboard sidebar has a currency selector that converts all displayed balances (USD, EUR, GBP, CAD, AUD, BRL and more). This is **display only**: the platform always computes in USD and pays out in USD or your chosen crypto asset. FX rates refresh periodically from a public provider. ## Taxes ReTarget.gg doesn't withhold taxes. You're responsible for reporting publisher income to your local authorities. Contact billing support at year-end if you need a statement. For EU publishers operating under a company, add your VAT number in **Settings → Organization** so we issue zero-rated invoices (reverse charge). ## Holds and compliance Occasionally we hold earnings or pause a withdrawal when: - **Spike detection**: sudden earnings growth on a new site may be paused for fraud review. - **Advertiser dispute**: an advertiser challenges specific events; held until resolution. - **Compliance review**: new accounts or first large withdrawals. When a hold affects you, the dashboard shows it on the affected withdrawal or organization balance with a short reason. Reach out via [support](/contact) if one is blocking a payout. ## Related Where earnings come from; filter by site, geo, and offer. Spin up more sites to increase earning potential. The other side: how advertisers top up before their clicks pay you. Earn commissions on every publisher or advertiser you refer. --- ### Manage Your Team URL: https://retarget.gg/docs/team-management Category: operate Audience: operator Last reviewed: 2026-07-15 Keywords: ReTarget.gg team management, invite team members, publisher team roles, affiliate account collaboration, role-based access Bring your team into ReTarget.gg with role-based access. Marketers can manage campaigns without seeing billing; finance can read earnings without touching integrations; developers can install widgets without wire transfer permissions. ## Open Team settings Dashboard sidebar → **Team**. Available after you complete onboarding. ## Invite members Top-right of the Team page. The team can have up to 10 members. Pick from Viewer, Manager, Admin, or Owner (see matrix below). The invitee receives an email. Clicking the email signs them in and joins them to your organization. Pending invitations show on the Team page with the option to **cancel** or **resend**. ## Roles Four roles with increasing scope: | Role | Sees websites & analytics | Edits widgets & geo rules | Creates advertiser offers | Billing & payouts | Team & org settings | | --- | --- | --- | --- | --- | --- | | **Viewer** | ✅ |: |: |: |: | | **Manager** | ✅ | ✅ | ✅ |: |: | | **Admin** | ✅ | ✅ | ✅ | ✅ | ✅ | | **Owner** | ✅ | ✅ | ✅ | ✅ | ✅ (+ transfer / delete org) | Only **Admins** and **Owners** can add or remove members. Owner is the original account holder. Give finance **Admin** (needs billing), marketing and ops **Manager** (widgets + campaigns but no payouts), and read-only stakeholders **Viewer**. ## Shared websites A website's **ownership** is set at creation: - **Personal**: only you see and edit it. Hidden from the team roster. - **Team**: shared with your organization; every team member sees it (subject to role). ## Organization name Set during onboarding and editable in **Settings → Organization**. It appears on invoices, CSV exports, and team invite emails. Any **Admin** or **Owner** can update it. ## Removing a member Any **Admin** or **Owner** can remove a member. Access is revoked immediately; the member keeps any personal websites they owned. ## Ownership changes Changing the org owner requires a few manual steps: [contact support](/contact) and we'll make the change after verifying both sides. ## Related Your personal profile, phone verification, and notifications. Earn commissions on teams you refer. Who sees earnings; who can request payouts. Create personal vs team-owned sites. --- ### Account Settings URL: https://retarget.gg/docs/account-settings Category: operate Audience: operator Last reviewed: 2026-07-15 Keywords: ReTarget.gg account settings, publisher profile settings, notification preferences, ReTarget.gg preferences, phone verification Everything you need to manage your personal profile, your organization, and your notification cadence lives on **Settings**. Sidebar → **Settings**. ## Profile - **Email**: The email you sign in with. Read-only. To change, contact [support](/contact) and we'll walk you through a verified migration. - **Phone**: Your verified recovery phone number. Update anytime by sending a new SMS code. - **Display name**: How you appear in team rosters and comments. Editable anytime. ### Updating your phone On the Settings page, **Profile** section. Full international format (e.g. `+44 20 7946 0958`). Arrives within seconds. If it doesn't, try the **Resend** link. The phone is updated immediately. The old number is invalidated for recovery. ## Organization - **Company / legal name**: Appears on invoices, CSV exports, and the team workspace. Any **Admin** or **Owner** can edit. - **VAT / Tax ID**: EU companies can enter a VAT number for zero-rated invoicing (reverse charge). Optional for non-EU. - **Billing email**: Where receipts, invoices, and low-balance alerts go. Defaults to the Owner's email. ## Notifications & reports Under **Notifications & reports** on the Settings page you'll find: - **Weekly performance digest**: weekly email summary of your websites. Toggle on/off with the switch. Applies to your user. - **Scheduled CSV exports**: quick overview of the per-website CSV schedules you've configured. Click any entry to jump to that website's scheduled-export settings to edit or pause. In-product notifications (withdrawal updates, application decisions, team events, and so on) surface in the dashboard's notification bell. See [Analytics & reporting](/docs/analytics-and-reporting) to set up new CSV schedules. ## Security - **Authentication**: sign in with the method you picked during onboarding (email link or supported social sign-in). If you need to change it, contact [support](/contact). - **Delete account**: requests are human-reviewed. Publisher earnings must be withdrawn first; advertiser balances drawn down or refunded. Use **Danger zone** on the Settings page. ## Related Invite members, roles, and transfer ownership. Find your referral link and track commissions. Earnings, withholding, and payouts. Scheduled CSV exports. --- ### Earn Commissions with the Affiliate Program URL: https://retarget.gg/docs/affiliate-program Category: operate Audience: operator Last reviewed: 2026-07-15 Keywords: ReTarget.gg affiliate program, affiliate referral commission, refer publishers earn money, ReTarget.gg referral link, affiliate partner Every ReTarget.gg account has a built-in affiliate program. Share your referral link, and when someone signs up and starts using the platform: as a publisher or an advertiser: you earn a share of the fees that flow. ## Your referral link Dashboard sidebar → **Affiliate program**. The page shows your personal referral URL: the shape is `https://app.retarget.gg/signin?ref=YOUR_CODE`: and a **Copy** button. It's ready the moment you finish onboarding. ## How it works Your website, newsletter, social, the ReTarget.gg community: anywhere relevant publishers or advertisers hang out. The `ref` (or `referral` / `referalcode`) query parameter is stored in their browser's local storage. It's carried through to signup even if they browse around first. At account creation, their new organization is stamped with your affiliate code. The attribution is permanent for that org. Every time they deposit (as an advertiser) or withdraw (as a publisher), we pay you a commission credited to your org's affiliate earnings. ## Commission structure Two recurring streams, both paid for the lifetime of the referred org: - **Deposit share**: **35% of the fees we collect** on advertiser deposits. - **Withdrawal share**: **5% of the fees we collect** on publisher withdrawals. The numbers you see on your Affiliate program page (deposit / withdrawal share percentages and lifetime earnings) are the authoritative source. ## Tracking referrals The Affiliate page shows: - **Total referrals**: count of orgs that signed up through your link. - **Referrals this week**: new signups in the last 7 days. - **Lifetime earnings (USD)**: cumulative commission credited to your org. - **Referral list**: recent referred organizations with their name and join date. ## Marketing tips - **Pair the link with educational content.** A blog post titled "How we monetize geo-blocked traffic with ReTarget.gg" converts better than a naked banner. - **Put it in your outbound.** If you're in affiliate marketing and work with publishers, the link is an easy value-add in your intro emails. - **Focus on qualified referrals.** One high-volume publisher is worth 50 stale signups. Referral codes are stored in the visitor's browser `localStorage`: not a cookie. That means the attribution survives across pages on your site and between visits, but is scoped to the same browser / device. ## Payout Referral earnings accumulate in your organization and are released through our standard schedule once you request. Contact [support](/contact) for payout timing on referral-specific balances. ## Policies - **No self-referrals.** Signing yourself up under your own link (or close family / same-IP sockpuppets) is fraud. Detected on review; offending commission is clawed back. - **No cookie / storage stuffing.** Setting our referral key in hidden iframes or without user intent is fraud. - **Truth in advertising.** Claims about ReTarget.gg earnings must match reality: don't promise numbers we can't back up. ## Related How earnings cash out. The flow you'll want new advertiser referrals to walk through. Update profile and notifications. Context on publisher / advertiser roles. --- ### Debugging Checklist URL: https://retarget.gg/docs/debugging Category: operate Audience: developer Last reviewed: 2026-07-15 Keywords: ReTarget.gg debugging, widget not showing, ReTarget.gg not working, integration health, decision API 403, ReTarget.gg troubleshooting The widget is intentionally quiet on the happy path. Here's how to get it talking when something's off. Dashboard → Websites → click the site → look at the **Visitors** panel. A green **Live** pill means the widget is pinging us; **No pings in 24h** means the install isn't reaching the API: that's almost always install-side, which we'll cover first. ## Symptom: widget never renders ### 1. Confirm the script tag loads In your browser's DevTools → Network tab, filter for `widget`. You should see a single request to `https://cdn.retarget.gg/widget.js` (or `widget-section.js` / `widget-decline.js`) returning **200**. - **404** → wrong URL. Check the `src` is exactly `https://cdn.retarget.gg/widget.js`. - **Request doesn't appear** → script tag never rendered. Check your CMS / framework is outputting it. In Next.js make sure you're using ``} {`curl -sS "https://api-v2.retarget.gg/v1/decision?pub=YOUR_PUBLIC_KEY&website=YOUR_WEBSITE_KEY" \\ -H "Origin: https://example.com" | jq`} Or use a real VPN (Mullvad, Proton, or any regional VPN). VPNs matter for the full signal: the API resolves the country server-side from the request IP, not from browser locale. ## Symptom: blocked, but no offers show Decision returned `allowed: false` but the overlay renders a "No offers available in your region right now" card, or the offer grid is empty. - **No active offers for that country**: common during quiet hours or for less-trafficked markets. Switch your VPN to a major market (US / UK / DE / AU / CA) to confirm. - **Vertical mismatch**: the advertiser pool is scoped by vertical. Check the website's vertical matches your intended advertiser category. - **Rate limited**: under heavy load `/v1/offers` can return `429`. In practice you'll only see this under automated testing. ## Symptom: widget renders but clicks don't count Offers appear, you click, but analytics stays at 0. - **Consent manager blocking the event**: your CMP may be blocking non-essential POSTs. Allowlist `https://api-v2.retarget.gg`. - **Invalid-click filter hit**: if you're testing from your own IP repeatedly, the filter counts only the first click per visitor per offer within a window. Use a VPN or second device. - **Wrong redirect path**: don't rewrite the card's `href`. The widget fires a click event before navigating, and short-circuiting the flow breaks attribution. ## Symptom: Visitors panel says "No pings in 24h" The widget posts to `/v1/events/snippet-ping` on every load (both allowed and blocked paths). If the dashboard's Visitors panel shows **No pings in 24h**: 1. Open the page with the script. 2. DevTools → Network → filter `snippet-ping`. You should see a POST returning **204**. 3. No request → the widget script didn't finish loading. Check the widget script's request in Network for a 5xx or timeout. 4. Request present and 204, but dashboard still empty → you're likely viewing the wrong website. The `data-website` on the script must match the `web_` key of the website you're viewing. ## Symptom: Trigger Widget session never becomes ready You loaded `widget-decline.js`, sent the `sessionId` to your server, and called trigger, but the embed never shows the overlay. - **Trigger didn't succeed** → `POST /v1/widget-decline/trigger` should return **200** with `{ ok: true }`. If it's an error status, check the response body. Common: session expired, wrong `sessionId`, or a revoked integration secret. - **Wrong session id** → your client sent a newer `sessionId` but your server is triggering an older one. Make sure the id you send matches `window.__RETARGET_DECLINE_SESSION_ID` or the payload of the `retarget:decline-session` event. - **Integration secret leaked and rotated** → your old env var is stale. Pull the current secret from the website's Trigger Widget section and redeploy. - **Polling blocked by CSP / CORS** → the embed polls the Trigger Widget session endpoint. Make sure `connect-src` includes `https://api-v2.retarget.gg`. ## Symptom: analytics numbers look wrong - **Timezones**: all timestamps are UTC. If you compare against your own analytics in local time, expect offset. - **Ad-blockers**: desktop ad-blockers intercept `/v1/events/*`. Our numbers will be slightly below what your own first-party beacons report if you run both; that's expected. - **Device attribution**: we bucket device from viewport width (`mobile` < 640, `tablet` < 1024, else `desktop`). On shared devices this is best-effort. - **Country mismatch**: the API resolves country from the request IP. VPNs and new IP ranges occasionally land as an adjacent country. ## Where to get a log For self-service debugging, add `data-comments="true"` to the script tag temporarily: the widget prints decision, offer, impression, and click activity to the browser console with timestamps. When you contact [support](/contact), include the **full request URL** (with query string), the approximate **timestamp in UTC**, and the **response body** from any failing request. That's enough for us to pinpoint the event. The widget accepts `data-dev-country` / `data-dev-region` only when the page host is `localhost` / `127.0.0.1`: or when you explicitly set `data-allow-localhost="true"` (use sparingly, only in gated dev environments). On production hosts it's silently ignored. Use a real VPN there. ## Still stuck? Every attribute and every event: full reference. Request and response shapes for every `/v1/*` endpoint. Non-technical questions and common misunderstandings. Response within one business day. Include the failing request URL and UTC timestamp. --- ### Integrate ReTarget.gg with Next.js URL: https://retarget.gg/docs/recipes-nextjs Category: recipes Audience: developer Last reviewed: 2026-07-15 Keywords: ReTarget.gg Next.js, Next.js App Router widget, Next.js affiliate script, Next.js CSP widget, Next.js script strategy, ReTarget.gg TypeScript Production install for Next.js 14/15 App Router. Covers env-scoped keys, CSP headers, and a typed helper for the Trigger Widget flow. The patterns translate 1:1. Swap `app/layout.tsx` for `pages/_app.tsx` and wrap your component tree with the same ` ``` ### Option B: Replace GeoTargetly entirely with the geo-block widget Once you're comfortable, remove GeoTargetly and let ReTarget.gg handle both gating and monetization. Drop `widget.js` in your `` with your keys. In the dashboard → **Websites** → your site → **Geo rules**, recreate the same allow/block list you had in GeoTargetly. For a day or two, keep both running. Compare: does ReTarget.gg block the same visitors GeoTargetly did? Our decision is IP-based, same as theirs, so results should be 99%+ identical. Once shadow-test passes, deactivate the old tool. You're now earning on every blocked click. See [Widget geo-blocking](/docs/widget-geo-blocking) for the full overlay experience. ### Option C: Use the Trigger Widget for checkout / KYC declines If your block isn't a pure geo-block but rather a payment, KYC, or account decline, use the [Trigger Widget](/docs/widget-decline) instead. It is the same idea. A declined user sees offers, triggered by your backend rather than by geo rules alone. ## Feature comparison | | GeoTargetly | ReTarget.gg | | --- | --- | --- | | IP-based geo-blocking | ✅ | ✅ | | Per-country rules | ✅ | ✅ | | Country-specific redirects | ✅ | ✅ (via Trigger Widget + offers) | | Monetization on blocked visitors |: | ✅ (CPC earnings) | | Analytics | Basic | Real-time clicks, impressions, geo breakdown | | Integration time | Minutes | Minutes | | Pricing | Paid subscription | Free: you earn per click | | Custom branding | Limited | Full HTML/CSS control (section widget or iframe) | ## Gotchas - **Your "blocked" page must be allowed by your own middleware**: If your existing redirect rule sends blocked traffic to `/blocked`, make sure that page itself isn't also blocked. Common mistake. - **CDN caching**: If your CDN caches HTML without varying on IP, you'll need to bypass cache on `/blocked` and on the widget script's requests. Our `/v1/decision` call is always fresh (no CDN caching) so that side is fine. - **Dual-blocking ambiguity**: If you run both GeoTargetly AND ReTarget.gg geo rules during shadow-test, the stricter one wins. That's generally safe but remember to reconcile the configs before removing GeoTargetly. ## Related Keep GeoTargetly, drop an iframe in its popup. The overlay that replaces plain geo redirects. Configure the gate conditions. Same pattern for checkout / KYC declines. --- ### Integrate ReTarget.gg with Cloudflare URL: https://retarget.gg/docs/recipes-cloudflare Category: recipes Audience: developer Last reviewed: 2026-07-15 Keywords: Cloudflare Workers ReTarget.gg, Cloudflare Pages widget, Cloudflare Zaraz, edge inject script, HTMLRewriter widget Cloudflare is three different products that all solve this differently. This recipe covers the three paths we see most: 1. **Workers**: inject the widget into every HTML response at the edge, using `HTMLRewriter`. Zero changes to your origin. 2. **Pages**: a static or SSR project hosted on Cloudflare Pages. Drop the tag into your template. 3. **Zaraz**: tag-manager style; paste the same one-liner into a Custom HTML tag. If you use **Cloudflare Workers** for an advertiser-side backend (e.g. KYC or payment), skip to [Server-triggered Trigger Widget from a Worker](#server-triggered-trigger-widget-from-a-worker). ## Option 1: Inject from a Worker with HTMLRewriter Use this when your origin is yours-to-break. The Worker sits in front, gets the HTML back, and streams a rewritten response with our tag appended to ``. Nothing on the origin changes. {`export default { async fetch(request, env) { // Fetch the origin response, untouched. const response = await fetch(request); // Only rewrite HTML. Pass-through everything else (images, JSON, …). const contentType = response.headers.get("content-type") || ""; if (!contentType.toLowerCase().includes("text/html")) { return response; } return new HTMLRewriter() .on("head", new TagInjector(env)) .transform(response); }, }; class TagInjector { constructor(env) { this.env = env; } element(head) { // Keep this string in sync with the tag the dashboard generates. // The guard on data-website makes it safe to fire on every request // (even on repeated edge passes with HTMLRewriter in dev). const tag = \`\`; head.append(tag, { html: true }); } }`} Wire it up with `wrangler.toml`: {`name = "retarget-edge" main = "src/worker.js" compatibility_date = "2025-09-01" # Route it in front of your site. [[routes]] pattern = "yoursite.com/*" zone_name = "yoursite.com" `} Deploy: {`npx wrangler deploy`} `HTMLRewriter` streams the response: no buffering, no memory spike on large pages, and no risk of injecting the tag twice if your origin already contains `` fragments in odd places. It also plays nicely with Cloudflare's streaming to the browser, so **time-to-first-byte isn't affected**. ### Bot-only exclusions If you want to skip the widget for known bots (SEO, monitoring, health-checks): keep the origin HTML clean for them: gate the rewrite on the User-Agent: {`const BOT_RE = /(bot|crawler|spider|pingdom|uptimerobot|lighthouse|GoogleOther)/i; export default { async fetch(request) { const ua = request.headers.get("user-agent") || ""; const response = await fetch(request); if (BOT_RE.test(ua)) return response; const ct = response.headers.get("content-type") || ""; if (!ct.toLowerCase().includes("text/html")) return response; return new HTMLRewriter().on("head", new TagInjector()).transform(response); }, };`} ## Option 2: Cloudflare Pages If your site is hosted on Cloudflare Pages (static, Next.js, Astro, Nuxt, Remix, SvelteKit…), the widget is just a ``} For framework-specific placement (Next.js App Router, etc.), see the [Next.js recipe](/docs/recipes-nextjs). Pages projects can attach a Worker as **Pages Functions** (`functions/_middleware.js`). If you want to keep the tag out of your source code, use the `HTMLRewriter` approach from Option 1 in a middleware function: same code, same behavior. ## Option 3: Cloudflare Zaraz Zaraz is Cloudflare's built-in tag manager. Pasting our tag is the same short story as Google Tag Manager, covered in full here: **[GTM recipe](/docs/recipes-gtm)**. Short version for Zaraz: Pick **Custom HTML** as the tool type. Drop the exact same snippet shown in Option 2. Set the **trigger** to **Page view** (all pages). Zaraz deploys the tag to your zone immediately: no Save → Publish cycle like GTM. ## Server-triggered Trigger Widget from a Worker If you're running advertiser-side logic in a Worker (for example, a KYC check that denies a user), you can open the Trigger Widget overlay on the user's page without calling back through your origin. The two endpoints you need: {`export default { async fetch(request, env) { // …your KYC / risk check… const declined = await runKyc(request); if (!declined) return new Response("approved", { status: 200 }); // 1. Fire the trigger from the Worker. The browser is already polling // /v1/widget-decline/session/{sessionId}: the next poll will pick // it up and render the overlay. await fetch("https://api-v2.retarget.gg/v1/widget-decline/trigger", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": \`Bearer \${env.RETARGET_INTEGRATION_SECRET}\`, }, body: JSON.stringify({ sessionId: request.headers.get("x-retarget-session"), reason: "kyc_failed", }), }); return new Response("declined", { status: 200 }); }, };`} The client-side `widget-decline.js` emits the session ID when it mounts. Capture it and forward with your Worker request. Full client-side setup: **[Trigger Widget integration](/docs/widget-decline)**. `/v1/widget-decline/trigger` requires a **server-only integration secret** (bearer token). Never ship it to the browser. Put it in `wrangler secret put RETARGET_INTEGRATION_SECRET` and access it via `env.RETARGET_INTEGRATION_SECRET`. ## Cache considerations If your Pages project or Worker sits behind Cloudflare's cache: - The widget is a static JS file served from `https://cdn.retarget.gg/widget.js` with a short `Cache-Control`. You don't need to cache it yourself. - The **API** (`api-v2.retarget.gg`) responses set `Cache-Control: no-store`: decisions are per-request. Don't add `Cache-Rules` that cache them. - Your origin HTML caches as normal: the `HTMLRewriter` injection from Option 1 re-runs on every HTML response, but since `widget.js` itself is a tiny external script, there's no duplicate-content concern. ## Verify Check the Network tab for `widget.js` and a `GET /v1/decision` call to `api-v2.retarget.gg`. Both should be 200. On `localhost` add `data-dev-country="US"` to the injected script tag to simulate a blocked visitor (see [Debugging](/docs/debugging)). On production, spin up a quick VPN test. Open **Websites → your site → Analytics**. A successful page-load produces a `snippet-ping`; a blocked user produces an `impression` per card. If nothing shows after 2 minutes, revisit [Debugging](/docs/debugging). ## Related Full App Router + `next/script` walkthrough. Single-tag paste, no code changes. Client-side widget + server trigger: full protocol. --- ### Deploy ReTarget.gg with Google Tag Manager URL: https://retarget.gg/docs/recipes-gtm Category: recipes Audience: developer Last reviewed: 2026-07-15 Keywords: ReTarget.gg GTM, Google Tag Manager affiliate script, GTM custom HTML tag, GTM widget install, no-code widget install If you manage your site's tags through Google Tag Manager, you can install ReTarget.gg without touching a single line of source code. One Custom HTML tag. One All-Pages trigger. Publish. ## Install In your GTM workspace, create a new tag. Pick **Custom HTML** as the tag type. This is the exact tag the dashboard generates. The guard on `data-website` makes it safe to fire on every page: the widget still loads exactly once per document, even after SPA route changes or GTM refire. {``} If you're logged into the dashboard, the snippet above already shows your real `data-pub` and `data-website` values. Copy it as-is: no placeholder substitution needed. Under **Triggering**, pick **All Pages** (the built-in page-view trigger). Don't add a DOM-Ready or Window-Loaded trigger: the widget's guard handles the ordering. Name the tag something like `ReTarget.gg widget`. Click **Save**, then **Submit** your workspace as a new container version. Publish. ## Verify Open your site in a new tab with DevTools open: You should see a `200 OK` from `https://cdn.retarget.gg/widget.js`. A `GET /v1/decision` should fire with a `200` response. Blocked visitors will see the overlay; allowed visitors see nothing (and a `204 No Content` `snippet-ping` confirms the widget is alive). In GTM, click **Preview**, open your site in the Tag Assistant flow, and confirm the `ReTarget.gg widget` tag shows as **Fired** on the first page view. On SPAs the Custom HTML tag may refire when the History trigger fires. The `querySelector('script[data-website="..."]')` guard at the top of the snippet prevents a duplicate load: the widget runs exactly once per document, as designed. ## Using a different container for the section widget If you're installing the **inline section** widget instead of the default popup overlay, the tag is the same pattern: just swap the script URL and add `data-container`: {``} You still need a `
` somewhere in your page source: the widget mounts into the element by id. See [Section widget integration](/docs/widget-section) for placement tips. ## Troubleshooting The most common cause is an origin mismatch: your site's domain isn't registered on the website in the dashboard. Check the browser console for `[AF] decision failed · check domain + CORS`. Add the correct domain under **Websites → your site → Domain**. Either your geo rules allow the current country (that's a success: the widget is silent), or the `/v1/decision` request is failing. Open Network, look at the response. If `allowed: true`, it's behaving correctly. See [Debugging](/docs/debugging) for the full decision tree. ## Related Paste the same tag at the edge instead of via GTM. App Router + `next/script` instead of a tag manager. Inline placements for content sites. --- ### ReTarget.gg API Overview URL: https://retarget.gg/docs/api-overview Category: reference Audience: developer Last reviewed: 2026-07-15 Keywords: ReTarget.gg API, affiliate marketing API, ReTarget.gg REST, widget API, v1 API Everything ReTarget.gg does is driven by its public REST API. The widget calls it. Your dashboard calls it. You can too: for server-rendered offers, custom UIs, server-to-server integrations, or automation. For the full interactive reference with request/response schemas, see [`/docs/api`](/docs/api) which is powered by our OpenAPI spec and lets you try requests directly from the browser. ## Base URL ```text https://api-v2.retarget.gg ``` All endpoints are versioned under `/v1/`. We never remove or change a `/v1/` response shape; breaking changes ship under `/v2/` when they're needed. ## Authentication Two distinct modes depending on what you're calling: ### Widget / public endpoints: Public key + Origin Endpoints called from the browser authenticate with: - Your **public key** (`pk_…`) passed in the URL as `?pub=`. - Your **website key** (`web_…`) passed as `?website=`. - The browser's `Origin` header, which must match the domain registered on the website. No Authorization header needed. The widget handles this automatically when you pass `data-pub` and `data-website`. ### Server / private endpoints: Integration secret bearer Server-to-server endpoints (today: `POST /v1/widget-decline/trigger`) authenticate with: ```http Authorization: Bearer its_your_integration_secret ``` Integration secrets (`its_…`) are generated per website from the website's Trigger Widget settings in the dashboard. Each secret is scoped to one website. Revoke and rotate at will without affecting other sites. Treat `its_…` like a database password. If one leaks, rotate it immediately in the dashboard. ## Request format - **Method + path**: standard REST. Decision and offers use `GET` with query parameters; events and triggers use `POST` with JSON bodies. - **`Content-Type`**: `application/json` works on every POST. The widget uses `text/plain` for `/v1/events/*` so the browser skips the CORS preflight: the API parses both. - **Origin**: required on browser calls; must match the website's registered domain. ## Response format - **2xx**: success. JSON body shape documented per endpoint. - **204 No Content**: success with no body. Used by `/v1/events/impression`, `/v1/events/click`, `/v1/events/snippet-ping`. - **4xx**: client error. Body: `{ "error": "human-readable message" }` (plus occasionally a short machine-readable `reason`). Status 400 = bad request, 401 = auth missing, 403 = auth invalid or origin not allowed, 404 = resource not found, 429 = rate-limited. - **5xx**: our problem. Include a recent response body in your support ticket. `GET /v1/decision` and `GET /v1/offers` set `Cache-Control: no-store`: decisions and offer lists are per-request. ## Rate limits Rate limits are applied per source IP over a 60-second rolling window: | Endpoint class | Limit | Notes | | --- | --- | --- | | `GET /v1/decision` | 120 / min / IP | One per page view on the happy path. | | `GET /v1/offers` | 120 / min / IP | One per rendered offer list. | | `POST /v1/events/impression` | 200 / min / IP | One per offer rendered. | | `POST /v1/events/click` | 200 / min / IP | One per click. | | `POST /v1/events/snippet-ping` | 200 / min / IP | One per page load. | | `/v1/widget-decline/*` | 120 / min / IP | Trigger Widget session + poll. | Rate-limited requests return **429 Too Many Requests** with standard rate-limit response headers. If you hit a limit in production, [reach out](/contact) and we'll look at it. ## Endpoint groups `/v1/decision`, `/v1/offers`: the core real-time geo + offer matching. `/v1/events/snippet-ping`, `/events/impression`, `/events/click`: attribution signals. `/v1/r`: signed, attributed outbound link. Session, trigger, and poll: the async Trigger Widget protocol. ## SDK? Not yet. The API is small enough that a plain `fetch` or any HTTP client works well. If you'd like an official SDK in a specific language, [let us know](/contact): we prioritize based on request volume. Community SDKs (none official) may exist on npm or PyPI; we don't certify those. ## Versioning All endpoints live under `/v1/`. Breaking changes would ship under a new major (`/v2/`) with a deprecation window communicated via email. Non-breaking additions (new fields, new endpoints) land continuously: you don't need to do anything for those. ## Related Live request/response explorer powered by our OpenAPI spec. Using the API through the widget: `data-*` attributes, events, and hooks. Common errors, what to check first, and how to share evidence with support. SDK requests, higher rate tiers, enterprise contracts. --- ## Blog (5 essays) ### How to monetize geo-blocked traffic in iGaming without breaking compliance URL: https://retarget.gg/blog/monetize-geo-blocked-traffic-igaming Author: ReTarget Team Published: 2026-04-29 Updated: 2026-05-10 Keywords: geo-blocked traffic monetization, iGaming affiliate revenue, monetize blocked iGaming traffic, geo-blocking strategy, iGaming compliance affiliate If you operate an iGaming brand, anywhere from **5 % to 35 % of your inbound traffic never sees your product**. Licensing maps, KYC outcomes, cooling-off rules, self-exclusion lists, and risk thresholds all end the session before it can monetize. Most operators write that revenue off as the price of compliance. It doesn't have to be. ## Why geo-blocked sessions are higher-intent than they look Geo-blocked traffic is *self-selected*. The visitor was Googling for casino, sportsbook, prediction-market, or sweepstakes content; they typed a URL or clicked an ad; their browser sent a request. The friction that ended the session is **not** lack of interest — it's a regulatory mismatch. For iGaming specifically, the mismatch usually falls into one of four buckets: 1. **Out of license region.** Visitor sits in a country your operator can't legally serve. 2. **In-country, wrong sub-region.** US states are the canonical example: NJ legal, NY illegal. 3. **KYC declined.** Identity, age, or PEP checks failed. 4. **Cooling-off / self-exclusion.** Account flagged, even temporarily. Each of these has a different compliant next step — and that's the lever ReTarget.gg gives you. ## The compliance-safe playbook ### 1. Replace the dead-end with a *licensed* alternative The classic mistake is showing the geo-blocked visitor a dismissible "service unavailable" panel and letting them bounce. Instead, render an offer overlay that's **only eligible for advertisers licensed in that visitor's region**. Your brand stays out of the auction; a different licensed brand gets the click. [Geo Popup](/publishers) handles this: one script tag, region rules, and the network handles eligibility. Your visitor finds something they *can* use, the network earns CPC, and you book a per-click revenue line you didn't have yesterday. ### 2. Treat KYC and risk declines as a separate event A visitor who failed KYC isn't the same as one who's geo-blocked. They're already *in your funnel*. They just can't progress with you specifically. [Decline Popup](/publishers) lets you trigger an overlay manually from your own platform — the moment your KYC API returns a fail, you fire the trigger and the visitor sees a relevant offer instead of a static "we couldn't verify your account" page. This is the **highest CPM moment** in the session: the visitor is already authenticated as a real human, they've shown deposit intent, and they have a verified email on file with you. Advertisers pay accordingly. ### 3. Keep your licensed inventory off the network The compliance unlock here is that ReTarget's matching engine **only enters auctions where the advertiser is licensed in the visitor's region**. Your own licensed-only properties never bid against the network — you're the publisher, not an advertiser. This sounds obvious but it's the one detail every iGaming compliance officer asks first. Read [Geo rules](/docs/geo-rules) and [How ReTarget.gg works](/docs/how-it-works) for the full eligibility flow. ## What to measure If you ship Geo Popup + Decline Popup, instrument three metrics from day one: - **Recovered impression rate** — % of blocked sessions that saw an overlay. - **Click-through rate from blocked → offer** — usually 8–15 % vs. 1–2 % for in-region display. - **Net per-visitor recovered revenue** — total network payout / blocked session count. Compare those against your acquisition CPM. In our pilot networks the recovered revenue lands somewhere between **8 % and 22 % of the visitor's original acquisition cost** — meaning every blocked impression effectively rebates a fifth of what you paid to acquire that user. ## Where to start 1. Read [Quickstart](/docs/quickstart) (two-minute install). 2. Configure [Geo rules](/docs/geo-rules) for your licensed allowlist. 3. Wire [Decline Popup](/docs/widget-decline) into your KYC or risk callbacks. 4. Watch [Analytics](/docs/analytics-and-reporting) for the first week. Or [open the dashboard](https://app.retarget.gg) and ship today. --- ### CPC vs CPA vs eCPM in 2026: which bid model actually pays for affiliate networks URL: https://retarget.gg/blog/cpc-vs-cpa-vs-ecpm-2026 Author: ReTarget Team Published: 2026-04-22 Keywords: CPC vs CPA, CPA vs eCPM, affiliate bid model 2026, affiliate network pricing model, iGaming CPC CPA The "which bid model is best" debate has been running since the 2000s, and the answer hasn't really changed: **it depends on where the friction lives in your funnel.** What *has* changed is that running all three at once is now standard, and advertisers who pick a single model are leaving 20–40 % of yield on the table. This post is the cheat-sheet we wish existed when we were sizing our own auctions. ## CPC — Cost per click **You pay when a user lands on your page.** That's it. The publisher and network take risk on whether the click converts. **Best for:** - Brand-aware advertisers with strong landing pages. - Funnel optimization (you can A/B the LP without renegotiating). - Cold-traffic discovery — when you don't yet know what conversion rate to expect. **Worst for:** - Long, multi-step conversions (registration → KYC → first deposit). The drop-off after the click is yours to absorb. - Markets where click fraud / bot traffic isn't well-controlled (ReTarget controls for both, but it's worth saying). **Pricing realism for iGaming:** $0.15–$2.50 per click depending on geo. Tier-1 EU markets clear higher; LATAM and Eastern Europe lower. ## CPA — Cost per acquisition **You pay when a defined event fires.** Registration, first deposit, qualified lead — the advertiser picks. The network and publisher take all upstream risk. **Best for:** - Mature offers with a stable conversion rate. - Regulated funnels where the conversion is meaningful (FTD, KYC pass). - Advertisers who don't want to think about LP optimization. **Worst for:** - Test campaigns. Publishers won't ship volume against an unproven CPA. - Brands that can't fire the conversion server-side — measurement is the entire game. **Pricing realism for iGaming:** $25–$300 per qualified registration; $80–$600 per FTD. ## eCPM — Effective cost per mille **You pay per thousand impressions.** Not clicks, not actions — eyeballs on your offer. eCPM is the bid metric the *network* uses to decide who wins an auction; some networks let advertisers buy directly on it. **Best for:** - Brand campaigns where impression count is the goal. - Inventory-rich placements (overlays, full-screen interstitials) where you can bank a lot of impressions per session. - Performance benchmarking — eCPM is the lingua franca for comparing channel quality. **Worst for:** - Direct-response advertisers without strong creative. - Markets where an impression doesn't equal much value (low-spend regions). **Pricing realism for iGaming:** $0.40–$8 eCPM for blocked / declined traffic, often higher than display-network averages because the visitor is high-intent. ## The 2026 reality: run all three In a classical affiliate network you pick one model per offer, and the publisher gets stuck with whichever the advertiser chose. That made sense in 2010. In 2026 it's a bug, because: - **Conversion latency varies by geo.** Tier-1 markets have fast funnels (CPA works). Tier-3 markets often need brand exposure first (eCPM works). - **Inventory varies by overlay type.** Geo Popup yields high impression counts; Decline Popup yields fewer but higher-intent. Different bid models suit each. - **Advertisers test on CPC, scale on CPA.** Forcing a single model burns the test budget. [ReTarget.gg's auction](/advertisers) lets advertisers fund a single prepaid balance and select CPC, CPA, or eCPM **per campaign**. The network normalizes everything to eCPM internally for ranking, but the advertiser pays under whichever model they chose. Publishers see one yield number. ## The decision matrix | Your situation | Pick | |---|---| | New offer, unknown conversion rate | CPC | | Mature offer, stable funnel | CPA | | Brand awareness in regulated geos | eCPM | | Mix of all three across markets | All three (one auction) | ## Where to start - Compare network economics: [ReTarget.gg vs classic networks](/compare). - Read the [Advertiser guide](/docs/advertiser-guide). - Set up [Advertiser deposits](/docs/advertiser-deposits) and ship your first campaign in 10 minutes. --- ### What to show users after a failed KYC check (and why a blank page is the worst answer) URL: https://retarget.gg/blog/what-to-show-after-failed-kyc Author: ReTarget Team Published: 2026-04-15 Keywords: failed KYC monetization, KYC decline page, what to show after KYC fail, decline popup, publisher KYC strategy A user signs up. Submits their docs. The KYC API returns `failed`. Most platforms now do one of three things: 1. Show a static "we couldn't verify your account, please contact support" page. 2. Auto-redirect to a help center article. 3. Silently log them out. All three are wasted moves. The visitor is **already authenticated as a real human**, has expressed deposit / signup intent, and has a verified email address with you. That's a more valuable session than 95 % of your inbound paid traffic. Throwing them at a static panel is the publisher equivalent of burning money. ## What the data says Across the publishers we've worked with, the **post-KYC-fail moment** has these signal-strength characteristics: - **5–9× the click-through rate** of cold display impressions. - **2–4× the eCPM** of in-region overlays. - **~70 % of users** will engage with *something* on the page rather than bouncing immediately. The reason is simple: friction. The visitor invested time in your funnel. They're at the bottom of an investment curve and looking for an off-ramp that *isn't* "give up and close the tab". ## The compliant alternative This is exactly the moment [Decline Popup](/publishers) was built for. Instead of the static rejection page, you fire the overlay manually from your KYC handler: ```js // In your KYC failure callback window.RetargetWidget?.showDecline({ reason: "kyc_failed", metadata: { sessionId: req.sessionId }, }); ``` The overlay surfaces an offer that's: 1. **Eligible in the user's region** (your KYC just confirmed where they are). 2. **From a different vertical or operator** than your own product. 3. **Tagged with the rejection context** so advertisers can bid differently for KYC-fail vs cooling-off vs risk-flag triggers. The user gets a useful next step. You earn per click. Your KYC vendor's failure rate becomes a revenue line. See [Widget decline integration](/docs/widget-decline) for the full callback surface. ## The compliance question Every operator we onboard asks: "Can we legally do this?" Short answer: yes, because **you are not the advertiser**. You're showing the user a third-party offer. The advertiser is the one bound by license to be eligible in that region, and ReTarget enforces that at auction time. The longer answer is: check your jurisdiction's specific rules around "post-rejection routing", and make sure your decline page makes clear that the user is being shown sponsored offers from third parties. We surface this disclosure in the overlay header by default. ## What to instrument If you wire Decline Popup, add these to your dashboard: - **Decline volume by reason** (KYC vs risk vs cooling-off vs no-deposit). - **Decline → impression rate** (should be 100 % minus a small no-eligible-offer rate). - **Decline → click rate** (target 8–12 % for KYC-fail). - **Decline → revenue per session** (compare to your acquisition CPM). The fourth metric is the one that gets the next conversation with finance going. ## Where to start 1. [Add your website](/docs/adding-your-first-website) and grab your keys. 2. Install [Decline Popup](/docs/widget-decline) — same script tag as the geo widget, with extra hooks. 3. Wire your KYC handler. 4. Watch for the first 50 declines, validate the offers shown, then scale. Want help routing your specific KYC vendor (Veriff, SumSub, Persona, Onfido, etc.)? [Contact the team](/contact) and we'll send a sample integration. --- ### Geotargetly alternatives for affiliate monetization (and when ReTarget.gg is the right fit) URL: https://retarget.gg/blog/geotargetly-alternatives Author: ReTarget Team Published: 2026-04-08 Keywords: geotargetly alternative, geotargetly competitor, geo redirect tool alternative, geo blocking monetization, geotargetly vs retarget Geotargetly is a fine tool. It does geo redirects, geo popups, and geo content swaps. We recommend it to a lot of teams who don't need monetization yet. But the question we hear weekly is: *"We use Geotargetly for the geo-blocking notice — is there a better option if we also want to monetize?"* Yes — and the honest framing is, **the two tools solve different problems**. ## What Geotargetly does well - **Redirect rules.** Visitor from FR → send them to your French page. - **Geo notifications.** "Hi, looks like you're in Germany — switch to the German store?" - **Country-specific content blocks.** Show a different banner per region. It's a generic geo-targeting toolkit. It doesn't have an advertiser side, doesn't run an auction, and doesn't pay you per click. ## What ReTarget.gg does that Geotargetly doesn't - **Two-sided marketplace.** Advertisers fund prepaid balances; publishers earn per click. - **Eligibility-aware auction.** Only offers licensed in the visitor's region enter the auction. - **Decline Popup trigger API.** Manually fire overlays after KYC, risk, or cooling-off events — Geotargetly is page-load-only. - **CPC, CPA, eCPM bid models** in a single auction. - **Revenue dashboard.** Per-region, per-overlay, per-trigger. For teams who need *monetization* of blocked / declined sessions, Geotargetly is the wrong layer. It's a router, not a yield engine. ## The full comparison table | Capability | Geotargetly | ReTarget.gg | |---|---|---| | Geo redirect | ✅ | ❌ (out of scope) | | Geo notification banner | ✅ | ❌ | | Country-specific content swap | ✅ | ❌ | | **Geo Popup overlay (monetized)** | ❌ | ✅ | | **Decline Popup (manual trigger)** | ❌ | ✅ | | **Advertiser auction** | ❌ | ✅ | | **Revenue dashboard** | ❌ | ✅ | | Setup time | ~10 min | ~2 min | | Pricing for publishers | Subscription | Free | | Pricing for advertisers | N/A | Prepaid CPC/CPA/eCPM | See the [longer-form comparison](/compare) for the framing alongside classic affiliate networks. ## When to use each **Use only Geotargetly when:** - You don't need monetization, just routing. - You have a single product across a single licensing region. - You're routing visitors between language sub-sites of the same brand. **Use only ReTarget.gg when:** - You want to monetize geo-blocked or declined sessions. - You have iGaming / regulated traffic where eligibility matters. - You want a single revenue line for all out-of-region traffic. **Use both when:** - You want to redirect *some* visitors (e.g. PT → BR site) and *monetize* the rest. - Your routing logic and your monetization logic are separate concerns. - You already pay for Geotargetly and want incremental revenue without ripping it out. We have a [Geotargetly integration recipe](/docs/recipes/geotargetly-integrate) that walks through running both tools on the same page without conflicts. ## The migration path If you're currently using Geotargetly's free "service unavailable in your country" panel and want to monetize it: 1. Read [Quickstart](/docs/quickstart). 2. Install the ReTarget.gg widget at the same DOM hook Geotargetly uses. 3. Configure the same allowlist / blocklist that Geotargetly enforces. 4. Optionally disable Geotargetly's blocking panel — the ReTarget overlay covers the same UX. Or [open the dashboard](https://app.retarget.gg) and ship in two minutes. --- ### Cloudflare Workers + an affiliate widget: the cleanest way to monetize geo-blocked traffic in 2026 URL: https://retarget.gg/blog/cloudflare-workers-affiliate-widget Author: ReTarget Team Published: 2026-04-01 Keywords: cloudflare workers affiliate, cloudflare geo block monetize, edge worker geo overlay, monetize cloudflare zero trust, workers affiliate monetization Cloudflare's edge gives you everything you need to detect and act on geo-blocked traffic before the page even renders. Combined with an eligibility-aware overlay like ReTarget.gg, you can flip a per-region revenue line on without touching your origin. This is the pattern we recommend. ## The 30-second mental model ``` Visitor → Cloudflare edge → Worker checks region → Origin (with overlay key) ↘ ↑ (no origin) ↑ | ReTarget overlay rendered at edge ``` The Worker decides whether the visitor is eligible for your product. If they're not, you can either: 1. **Pass through to origin** and let the ReTarget script tag detect & monetize on the client (simplest). 2. **Inject the overlay at the edge** via HTMLRewriter, so blocked visitors never even hit your origin (cheapest at scale). Pattern 2 saves origin bandwidth on every blocked impression. For a site doing seven-figure pageviews from blocked geos, that's measurable. ## The edge-injection pattern Here's the minimal Worker: ```typescript // worker.ts const RETARGET_SCRIPT = ` `; const ALLOWED = new Set(["GB", "DE", "CA", "IE"]); // your licensed regions export default { async fetch(request: Request): Promise { const country = request.cf?.country ?? "XX"; const upstream = await fetch(request); if (ALLOWED.has(country)) { return upstream; // pass through } return new HTMLRewriter() .on("head", { element: (el) => el.append(RETARGET_SCRIPT, { html: true }) }) .transform(upstream); }, }; ``` That's it. Cloudflare gives you `request.cf.country` for free, you check it against your licensed-region set, and only inject the script for blocked visitors. In-region visitors get the unmodified origin response. ## Why edge-side instead of client-side detection You **can** ship the script on every page and let the client-side widget detect blocked visitors. That's the [default install pattern](/docs/widget-installation). It works fine. But for high-traffic sites: - **Edge-side detection avoids loading the widget for in-region visitors.** Saves ~14 KB per session. - **Edge-side is harder to block with adblockers.** The widget is injected as inline HTML into your origin response, not loaded as a third-party script. - **You can pre-decide the auction context.** Pass `data-region`, `data-vertical`, etc. as attributes from the Worker. ## The eligibility check still happens at auction time A common worry: if the Worker injects the script for *any* blocked visitor, won't unlicensed-region visitors see ineligible offers? No — the [eligibility engine](/docs/how-it-works) runs server-side at auction time. The Worker just injects the script; the script asks `api-v2.retarget.gg` for an eligible offer; the API only returns offers from advertisers licensed in the visitor's region. Worst case, the API returns no offer and the overlay never renders. ## When to **not** use this pattern - **You don't already use Cloudflare.** Don't migrate just for this. The client-side pattern is fine. - **You have aggressive caching at the edge.** HTMLRewriter doesn't compose with Workers Cache 1:1; test before shipping. - **You serve dynamic per-user content from origin.** The Worker still calls origin; you're not bypassing it. For a deeper walk-through, see [Cloudflare integration recipe](/docs/recipes/cloudflare). ## Where to start 1. Get your public + website keys from [the dashboard](https://app.retarget.gg). 2. Read [Quickstart](/docs/quickstart) to understand the script. 3. Drop in the Worker above. 4. Watch [Analytics](/docs/analytics-and-reporting) for the first 1,000 blocked impressions. If you want help wiring this for a specific stack, [contact the team](/contact). ---