
Your community lives in Discord, and you want the FiveM server to know who is who: only Discord members get in, the Staff role gets admin commands, the Police role can clock in. This guide shows how FiveM sees a player's Discord account, the bot you need, the resources that read roles, and the exact lines that turn a Discord role into an ace permission. Every setting comes from the resources' own docs and source code, checked on 4 November 2026.
Quick answer: FiveM links Discord on the player's side by itself: when the Discord desktop app runs on the same PC, the server sees a discord: identifier with the player's Discord user ID (the web app does not work). On the server you need a Discord bot in your Discord server. To let in only Discord members, or only members with certain roles, use txAdmin's built-in bot. To use Discord roles for permissions in FiveM, install Badger_Discord_API with your bot token and server ID, then DiscordAcePerms, and map each role ID to an ace group such as group.admin or qbcore.admin. For one fixed admin, one line in server.cfg is enough: add_principal identifier.discord:YOUR_ID group.admin.
How to Link Discord to FiveM as a Player
FiveM has no linking page for Discord. Every player who connects brings a list of identifiers, and the Cfx.re docs for GetPlayerIdentifiers list discord among them, next to license, fivem, steam and ip. The client picks it up from the Discord desktop app: txAdmin's own rejection message tells players to "open the Discord Desktop app and try again (the Web app won't work)."
When a player says your server cannot find their Discord, the fix is on their PC:
- Close FiveM completely.
- Start the Discord desktop app and sign in with the account that is in your Discord server.
- Start FiveM again and reconnect.
To see what the server receives, use txAdmin's /info Discord command, which can show admins a player's identifiers, or this test command in a server script:
-- server.lua in one of your own resources
RegisterCommand('discordid', function(source)
if source == 0 then return end -- run it in game, not in the console
local id = GetPlayerIdentifierByType(source, 'discord')
print(('%s: %s'):format(GetPlayerName(source), id or 'no discord identifier'))
end, false)
The result looks like discord:123456789012345678. Everything below works with that number.
Create the Discord Bot and Copy Your IDs
Server-side integrations talk to Discord through a bot account. From Discord's getting started guide:
- In the Discord Developer Portal, click Create App, enter a name and press Create.
- On the Bot page, click Reset Token and copy the token. Discord shows it only once and warns never to share it or check it into version control.
- On the Installation page, under Guild Install, add the
applications.commandsandbotscopes and pick the bot's permissions. Open the Install Link in your browser and choose Add to server. - In Discord's settings, turn on Developer Mode. Right-click your server icon and choose Copy ID: that is the server ID, which the resources call the guild ID. Roles and users get a Copy ID option too.
Three rules before you paste the token anywhere:
- txAdmin's bot needs the Server Members intent. Switch it on at the Bot page under Privileged Gateway Intents, or txAdmin reports that the bot "does not have a required privileged intent." Badger's install guide names no intent; that resource reads one member at a time through Discord's REST API.
- Use two bots for txAdmin and Badger_Discord_API. txAdmin's settings say "Do not reuse the same token for another bot", and txAdmin switches its bot off when it holds Administrator, Manage Roles, Kick Members, Ban Members or other moderator permissions.
- Keep tokens in server-only files. Badger_Discord_API loads its config.lua as a server script. Files under
client_scriptgo into the packfile every player downloads, per the Cfx.re manifest docs.
Option 1: txAdmin's Discord Bot and Allowlist
txAdmin has a Discord bot built in (v8.1.2 was its latest GitHub release on 4 November 2026). It decides who may join; it does not turn roles into in-game permissions. In txAdmin, open Settings:
- Discord tab: switch Discord Bot to Enabled, paste the Token, fill in the Guild/Server ID and, optionally, a Warnings Channel ID. Save.
- Allowlist tab: pick the Allowlist Mode and write the Allowlist Instructions that rejected players see.
| Allowlist Mode | Who can join |
|---|---|
| Disabled | Everyone who is not banned |
| Admin-only (maintenance mode) | Players whose fivem: or discord: identifier is attached to a txAdmin admin |
| Discord Server Member | Players with a discord: identifier who are in your Discord server |
| Discord Server Roles | Members of your Discord server with at least one role from Allowlisted Discord Roles (role IDs, comma separated) |
| Approved License | Players whose license: identifier an admin approved on the Allowlist page or with the /allowlist bot command |
| External Allowlist Resource | Another resource you run decides |
The two Discord modes stay disabled until the bot is enabled. The bot also adds slash commands to your Discord: /allowlist request approves a request ID such as R1234 from a player who was turned away in Approved License mode, /allowlist member adds a member to the approvals, /info looks a player up, and /status add posts a status embed that txAdmin updates every minute. Our txAdmin guide covers the rest of the panel. On our FiveM hosting txAdmin comes pre-installed, so this option only needs the bot token and your server ID.
Option 2: Badger_Discord_API for Role Checks in Your Scripts
Badger_Discord_API by JaredScar (Badger) reads Discord data for other scripts: a player's roles, name, nickname and avatar, and your server's role list. It can also add or remove roles and set nicknames. Its manifest says version 1.6, the repository was last changed in May 2024, and the README warns that some methods may not fully work, so test on a development server first.
- Download it from GitHub and put the folder in
resources, named exactlyBadger_Discord_API. A GitHub zip unpacks asBadger_Discord_API-main, and with that name the resource prints "This resource must be named Badger_Discord_API for it to work properly with other scripts". - Fill in the
Configblock at the top of config.lua. Leave theConfig.Splashblock under it in place, because server.lua reads it when the resource starts:
Config = {
Guild_ID = '123456789012345678', -- your Discord server ID
Multiguild = false,
Guilds = {
["name"] = "guild_id",
},
Bot_Token = 'YOUR_BOT_TOKEN',
RoleList = {},
DebugScript = false,
CacheDiscordRoles = true,
CacheDiscordRolesTime = 60, -- seconds a player's roles stay cached
}
- Add
ensure Badger_Discord_APIto server.cfg, above every resource that uses it.
By default, Config.Splash shows every connecting player a welcome card for 10 seconds; set Enabled = false there to skip it.
RoleList is optional and gives role IDs a name, such as ['Admin'] = 597446815054430219. Write role IDs, not role names: the README notes that names break as soon as someone renames the role in Discord.
The exports are server-side. This check lets only players with your Police role go on duty:
-- server.lua of your own resource
local POLICE_ROLE = '123456789012345678' -- Copy ID on the role in Discord
local function hasDiscordRole(src, roleId)
local roles = exports.Badger_Discord_API:GetDiscordRoles(src)
if not roles then return false end -- no discord identifier, not in your Discord, or an API error
for i = 1, #roles do
if tostring(roles[i]) == roleId then return true end
end
return false
end
RegisterCommand('policeduty', function(source)
if source == 0 then return end
if hasDiscordRole(source, POLICE_ROLE) then
print(GetPlayerName(source) .. ' may go on duty')
else
print(GetPlayerName(source) .. ' does not have the Police role')
end
end, false)
Roles stay cached for CacheDiscordRolesTime seconds (60 by default), so a role you give in Discord reaches the server within about a minute. Giving roles from the game (AddRole, RemoveRole, SetRoles) needs Manage Roles on the bot, per Discord's Modify Guild Member docs, and the README says these calls return error 403 when the member ranks above the bot, so drag the bot's role above the roles it changes.
Discord Roles for Permissions in FiveM: Map Roles to Ace Groups
FiveM permissions are aces. In the Cfx.re server commands, add_ace gives a principal a permission and add_principal makes one principal inherit from another. A Discord account is a principal too: identifier.discord: plus the user ID. For one fixed person you need nothing else:
# server.cfg
add_ace group.admin command allow
add_principal identifier.discord:123456789012345678 group.admin
For whole roles, use DiscordAcePerms by the same author. When a player connects it reads their roles through Badger_Discord_API and runs add_principal identifier.discord:<id> <group> for each role in your list. When they leave, it removes those entries.
- Install Badger_Discord_API first, as above.
- Put the folder in
resourcesand name it exactlyDiscordAcePerms(a GitHub zip unpacks asDiscordAcePerms-master), because theadd_acelines below use that resource name. - Add these lines to server.cfg. The two
add_acelines come from Badger's install steps; without them the resource cannot grant anything.
add_ace resource.DiscordAcePerms command.add_principal allow
add_ace resource.DiscordAcePerms command.remove_principal allow
ensure Badger_Discord_API
ensure DiscordAcePerms
- In DiscordAcePerms' config.lua, set
Server_Name,Discord_LinkandWebsite_Link(it points to badger.store by default), then fillroleListwith pairs of a Discord role ID and an ace group.
The group names depend on your framework:
| Framework | Groups to map Discord roles to | Defined in |
|---|---|---|
| QBCore | qbcore.mod, qbcore.admin, qbcore.god | QBCore's txAdmin recipe server.cfg and QBCore.Config.Server.Permissions |
| Qbox | group.support, group.mod, group.admin | permissions.cfg in Qbox's txAdmin recipe |
| Standalone or vMenu | Your own group.<name> entries | Your add_ace lines in server.cfg |
For QBCore, the mapping looks like this:
-- DiscordAcePerms/config.lua, inside Config
roleList = {
{123456789012345678, "qbcore.mod"}, -- Moderator role
{234567890123456789, "qbcore.admin"}, -- Admin role
{345678901234567890, "qbcore.god"}, -- Owner role
},
This works because qb-core creates an ace for each level when it starts (add_ace qbcore.admin admin allow), and its HasPermission function asks IsPlayerAceAllowed(source, 'admin'). Still choosing a framework? Our ESX vs QBCore vs Qbox comparison sets them side by side.
ESX Legacy keeps each player's group in its own database and adds the matching group.<name> principal for the player's license when a character loads. A Discord role mapped through DiscordAcePerms adds ace rights next to that, but scripts that read the ESX group still see the value stored in ESX.
To check the setup, join the server. With Print_Perm_Grants_And_Removals = true (the default), the console prints a line such as [DiscordAcePerms] (playerConnecting) Added <name> to role group qbcore.admin. The resource prints that line just before it runs add_principal, so it also appears when the command is refused. To confirm the grant, while that player is online, test_ace identifier.discord:234567890123456789 admin in the server console should come back as allowed.
Permissions are set when a player connects. After a role change, the player can type /refreshPerms (on by default, once per 10 minutes through Refresh_Throttle = 600) or reconnect. If DiscordAcePerms cannot read someone's roles, for example because they never joined your Discord, it shows a card with a link to your Discord and a Play button. They can still play, just without the mapped permissions, so add txAdmin's Discord Server Member mode if membership must be required.
Send Server Logs to a Discord Channel
Logs need no bot. As Discord's help article describes it, open Server Settings, go to Integrations, click Create Webhook, pick the log channel and press Copy Webhook URL. Post to that URL from a server script. Discord's Execute Webhook endpoint takes content of up to 2000 characters, an optional username and up to 10 embeds. The token is part of the URL, so anyone with the URL can post in that channel: keep it server-side.
-- server.lua (never in a client script)
local WEBHOOK = 'https://discord.com/api/webhooks/WEBHOOK_ID/WEBHOOK_TOKEN'
local function sendLog(message)
PerformHttpRequest(WEBHOOK, function(status)
if status ~= 200 and status ~= 204 then
print(('Discord webhook failed with status %s'):format(status))
end
end, 'POST', json.encode({ username = 'Server logs', content = message:sub(1, 2000) }),
{ ['Content-Type'] = 'application/json' })
end
AddEventHandler('playerDropped', function(reason)
sendLog(('%s left the server: %s'):format(GetPlayerName(source), reason))
end)
A busy log channel gets hard to search. Fivemanage keeps the same kind of events in a dashboard you can filter by player, event or time, and our Fivemanage guide shows how to set it up.
Troubleshooting
| What you see | Likely cause | Fix |
|---|---|---|
| Badger_Discord_API error 401, "Your Discord Token is probably wrong" | Token mistyped or reset in the portal | Paste the current token into Bot_Token and restart the resource |
Error 403 on AddRole or SetNickname | The member's role is above the bot's role | Move the bot's role higher in Server Settings, Roles |
| "This resource must be named Badger_Discord_API" | Folder still called Badger_Discord_API-main | Rename the folder and update ensure |
| No "Added ... to role group" line in the console | Badger_Discord_API cannot read the player's roles, a role ID in roleList is wrong, or DiscordAcePerms starts before Badger_Discord_API | Check the console for Badger_Discord_API errors, copy the role IDs again and ensure Badger_Discord_API first |
The "Added" line shows, but test_ace says denied | The two add_ace resource.DiscordAcePerms lines are missing, so the server refuses its add_principal command | Add both lines to server.cfg and restart the server |
| txAdmin: "for your safety the bot has been disabled" | The txAdmin bot holds Administrator, Manage Roles or another moderator permission | Remove those permissions, and use a second bot for role changes |
FAQ
How do I link Discord to FiveM?
As a player you do nothing in FiveM itself. Open the Discord desktop app and sign in before you start FiveM, and servers receive your Discord user ID as a discord: identifier. Discord in a browser does not count. If a server still cannot find you, close FiveM, restart Discord and check you use the account that joined the server's Discord.
How do I use Discord roles for permissions in FiveM?
Install Badger_Discord_API with your bot token and server ID, then DiscordAcePerms. In its roleList, pair each Discord role ID with an ace group, such as qbcore.admin on QBCore or group.admin on Qbox, and add the two add_ace resource.DiscordAcePerms lines to server.cfg.
Can txAdmin and Badger_Discord_API share one bot? No. txAdmin says not to reuse its token for another bot, and it disables its bot when the bot has Manage Roles, which Badger needs for adding roles. Create a second application in the Developer Portal.
Do role changes in Discord apply while a player is online?
txAdmin checks membership and roles when a player joins. DiscordAcePerms also sets permissions on join; after a change the player can use /refreshPerms once every 10 minutes, or reconnect.
Do I need separate hosting for these Discord bots? No. txAdmin's bot runs inside txAdmin, and Badger_Discord_API talks to Discord from the FiveM server. Only a bot you write yourself, such as a ticket bot, runs as its own process; our Discord bot hosting runs Node.js, Python and Java bots.
Sources
All opened on 4 November 2026:
- Cfx.re docs: GetPlayerIdentifiers, server commands, resource manifest, GetPlayerIdentifierByType
- txAdmin on GitHub: allowlist settings, Discord settings, Discord bot, slash commands, messages, status embed, releases
- Badger: Badger_Discord_API, its install guide, DiscordAcePerms, its install docs
- Frameworks: QBCore recipe server.cfg, qb-core config, qb-core functions, qb-core commands, Qbox permissions.cfg, ESX player class
- Discord docs: getting started, gateway intents, guild members, webhooks; Discord support: Intro to Webhooks