Skip to content

Reliability, health & errors ​

Delivery semantics ​

  1. At-least-once delivery: the platform de-duplicates on (sourceId, table, seq).
    • Same seq, same content (compared as normalized JSON, key order does not matter): ignored and acknowledged as usual.
    • Same seq, different content: nack SEQ_CONFLICT.
  2. Ordered within a stream: sources must send in increasing seq; there is no ordering across tables.
  3. Gap detection: when an event's prev differs from the last seq the platform accepted, the answer is resync {table, from} and the source must resend everything after from.
  4. Acks only after persistence: an ack is sent once events are stored, and acks are cumulative: {"B001": 1538} means every event of that stream up to 1538 is accepted.
  5. Resume after reconnecting: when a WebSocket connects, welcome.resume gives the last acknowledged seq of each stream; continue after it. HTTPS sources can call GET /ingest/v1/resume?tables=….
  6. Source retention: sources must keep at least 24 hours of events available for resending.
  7. When you cannot resend: send a table.snapshot with gap: true and continue with live events. The snapshot does not need a matching prev, but its seq must still be greater than the last accepted seq; later events continue from the snapshot's seq. Rounds left open during the gap become abnormal rounds and are voided and refunded after a timeout.
  8. One owner per stream: a stream can only arrive over one WebSocket connection at a time. A new connection that declares the same table in hello takes it over, and further events for that table on the old connection get nack STREAM_TAKEN_OVER.
  9. Unbound source tables: events are acknowledged and archived but not applied; they apply once the platform binds the table.

Responses ​

HTTPS (POST /ingest/v1/events, /validate) answers for the whole batch:

json
{
  "acks": { "B001": 1538 },
  "nacks": [{ "code": "INVALID_EVENT", "table": "B002", "seq": 77, "message": "data.result.cards.P1: must match ^(A|[2-9]|10|J|Q|K)[SHDC]$" }],
  "resync": [{ "table": "B003", "from": 812 }]
}

WebSocket sends {"op":"ack","acks":{…}}, {"op":"nack",…} and {"op":"resync","table":"…","from":…} messages instead.

  • A nack with accepted: true (RESULT_INCONSISTENT) means the event was accepted and stored and the stream moved on; it is only a warning.
  • Any other nack means the event was not accepted and the stream stays at the previous event; fix it and resend with the same seq.
  • After a nack or resync for a stream within a batch, the rest of that stream's events in the batch are skipped; other streams are unaffected.
  • Every event, rejected ones included, goes into the raw archive for disputes and audits.

Health and heartbeats ​

  • Heartbeats (they use no stream sequence numbers) every 5 seconds report the health of each table as ok, degraded or down:

    json
    {"op":"heartbeat","ts":"2026-09-24T03:00:35.000Z","tables":{"B001":"ok","B002":"down"}}
  • When a source knows a table's upstream is down, it must mark the table down immediately; the platform stops betting on it at once.

  • No heartbeat and no event for 15 seconds means the source is lost: all its bound tables stop taking bets; open rounds wait for recovery and are voided and refunded after a timeout.

  • When the last WebSocket connection drops, the tables it carried stop taking bets immediately; they recover when the source reconnects and sends a heartbeat or events.

Error codes ​

CodeMeaningWhat the source should do
UNAUTHORIZEDBad signature, outside the time window, reused nonce, transport not enabled or IP not allowedCheck the key, clock and settings
SOURCE_DISABLEDThe source is disabledContact the platform
UNSUPPORTED_VERSIONUnsupported vUpgrade
TABLE_NOT_ALLOWEDThe source may not publish this tableContact the platform
INVALID_EVENTStructure or field errorsFix the event and resend with the same seq
SEQ_GAP (resync)prev does not matchResend after from
SEQ_CONFLICTSame seq with different contentA bug at the source; investigate
RESULT_CONFLICTDifferent result with the same rev (reserved; this version keeps the original result, records it and sends no nack)Use round.correct
RESULT_INCONSISTENTCards do not match the points, winner or drawing rules (Niu Niu: a repeated card, ranks or winners that do not match the cards, or cards dealt out of order; see Niu Niu)Accepted but held for review; send round.correct to fix it
STREAM_TAKEN_OVERA newer connection took over this tableStop sending the table on the old connection
RATE_LIMITEDToo much traffic (reserved; not returned in this version)Back off and retry

