1. Getting Started
[Game] FairPlay API
  • Getting Started
    • Introduction
    • Session and Token
    • Request and Response
    • Real-time Events (SSE)
    • Lotto Rules — หวยเฮงเฮง
    • Provably Fair
  • Player
    • Demo
      • Demo Login
    • Player Bet History
      GET
  • Common
    • Game
      • By Game Code
        • Provably Fair
          • Set Client Seed
          • Verify Result
          • Get Revealed Server Seed
        • Check Session
        • Get Balance
        • Get Provably-Fair State
      • Lobby Game List
  • Wallet
    • Wallet Status
      GET
  • Game
    • Lotto (หวยเฮงเฮง)
      • Lotto Rules
      • Rooms
        • By Room
          • Join Round
          • Place Multi-player Bet
          • Get Round Result
          • Room Event Stream
      • Tickets
        • Create Ticket
        • Inquiry My Tickets
        • Get Ticket
      • Round
        • Get My Bets
        • Get My Result
        • Get Round Provably Fair
      • Results
        • Inquiry Results
      • Get Rooms
      • Get Current Round
      • Get Pay Table
  • Appendix
    • Game List
    • Error Codes
    • Rate Limits
  • Language List
    GET
  • Ping
    GET
  • Schemas
    • DemoLoginRequest
    • DemoLoginResponse
    • DemoProfileResponse
    • ProfileResponse
    • GetHistoryResponse
    • BetRecord
    • GameInfo
    • Room
    • GetGameResponse
    • BetRequest
    • Bet
    • BetResponse
    • BetResult
    • GetProvablyFairResponse
    • SetClientSeedRequest
    • SetClientSeedResponse
    • RotateServerSeedResponse
    • VerifyResultRequest
    • VerifyResultResponse
    • GetServerSeedResponse
    • GetStatusResponse
    • JoinRoundResponse
    • PlaceMultiPlayerBetRequest
    • PlaceBetResponse
    • RoundResultResponse
    • SuccessResponse
    • ErrorResponse
  1. Getting Started

Lotto Rules — หวยเฮงเฮง

Rules the หวยเฮงเฮง (LOTTO) screens need. Live payout rates always come from Pay Table; the rates below are examples.

Bet types#

betTypeDigitsENTHWins whenExample rate
6-DIGIT66 digits6 หลักAll 6 drawn digits, in order.1000000
5-DIGIT55 digits5 หลักThe last 5 drawn digits, in order.50000
4-BOTTOM44 bottom4 ตัวล่างThe last 4 drawn digits, in order.10000
4-TOAD44 toad4 ตัวโต๊ดThe last 4 drawn digits in any order (a repeated digit must repeat the same number of times).2500
3-BOTTOM33 bottom3 ตัวล่างThe last 3 drawn digits, in order.500
3-TOAD33 toad3 ตัวโต๊ดThe last 3 drawn digits in any order (same digits, same counts).150
2-BOTTOM22 bottom2 ตัวล่างThe last 2 drawn digits, in order. The only bet type the lucky number boosts.90
1-TOP11 top (วิ่งบน)วิ่งบนThe digit appears anywhere in the last 3 drawn digits (pays once even if it appears more than once).3
1-BOTTOM11 bottom (วิ่งล่าง)วิ่งล่างThe digit appears in the first 2 drawn digits (pays once even if both match).4

Writing the number#

number is a JSON string of ASCII digits 0-9, exactly as long as the bet type's digit count (6-DIGIT 6, 5-DIGIT 5, 4-BOTTOM 4, 4-TOAD 4, 3-BOTTOM 3, 3-TOAD 3, 2-BOTTOM 2, 1-TOP 1, 1-BOTTOM 1).
Leading zeros are significant and must be sent ("07", "0412"). No spaces, signs or separators.
betType is exact and case-sensitive: upper-case with a hyphen. Old spellings such as "6digit" or "2-bottom" are rejected.
An unknown bet type, a wrong length or a non-digit answers 400 99930 (Game.InvalidBetArea).
Valid: 6-DIGIT "048291", 4-TOAD "1179", 2-BOTTOM "07", 1-TOP "0".
Invalid: 2-BOTTOM "7" (length 1, needs 2); 3-BOTTOM "12a" (non-digit); "2-bottom" "17" (wrong case).
Responses always give a line back as separate betType + number; the shared room endpoints use type + number, and their LOTTO winAreas use TYPE_NUMBER (e.g. 2-BOTTOM_17).

Ticket rules#

A ticket item is {betType, number, amount}; amount is a JSON number (a numeric string is also accepted), > 0, decimals allowed.
1-20 items per ticket (maxItemsPerTicket is always 20).
No duplicate betType + number pair in one ticket. The compare is on the exact strings, so permutations of a TOAD number are not duplicates.
amount must be ≥ the room's minimum for the currency when one is set (pay-table limits.minPerItem).
Round-wide cap: for each betType + number, the total over all players' tickets in the round plus this item must be ≤ the room's maximum when one is set (pay-table limits.maxPerNumber).
betType must have a positive rate in the round's payout table.
The whole ticket is all-or-nothing, with one debit of its total.

The draw#

