Executive summary: When KYC fails, most operators show a static rejection page and lose a session that cost real acquisition dollars. The visitor is authenticated, geo-confirmed, and invested in your funnel. Decline Popup converts that moment into compliant third-party offers — typically 5–9× the CTR of cold display and 2–4× the eCPM of in-region overlays.
A user signs up. Submits their documents. Waits. The KYC API returns failed.
Most platforms now do one of three things:
- Show a static "we couldn't verify your account, please contact support" page.
- Auto-redirect to a help center article.
- Silently log them out.
All three are wasted moves.
The visitor is already authenticated as a real human, has expressed deposit or signup intent, and often has a verified email address on file. That is a more valuable session than 95% of your inbound paid traffic. Throwing them at a static panel is the publisher equivalent of burning money.
This article is the complete playbook: the data behind post-KYC moments, compliant UX patterns, integration with major KYC vendors, and the metrics that get finance to pay attention.
Why the post-KYC-fail moment is uniquely valuable
Investment curve psychology
Signup funnels follow a predictable investment curve. Early steps are cheap for the user (email, password). Later steps are expensive (document upload, selfie, address proof). A KYC failure happens after the expensive steps — the visitor has sunk time and personal data into your product.
At that moment they are not looking for "another tab to close." They are looking for an off-ramp that is not "give up."
Signal strength vs. other inventory
Across publishers we have worked with, the post-KYC-fail moment shows:
| Signal | vs. cold display | vs. in-region overlay |
|---|---|---|
| Click-through rate | 5–9× higher | 2–3× higher |
| eCPM | 3–6× higher | 2–4× higher |
| Immediate bounce rate | ~30% lower | ~20% lower |
| Engagement with something on page | ~70% | N/A |
The reason is friction, not magic. The visitor invested in your funnel. A useful next step — even from a third party — beats a dead end.
What KYC failure actually confirms
When a reputable vendor (SumSub, Veriff, Persona, Onfido, Jumio) returns a failure, you typically have:
- Confirmed geography (document issuing country, IP consistency)
- Confirmed humanity (liveness check passed or attempted)
- Confirmed intent (they reached the KYC step)
- Contactable identity (email at minimum)
That is more than you know about a cold ad click. Advertisers price it accordingly in the ReTarget auction.
The anatomy of a bad decline page
Before fixing the problem, name what most operators ship today:
┌─────────────────────────────────────────┐
│ ✕ Verification unsuccessful │
│ │
│ We couldn't verify your identity. │
│ Please contact support@brand.com │
│ │
│ [ Back to home ] │
└─────────────────────────────────────────┘
What happens next: 60–80% bounce within 10 seconds. Support ticket volume spikes for ambiguous cases. Acquisition cost is fully sunk.
What the page communicates: "We wasted your time." Not a brand message you want at the bottom of a $40+ CPA funnel.
The compliant alternative: Decline Popup
Decline Popup was built for exactly this moment. Instead of a static rejection page, you fire the overlay from your KYC handler when failure is definitive:
// Browser-side: after KYC vendor returns failure to your frontend
async function onKycFailed(result) {
// Always show your legal rejection copy first (recommended)
showRejectionMessage(result.reason);
// Then surface compliant third-party offers
window.RetargetWidget?.showDecline({
reason: mapVendorReason(result.reason),
metadata: {
sessionId: result.sessionId,
vendor: "sumsub",
country: result.documentCountry,
failureCode: result.rejectCode,
},
});
}
function mapVendorReason(code) {
const map = {
DOCUMENT_MISMATCH: "kyc_failed",
UNDERAGE: "kyc_failed",
PEP_HIT: "risk_flag",
FRAUD_SIGNAL: "risk_flag",
};
return map[code] ?? "kyc_failed";
}Server-side trigger pattern
For operators who do not expose KYC results to the browser:
// Node.js — in your KYC webhook handler
app.post("/webhooks/kyc", async (req, res) => {
const { userId, status, reason, country } = req.body;
if (status === "failed") {
await db.users.update(userId, { kycStatus: "failed", kycReason: reason });
// Set a short-lived flag the frontend reads on next page load
await redis.set(`decline:${userId}`, JSON.stringify({
reason: "kyc_failed",
country,
}), "EX", 300);
}
res.sendStatus(200);
});// Frontend — on rejection page mount
const payload = await fetch("/api/decline-context").then((r) => r.json());
if (payload?.reason) {
window.RetargetWidget?.showDecline(payload);
}Full callback surface: Widget decline integration.
What the overlay surfaces
Offers shown are:
- Eligible in the user's confirmed region (KYC just verified geography via document or IP consistency).
- From a different operator or vertical than your own product.
- Tagged with rejection context so advertisers bid differently for
kyc_failedvscooling_offvsrisk_flag.
The user gets a useful next step. You earn per click. Your KYC vendor's failure rate becomes a revenue line instead of a cost center.
Compliance: the questions legal will ask
Every operator asks: "Can we legally do this?"
Short answer
Yes — because you are not the advertiser. You show third-party sponsored offers. The advertiser is bound by license to be eligible in that region, and ReTarget enforces eligibility at auction time.
Longer answer (bring this to legal)
| Topic | Guidance |
|---|---|
| License scope | You are not offering gambling services where you lack license; you are monetizing a publisher placement |
| Disclosure | Overlay header discloses third-party sponsored offers by default; customize to match your jurisdiction |
| Self-exclusion / cooling-off | Do not enable Decline Popup without legal review for these cases |
| Data sharing | Pass only session metadata to ReTarget — not document images or PII |
| User confusion | Rejection copy must clearly separate "we cannot serve you" from "sponsored alternatives" |
Jurisdiction-specific rules around "post-rejection routing" vary. UK, Malta, and Isle of Man frameworks differ on what is permissible after a failed onboarding. Get sign-off before scaling.
Vendor-specific integration notes
SumSub
Webhook applicantReviewed with reviewResult.reviewAnswer === "RED" is your trigger. Map rejectLabels to decline reasons. SumSub provides info.idDocs[0].country for geo confirmation.
Veriff
decision.status === "declined" in webhook payload. Use verification.code for reason mapping. Veriff's person.country field is reliable for eligibility.
Persona
inquiry.completed with status: "failed". Persona exposes structured failure codes ideal for metadata.failureCode.
Onfido
check.completed with result === "clear" inverse — watch for consider vs unidentified. Map Onfido sub-results to your decline reason enum.
Universal rule: trigger only on terminal failures, not on "retry with better photo" states. Premature triggers erode user trust and confuse auction context.
UX patterns that work
Pattern A: Rejection first, overlay second
Show your legal rejection message for 2–3 seconds, then animate the overlay. Users understand the sequence: "We can't serve you" → "Here are alternatives."
Pattern B: Split panel
Left column: rejection explanation and support link. Right column: Decline Popup placement. Works well on desktop; test mobile carefully.
Pattern C: Support escalation path
Keep a visible "I believe this is an error" link above the overlay. Reduces chargeback risk on support and signals good faith.
Avoid: auto-redirecting to a third-party operator without disclosure. That is not what Decline Popup does — and it is not what you want legally.
Metrics dashboard
Wire these from day one:
| Metric | Formula | Target |
|---|---|---|
| Decline volume by reason | count per reason tag | baseline week 1 |
| Decline → impression rate | impressions / declines | >90% |
| Decline → click rate | clicks / impressions | 8–12% for KYC-fail |
| Revenue per decline | payout / declines | compare to CPA |
| Support ticket delta | tickets / declines | should not spike |
The fourth metric — revenue per decline — is what gets the next conversation with finance going.
If revenue per decline exceeds 10% of your blended CPA, the program is usually self-funding on acquisition waste alone.
Track in Analytics and reporting.
Implementation checklist
Week 1
- Add your website and grab API keys
- Install Decline Popup (same script tag as Geo Popup)
- Map KYC vendor failure codes →
reasonenum - Legal review of rejection page + disclosure copy
Week 2
- Ship terminal-failure trigger (browser or server-side flag)
- Validate first 50 declines manually — check offer relevance per geo
- Confirm no overlay on "retry" states
Week 3
- Report revenue-per-decline to finance
- Expand to additional decline types (risk, no-deposit) if approved
- A/B rejection copy — overlay timing only, not compliance text
FAQ
Should we still show the rejection message if we show offers? Yes. Users must understand your product declined them. The overlay is a separate, disclosed sponsored placement.
What if the user is underage?
Do not show gambling offers. Map UNDERAGE to a no-overlay path. ReTarget returns no eligible offers for minors when age signals are passed — but your safest move is to skip the trigger entirely.
Will advertisers bid on KYC-failed traffic?
Yes — it is high-intent, geo-confirmed inventory. Some advertisers pay a premium for kyc_failed vs cold geo-block.
Does this work for non-iGaming? The mechanics work for any regulated funnel with KYC (fintech, crypto, lending). Eligibility rules differ; the Decline Popup pattern does not.
Can we pass document images to the overlay? No. Pass session metadata only. Document images stay in your KYC vendor boundary.
Related reading
- How to monetize geo-blocked traffic in iGaming
- CPC vs CPA vs eCPM in 2026
- Widget decline integration
- Publisher overview
Need help wiring your KYC vendor? Contact the team — we have sample integrations for SumSub, Veriff, Persona, and Onfido.