Reward Mailbox
The Reward Mailbox is a temporary holding area for Rewards. It acts like an inbox: your game can deliver rewards to a player (or team) mailbox, and the player can claim them later. Use it for post‑event payouts, reliable delivery, and claim‑later reward flows.
Key concepts #
At its core, mailboxes stores a list of reward entries for each player and, optionally, for teams. Each entry includes the reward payload plus lifecycle metadata so the client and server can determine if and when it can be claimed.
Entries #
Each entry includes:
- A unique
id
- A
reward
payload (currencies, items, energies, and modifiers) - Timestamps for
create_time_sec
,update_time_sec
,expiry_time_sec
(optional), andclaim_time_sec
(optional) - A
can_claim
flag that indicates if the reward is currently claimable
These fields enable clients to render state and enforce rules like “claimable until X” or “already claimed.”
Reward payload #
An entry’s reward
can include multiple types at once: virtual currencies, inventory items, energy, and reward or energy modifiers. This lets a single entry grant a cohesive bundle of resources (for example: 100 gold, 1 sword, and +50 stamina).
How it works #
Claiming rewards #
Claiming validates the entry, applies the reward, records claimed_at, and makes it no longer claimable. You can delete entries after a successful claim. Claims run through Hiro’s reward batching so currency, inventory, and other systems update together.
Expiry and cleanup #
Rewards can expire, as determined by the to_mailbox_expiry_sec
value. The server automatically prunes expired entries during mailbox operations (list, claim, delete, grant) so your UI and storage don’t accumulate stale items.
Team mailboxes #
Use team mailboxes for raid payouts, guild rewards, or event prizes distributed at a team level. The same patterns are used for teams via dedicated RPCs.
Use cases #
Use the Mailbox when you need reliable, claim‑later delivery:
- Gift code redemption: Deliver rewards triggered outside of your normal gameplay loop, like redeeming a code from a marketing campaign.
- Event and tournament payouts: Send rewards after results finalize; players claim when they next log in.
- Compensation: Grant items or currency to affected players without forcing an immediate claim path.
Design considerations #
Expiry strategy #
Set expiries to guide behavior—short windows for LiveOps drops; no expiry for compensation. Automatic pruning keeps storage and UI clear.
Capacity #
Cap mailbox size to prevent unbounded growth and evict the oldest entries when full.
Consistency and integrity #
Claims and grants are atomic with other reward systems to avoid partial updates and double claims.
Configuring mailboxes #
Set global limits, team-specific limits, and route individual rewards into the mailbox with an expiry.
Player mailbox config #
Define a cap on how many entries a player mailbox can hold. When the mailbox is full, the oldest entries are evicted. Add the following to your Hiro configuration file, for example, base-rewards-mailbox.json
:
{
"max_size": 100
}
Team mailbox config #
Place mailbox settings inside your teams configuration (for example, base-teams.json
) to cap team mailboxes independently.
{
"initial_max_team_size": 25,
"max_team_size": 50,
"mailbox": {
"max_size": 20
},
// other team systems, for example:
"wallet": {
"currencies": {
"coin": 0
}
},
Per-reward routing to the mailbox #
Route a reward to the mailbox by setting to_mailbox_expiry_sec
(in seconds) on the reward.
Behavior
- If
to_mailbox_expiry_sec
> 0: the reward is appended to the mailbox and the entry’sexpiry_time_sec
is set to “now + to_mailbox_expiry_sec”. - If omitted or ≤ 0: the reward is applied immediately and is not mailed.
Examples
Player reward routed to mailbox for 7 days (604,800 seconds):
{
"to_mailbox_expiry_sec": 604800,
"guaranteed": {
"currencies": {
"gold": { "min": 100, "max": 100 }
}
}
}
Team reward routed to the team mailbox for 24 hours (86,400 seconds):
{
"team_reward": {
"to_mailbox_expiry_sec": 86400,
"guaranteed": {
"currencies": {
"team_token": { "min": 1, "max": 1 }
}
}
}
}