Skip to content

Events & data model ​

Event envelope ​

json
{
  "v": 1,
  "type": "round.open",
  "table": "B001",
  "seq": 1532,
  "prev": 1531,
  "ts": "2026-09-24T03:00:30.000Z",
  "game": "baccarat",
  "shoe": "260924-03",
  "round": 12,
  "roundKey": "B001-260924-03-12",
  "data": {}
}
FieldTypeRequiredNotes
vintyesProtocol major version, currently 1; higher versions get UNSUPPORTED_VERSION
typestringyesEvent type (see below); types the platform does not know yet are acknowledged and ignored
tablestringyesSource table ID, [A-Za-z0-9_.:-]{1,64}
seqintyesStrictly increasing within the stream, ≥ 1; gaps are allowed
previntyesseq of the previous event in the stream; 0 for the first; must be less than seq
tsstringyesEvent time at the source, UTC ISO-8601 with milliseconds (2026-09-24T03:00:30.000Z), and a real date. Source hosts must use NTP
gamestringfor known typesbaccarat, dragontiger or niuniu
shoestringfor shoe and round eventsSource shoe ID, [A-Za-z0-9_.:-]{1,64}; never reused on the same table (include the date). Niu Niu has no shoe; this is the session (see Niu Niu)
roundintfor round eventsRound number within the shoe (for Niu Niu, within the session), 1–100000
roundKeystringfor round eventsUnique forever on the table, [A-Za-z0-9_.:-]{1,128}; corrections and voids reuse it
dataobjectyesDepends on the type
  • Unknown fields are ignored; adding fields is a compatible change and does not bump the major version.
  • "Shoe events": shoe.start, shoe.end. "Round events": round.open, round.close, card, reveal, round.result, round.correct, round.cancel (round events need shoe too).

Event catalog ​

typeWhendata
table.infoOnce per table after connecting; again when it changes{name, game, decks?, dealer?, video?[], capabilities: {cardByCard, countdown, snapshot}}
table.statusStatus changes{status: "open"|"paused"|"maintenance"|"closed", reason?}
table.snapshotWhen you cannot resend, or to re-align after reconnecting{status, phase: "idle"|"betting"|"closed"|"result", shoe?, round?, roundKey?, closesAt?, history: [{round, result}], gap}
shoe.startA new shoe starts{decks?}
shoe.endA shoe ends (optional){rounds}
round.openBetting opens{closesAt?, betSeconds?, lastInShoe?, dealer?}
round.closeBetting closes{}
cardA card is dealt (optional, needs cardByCard){pos, card, faceDown?}
revealNiu Niu hands are turned over one by one (optional){hands: ["P1"]}
round.resultThe result (first version){rev: 1, complete, result}
round.correctCorrects a published result{rev: n+1, complete, result, reason}
round.cancelThe round is void{reason: "misdeal"|"dealer_error"|"device_error"|"table_closed"|"other", note?}
dealer.changeDealer changes{dealer: {id, name, photoUrl?}}

What the platform does:

typeHandling
table.infoUpdates the table catalog; unbound source tables are listed as "discovered, not bound"
table.statusAnything but open stops betting immediately
table.snapshotRebuilds the state and the shoe's road from the snapshot
shoe.startClears the road and tells players about the new shoe
round.openOpens the round and computes when betting closes (see Timing); lastInShoe: true makes the game show "last round of the shoe"
round.closeCloses betting immediately
cardCloses betting at once if still open; animates the card. For Niu Niu, a card with faceDown: true is kept secret: players are only told that a card was dealt to that position
revealShows the face-down cards of those hands (the game turns them over hand by hand); Niu Niu only
round.resultChecked, then settled
round.correctRecalculated automatically or kept and marked disputed, depending on trust level and guardrails (see Trust levels)
round.cancelBefore settlement: full refund; after settlement: same rules as round.correct
dealer.changeShown in the game

Field limits: name 1–128 characters; decks 1–16; betSeconds 1–600; dealer.id 1–64 characters, dealer.name 1–128 characters, dealer.photoUrl must be https://; video at most 8 entries, each {protocol: "webrtc"|"flv"|"hls"|"agora"|"whep", url?, quality?: "sd"|"hd", auth: "none"|"token"|"referer"}; reason and note at most 500 characters; history at most 200 entries; closesAt is a UTC time with milliseconds; faceDown is a boolean; reveal's hands holds 1–4 distinct values out of B, P1, P2, P3.

More rules:

  • Another round.open for the same roundKey while betting is open updates the countdown (for example when the dealer resets the timer); after betting has closed it is ignored.
  • A result without a preceding round.open (for example when a source joins mid-round) only updates the road; nobody can bet on that round.
  • A result cannot be changed with a second round.result: use round.correct with a higher rev (round.result always has rev 1; round.correct has rev ≥ 2 and a reason). A different result with the same rev keeps the original result and is recorded as a conflict.

Game data model ​

Card encoding ​

<rank><suit>: ranks A 2 3 4 5 6 7 8 9 10 J Q K, suits S spades, H hearts, D diamonds, C clubs. For example AS, 10H, QD.

Baccarat result ​

