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.
!verify in Kick chatverify.html. Kick username is pre-filled and locked/register in the Discord serververify.html. Discord identity is pre-confirmed, they enter both Kick username and casino username/points, /buy, and /join in Discord immediately| Check | What is validated | If 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 |
After a successful Kick-chat verification, the viewer sees a "Connect Discord" button. This runs a standard Discord OAuth flow:
identify)discord-verify-callback.html with an auth code/users/@me to get the Discord user ID and usernamediscord_links/{discordUserId} in Firestore, linking their Discord ID → Kick username/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.
When a viewer types the giveaway keyword in chat (or clicks Join in Discord), these checks run in order:
activeProvider| Command | Requires Discord Link | Requires Casino Verify | Notes |
|---|---|---|---|
| /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 |