Guide
How to send FiveM server logs to Discord, step by step.
To send FiveM logs to Discord you need three things: a webhook created in the channel that should receive them, its URL stored in server.cfg where players cannot read it, and a server-side function that posts a JSON embed to that URL with PerformHttpRequest. This guide walks through all three, then explains the limits you will meet as the server grows and what to do about them.
No card · 3 GB free · Bandwidth never billed
On this page
Step 1: create the webhook in Discord
Open your Discord server’s Server Settings, go to the Integrations tab and click Create Webhook. Give it a name your staff will recognise, such as Server logs, choose the text channel it posts to, then press Copy Webhook URL. You need the Manage Webhooks permission on the server to do this.
Create one webhook per log channel: joins and leaves in one, admin actions in another, economy in a third. Each webhook is tied to a single channel, so the channel is chosen by which URL your script posts to.
Treat the URL like a password. Anyone who has it can post in that channel without a bot or an account. If it ever leaks, for example in a screenshot or a public repository, delete the webhook from the same Integrations page and create a new one.
Step 2: keep the URL in server.cfg, not in a script
Put the URL in a convar declared with set. A set convar stays on the server: client scripts cannot read it. Do not use setr, which replicates the value to every connected player, and never paste a webhook URL into a client script or a NUI page, where anyone can extract it from their game files.
Keeping the URL in server.cfg also means you can rotate it without editing a resource. Note that server.cfg is only read when the server starts, and the script below reads the convar once, when the resource loads. So change the line in server.cfg so the new URL survives the next restart, then apply it right away by typing the same set line in the server console (or txAdmin live console) and restarting the resource that reads it.
server.cfg
set discord_logs_webhook "https://discord.com/api/webhooks/<id>/<token>"Step 3: post an embed from a server script
In a server-side Lua file, read the convar with GetConvar and send a POST request with PerformHttpRequest. Discord’s Execute Webhook endpoint takes a JSON body with content (plain text, up to 2000 characters) or embeds (up to 10 per message), and an optional username that overrides the webhook’s name. An embed has a title, a description and an integer color.
The example below posts a red embed every time a player leaves, using the playerDropped event and its reason. Reuse discordLog from your other server code for bans, money changes or admin commands. Discord answers with a 2xx status when the message is accepted, so the callback prints anything else to the console instead of failing silently.
server.lua
local webhook = GetConvar('discord_logs_webhook', '')
local function discordLog(title, description, color)
if webhook == '' then return end
PerformHttpRequest(webhook, function(status, body)
if status < 200 or status >= 300 then
print(('discord log failed: %s %s'):format(status, body or ''))
end
end, 'POST', json.encode({
username = 'Server logs',
embeds = { { title = title, description = description, color = color } },
}), { ['Content-Type'] = 'application/json' })
end
AddEventHandler('playerDropped', function(reason)
discordLog('Player left', GetPlayerName(source) .. ' (' .. reason .. ')', 15158332)
end)Limits to know before your channel fills up
Embeds have hard limits: 256 characters for a title, 4096 for a description, 1024 for a field value and 6000 for everything in one message combined. Cut long text such as chat messages or inventory dumps before you send it, or Discord rejects the whole message.
Discord also rate-limits requests. When you send too fast it answers HTTP 429 with a retry_after value in seconds, and its documentation asks you to read that value rather than hard-code a delay. The simple function above does not retry, so a burst, such as a server restart that kicks everyone at once, can lose messages. Grouping several events into one message (up to 10 embeds) helps.
Webhook messages only turn user mentions into pings by default, not role or everyone mentions, but Discord may strip some characters from what you send. Player names and chat text are user input, so do not expect them to appear exactly as typed.
When a Discord channel stops being a good log
For a small server, the setup above is enough and costs nothing. The trouble starts with volume. After a few months a log channel holds tens of thousands of messages, Discord search cannot filter on a field, such as amounts over a value or every event for one license, and the record is only as complete as your retries. Finding everything one player did across five channels becomes a chore.
The usual fix is to store the log somewhere searchable and keep Discord for the moments a human needs to look. BasiX is one way to do that. Install the basix resource, put an upload-scoped key in server.cfg (it works on every plan, including the free one, where a full key is refused) and call exports.basix:info, warn or error with a player and metadata. Events are batched and sent every two seconds, and up to 400 are kept in memory if the API cannot be reached. Every plan can store and search logs, including the free one, which keeps them 3 days; Starter keeps them 30 days.
Discord then only gets alerts. In the dashboard, open Logs, then Categories, views & alerts, create a new alert rule and paste your Discord webhook URL. A rule fires when a number of matching events (by level, text, metadata or category) arrive within a time window, then stays quiet for a cooldown, so a burst produces one message instead of fifty. Alert rules are not included in the free plan: you get 5 on Starter (EUR 5 a month) and 20 on Pro, which you can try free for 14 days.
server.cfg and Lua
set basix_key "bx_live_yourkeyhere"
ensure basix
exports.basix:warn('Suspicious money add', {
player = GetPlayerIdentifierByType(source, 'license'),
metadata = { amount = 999999, source = 'admin' },
})Questions
Server Settings, then Integrations, then Create Webhook. Pick the channel and press Copy Webhook URL. You need the Manage Webhooks permission.
Related
Updated Oct 29, 2026