Features
F5 Redeem Code turns a short string into instant, validated rewards. Every rule below is enforced server-side — the NUI only collects input.
Overview
| Feature | Summary |
|---|---|
| Money rewards | Pay out to one or several money accounts at once (cash, bank, crypto, black money…) |
| Item rewards | Grant up to maxItems distinct inventory items, each up to maxItemAmount |
| Reward types | money, item, or both |
| Public codes | Redeemable by anyone, once per player |
| Private codes | Locked to a list of target Citizen IDs (multi-target) |
| Usage limit | Optional cap on total redemptions (max_uses, 0 = unlimited) |
| Expiry | Optional expiry date/time |
| One per player | Each Citizen ID can redeem a given code only once |
| Cooldown | Per-player debounce between attempts |
| Case-insensitivity | Optional — SUMMER and summer treated as the same code |
| Inventory safety | Item rewards are refused (nothing granted) if the player can't carry them |
| Reusable names | Deleted codes free their name for re-creation |
Rewards
A code's reward_type decides what it grants:
reward_type | Grants |
|---|---|
money | Money only |
item | Items only |
both | Money and items |
Money (single or multi-account)
Money rewards are stored as a per-account list (moneys), so one code can pay, for example, $5000 bank and $250 crypto in a single redeem. Each entry targets a money type from F5Cfg.Money.types.
- Only active money types (those with an account for the running framework) are accepted; unknown or inactive types are dropped.
- Amounts are floored to whole numbers; entries
≤ 0are dropped; duplicate types are summed. - The total across all accounts must not exceed
F5Cfg.Rewards.maxMoney, or creation is rejected (admin_money_over_limit). - On redeem, each account is credited via the framework bridge. If a specific account credit fails it is logged and skipped; the others still apply.
Older codes created before multi-account support stored a single money amount with no account list. On redeem these pay out to the configured F5Cfg.Money.payoutType account, and the admin console surfaces them on that account so an edit migrates them to the moneys format automatically.
Items
Item rewards are a list of { name, amount } pairs:
- Up to
F5Cfg.Rewards.maxItemsdistinct items per code (extras dropped). - Each amount is clamped to
1 … F5Cfg.Rewards.maxItemAmount. - Item names are resolved to their inventory label and icon for display in the panels and history.
- Granting goes through the inventory bridge, so it works across ox / qb / qs / ps / codem / tgiann / ESX-native / custom.
Code scope: public & private
| Scope | Behaviour |
|---|---|
Public (is_private = 0) | Any player may redeem it, subject to the usual once-per-player, expiry and usage-limit rules |
Private (is_private = 1) | Only players whose Citizen ID is in the code's target list may redeem it; everyone else gets redeem_private |
Private codes support multiple targets — a list of Citizen IDs (target_citizenids), capped at F5Cfg.Codes.maxPrivateTargets. A single legacy target (target_citizenid) is still honoured for older codes.
Target validation (on create/edit)
When F5Cfg.Codes.validatePrivateTarget = true (default), targets are verified as the code is saved:
- Input Citizen IDs are sanitized, de-duplicated and capped at
maxPrivateTargets. - Online targets are confirmed instantly.
- Remaining targets are checked in one batched query against the framework's player table (
players.citizenidfor QB/QBox,users.identifierfor ESX). - If a target can't be found the save is rejected (
admin_targets_not_found); if the DB can't be queried it fails closed (admin_target_unverifiable).
Set validatePrivateTarget = false to skip the DB check and trust the entered IDs. See Configuration → Codes.
Usage limits & expiry
| Rule | Field | Behaviour |
|---|---|---|
| Max uses | max_uses | 0 = unlimited. Otherwise, once total redemptions reach max_uses, further attempts return redeem_max_uses |
| One per player | — | Each Citizen ID may redeem a given code only once (redeem_already_used) — tracked in f5_code_redemptions |
| Expiry | expiry | Optional date/time. After it passes, attempts return redeem_expired. Leave empty for a code that never expires |
Redeem validation
When a player submits a code, the server runs these checks in order and stops at the first failure:
| # | Check | Failure message (locale key) |
|---|---|---|
| 1 | Per-player cooldown (cooldownMs) | redeem_cooldown |
| 2 | Non-empty code | redeem_empty |
| 3 | Player has a valid Citizen ID | redeem_error |
| 4 | Code exists (and not deleted) | redeem_invalid |
| 5 | Not expired | redeem_expired |
| 6 | Below max_uses | redeem_max_uses |
| 7 | Player allowed (public, or a private target) | redeem_private |
| 8 | Not already redeemed by this player | redeem_already_used |
| 9 | Inventory has room for all items | redeem_inventory_full |
| ✓ | All passed → grant rewards | redeem_success |
The inventory-space check (step 9) runs before anything is granted. If any single item wouldn't fit, the whole redemption is refused and nothing is given — the player can free space and try again.
On success the server increments the code's use count, records the redemption (Citizen ID, name, Steam HEX, rewards, timestamp), optionally fires the code_redeem webhook, and returns the reward summary to the panel.
Case-insensitivity
With F5Cfg.Codes.caseInsensitive = true (default), code lookups and duplicate-name checks use LOWER(), so WELCOME2024 and welcome2024 are the same code. Set it to false for case-sensitive codes.
Reusable code names
Codes are soft-deleted — deleting sets a deleted flag rather than removing the row, preserving redemption history. A generated code_active column keeps the unique constraint only on live codes, so a deleted code's name becomes available again for a brand-new code. See Framework Compatibility → Database schema.
See Also
- Admin Panel — creating codes with these options
- Configuration → Rewards and → Codes
- Framework Compatibility — money accounts and inventories
- Webhooks — the audit trail for redemptions and admin actions