Skip to Content

FiveM API Keys and Secrets

FiveM resources may need external credentials such as a Steam Web API key, Discord bot token, webhook URL, or vendor API key. Keep these values on the server, load them through the method supported by the resource, and never send them to a client script.

Looking for the key required to start FXServer? Use the separate FiveM license key setup. This page covers optional integration credentials only.

Choose where to store a secret

Use this order of preference:

  1. A secrets manager or protected environment file supported by the service.
  2. A server-only resource configuration excluded from Git.
  3. A non-replicated convar loaded from a protected secrets.cfg.

Do not use setr or sets for secrets. Replicated and server-info convars are not private storage.

Protected secrets.cfg

If a resource reads its credentials with GetConvar, keep secret values in a separate file:

# server.cfg exec secrets.cfg
# secrets.cfg — never commit this file set steam_webApiKey "replace-with-steam-web-api-key" set myresource_discordToken "replace-with-discord-bot-token"

Protect the file on Linux:

sudo chown fivem:fivem /opt/fivem/server-data/secrets.cfg sudo chmod 600 /opt/fivem/server-data/secrets.cfg

Ignore local secret files:

server.cfg secrets.cfg .env .env.* *.key *.token

A standard convar can still be read by server resources. Give untrusted resources no access to a server that holds valuable credentials.

Steam Web API key

Some resources use a Steam Web API key to query Steam profiles or work with Steam identifiers.

Create the key

  1. Open the Steam Web API key page .
  2. Sign in with the Steam account that will own the key.
  3. Register the domain requested by Steam.
  4. Store the key in your secrets manager or protected config.

Configure FiveM

The standard server convar is:

set steam_webApiKey "replace-with-steam-web-api-key"

Only configure it when a resource or authentication flow actually needs it. A Steam API key does not replace the Cfx.re registration key.

Read it from a server script

local steamApiKey = GetConvar('steam_webApiKey', '') if steamApiKey == '' then print('Steam Web API integration is disabled: key not configured') return end

Never include the key in an HTTP URL that you log. Redact request URLs and headers before writing integration errors.

Discord bot tokens

A Discord bot token authenticates an application as a bot. It is not the same as a Discord application ID, guild ID, role ID, or webhook URL.

Create and authorize a bot

  1. Open the Discord Developer Portal .
  2. Create or select an application.
  3. Open Bot and generate the token.
  4. Enable only the privileged intents the integration requires.
  5. Use the OAuth2 installation flow to grant the minimum required permissions.
  6. Store the token immediately; do not paste it into server.cfg unless the resource specifically documents a protected convar.

Resource configuration

Different resources load Discord credentials differently. Follow the resource’s current documentation and keep its server-only config out of Git:

-- server/config.lua — load on the server only return { token = GetConvar('myresource_discordToken', ''), guildId = 'replace-with-guild-id' }

Do not add this file to shared_scripts or client_scripts. Client-delivered resource files are not secret.

Discord webhooks

A webhook URL is also a credential: anyone with the URL can normally post to that webhook.

local webhookUrl = GetConvar('myresource_discordWebhook', '') local function sendWebhook(payload) if webhookUrl == '' then return end PerformHttpRequest(webhookUrl, function(statusCode) if statusCode < 200 or statusCode >= 300 then print(('Discord webhook failed with HTTP %s'):format(statusCode)) end end, 'POST', json.encode(payload), { ['Content-Type'] = 'application/json' }) end

Do not log webhookUrl, request headers, or complete failed request objects.

Validate configuration without exposing it

Check whether required values exist:

local requiredConvars = { 'steam_webApiKey', 'myresource_discordToken' } for _, name in ipairs(requiredConvars) do local isConfigured = GetConvar(name, '') ~= '' print(('%s configured: %s'):format(name, isConfigured)) end

This reports presence only. Avoid printing lengths, prefixes, suffixes, or masked fragments that make secrets easier to reconstruct.

Rotate a compromised credential

  1. Revoke or reset the credential in its provider dashboard.
  2. Store the replacement in the protected configuration.
  3. Restart only the services or resources that need to reload it.
  4. Test the integration without logging the new value.
  5. Search Git history, deployment logs, tickets, and chat for the exposed value.
  6. Remove retained copies where possible.

Rotate credentials when they are exposed, when access ownership changes, or when the provider’s policy requires it. Arbitrary rotation without a tested rollover process can cause avoidable outages.

Common failures

The value is empty in GetConvar

  • Confirm exec secrets.cfg runs before the resource starts.
  • Confirm the convar name and capitalization match.
  • Confirm txAdmin is loading the expected profile and server.cfg.
  • Confirm the secret file is readable by the FXServer service account.

Discord returns 401 Unauthorized

The bot token is invalid, revoked, or loaded incorrectly. Reset it in the Discord Developer Portal and update the protected configuration.

Discord returns 403 Forbidden

The token is valid, but the bot or webhook lacks permission for the requested action or channel. Adjust the specific permission instead of granting administrator access.

Steam integration fails

Confirm the Steam key is active, the request uses the expected Steam identifier format, and Steam’s API is reachable. Do not assume every FiveM player exposes a Steam identifier.