A Discord verification bot checks new members before they can read or post in your server. They pass a captcha, answer questions, accept the rules or sign in with an outside account, and the bot gives them a Verified role. I build custom verification bots on your own Discord application, so the gate, the log and the data stay yours.
Start with what Discord already does, because it's free and often enough. Verification levels, Rules Screening, Onboarding, Apply to Join and Raid Protection cover most public servers, and free bots like Captcha.bot and Double Counter add a captcha or alt checks (as of September 2026). A custom bot is worth paying for when the check depends on something only you have: a school or company email domain, a customer record, a game account or your own rules about who gets in.
What Discord already does
From Discord's help center, September 2026:
| Built-in feature | What it does | Enough when |
|---|---|---|
| Verification level | Five levels from None to Highest: a verified email, an account older than 5 minutes, 10 minutes in the server, a verified phone. A verified phone skips all the others. | You want to slow down throwaway accounts |
| Rules Screening | Members accept up to 16 rules before they can talk, react or DM other members. Community servers only. | You need a rules agreement and nothing more |
| Onboarding | Questions before joining that hand out roles and channels, and can be required. Community servers only. | Questions sort people, but no answer is wrong |
| Apply to Join | Your own application questions; staff approve, reject or interview, and see when the account was created. Still rolling out. | A person should look at every join |
| Raid Protection and AutoMod | Detects join raids, alerts your mods and requires a CAPTCHA from new joiners for the next hour. AutoMod blocks keywords, spam, mention spam and blocked words in names. | Raids and spam are the problem, not identity |
The gaps: the account-age check stops at 5 minutes, pausing invites during a spike is left to a moderator, nothing checks anything outside Discord, and the developer docs give bots no way to read or approve Apply to Join applications.
Rules Screening also blocks DMs from members who haven't accepted, which Discord notes role-gate bots can't do, so I usually keep it on in front of the bot's check.
What the bot does
For the member
- They join and see one channel with a Verify button. Everything else is hidden from the
@everyonerole. - They press it and get the check you picked:
- Captcha. An image code shown only to them, typed into a pop-up form, or a short web page with Cloudflare Turnstile, which has a free plan.
- Questions. A pop-up form with up to five parts: text boxes, dropdowns, radio buttons or an "I agree" checkbox. Answers are graded or sent to staff.
- Sign-in. A link to sign in with Discord, a school or company account, or a game account.
- A pass gives the Verified role. Repeated failures lead to a kick or staff review.
For your staff
- A log. Every attempt with the method, the result, the account's age and whether the member has joined before.
- A hold queue. Accounts younger than the age you set wait for a moderator's approve or deny button.
- Lockdown. When joins spike, the bot pauses invites (Discord allows up to 24 hours) and posts an alert.
- Manual approval and re-checks. Staff can let someone in with a reason on record, or ask everyone to verify again after a rule change.
Account linking with OAuth2
Linking is where a custom bot earns its price: it proves something Discord can't.
- Discord sign-in. The
identifyscope ties the member's Discord ID to your records. Theemailscope adds the account's email and whether it's verified, but that is usually a personal address. - School or company servers. Members sign in with Google Workspace or Microsoft, and the bot checks the organization in the signed token: the
hdclaim from Google, which Google says to validate in the token rather than trust the request, or thetidtenant ID from Microsoft. Other providers get a one-time emailed code. - Game accounts. With the
connectionsscope, the bot reads the accounts a member has already linked in Discord, including Steam, Xbox, PlayStation, Roblox and Twitch, with a flag for whether each is verified. Your game's own sign-in works too. For Roblox, see Roblox Discord bots. - Customers. The bot matches the member to a Stripe customer or your own account record. Paid membership bots cover the payment side.
- Linked Roles. Your app can appear in Discord's own role settings as a requirement, with up to five fields of your data, such as plan or graduation year.
I built Discord sign-in and a username sync for Rivals League's player site, which is the same OAuth2 flow.
Anti-raid and alt accounts: what a bot can see
A bot sees a member's account creation date (built into every Discord ID), whether they have joined before, their name and avatar, and whatever they link through OAuth2. Discord's API never gives it IP addresses or devices.
So account-age holds and join-rate lockdowns are dependable, and alt detection isn't. Double Counter compares IP addresses, devices and browser signals through a verification web page, across a network it says covers more than 600,000 communities. No single custom bot has that. Even that misfires: its own docs say VPN flags often come from ad blockers, iOS Private Relay or privacy browsers, and a shared IP address can mark someone as an alt. If network-wide alt detection is what you need, use Double Counter and I'll build the rest around it.
Without IP data I can still enforce one school email or game account per Discord account, check links against banned members, and hold new accounts.
Free bot or custom?
Prices from each vendor's own page, September 2026:
| Option | Price | What you get | Pick it when |
|---|---|---|---|
| Captcha.bot | Free; Premium $4.99/mo or $55/yr | Free: one verified role, a log channel, web verification. Premium: five verified roles, image verification, 30 days of analytics | You want a captcha on every join |
| Double Counter | Free; Pro sold through Discord | Free: alt detection, raid and VPN blocking, three verification modes; its verification page shows ads. Pro removes ads and adds fingerprinting | Alt accounts are your main problem |
| Wick | Premium $5/mo; VIP $20/mo | Premium: advanced anti-raid, quarantine, lockdown, customizable verification. VIP: 12 servers, custom branding | You want one security bot for everything |
| Security Bot | Premium from $3.49/mo; Premium+ from $11.99/mo | Captcha and one-click verification; paid plans add advanced anti-nuke, and Premium+ adds branding | Anti-nuke matters as much as the gate |
| Custom bot | From $590, plus Care from $49/mo | Your checks, your linking, your data, on your own bot, with the code | The check depends on your data |
If your goal is "no bots, no raiders," a free bot on top of Discord's settings does it, and paying me to rebuild that is poor value. Custom makes sense when members must prove they belong to your school, company, customers or game.
What verification data a bot should keep
As little as the check needs: by default the Discord ID, method, time and result. For email checks I store the domain, not the address, and sign-in tokens are discarded after the check unless roles re-sync from them.
Discord's Developer Policy limits API data to the app's stated function and forbids profiling users or passing data to ad networks. Double Counter's privacy policy, for example, says it stores the IP address from its verification link, tied to the Discord ID. With your own bot, you set the retention period, and deleting a member's record is one command.
What a custom Discord verification bot costs
Most verification bots are Starter builds: from $590, delivered in 1 to 2 weeks. That covers the panel, one check, the hold queue, lockdown, the staff log, manual approval and one linking source such as a school email domain. You get the source code, setup docs, 30 days of bug fixes and the first month of Care.
It becomes Community, from $1,490, when verification needs a database-backed history and several sign-in providers, or is one of up to three systems in the same bot. A staff dashboard for reviewing verifications across servers is Platform work, from $3,500. See the pricing page for all tiers. For comparison, Upwork's hiring guide lists moderation, onboarding or ticketing bots at $1,000 to $4,000 per project (September 2026).
Re-verifying thousands of existing members or several servers sharing one list push the quote up; one check and a log channel keep it down. Hosting runs on Care at $49 a month.
How the build works
- Brief. Send a short brief: who gets in, who stays out, what you use today. I reply within one business day.
- Spec. We agree on the check, failure rules, hold threshold, what gets logged and for how long. That list is the acceptance checklist.
- Test. I build in a private test server and you try it with fresh and old accounts.
- Launch. The bot goes live on your Discord application, and you get the repository and docs.
Builds are paid 50% up front and 50% on acceptance, and I take a limited number each month.
Technical notes
- Intents. Guilds, plus the privileged Server Members intent for join events. Under 10,000 users you switch it on in the Developer Portal; above that, Discord reviews the request. Message Content isn't needed.
- Permissions. Manage Roles, with the bot's role above Verified and hold roles; Kick Members if failures kick; Manage Server to pause invites; Send Messages and Embed Links in the verify and log channels. Never Administrator. The permissions calculator builds the invite link.
- Rate limits. A bot can make at most 50 API requests a second in total, so re-checking thousands of existing members runs as a background job.
This is the join-time part of the gate: new accounts get the hold role, and a burst of joins pauses invites for an hour. Each step fails on its own, so one missing permission can't block the other. Tested with discord.js 14.27.0 on Node 22.22.2 and 24.15.0, running join events through real discord.js member objects with every API call checked.
// join-gate.js: holds brand-new accounts and pauses invites during a join spike.
// Needs the privileged Server Members intent (GatewayIntentBits.GuildMembers).
const { Events, GuildMemberFlags } = require('discord.js');
const MIN_ACCOUNT_AGE_DAYS = 7; // younger accounts get the hold role
const SPIKE_JOINS = 15; // this many joins...
const SPIKE_WINDOW_MS = 60_000; // ...within one minute counts as a spike
const PAUSE_INVITES_MS = 60 * 60_000; // Discord allows at most 24 hours
const recentJoins = new Map(); // guild ID -> timestamps of recent joins
function registerJoinGate(client, { holdRoleId, logChannelId }) {
client.on(Events.GuildMemberAdd, async (member) => {
if (member.user.bot) return;
const { guild } = member;
const now = Date.now();
const joins = (recentJoins.get(guild.id) ?? []).filter((t) => now - t < SPIKE_WINDOW_MS);
joins.push(now);
recentJoins.set(guild.id, joins);
const log = guild.channels.cache.get(logChannelId);
if (joins.length === SPIKE_JOINS) {
try {
const until = new Date(now + PAUSE_INVITES_MS);
await guild.setIncidentActions({ invitesDisabledUntil: until });
await log?.send(`${SPIKE_JOINS} joins in a minute. Invites paused until <t:${Math.floor(until.getTime() / 1000)}:t>.`);
} catch (error) {
console.error(`Pausing invites failed in ${guild.id}:`, error);
}
}
const ageDays = (now - member.user.createdTimestamp) / 86_400_000;
if (ageDays < MIN_ACCOUNT_AGE_DAYS) {
try {
const rejoined = member.flags.has(GuildMemberFlags.DidRejoin) ? ', has joined before' : '';
await member.roles.add(holdRoleId, `Account is ${ageDays.toFixed(1)} days old`);
await log?.send(`Held ${member}: account is ${ageDays.toFixed(1)} days old${rejoined}.`);
} catch (error) {
console.error(`Holding ${member.id} failed in ${guild.id}:`, error);
}
}
});
}
module.exports = { registerJoinGate };Call registerJoinGate() once after creating the client. The hold role grants nothing by itself: the Verify button turns away anyone who has it until a moderator approves them.