Plug into the platform.
Everything a game needs: player sign-in, records, leaderboards, and chip currency — one REST API. Register your app at /developers and start calling.
Basics
Base URL: https://games.haolabs.co/api/v1. Every response is JSON in one envelope:
{ "success": true, "data": { … } }
{ "success": false, "error": "human-readable reason" }
POST bodies may be application/json or form-encoded — both are parsed.
CORS is fully open (Access-Control-Allow-Origin: *), so browser games can call
the API directly; tokens ride the Authorization header, never cookies.
Authentication
Standard OAuth 2.0 authorization-code flow. You need an app registered at
/developers — that gives you a client_id,
a client_secret (shown once), and your game_slug.
https://games.haolabs.co/oauth/authorize?client_id=…&redirect_uri=…&scope=identity%20records&state=…Players who aren't signed in log in first, then approve.
redirect_uri must
exactly match the one you registered.<redirect_uri>?code=…&state=…. The code is single-use and expires in 5 minutes.
On deny you get ?error=access_denied.# POST /api/v1/oauth/token curl -X POST https://games.haolabs.co/api/v1/oauth/token \ -d "grant_type=authorization_code" \ -d "client_id=hlgapp_…" -d "client_secret=hlgsec_…" -d "code=…" # → 200 { "success": true, "data": { "access_token": "hlg_9f2c…", "token_type": "Bearer", "expires_in": 2592000, "scope": "identity records" } }
Tokens live 30 days; there is no refresh token yet — send the player back through the (instant, already-consented) authorize flow to get a new one. Then call everything with:
Authorization: Bearer hlg_9f2c…
Scopes
| Scope | Grants |
|---|---|
identity | Username, 18+ flag, team, member-since. Always included. |
records | Read the player's game records; post scores under your game_slug. |
wallet | Read the player's chip balance. |
wallet:spend | Debit and credit chips inside your game (capped — see Wallet). |
Ask only for what you need — the consent screen shows the player every scope you request, and you can only request scopes your app registered.
Identity
/api/v1/meidentityWho the token belongs to.
{ "success": true, "data": {
"id": 42,
"username": "crazy_max",
"adult": true,
"team": { "name": "Night Shift", "tag": "NSFT", "color": "#ff4a2d" },
"member_since": "2026-07-09 16:12:02" } }
Records & scores
/api/v1/me/recordsrecords| Param | Description |
|---|---|
game | Optional filter. Defaults to your app's game_slug; pass ?game= (empty) for all games. |
{ "success": true, "data": {
"games": [ { "game": "neon-runner", "played": 31, "wins": 18,
"best": 4200, "last_played": "2026-07-10 12:13:04" } ],
"recent": [ { "game": "neon-runner", "mode": "arcade", "score": 4200,
"won": true, "at": "2026-07-10 12:13:04" } ] } }
/api/v1/scoresrecordsRecord a result for the player, under your game_slug. It feeds their public profile, your leaderboard, and their team's standings.
| Field | Description |
|---|---|
score | Integer, ±100,000,000. Required. |
mode | Your own label, a-z0-9_-, 2-16 chars. Default match. |
won | Optional 1/0 — drives win rates and team points. |
curl -X POST https://games.haolabs.co/api/v1/scores \ -H "Authorization: Bearer hlg_…" -H "Content-Type: application/json" \ -d '{"score": 4200, "mode": "arcade", "won": 1}' # → { "success": true, "data": { "recorded": 4200, "game": "neon-runner" } }
Limit: 100 submissions per player per day per game.
Wallet
Chips are the platform's virtual currency. Wallet routes work only when the platform's
betting flag is on and the player is a verified 18+ account — check
betting before showing chip features.
/api/v1/me/walletwallet{ "success": true, "data": { "chips": 1250, "betting": true } }
/api/v1/wallet/debitwallet:spend| Field | Description |
|---|---|
amount | 1 – 2,000 chips per transaction. |
reason | Optional label (≤24 chars) shown in the player's ledger. |
# entry fee, buy-in, item… { "success": true, "data": { "chips": 700, "debited": 300 } } # 402 { "success": false, "error": "Not enough chips" }
Cap: 5,000 chips net per player per day per app.
/api/v1/wallet/creditwallet:spendPayouts and refunds. Same fields as debit. Hard invariant: across all
time, your app can never credit a player more than it has debited from them — apps
cannot mint chips. A credit that would cross that line returns
403 "Credit exceeds what this app has debited".
Leaderboard
/api/v1/leaderboardpublicYour game's top 10 — no token needed, just your client_id.
Perfect for a title-screen board.
| Param | Description |
|---|---|
client_id | Required. |
period | weekly (default) or alltime. |
{ "success": true, "data": { "game": "neon-runner",
"leaderboard": [ { "rank": 1, "name": "crazy_max", "score": 4200 }, … ] } }
Errors & limits
| Status | Meaning |
|---|---|
400 | Bad input — the error string says what. |
401 | Missing/expired token, bad client credentials, or bad code. |
402 | Not enough chips for a debit. |
403 | Token lacks the scope, chip play unavailable (flag/under-18), or the no-minting rule. |
404 | Unknown route. |
429 | Rate limit (120 req/min per token) or a daily cap. |
Handle 429 by backing off for the rest of the minute. All limits are per player
per app, so one heavy player never throttles your whole game.
Quickstart — browser game in 20 lines
// 1. kick off sign-in (your server keeps the secret + handles /callback) location.href = 'https://games.haolabs.co/oauth/authorize?' + new URLSearchParams({ client_id: 'hlgapp_…', redirect_uri: 'https://yourgame.com/callback', scope: 'identity records', state: crypto.randomUUID() }); // 2. your server traded ?code for a token — now, from the game: const api = (route, body) => fetch('https://games.haolabs.co/api/v1/' + route, { method: body ? 'POST' : 'GET', headers: { Authorization: 'Bearer ' + token, ...(body && { 'Content-Type': 'application/json' }) }, body: body && JSON.stringify(body) }).then(r => r.json()); const player = await api('me'); // hello, {player.data.username} await api('scores', { score: 4200, won: 1 }); // onto the boards const top = await api('leaderboard?client_id=hlgapp_…'); // show it off
Questions or a stuck integration? Register your app at /developers — the panel shows live player counts per app so you can see connections land.