Self-hosted referral / invite system for Discord servers:
- each member gets their own invite link
- they have a limited token quota per week
- the bot verifies that invited users stay in the server (and keep a role)
- it rewards good inviters and exposes a leaderboard
- it fights spam/abuse with account-age checks, role gates and expiries
Ideal for servers running referral campaigns, growth programs, or needing a safer alternative to vanilla Discord invites.
If you run a server and you want to:
- let members invite people with their own link
- limit spam with weekly quotas instead of unlimited invites
- only count referrals when the invited user stays in the server and gets a specific role
- show a leaderboard and give rewards at configurable thresholds (default 5 / 15 confirmed referrals)
- avoid abuse from fresh accounts / alt accounts
โฆthis bot automates all of that.
Each user:
- gets a personal invite link
- has a weekly pool of tokens (default 5)
- each valid join consumes 1 token
- if the invitee passes all checks and stays long enough, the referral is confirmed
- confirmed referrals count for rewards and leaderboard
- ๐งพ Self-service referral links โ each member gets their own invite
- โณ Weekly token quotas โ limit how many joins count per week
- โ Smart confirmation flow โ only confirmed referrals (user stays + keeps role) are rewarded
- ๐ Leaderboard โ week / month / all-time rankings
- ๐ Configurable rewards โ default thresholds 5 and 15 confirmed referrals, configurable via
.env - ๐ก๏ธ Anti-abuse โ account-age check, role gate to create links, deactivated invites at 0 tokens
- ๐ Logging โ configurable log channel for staff, DM notifications to inviters
- ๐ Schedulers โ weekly resets & hourly processing for expiries/confirmations
- ๐พ SQLite (default) or MySQL/MariaDB with migrations
- Runtime: Node.js 18+
- Discord: discord.js v14 (slash commands)
- Database: SQLite (default) or MySQL/MariaDB
- Config:
.env+configtable in DB - Scheduling: in-process cron-style jobs (weekly + hourly)
You need to be able to run a Node.js bot yourself (this is not a hosted/public bot).
-
Node.js 18+
-
A Discord application + bot token
-
Either:
- SQLite (default, file-based, zero setup), or
- MySQL/MariaDB (if you prefer a real DB)
In the Discord Developer Portal:
-
Enable Gateway Intents:
GuildsGuild MembersGuild Invites
-
Invite the bot to your server with at least:
Manage GuildManage Channels(for invites)View ChannelsRead Message HistoryManage Roles(if you want the bot to assign/remove roles)
Create the following in your guild:
-
Channels
#invitesโ where users will run/referraland see info#referral-logโ staff-only logs- (optional)
#referral-rewards,#referral-leaderboard
-
Roles
@Requiredโ role that marks a โvalidโ referral (e.g. your โmemberโ role or a role given after verification)- (optional)
@Entry,@Invited,@Staff,@Link Creator
Make sure the bot role is above any role it has to assign/remove.
Create a .env file next to package.json:
# Required
DISCORD_TOKEN=your-bot-token
APPLICATION_ID=your-application-id
GUILD_ID=your-guild-id
# Channels (IDs)
INVITE_CHANNEL_ID=123456789012345678 # #invites
LOG_CHANNEL_ID=123456789012345679 # #referral-log
# Roles (IDs)
REQUIRED_ROLE_ID=123456789012345680 # @Required
# Behavior
TIMEZONE=Europe/Rome
DB_ENGINE=sqlite # sqlite or mysql
ENFORCE_BOT_INVITES=0
REWARD_TIER1=5 # first reward threshold (confirmed referrals)
REWARD_TIER2=15 # second reward threshold (confirmed referrals)This is enough to get started. All other variables are optional and documented below.
npm install
npm run register # register slash commands (guild-scoped if GUILD_ID is set); rerun after you add/change commands
npm start # start the botOnce the bot is online (npm start), your users can start with:
/referral linkโ get their personal invite link
From there, the bot automatically tracks joins, confirmations, and updates the leaderboard based on your configuration.
# Required
DISCORD_TOKEN=your-bot-token
APPLICATION_ID=your-application-id
# Recommended for testing (guild-scoped command registration)
GUILD_ID=your-guild-id
# Channels (IDs)
INVITE_CHANNEL_ID=
LOG_CHANNEL_ID=
REWARD_CHANNEL_ID=
LEADERBOARD_CHANNEL_ID=
# Roles (IDs)
REQUIRED_ROLE_ID= # role that validates a referral (needed for confirmations)
ENTRY_ROLE_ID= # optional: assigned on join
INVITED_ROLE_ID= # optional: assigned on join; removed on expiry
LINK_CREATOR_ROLE_ID= # optional: who can create personal invites
STAFF_ROLE_ID= # optional: who can use staff commands / get tagged on rewards
REMOVE_ON_VALIDATE_ROLE_ID=# optional: role to remove when validated
# Behavior
TIMEZONE=Europe/Rome
DB_ENGINE=sqlite # or mysql
ENFORCE_BOT_INVITES=0 # 1 to enforce bot-created invites only
REWARD_TIER1=5 # first reward threshold (confirmed referrals)
REWARD_TIER2=15 # second reward threshold (confirmed referrals)
# MySQL (set DB_ENGINE=mysql to use these)
MYSQL_HOST=localhost
MYSQL_PORT=3306
MYSQL_USER=
MYSQL_PASSWORD=
MYSQL_DATABASE=referral_bot- SQLite (default): DB file at
data/referral.dbis created automatically. - MySQL/MariaDB: apply
data/schema.sql, setDB_ENGINE=mysqlandMYSQL_*.
-
/referral linkShow or create your personal invite and how many tokens you have left this week. -
/referral statsShow your pending / confirmed / failed referrals and token balance. -
/referral leaderboard [period]Show the leaderboard forweek | month | all. Can be ephemeral when used in the invite channel.
-
/admin set-hold <days>Set how many days a user must keep the required role before a referral is auto-confirmed. -
/admin grant-tokens <user> <n>Add or remove tokens (negative values remove). -
/admin reset-tokensForce a weekly reset now. -
/staff validate <user>Manually validate a referral by assigning the required role (bypasses waiting). -
/staff who-invited <user>Show who invited a user and the referral status. -
/staff invited <user> [status] [limit]List referrals by inviter. -
/staff process-referrals [force_confirm]Process expiries/holdings immediately. -
/staff check-confirmationsConfirm holdings that completed the hold period. -
/staff check-pendingShow remaining time for holdings.
-
Join
- A user joins via a personal invite.
- The inviter spends 1 token.
- Referral state:
pendingwith a TTL (default 7 days).
-
Gets required role
- Invited user receives
REQUIRED_ROLE_ID(e.g. passes verification). - Referral becomes
holdingfor hold days (default 7 days).
- Invited user receives
-
Outcome
- If the user still has the required role at the end of hold โ referral becomes confirmed.
- If they leave / lose the role / TTL expires โ referral becomes failed and the token is refunded.
States:
pendingโ waiting for required role or expiryholdingโ waiting for hold period to completeconfirmedโ counts for rewards & leaderboardfailedโ no reward, token refunded
-
Leaderboard supports week / month / all-time views.
-
Default reward tiers:
- 5 confirmed referrals
- 15 confirmed referrals
You can change these thresholds via
REWARD_TIER1andREWARD_TIER2in your.env.
-
You can:
- post rewards in
REWARD_CHANNEL_ID - optionally ping
STAFF_ROLE_IDwhen someone reaches a milestone.
- post rewards in
-
Minimum account age check (configurable in DB/config).
-
Optional role gate to be allowed to create personal links (
LINK_CREATOR_ROLE_ID). -
Invites are deactivated when a user hits 0 tokens:
- joins no longer count until tokens are reset.
-
Automatic role cleanup when referrals expire or fail.
The bot runs scheduled jobs:
-
Weekly (Monday 00:00 in
TIMEZONE):- reset weekly tokens.
-
Hourly:
- expire
pendingreferrals past TTL - process
holdingreferrals (confirm or fail) - refund tokens on failures
- update roles and leaderboard on confirmations
- expire
commands/
admin.js
referral.js
staff.js
db.js
index.js
inviteCache.js
registerCommands.js
scheduler.js
schema.sql
.env.example
package.json
README.md
-
Keep the bot role above any role it should assign/remove.
-
Use
GUILD_IDduring testing to avoid global command propagation delay; remove it when youโre ready for global registration. -
Leaderboard embed is auto-upserted:
- channel/message IDs are stored in config when first sent.
This project is licensed under the MIT License.
See the LICENSE file for details.