FieldDigitsUsed by
drawnNumberd0 d1 d2 d3 d4 d56-DIGIT
bottom5d1 d2 d3 d4 d55-DIGIT
bottom4d2 d3 d4 d54-BOTTOM (in order), 4-TOAD (any order)
bottom3d3 d4 d53-BOTTOM (in order), 3-TOAD (any order)
bottom2d4 d52-BOTTOM; also becomes the next round's lucky number
front2d0 d1No bet type (informational only)
runBot[d3, d4, d5]1-TOP (วิ่งบน)
runFront[d0, d1]1-BOTTOM (วิ่งล่าง)
Worked example:
{
    "drawnNumber": "482917",
    "bottom5": "82917",
    "bottom4": "2917",
    "bottom3": "917",
    "bottom2": "17",
    "front2": "48",
    "runBot": [
        "9",
        "1",
        "7"
    ],
    "runFront": [
        "4",
        "8"
    ],
    "winners": [
        "6-DIGIT: 482917",
        "5-DIGIT: 82917",
        "4-BOTTOM: 2917",
        "4-TOAD: any permutation of 2, 9, 1, 7 (e.g. 1279)",
        "3-BOTTOM: 917",
        "3-TOAD: any permutation of 9, 1, 7 (e.g. 179)",
        "2-BOTTOM: 17",
        "1-TOP: 9, 1 or 7",
        "1-BOTTOM: 4 or 8"
    ]
}

Payout#

rate is a gross multiplier including the stake: winAmount = amount × rate, truncated to 2 decimals. A losing line pays 0.
netWin = total winAmount − total amount.
Each line settles on its own; one ticket can have several winning lines.
A ticket's outcome is WON when at least one line won (its netWin can still be negative), else LOST.
A round settles at the rates captured when it opened (the live rates are used only for a round that has none captured). GET …/pay-table shows the live rates.
exampleRate above is the default rate only; always read live rates from GET …/pay-table.
The LOTTO endpoints return only the 6-digit number; the split fields above come in gameResult of GET …/rooms/{roomId}/result and SSE round_settled.

Lucky number#

Only 2-BOTTOM lines whose number equals the lucky number are boosted: rate = 2-BOTTOM rate × multiplier. The line still has to win (number == this round's last 2 digits) to pay.
The lucky number is the last 2 digits of the previous settled round of the same room + currency (demo and real separately), fixed when the round opens. A room's first round has none, so nothing is boosted.
The multiplier comes from the game settings; unset or ≤ 0 means 4. It is fixed when the round opens, as is the on / off switch, and settlement uses those fixed values.
Preview endpoints (rooms, round, pay-table, tickets/create, my-bets, tickets/get while WAITING) recompute the lucky number from the latest settled round and the current settings; they match settlement while the round is open and the settings are unchanged.
Where it shows: rooms[].lucky and round.lucky {number, multiplier, rate}; pay-table lucky {enabled, multiplier, appliesTo}; items[].isLucky; my-result items[].baseRate / luckyMultiplier / rate; results/inquiry luckyNumber (the value in effect for that round).

Round modes#

ModeHow the number is drawnClient seed role
MULTICommit-reveal. At round open the server draws 6 random digits and embeds them in the seed {brand}_{gameCode}_{roomID}_{unixNano}_{drawn6}_{nonce 64 hex} (brand is currently always default); serverSeedHash = SHA-256 hex of that seed is published at once. At settlement the seed is revealed as revealedServerSeed and number is its 5th _-separated part.None; players cannot influence the result.
MULTI_CSEEDAt round open the server creates a round server seed (32 random bytes, 64 hex) and publishes serverSeedHash = SHA-256 hex of it. At settlement: msg = seed0-nonce0:seed1-nonce1:… over all slots in index order; h = HMAC-SHA256(key = round server seed, msg); digit i = h[i] % 10 for i = 0..5. The revealed seed is the round server seed, and round/provably-fair returns msg as combinedClientSeed.The round has N client-seed slots (room setting, 3 when unset), pre-filled with default seeds hex(HMAC-SHA256(key = round server seed, msg = "default:{i}")), nonce 0, owner 0. Each successful ticket, after its debit, claims the first unclaimed slot with the player's client seed + nonce (the client seed is "default" until set with POST /gapi/v1/c/g/{gameCode}/provably-fair/client-seed; the nonce then increments). First come, first served; once all N are claimed further tickets contribute nothing, and one player can claim several slots with successive tickets. Each claim is broadcast as SSE seed_slot_updated; a failed claim never fails the ticket.

Round timing#

Round length is the room's roundDurationSec (0 means 900 s). Rounds are aligned to wall-clock boundaries (24-hour rooms align to 00:00 Asia/Bangkok).
closeAt is when betting closes; drawAt always equals closeAt.
Lifecycle: betting → (closeAt passes and the server closes it) rolling → settled. On a settlement failure the round becomes cancelled: its tickets are refunded and stay WAITING in ticket history.
LOTTO sends no round_started event: after SSE round_settled, or once closeAt passes, call POST …/round (or GET …/rooms) for the next round.

Status values#

Value setValues
roundStatusbetting, rolling, settled, cancelled
ticketStatusWAITING, SETTLED
ticketOutcomeWON, LOST
ticketItemResultWAITING, WON, LOST

Demo#

Demo or real comes from the session. Demo players have their own rounds, tickets and results, so a real roundId / betID is not found from a demo session, and the other way round.
Modified at 2026-09-30 13:23:45
Previous
Real-time Events (SSE)
Next
Provably Fair
Built with