Skip to content

Bot-detection signals

@moosehq/game-client-sdk/behavior-reporter's BehaviorReporter collects raw interaction timing in the browser (via the underlying BehaviorCollector) and posts the resulting digest straight to the platform — there's no backend of yours in this path at all. A bot's cadence tends to be fast and unnaturally regular compared to a human player's, which is part of the signal being reported; a programmatically dispatched click is another.

This is unrelated to reporting lifecycle events — the two travel over entirely different transports (BehaviorReporter posts directly to the platform over fetch; GameBridge posts to the operator's shell over postMessage) and are constructed independently.

ts
import { BehaviorReporter } from '@moosehq/game-client-sdk/behavior-reporter'

// ingestUrl is the platform's dedicated behavior-ingestion endpoint —
// a separate base URL from the one you pass ProviderClient, given to
// you by your integration contact. See Environments & base URLs.
const behavior = new BehaviorReporter({ ingestUrl: behaviorIngestUrl, sessionToken })

// That's the entire integration — every click on the page is already
// being recorded (see "Automatic collection" below).

// Call this when the game is torn down (e.g. before navigating away from
// a SPA route), to cancel its timer and detach its listeners:
behavior.stop()

Automatic collection

Constructing a BehaviorReporter auto-attaches a document-level click listener that records every click on the page as an interaction — there's nothing else to wire up. stop() detaches it along with the reporter's other listeners.

If you want to scope or supplement collection yourself instead, pass autoCollectClicks: false and call recordInteraction directly:

ts
const behavior = new BehaviorReporter({
  ingestUrl: behaviorIngestUrl,
  sessionToken,
  autoCollectClicks: false,
})

spinButton.addEventListener('click', (event) => {
  behavior.recordInteraction(event) // pass the Event so isTrusted can be inspected
  placeBet()
})

Passing the triggering Event (whether via auto-collection or a manual call) lets BehaviorReporter inspect isTrusted: a real click always has isTrusted === true, while a programmatically dispatched one (element.click() from a script) has isTrusted === false and is flagged as an automation signal.

Reporting

BehaviorReporter flushes on its own timer and on tab-hide/page-unload, posting directly to ingestUrl with the session token as an X-Session-Token header (not a signed call: a browser can't hold your HMAC secret, so this one endpoint authenticates by session token instead). Call behavior.stop() when tearing the game down, to cancel its timer and detach its listeners.

This is a best-effort telemetry signal, not part of the gameplay loop — a failed report is swallowed internally and never surfaces to the player or blocks a bet. See BehaviorReporter in the SDK reference for the exact types.