WebSocket close codes:

CodeMeaning
4001Authentication failed (for example hello.source does not match the credentials)
4002Unsupported version
4003The source is disabled
4008Limit violated (message or batch too large)
1012The platform is restarting: reconnect at once and resume

Reconnect backoff: start at 1 second, double each time, cap at 30 seconds, plus random jitter.

Source trust levels ​

The platform assigns each source a trust level that decides how much is automated. Every case is handled automatically; only the guardrails differ:

LevelIncomplete result (complete: false)Result check failsCorrection (round.correct)Void after settlement (round.cancel)
trustedZones that can be settled are settled, the rest refundedSettled on the source's winner and flaggedRecalculated automaticallyReversed automatically
standardSameHeld unsettled; voided and refunded after a timeoutRecalculated within the guardrails; outside them the original stands and the round is disputedSame as left
restrictedSameSameSameSame

Guardrails: the correction or void arrives within 30 minutes of settlement, and the new result is complete and passes the drawing rules. Outside the guardrails no money moves; the round is only recorded as disputed.

WebSocket session example ​

text
← {"op":"welcome","v":1,"session":"s_8f2…","heartbeat":5,"maxBatch":500,"maxInflight":2000,
   "resume":{"B001":1530,"D001":0},"serverTime":"2026-09-24T03:00:29.950Z"}
→ {"op":"hello","v":1,"source":"acme-live","agent":"acme-feeder/2.0",
   "tables":["B001","D001"],"capabilities":{"cardByCard":true,"countdown":true,"snapshot":true,"replayHours":168}}
→ {"op":"events","events":[
     {"v":1,"type":"round.open","table":"B001","seq":1531,"prev":1530,"ts":"2026-09-24T03:00:30.000Z",
      "game":"baccarat","shoe":"260924-03","round":12,"roundKey":"B001-260924-03-12",
      "data":{"closesAt":"2026-09-24T03:00:55.000Z","betSeconds":25}}]}
← {"op":"ack","acks":{"B001":1531}}
→ {"op":"heartbeat","ts":"2026-09-24T03:00:35.000Z","tables":{"B001":"ok","D001":"ok"}}
→ {"op":"events","events":[
     {"v":1,"type":"round.close","table":"B001","seq":1532,"prev":1531,"ts":"2026-09-24T03:00:55.010Z",
      "game":"baccarat","shoe":"260924-03","round":12,"roundKey":"B001-260924-03-12","data":{}},
     {"v":1,"type":"round.result","table":"B001","seq":1538,"prev":1532,"ts":"2026-09-24T03:01:20.400Z",
      "game":"baccarat","shoe":"260924-03","round":12,"roundKey":"B001-260924-03-12",
      "data":{"rev":1,"complete":true,"result":{
        "cards":{"P1":"2C","B1":"4H","P2":"3D","B2":"2S","P3":"10S"},
        "player":5,"banker":6,"winner":"B","playerPair":false,"bankerPair":false}}}]}
← {"op":"ack","acks":{"B001":1538}}
  • seq may skip numbers (1532 is followed by 1538 above), but each prev must equal the previous seq.
  • hello.source must equal X-Source-Id, otherwise the connection is closed with 4001.
  • hello.tables declares the tables this connection carries; a new connection takes over the same tables.

Going live ​

  1. Ask the platform for a source: ID, type, transports and IP allowlist → you receive an HMAC key.
  2. Integrate against the test environment (elite-dev-ingest.ewin888.com) first; use POST /ingest/v1/validate for dry runs, or check formats with the GFI event validator.
  3. Pass the compatibility checks: resume after disconnects (from any seq), resending on resync, sending a snapshot when you cannot resend, heartbeats and down marking, corrections and voids, clock offset < 500 ms, and handling of rejected events.
  4. The platform binds your source tables to public tables, sets priorities and goes live.

elite Tenant Integration API v1