HTTP requests & webhooks: PerformHttpRequest
Talking to the outside world, Discord, your panel API, a license-check service, means HTTP. CFX gives you exactly one native for it: PerformHttpRequest. It is callback-based, so the response arrives later, not on the same line. It will surprise you with rate limits. And JSON bodies must be built by hand. This lesson teaches the pattern that scales from a single ban-log to a 50-server fleet.
Build it
Make the resource folder
Using Windows File Explorer (or mkdir in a Linux shell, or the VS Code Explorer), go to your server-data folder's resources directory - the same folder your other resources live in - and create a new folder. The full path is your server-data path plus:
resources/qu_http_webhooks
Create the files
Create this exact file layout:
resources/qu_http_webhooks/
fxmanifest.lua
server.lua
Write fxmanifest.lua
Open fxmanifest.lua and paste this:
fx_version 'cerulean'
game 'gta5'
server_script 'server.lua'
Older tutorials add a lua54 'yes' line here. As of June 2025 that setting is deprecated and ignored: Lua 5.4 is now the only Lua runtime, so you leave it out. Notice there is no client_script line. Everything in this lesson runs on the server only, which is exactly what keeps the webhook URL off players' machines.
Set the webhook in server.cfg
The webhook URL is a secret. Anyone who has it can post to your Discord channel forever. Never paste the real URL into chat, a screenshot, a public repo, or a screen-share - and if it leaks, delete that webhook in Discord and create a new one (this rotates the token). It does not go in the script. It goes in server.cfg as a convar, above the ensure qu_http_webhooks line you will add in Step 6:
set qu_http_webhooks_webhook "https://discord.com/api/webhooks/123456789/your-token-here"
Create the webhook in Discord under Server Settings, then Integrations, then Webhooks. Copy the URL it gives you and paste it in place of the example above.
Write the lesson code
Open server.lua and paste this. It reads the URL from the convar, builds a JSON body, POSTs it, and retries once if Discord rate-limits you:
local webhook = GetConvar('qu_http_webhooks_webhook', '')
local function postToDiscord(message, attempt)
attempt = attempt or 1
local body = json.encode({ username = 'qu_http_webhooks', content = message })
PerformHttpRequest(webhook, function(code, response, headers)
if code == 429 and attempt == 1 then
local data = response and json.decode(response) or nil
local retryAfter = tonumber(data and data.retry_after)
or tonumber(headers['retry-after'] or headers['Retry-After'])
or 1
print('[qu_http_webhooks] rate-limited, retrying in ' .. retryAfter .. 's')
SetTimeout(retryAfter * 1000, function()
postToDiscord(message, 2)
end)
return
end
print('[qu_http_webhooks] Discord status ' .. code)
end, 'POST', body, { ['Content-Type'] = 'application/json' })
end
RegisterCommand('webhooktest', function(src)
if webhook == '' then
print('[qu_http_webhooks] missing webhook, set qu_http_webhooks_webhook in server.cfg')
return
end
local name = src == 0 and 'console' or GetPlayerName(src)
postToDiscord('test from ' .. name, 1)
end, true)
That final true marks the command restricted: it needs the command.webhooktest ACE permission. The server/txAdmin console always has it, which is why you run the test there. A normal in-game player would be silently denied unless you grant them with an add_ace line in server.cfg.
Start and test it
Open server.cfg and add this line below the set line from Step 4:
ensure qu_http_webhooks
Save, then run:
restart qu_http_webhooks
Open the txAdmin web panel (by default http://localhost:40120, or your panel URL), click Live Console in the left sidebar, then type this command into the input box at the bottom and press Enter:
webhooktest
A message appears in your Discord channel, and the console prints:
Keep reading the full lesson
Sign in to start, then unlock every step of this lesson and the full FiveM School with a membership.
- How it works
- If something went wrong
- What you can do now
- Try it yourself
The remainder of HTTP requests and webhooks is available to FiveM School members.