Skip to content

@moose/provider-sdk reference

See the server-side integration guide for a walkthrough.

ProviderClient

ts
new ProviderClient(options: ProviderClientOptions)
OptionTypeNotes
baseUrlstringPlatform base URL, no trailing slash
tenantIdstringYour provider tenant ID
secretstringHMAC secret — server-side only
retryPartial<RetryOptions>Defaults to { maxAttempts: 5, baseDelayMs: 200 }
timeoutMsnumberPer-attempt request timeout. A timed-out attempt is aborted and retried like any other network error. Defaults to 15000
now() => DateInjectable clock, for tests
onDebug(event: DebugEvent) => voidOpt-in, off by default — see Debugging
fetchtypeof fetchInjectable fetch implementation — point it at createMockPlatform for offline tests. Defaults to the global fetch
ts
type RetryOptions = {
  maxAttempts: number // total attempts, including the first — not the retry count
  baseDelayMs: number  // delay before the first retry; doubles each subsequent attempt
  sleep?: (ms: number) => Promise<void> // injectable, for tests; defaults to a real setTimeout-based sleep
}

Backoff is exponential: baseDelayMs * 2^(attempt-1).

Methods

  • verifySession(sessionToken: string): Promise<VerifySessionResponse>
  • submitTransaction(req: TransactionRequest): Promise<TransactionResponse>
  • getBalance(sessionToken: string): Promise<BalanceResponse> — queries the player's current balance on demand, without moving any money. See POST /v1/wallet/balance.
  • verifyReplay(replayToken: string): Promise<VerifyReplayResponse> — resolves an operator's replay link (the ?replay= token in the URL) back to the round it points at. See Round replay.

Note there's no submitBehavior — bot-detection telemetry is posted straight from the browser via @moose/game-client-sdk/behavior-reporter's BehaviorReporter, never through this SDK. See Reporting lifecycle events.

Types

See Data model & enums for field-level semantics (constraints, validation rules, what metadata is for) behind the types below — this section is the exact TypeScript declarations.

ts
type TransactionType = 'BET' | 'WIN' | 'ROLLBACK'
type ResponseStatus = 'OK' | 'DECLINED'

type TransactionRequest = {
  transactionId: string
  sessionToken: string
  type: TransactionType
  roundId: string
  roundComplete: boolean
  originalTransactionId?: string // required when type === 'ROLLBACK'
  playerRef: string
  amount: number // minor units
  currency: string // ISO-4217, uppercase, 3 letters (e.g. "USD")
  gameId: string
}

type TransactionResponse = { status: ResponseStatus; balance: number }

type VerifySessionConfig = {
  rtpProfile: string
  minBetMinor: number
  maxBetMinor: number
  language: string // BCP-47 tag (e.g. "en", "zh-TW") the operator requested at launch time, or "" if none was requested — fall back to your own default (e.g. "en") in that case
}
type VerifySessionResponse = {
  playerRef: string
  gameId: string
  operatorId: string
  currency: string
  config: VerifySessionConfig
}

type BalanceResponse = { balance: number } // minor units

type VerifyReplayResponse = {
  roundId: string // look this up in your own ReplayStore — see Round replay
  gameId: string
  playerRef: string
  currency: string
  language: string // BCP-47 tag the round was originally played in, or "" if none was set — same fallback rule as VerifySessionConfig.language
}

Errors

PlatformApiError — thrown for non-retryable HTTP responses (400/401/403/404/429); has status: number and body: string (the raw response body), plus requestId?: string (the platform's X-Request-Id response header, when present — hand this to platform support for server-side log correlation), retryAfter?: string (the Retry-After header, when present — notably on a 429, which the SDK never retries automatically), and hint?: string (a short pointer at the likely cause for that status — e.g. a 401's hint walks through the usual signing mistakes). No other response headers are exposed. isRetryableStatus(status) — the SDK's own retry-eligibility check (true only for 409 and any >= 500429 is not retried automatically), exported for callers building custom retry logic. RequestTimeoutError — thrown when a request attempt exceeds timeoutMs; has timeoutMs: number and hint: string. It's treated as a network-level error and retried like any other, so this only reaches a caller once every attempt (across all retries) has timed out.

See Errors & retry for the full status code reference, including the master table this section's classes are built on.

Low-level signing primitives

signRequest / generateNonce — used internally by ProviderClient, and exported for callers that need to sign a provider-side request the client doesn't cover. explainSignature computes the same signature but also returns the raw canonical string it hashed, for diagnosing an unexplained 401 — see Debugging. See Signing & authentication for the full canonical-string contract these functions implement.

ts
type SignRequestInput = {
  method: string
  path: string     // URL path only — no scheme/host/query
  tenantId: string
  secret: string
  body: string      // the exact bytes that will be sent as the request body
  now: Date
  nonce: string     // from generateNonce() — must be unique per signed request
}

type SignedHeaders = {
  'X-Tenant-ID': string
  'X-Timestamp': string
  'X-Nonce': string
  'X-Signature': string
}

type SignatureExplanation = {
  canonicalString: string // the exact bytes that were hashed
  timestamp: string
  signature: string
  headers: SignedHeaders
}

function signRequest(input: SignRequestInput): SignedHeaders
function explainSignature(input: SignRequestInput): SignatureExplanation
function generateNonce(): string // 16 random bytes, hex-encoded

Debugging

DebugEvent — what onDebug receives; a discriminated union over phase:

ts
type DebugEvent =
  | { phase: 'request'; method: string; path: string; tenantId: string; transactionId?: string; attempt: number }
  | { phase: 'response'; method: string; path: string; status: number; attempt: number; latencyMs: number; requestId?: string; clockSkewMs?: number }
  | { phase: 'retry'; method: string; path: string; attempt: number; reason: 'network' | 'timeout' | 'conflict-409' | 'server-5xx'; delayMs: number }

See the Debugging guide for a walkthrough of onDebug, explainSignature, and PlatformApiError's hint/requestId.

Offline mock platform

createMockPlatform(options: MockPlatformOptions): MockPlatform — a signable, in-memory stand-in for the platform's wallet/RGS endpoints, for integration tests with no network. See Testing with createMockPlatform for a worked example.

ts
type MockPlatformOptions = { secret: string; tenantId: string; now?: () => Date }

type MockSession = {
  sessionToken: string
  playerRef: string
  gameId: string
  operatorId: string
  currency: string
  config: VerifySessionConfig
  balanceMinor: number
}

type MockPlatform = {
  fetch: typeof fetch // pass this as ProviderClientOptions.fetch
  registerSession(session: MockSession): void
  registerReplay(replayToken: string, resolved: VerifyReplayResponse): void
  getBalance(sessionToken: string): number | undefined
}