@moose/provider-sdk reference
See the server-side integration guide for a walkthrough.
ProviderClient
new ProviderClient(options: ProviderClientOptions)| Option | Type | Notes |
|---|---|---|
baseUrl | string | Platform base URL, no trailing slash |
tenantId | string | Your provider tenant ID |
secret | string | HMAC secret — server-side only |
retry | Partial<RetryOptions> | Defaults to { maxAttempts: 5, baseDelayMs: 200 } |
timeoutMs | number | Per-attempt request timeout. A timed-out attempt is aborted and retried like any other network error. Defaults to 15000 |
now | () => Date | Injectable clock, for tests |
onDebug | (event: DebugEvent) => void | Opt-in, off by default — see Debugging |
fetch | typeof fetch | Injectable fetch implementation — point it at createMockPlatform for offline tests. Defaults to the global fetch |
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. SeePOST /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.
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 >= 500 — 429 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.
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-encodedDebugging
DebugEvent — what onDebug receives; a discriminated union over phase:
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.
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
}