Skip to the content
Docs

Discord Webhooks

On this page10

Seven channels. Every one of them is optional, and all of them are off until you give them a URL.

The channels

lua
ServerConfig.Webhooks = {
    Default   = '',
    Proofs    = '',
    Deaths    = '',
    Admin     = '',
    Exports   = '',
    Errors    = '',
    Anomalies = '',
}
ChannelConvarWhat lands there
Defaultrecognition_webhook_defaultAnything without its own channel.
Proofsrecognition_webhook_proofsAim, damage, kill, cuff, rob and search snapshots.
Deathsrecognition_webhook_deathsNew Life Rule resets, and admin restores of them.
Adminrecognition_webhook_adminAdmin edits, deletes, identity resets, settings changes.
Exportsrecognition_webhook_exportsOne-click "Export to Discord" from the admin panel.
Errorsrecognition_webhook_errorsScript errors — refund failures and the like.
Anomaliesrecognition_webhook_anomaliesPossible cheating or abuse, for staff to review.

Set them from server.cfg instead, which is the better habit:

cfg
set recognition_webhook_default   "https://discord.com/api/webhooks/..."
set recognition_webhook_proofs    "https://discord.com/api/webhooks/..."
set recognition_webhook_deaths    "https://discord.com/api/webhooks/..."
set recognition_webhook_admin     "https://discord.com/api/webhooks/..."
set recognition_webhook_exports   "https://discord.com/api/webhooks/..."
set recognition_webhook_errors    "https://discord.com/api/webhooks/..."
set recognition_webhook_anomalies "https://discord.com/api/webhooks/..."

The convar always wins over the value in the file. An empty or unset convar leaves the file value alone.

A channel that is off

A URL that does not start with https:// is treated as not configured. Discord logging is optional, so a channel you simply left empty gets no line of its own. At boot you see one line in total:

Code
[5code-recognition] Discord log webhooks: none configured (optional).

or, when some channels have a URL, the list of the ones that post:

Code
[5code-recognition] Discord log webhooks on: admin, proofs.

or, when default has a URL (every channel without a URL of its own then posts to it, the usual one-webhook setup):

Code
[5code-recognition] Discord log webhooks on: all 7 channels.

A channel whose value was set but typed wrongly gets a warning of its own, naming the reason — it is an http:// URL, it does not start with https://, it is not a URL, the value is not text — so a typo never hides behind "I did not use that channel":

Code
[5code-recognition] Webhook 'proofs' was ignored (it is an http:// URL), so it is off. Set an https:// URL in config_server.lua or its convar.

When default has a URL, the end of that line says so it posts to the default webhook instead. The lines are printed once per start, not again when settings are reloaded.

What a message contains

lua
ServerConfig.Discord = {
    Name = '5code-recognition',
    Avatar = '',
    IncludeServerIds = true,
    IncludeNames = true,
    IncludeCharKeys = true,
    IncludeLicenses = false,
    AttachFullData = true,
    MaxFileBytes = 1048576,
    ExportCooldownSeconds = 10,
    ExportsPerHour = 30,
    ThreadIds = {},
    SuppressNotifications = false,
}
KeyDefaultAllowedWhat it does
Name'5code-recognition'textWebhook user name.
Avatar''URLWebhook avatar. '' = Discord default.
IncludeServerIdstruetrue / falseInclude server ids.
IncludeNamestruetrue / falseInclude character names.
IncludeCharKeystruetrue / falseInclude character keys (citizenid and equivalents).
IncludeLicensesfalsetrue / falseInclude license identifiers.
AttachFullDatatruetrue / falseAttach a file with the full data to exports.
MaxFileBytes1048576 (1 MB)bytesLargest attached file.
ExportCooldownSeconds10secondsPer admin, between exports.
ExportsPerHour30countPer admin.
ThreadIds{}{ Exports = '123…' }Post into a thread or forum post instead of the channel.
SuppressNotificationsfalsetrue / falseSend messages silently.

Presentation

Chart style and attachment format live in config.lua, because they are about how the export looks, not what it contains.

KeyDefaultAllowedWhat it does
Config.Discord.Chart'sparkline''sparkline' · 'none'The trend line in an export. sparkline = text bars, which always work and need nothing. none = no trend line. (The other values listed in config.lua send the same text sparkline in this version.)
Config.Discord.AttachFormat'both''json' · 'csv' · 'both'Format of the attached full-data file.

The sending queue

Discord rate-limits webhooks hard. The queue keeps us inside their limits and backs off when we hit one anyway.

KeyDefaultWhat it does
Queue.MinGapMs400Minimum gap between two messages on the same URL.
Queue.BurstPer2s5Messages allowed in any two seconds.
Queue.PerMinute30Messages per minute.
Queue.Backoff429After3Repeated 429s before a hard back-off.
Queue.Backoff429Seconds60How long that back-off lasts.
Queue.MaxQueue200Queue depth per URL.
Queue.MaxRetries5Retries per message.
Queue.TimeoutMs15000Request timeout.

The proof log check value

KeyDefaultWhat it does
AnchorEveryRows100Post a check value of the proof log to the Proofs webhook every N proofs, and at shutdown.

That message is an outside copy nobody on the server can edit, so you can compare the proof log with it later. Check the log in game or in the console with:

Code
recognition:verifyproofs [fromId]

Filtering proofs

A busy server produces a lot of on-duty police cuffs that nobody will read. Two keys thin them out without touching what is stored:

KeyDefaultWhat it does
Config.Proof.DiscordCausesall truePer-cause switch: aim, damage, kill, cuff, uncuff, rob, search, escort, kidnap, custom.
Config.Proof.DiscordOnlyBelowfalsetrue = only proofs below the act line go to Discord.
Config.Proof.DiscordSkipOnDutyJobs{}e.g. { 'police' } — on-duty actions of those jobs skip Discord.
Config.Proof.DiscordBatchSeconds5Batching window.

Everything is still written to recognition_proofs either way. See Proofs.

Admin actions

KeyDefaultWhat it does
Config.Admin.DiscordActionstruetrue = every logged admin action goes to the Admin webhook. false = none. A list = only those actions, e.g. { 'settings_save', 'settings_reset', 'identity_reset' }.

Photos

Photos have their own optional webhook, used only when ServerConfig.PhotoHost = 'discord':

lua
ServerConfig.PhotoDiscord = {
    Webhook = '',   -- convar: recognition_photo_webhook
}

See Photos & Screenshots.

Checklist

  • URLs are in config_server.lua or in set convars — not config.lua, not setr.
  • IncludeLicenses is false unless you have a written reason.
  • The channels are private to staff.
  • Errors and Anomalies point somewhere a human actually reads.
  • The Proofs channel is filtered, or it will be unreadable by week two.