Guides
Reward players
Copy page
Pay players in $LIA from your app's reward pool, from your server. A daily bonus as the example, safe to retry and within your limits.
Your app needs a reward pool, funded by the Lia team (see Reward pools). Rewards only ever come from your server, with your key.
#A daily bonus
Each player can claim 2 LIA once a day, after finishing a level.
import { lia } from "./lia.js";
app.post("/api/daily-bonus", async (req, res) => {
const playerId = await playerFrom(req.body.session); // see Verify on your server
if (!playerId) return res.status(401).end();
if (!(await db.finishedALevelToday(playerId))) return res.status(403).json({ error: "play_first" });
const day = new Date().toISOString().slice(0, 10); // 2026-11-01
try {
const result = await lia("POST", "/rewards", {
playerId,
amount: 2,
key: `daily:${day}:${playerId}`, // one a day per player, however often this runs
reason: "Daily bonus",
});
res.json({ paid: result.status === "paid", amount: result.amount });
} catch (error) {
if (error.data?.error === "pool_empty") return res.status(503).json({ error: "bonus_paused" });
if (error.data?.error === "daily_limit") return res.status(429).json({ error: "come_back_tomorrow" });
throw error;
}
});In the page:
const { paid, amount } = await api("/api/daily-bonus", {});
if (paid) {
Lia.haptic("success");
Lia.toast("+" + amount + " LIA");
}#Choosing keys
The key is what makes a reward safe. The same key never pays twice, so make it name the reason for the reward, not the request:
| Reward | Key |
|---|---|
| A daily bonus | daily:2026-11-01:p_3f9c… |
| A tournament place | tournament-44:place-1:p_3f9c… |
| A finished quest | quest-7:p_3f9c… |
| A watched video | ad:ad_8e1c… |
A retry with the same key after a timeout answers { "status": "duplicate" } and pays nothing more, so retrying is always safe. Never retry with a new key.
#Keep an eye on the pool
const { balance, dailyCapPerPlayer } = await lia("GET", "/pool");
if (balance < 100) alertTheTeam(`Reward pool is down to ${balance} LIA`);When the pool can't cover a reward, POST /rewards answers 409 pool_empty. Tell players kindly and ask the Lia team to top it up. When a player has had your app's daily limit, it answers 429 daily_limit (the limit is per player per UTC day, set by the team).
#In test
With your test key, rewards are pretend: recorded, with their webhook, but no $LIA moves, and the pool and daily limit aren't used. Try your keys and your error handling there first.
#Make rewards fair
- Decide on the server. What someone earned should come from what your server saw (a finished game, a verified video), not from the page saying so.
- Mind your own limits too, per player and per day, so one player can't drain the pool.
- Say why. The
reasonshows in the player's activity under your app's name.