Rewarding your players
You can pay your own users for playing our games. Before the mechanics, the one thing that decides your whole design:
A browser game cannot be trusted to say "I won"
Whatever the page can send, the player can forge. The console is right there. That is true of our games, of every embedded game on the web, and of anything you build yourself.
So we do not pretend otherwise. Every event we send you carries a tier saying what it is actually worth, and you decide what to pay for each.
| Tier | What it means | Cost of forging one | Pay for it? |
|---|---|---|---|
signal |
A postMessage to your page |
one line in a console | Never. Use it for a progress bar |
attested |
Our server saw a session of a plausible shape — long enough, in order, under the caps | a script somebody has to write, earning at most the daily cap | Small, capped rewards |
proved |
We replayed the move log against that day's board and it is a genuine solution | you have to actually solve the puzzle | Yes. This one is real |
A partner who pays cash for attested has misread this page.
How proved works
Our daily puzzles are deterministic: every player on earth gets the same board, generated from the same seed. When a game reports a completion it sends the move log. Our server rebuilds that day's board and replays every move through the game's own rules, checking each one was legal before applying it.
If the log ends in a win, it is a win. It does not matter whether a person or a script produced it — they solved the puzzle, which is the thing you were paying for.
Games that can be verified today: Solitaire. Others report attested and
say so in the tier field; more are added as their rules are wired up.
Setting it up
- Open your manage link, mint a signing secret, and copy it. It is shown once.
- Set your postback URL — an
httpsaddress on your own domain. - Choose which events you want. Save.
- Press Send a test postback. It fires a signed message at your endpoint and shows you the exact signed string, so you can check your own signature code against a message you know.
Identifying your players
Add your own user id to the embed URL:
<iframe src="https://unlatchgames.com/play/solitaire/?pub=YOUR_CODE&uid=USER_ID"></iframe>
The uid is yours and opaque to us. It must not be an email address or
anything else that identifies a person — anything with an @ in it is refused.
Sign it once your server can. Your page adds a timestamp and an HMAC:
sig = HMAC_SHA256(your_secret, `${pub}:${uid}:${ts}`) // ts = unix seconds
then &ts=…&sig=… on the URL. Signing stops a player minting user ids or
crediting somebody else's account. It cannot stop a player editing their
own uid, because that value lives in their browser — nothing can. Switch on
"refuse player ids my site did not sign" in your manage page once you are
sending it.
What arrives at your endpoint
A JSON POST:
{
"id": "pub_abc:user-42:solitaire:daily_complete:2026-09-24",
"pub": "pub_abc", "uid": "user-42", "game": "solitaire",
"event": "daily_complete", "tier": "proved", "verified": "1",
"day": "2026-09-24", "at": "2026-09-24T09:15:02.000Z", "v": "1",
"sig": "…"
}
Three things you must do:
- Verify
sig. Take every field exceptsig, sort the keys, join them askey=value&key=value, and HMAC-SHA256 that string with your secret. Anything that skips this is an open endpoint that mints rewards for strangers. - Dedupe on
id. We retry, and a retry after a timeout looks exactly like a first delivery from your side. - Answer 2xx. Anything else is a failure; we try three times, then park it so it can be re-sent by hand.
A worked example in Node:
import crypto from 'node:crypto';
function valid(body, secret) {
const message = Object.keys(body).filter((k) => k !== 'sig').sort()
.map((k) => `${k}=${body[k]}`).join('&');
const mine = crypto.createHmac('sha256', secret).update(message).digest('hex');
return crypto.timingSafeEqual(Buffer.from(mine), Buffer.from(body.sig));
}
The events
| Event | Best tier it can reach | Fires |
|---|---|---|
play |
attested |
once a session has lasted long enough to mean anything |
complete |
attested |
a level, a hand, a round finished |
daily_complete |
proved |
the day's board solved. Once per player per day |
The limits, so they are not a surprise
- A session under 30 seconds earns nothing.
- A session over two hours earns nothing — a tab left open overnight is not four hundred minutes of play.
- 40 events per player per game per day, total. This is the ceiling on what a working forgery is worth.
daily_completeis once per player per day, whatever the log says.