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

  1. Open your manage link, mint a signing secret, and copy it. It is shown once.
  2. Set your postback URL — an https address on your own domain.
  3. Choose which events you want. Save.
  4. 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:

  1. Verify sig. Take every field except sig, sort the keys, join them as key=value&key=value, and HMAC-SHA256 that string with your secret. Anything that skips this is an open endpoint that mints rewards for strangers.
  2. Dedupe on id. We retry, and a retry after a timeout looks exactly like a first delivery from your side.
  3. 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_complete is once per player per day, whatever the log says.