Core Concepts
Vouchers vs codes, where everything is configured, and how a redemption flows.
A quick mental model so the rest of the documentation clicks into place.
Vouchers and codes
ProVouchers has two delivery methods that share everything else:
| Voucher | Code | |
|---|---|---|
| What it is | A physical item players hold | A word players type |
| How it is redeemed | Right-click the item | /voucher redeem <code> |
| Defined in | vouchers/<id>.yml | codes/<id>.yml |
| Use limit | Per item (each item redeems once) | Per player and/or global counter |
| Rewards, conditions, effects | Same system | Same system |
If you understand one, you understand the other. The fields that differ are listed on the Voucher Reference and Code Reference pages; everything shared (rewards, conditions, text) has its own page and applies to both.
Where everything lives
plugins/ProVouchers/
├── config.yml # storage backend, anti-dupe, metrics (see Config Reference)
├── data.db # created only when the storage backend is sqlite
├── vouchers/ # one voucher per .yml file
│ ├── crate_key.yml
│ └── rank_token.yml
└── codes/ # one code per .yml file
└── launch_gift.yml- Server-wide settings (which database, anti-dupe behaviour, metrics) live in
config.yml. - Each voucher and code is its own file. The file name without
.ymlis the defaultid/code, but you can override it with theid:orcode:key inside the file. - After editing any file, run
/voucher reload. There is no need to restart.
Anatomy of a voucher file
id: crate_key # identity (defaults to the file name)
display-name: "<gold>Key" # how the item is named (MiniMessage)
item: { material: TRIPWIRE_HOOK, glow: true } # what the item looks like
lore: [ "<gray>Right-click" ] # item description lines (MiniMessage)
cooldown: 0 # per-player seconds between redeems
expiry: "" # when the item stops working
conditions: [ ... ] # who may redeem it, and where (see Conditions)
rewards: [ ... ] # what they get (see Rewards)
random-rewards: [ ... ] # weighted "pick one" rewardsOnly item is strictly required (and even then material defaults to PAPER).
Everything else has a sensible default.
How a redemption flows
When a player right-clicks a voucher (or runs /voucher redeem), ProVouchers runs
these steps in order and stops at the first failure:
- Is it a real voucher/code? Unknown or removed ids are rejected.
- Is it redeemable?
unredeemable: truevouchers are display-only. - Owner check (vouchers only):
owner-onlyvouchers reject other players. - Expiry check: expired vouchers and codes are rejected.
- Game mode gate: creative and spectator are blocked by default.
- Cooldown check: per-player, per-voucher. Right after a player joins, a voucher with a cooldown waits until their stored cooldowns have loaded.
- Conditions: every condition must pass; the first failure shows its message.
- Anti-dupe / use limits (checked off the main thread): a duplicate voucher item or an exhausted limit is rejected. A use is reserved in the database here, so players redeeming at the same moment cannot exceed a limit.
- Rewards run: the voucher item is taken from the inventory, then always-run rewards plus one weighted random set run, each reward executed independently.
Steps 1 to 7 are instant and on the player's thread. Step 8 touches the database, so it runs asynchronously, then control returns to the player to grant rewards. This keeps the server (and Folia regions) responsive. If the player logs out, or moves the voucher out of their inventory, before the rewards run, nothing is used up: the reserved use is given back and the voucher still works. While a redeem is in progress, further clicks on the same voucher are ignored.
One self-contained plugin
Everything ProVouchers needs (scheduling, storage, text rendering, conditions, integrations, metrics) ships inside the plugin. Practically, this means:
- Conditions (permission, region, economy, and so on) share one consistent config shape (see Conditions).
- Integrations (economy, permissions, custom items, placeholders) are detected automatically, so installing a supported plugin "just works" without ProVouchers-specific setup (see Integrations).
- Text is rendered with MiniMessage and PlaceholderAPI in one place (see Text and Placeholders).
That is the whole model. The rest of the docs are reference pages for each piece.