Verification System, How It Works
All flows, validations, and cross-checks · v1.0 · May 2026
1 Overview

Verification connects a viewer's Kick username to their casino account and optionally their Discord account. This unlocks giveaway eligibility, points tracking, and Discord commands like /buy and /points. There are two entry points, Kick chat and Discord, both funnel to the same verification page.

💬 Kick Chat Flow !verify
  1. 1Viewer types !verify in Kick chat
  2. 2WenBot generates a one-time token tied to their Kick username and posts a link (5 min expiry)
  3. 3Viewer opens verify.html. Kick username is pre-filled and locked
  4. 4Viewer enters their casino username and submits
  5. 5Casino account verified ✅ — record saved to Firestore
  6. 6Optional "Connect Discord" OAuth button appears, viewer can link their Discord account too
🎮 Discord Flow /register
  1. 1Viewer runs /register in the Discord server
  2. 2WenBot generates a Discord token (dtoken) tied to their Discord user ID and sends a private link (10 min expiry)
  3. 3Viewer opens verify.html. Discord identity is pre-confirmed, they enter both Kick username and casino username
  4. 4Casino account verified AND Discord linked in one step
  5. 5Viewer can now use /points, /buy, and /join in Discord immediately
2 Validations & Cross-Checks (in order)
CheckWhat is validatedIf it fails
🔑 Token validity Token exists in Firestore and has not been used yet (used: false). Runs inside a Firestore transaction to prevent race conditions. ❌ Blocked, "Invalid or expired verification link"
⏱ Token expiry Token's expiresAt timestamp has not passed. Kick tokens expire in 5 minutes; Discord tokens in 10 minutes. ❌ Blocked, "Link has expired, get a new one"
🎰 Active casino match The casino in the link matches the streamer's currently active casino (activeProvider on their profile). Prevents verifying at the wrong casino. ❌ Blocked, "Streamer is at [Casino X], verify there instead"
🔒 Casino username uniqueness The casino username being submitted is not already linked to a different Kick account. Prevents sharing casino accounts. ❌ Blocked, "Already linked to another Kick account"
📡 API leaderboard check Gambulls For API-backed casinos, the casino username is looked up live on the streamer's leaderboard. Determines whether the user is wagering under the streamer's affiliate code. ⚠ Soft-pass. User is still verified as "General" (not "Under Code"). They can enter non-code giveaways.
🤝 Honor-system casinos All others For non-API casinos (Stake, Rainbet, etc.) the username is taken at face value, no API check is possible. Streamer accepts on trust. N/A, always passes if username is entered
🧹 Legacy doc cleanup If the viewer previously verified under an old doc ID format (just kickUsername without casino suffix), that old doc is deleted and replaced with the new kickUsername_casino format. N/A, automatic cleanup, no user impact
3 Discord Linking (Kick-Initiated Optional Step)

After a successful Kick-chat verification, the viewer sees a "Connect Discord" button. This runs a standard Discord OAuth flow:

  1. 1Viewer clicks "Connect Discord" → redirected to Discord OAuth (scope: identify)
  2. 2Discord redirects back to discord-verify-callback.html with an auth code
  3. 3Callback page exchanges code for an access token, then calls /users/@me to get the Discord user ID and username
  4. 4Saves discord_links/{discordUserId} in Firestore, linking their Discord ID → Kick username
  5. 5Viewer can now use Discord bot commands immediately
Note: The Discord /register flow skips this OAuth step entirely, it already knows the Discord user ID from the slash command interaction, so casino verification and Discord linking happen in a single form submission.
4 Giveaway Entry Eligibility Checks

When a viewer types the giveaway keyword in chat (or clicks Join in Discord), these checks run in order:

Code Giveaway
  • Giveaway must be active
  • Not already entered
  • Sub-only check (if enabled)
  • Must be casino-verified at streamer's active casino
  • Verified casino must match current activeProvider
  • If leaderboard available: must appear on it
  • Min wager must be met (if set)
  • Extra tickets awarded per $1,000 wagered (if wager luck > 1)
Verified Giveaway
  • Giveaway must be active
  • Not already entered
  • Sub-only check (if enabled)
  • If "Verified Casino" required: must be casino-verified
  • If "Verified Discord" required: must have Discord linked
  • At least one verification type must be required
Everyone Giveaway
  • Giveaway must be active
  • Not already entered
  • Sub-only check (if enabled)
  • No verification required: any viewer can enter
5 Discord Command Requirements
CommandRequires Discord LinkRequires Casino VerifyNotes
/points ✅ Yes ✅ Yes Looks up points balance by Kick username resolved from Discord link
/store No No Anyone can browse the store
/buy ✅ Yes ✅ Yes Deducts points, writes redemption to Firestore, announces in Discord channel
/join No Depends on giveaway type Code giveaways require Discord link to resolve casino identity
/register No No Starts the verification flow, this IS the link step
6 Firestore Data Structure
Casino verification record
streamers/{uid}/verified_users/{kickKey}_{provider}
  • kickName Display name from Kick
  • providerUsername Casino username (API-normalized)
  • providerUsername_lower Lowercase for dedup checks
  • provider gambulls / stake / etc.
  • underAffiliate true if on streamer's leaderboard
  • apiVerified true if confirmed via live API
  • verifiedAt Unix timestamp
Discord link record
streamers/{uid}/discord_links/{discordUserId}
  • kickUsername Linked Kick username
  • discordUsername Discord display name
  • linkedAt Unix timestamp
Kick verify token (one-time)
streamers/{uid}/verify_tokens/{token}
  • kickUsername Proven by Kick chat identity
  • expiresAt 5 minutes from issue
  • used Flipped to true on consumption
Discord verify token (one-time)
discord_verify_tokens/{dtoken}
  • discordUserId Proven by Discord slash command
  • discordUsername Discord display name
  • guildId Which server it was issued in
  • streamerUid Which streamer's server
  • expiresAt 10 minutes from issue
  • used Flipped to true on consumption
WenBot Verification System · Internal documentation · Generated May 2026