json
{
  "cards": { "P1": "2C", "B1": "4H", "P2": "3D", "B2": "2S", "P3": "10S" },
  "player": 5,
  "banker": 6,
  "winner": "B",
  "playerPair": false,
  "bankerPair": false
}
  • Card positions P1 B1 P2 B2 P3 B3; undealt cards are omitted.
  • player and banker are points 0–9; winner is P, B or T (tie).
  • With complete: true all cards must be present (at least P1 B1 P2 B2). The platform recomputes points, winner and pairs and checks the drawing rules; a mismatch yields RESULT_INCONSISTENT, handled according to the trust level.
  • With complete: false you may send only player, banker, winner, playerPair and bankerPair, plus optionally cardCount (total cards on both sides, 4–6).
  • The example is banker wins with 6: player 2+3=5, draws a 10 and stays at 5; banker 4+2=6 and stands on the player's third card of 0.

Dragon tiger result ​

json
{ "cards": { "D": "KH", "T": "9S" }, "winner": "D" }
  • winner: D dragon, T tiger, TIE.
  • Ranks decide: A=1 … K=13 (ace lowest), suits do not count; equal rank with equal suit is a suited tie.
  • With complete: false you may send only winner; a suited tie and big/small/odd/even (which look at each side's card) cannot be decided then, so those bets are refunded. card events already sent are still used.
  • card events use the positions D and T.

Niu Niu result ​

json
{
  "cards": {
    "F": "7H",
    "B-1": "KS", "B-2": "QD", "B-3": "10C", "B-4": "3S", "B-5": "5H",
    "P1-1": "4C", "P1-2": "6D", "P1-3": "JS", "P1-4": "9C", "P1-5": "KH",
    "P2-1": "2D", "P2-2": "8S", "P2-3": "QC", "P2-4": "AH", "P2-5": "2C",
    "P3-1": "7D", "P3-2": "3H", "P3-3": "KD", "P3-4": "5C", "P3-5": "5D"
  },
  "hands": { "B": 8, "P1": 9, "P2": 3, "P3": 10 },
  "winners": { "P1": "P", "P2": "B", "P3": "P" }
}
  • Positions: F first card; banker B-1 to B-5; player 1 P1-1 to P1-5, player 2 P2-1 to P2-5, player 3 P3-1 to P3-5 (-n is the n-th card dealt to that hand).
  • hands: each hand's rank code, 0 No Bull, 1–9 Bull 1–9, 10 Niu Niu, 11 Five Face Bull, 12 Bomb, 13 Five Small Bull (11–13 only at tables with special hands turned on). winners: P1, P2 and P3 each against the banker, P player wins, B banker wins (there are no ties). Hand and ranking rules are in Tables & round results.
  • With complete: true all 21 cards must be present (the first card plus 5 per hand); hands may be left out.
  • With complete: false at least winners is required (enough to settle equal bets). A double bet needs the winning hand's rank, so send hands whenever you can; without it double bets are refunded. card events already sent are still used.
  • The platform always recomputes ranks and winners from the cards and only compares the source's hands and winners against them. It checks that the 21 cards are all different (one deck per round) and that the ranks and winners match the cards (11–13 at a table with special hands off is a mismatch). When cards come one by one it also checks that the first card comes first, that the n-th card after it goes to the position the first card dictates, that no position is dealt twice, and that the result's cards match the cards dealt. Any mismatch counts as RESULT_INCONSISTENT and is handled by trust level.
  • Deal order: a first card of A 5 9 K starts with the banker, 2 6 10 with player 1, 3 7 J with player 2, 4 8 Q with player 3; the deal then goes round banker → player 1 → player 2 → player 3, 5 cards each. In the example the first card is 7♥, so the cards go to P2-1, P3-1, B-1, P1-1, P2-2…; the banker has Bull 8, player 1 Bull 9 wins, player 2 Bull 3 loses, player 3 Niu Niu wins.
  • Sessions: a fresh deck every round means there is no shoe, so shoe is the session (for example send shoe.start for a new session every 60 rounds or at a dealer change) and round is the round within the session; decks in table.info should be 1.

Events for dealing and revealing (card by card is recommended, with capabilities.cardByCard: true in table.info):

  1. round.open → round.close (the first card also closes betting at once).
  2. The first card: card {"pos": "F", "card": "7H"}. It is always dealt face up; F cannot carry faceDown: true.
  3. The 20 cards in the order the first card dictates, with faceDown: true for cards dealt face down: card {"pos": "P2-1", "card": "2D", "faceDown": true}. The platform stores the card but keeps it secret, telling players only that a card was dealt to player 2; cards without faceDown are face up and shown as usual.
  4. Reveal the hands: reveal {"hands": ["P1"]}, preferably once per hand in the order player 1 → player 2 → player 3 → banker (one event may reveal several hands); the platform shows those hands' cards. reveal is for Niu Niu only (other games get INVALID_EVENT) and, like other round events, needs shoe, round and roundKey.
  5. round.result (complete: true, 21 cards). Hands not revealed yet are revealed by the platform in the order player 1 → player 2 → player 3 → banker before the result is shown.

Timing and betting window ​

  • The source's closesAt is the authoritative betting deadline. The platform closes betting at the earliest of the following, about 1 second early (to absorb player latency):
    • closesAt adjusted by the estimated clock offset
    • when round.open was received plus the table's maximum betting time
    • when round.close or the first card was received
  • Sources without the countdown capability are timed with betSeconds or the table setting.
  • Clock sync: heartbeats carry the source time, from which the platform estimates the clock offset and monitors latency. Closing betting on time depends on a correct clock, so always use NTP.

elite Tenant Integration API v1