@playcademy/sdk
API reference for the Playcademy SDK
@playcademy/sdk
Official TypeScript SDK for the Playcademy platform
The Playcademy SDK provides a comprehensive, type-safe interface for building games on the Playcademy platform. It handles authentication, game sessions, user data, inventory management, and all platform APIs through a unified client interface.
Overview
The SDK serves as the primary interface between your game and the Playcademy platform, providing:
- Automatic Environment Detection: Seamlessly works in development and production
- Type-Safe API Access: Full TypeScript support with comprehensive type definitions
- Session Management: Automatic game session handling and state persistence
- Event System: Real-time notifications for inventory changes, level ups, and more
- Developer Tools: Built-in support for game development and testing workflows
Public vs Internal SDK
The Playcademy SDK is split into two entry points:
Public SDK (@playcademy/sdk)
For game developers building games on the Playcademy platform. Includes 8 essential namespaces:
- Core:
identity,runtime,backend,users - Economy:
credits - Gameplay:
scores - Multiplayer:
realtime - Integrations:
timeback
import { PlaycademyClient } from '@playcademy/sdk'
const client = await PlaycademyClient.init()
await client.timeback.endActivity({
/* ... */
})
await client.users.inventory.add('gold-coin', 100)Internal SDK (@playcademy/sdk/internal)
For CLI, platform, and admin tools. Includes all 21 namespaces (8 public + 13 internal):
Additional internal namespaces:
- Platform Auth:
auth(email/password login, API keys) - Administration:
admin(game management, items, currencies) - Developer Tools:
dev(publish games, uploads) - Game Directory:
games(fetch, list, sessions) - Overworld Features:
character,achievements,leaderboard,levels,shop,maps,sprites,notifications - Analytics:
telemetry
import { PlaycademyClient } from '@playcademy/sdk/internal'
const client = new PlaycademyClient({ baseUrl: 'https://hub.playcademy.net' })
await client.auth.login({ email: 'dev@example.com', password: '***' })
const games = await client.games.list()
await client.admin.games.pauseGame('game-123')Note: The internal SDK is for trusted code only (CLI, platform internals). Game developers should use the public SDK.
Key Benefits
- Zero Configuration: Automatic initialization with environment detection
- Production Ready: Battle-tested API patterns with robust error handling
- Real-Time Communication: Open game-scoped WebSocket channels for multiplayer features.
- Event System: Subscribe to platform events like inventory changes and level ups
- Comprehensive Coverage: Access to all Playcademy platform features
- Development Experience: Integrated with sandbox environment for local development
Use Cases
- Game Development: Primary SDK for building games on Playcademy
- Web Applications: Frontend applications interacting with the platform
- Developer Tools: Scripts and utilities for game management
- Server Integration: Backend services integrating with Playcademy APIs
- Testing & Automation: Automated testing of platform integrations
Connection Monitoring
The SDK automatically monitors network connectivity and provides hooks for games to handle disconnects gracefully:
- Automatic Detection: Multi-signal approach detects offline, slow, and degraded connections
- Game Handlers: Implement custom disconnect behavior (e.g., return to lobby, pause game)
- Platform Integration: Built-in helpers for displaying connection alerts
- Zero Config: Works out of the box, customizable when needed
See the SDK Browser documentation for detailed connection monitoring documentation and examples.
Installation
Install the SDK using your preferred package manager:
# Using Bun (recommended)
bun add @playcademy/sdk
# Using npm
npm install @playcademy/sdk
# Using yarn
yarn add @playcademy/sdk
# Using pnpm
pnpm add @playcademy/sdkQuick Start
Automatic Initialization (Recommended)
For most game development scenarios, use automatic initialization:
import { PlaycademyClient } from '@playcademy/sdk'
async function initializeGame() {
try {
// Automatic initialization - detects environment and configures appropriately
const client = await PlaycademyClient.init({
// Optional: Handle connection issues gracefully
onDisconnect: ({ state, displayAlert }) => {
if (state === 'offline') {
displayAlert?.('Connection lost. Reconnecting...', { type: 'warning' })
// Return to safe state (e.g., lobby)
}
},
})
// Get current user
const user = await client.users.me()
console.log('Welcome,', user.name)
// The client is ready for all platform operations
return client
} catch (error) {
console.error('Failed to initialize Playcademy SDK:', error)
throw error
}
}Environment Detection
The SDK automatically detects and configures for different environments:
- Development: Connects to local sandbox (started by
@playcademy/vite-plugin) - Production: Receives configuration from Playcademy platform loader
- Testing: Falls back to mock configuration for automated testing
Core Features
Game Session Management
// Automatic session management when gameId is available
const client = await PlaycademyClient.init()
// Save transient game state (position, health, temporary data)
await client.games.saveState({
currentLevel: 'forest_glade',
playerPosition: { x: 100, y: 200 },
health: 85,
activePowerUps: ['speed_boost'],
})
// Load previously saved state
const gameState = await client.games.loadState()
console.log('Loaded state:', gameState)
// Exit game (automatically ends session if managed)
await client.runtime.exit()User & Inventory Management
// Get user information
const user = await client.users.me()
// Inventory operations (accepts UUIDs or slugs)
const inventory = await client.users.inventory.get()
await client.users.inventory.add('magic-sword', 1)
await client.users.inventory.remove('health-potion', 1)
// Check item quantities and ownership
const goldCount = await client.users.inventory.quantity('gold-coin')
const hasKey = await client.users.inventory.has('dungeon-key')
const hasEnoughGold = await client.users.inventory.has('gold-coin', 100)Credits & Currency
// Platform currency management
const balance = await client.credits.balance()
await client.credits.add(100)
await client.credits.spend(50)
// Check affordability
if ((await client.credits.balance()) >= 100) {
await client.credits.spend(100)
console.log('Purchase successful!')
}Experience & Levels
// Level management
const userLevel = await client.levels.get()
const progress = await client.levels.progress()
console.log(`Level ${userLevel.currentLevel}, ${progress.xpToNextLevel} XP to next level`)
// Note: XP is now managed entirely through TimeBack integration.
// XP updates come from TimeBack webhooks only.TimeBack Integration
Access user role and enrollments, and track learning activities:
// Access TimeBack role and enrollments
const role = client.timeback.role // 'student' | 'parent' | 'teacher' | 'administrator'
const enrollments = client.timeback.enrollments // [{ subject, grade, courseId }]
// Check if user is enrolled in a specific grade
const grade3Enrollment = enrollments.find(e => e.grade === 3)
if (grade3Enrollment) {
// Show grade 3 content
}
// Start tracking an activity (only activityId required!)
client.timeback.startActivity({
activityId: 'math-quiz-level-1',
})
// Auto-derived: activityName "Math Quiz Level 1"
// Auto-filled by backend: appName, subject, sensorUrl
// ... player completes activity ...
// End activity and submit results (XP calculated automatically)
await client.timeback.endActivity({
correctQuestions: 8,
totalQuestions: 10,
})
// XP calculation: base (1 min = 1 XP) × accuracy multiplier
// 100%: 1.25x | 80-99%: 1.0x | 65-79%: 0.5x | <65%: 0xReal-time Authentication
Get authentication tokens for WebSocket connections used by the platform's multiplayer system.
// Get a realtime token for WebSocket authentication
const { token } = await client.realtime.token.get()
// Token is used internally by the platform's multiplayer WebSocket client
// for presence tracking, player positions, and real-time game featuresAPI Reference
Core Modules
Authentication (client.auth)
logout(): Logs out user and clears authentication token
Users (client.users)
me(): Get current user information- Inventory (
client.users.inventory):get(): Get user's inventoryadd(identifier, quantity): Add items to inventoryremove(identifier, quantity): Remove items from inventoryquantity(identifier): Get item quantityhas(identifier, minQuantity?): Check item ownership
Games (client.games)
list(): Get all available gamesfetch(gameIdOrSlug): Get specific game detailssaveState(state): Save transient game stateloadState(): Load saved game statestartSession(gameId?): Start game sessionendSession(sessionId, gameId?): End game session
Credits (client.credits)
balance(): Get current credits balanceadd(amount): Add credits to userspend(amount): Spend user credits
Levels (client.levels)
get(): Get current user level informationprogress(): Get level progress and XP to next leveladdXP(amount): Add experience points- Config (
client.levels.config):list(): Get all level configurationsget(level): Get specific level configuration
Maps (client.maps)
elements(mapId): Get map elements and points of interest
Runtime (client.runtime)
getGameToken(gameId, options?): Get game-specific authentication tokenexit(): Signal platform to exit game view
Real-time (client.realtime)
token.get(): Retrieves a JWT for WebSocket authentication (used by the platform's multiplayer system).
TimeBack (client.timeback)
role: The user's TimeBack role ('student','parent','teacher', or'administrator')enrollments: Array of course enrollments withsubject,grade, andcourseIdstartActivity(metadata): Start tracking an activity (stores start time and metadata)metadata.activityId: Unique activity identifier (required)metadata.activityName: Human-readable activity namemetadata.subject: Subject area (Math, Reading, Science, etc.)metadata.appName: Application namemetadata.sensorUrl: Sensor URL for tracking
endActivity(scoreData): End activity and submit resultsscoreData.correctQuestions: Number of correct answersscoreData.totalQuestions: Total number of questions
- XP Query (
client.timeback.xp):today(options?): Get today's XP (supports timezone parameter)total(): Get total accumulated XPhistory(options?): Get XP history with optional date filteringsummary(options?): Get both today's and total XP in one call
Leaderboard (client.leaderboard) - Game-specific
fetch(options?): Get leaderboard for a specific gameoptions.timeframe: Filter by time period ('all_time','monthly','weekly','daily')options.gameId: Game ID to fetch leaderboard for (required)options.limit: Number of entries to return (default: 10)options.offset: Pagination offset (default: 0)
Scores (client.scores) - Platform-wide
submit(gameId, score, metadata?): Submit a score for any gamegetUserScores(userId, options?): Get all scores for a useroptions.gameId: Filter by specific game (optional)options.limit: Number of scores to return (default: 50)
Developer Tools
Developer Authentication (client.dev.auth)
applyForDeveloper(): Apply for developer statusgetDeveloperStatus(): Check current developer status
Game Management (client.dev.games)
upsert(slug, metadata, gameFile): Create or update gameupdate(gameId, updates): Update game propertiesdelete(gameId): Delete game
API Keys (client.dev.keys)
createKey(gameId, name): Create API key for server authenticationlistKeys(): List all API keysrevokeKey(keyId): Revoke API key
Item Management (client.dev.items)
list(gameId): List all items for a gameget(gameId, slug): Get specific itemcreate(gameId, slug, data): Create new game itemupdate(gameId, itemId, updates): Update existing itemdelete(gameId, itemId): Delete item
Event System
The SDK provides real-time event notifications for important platform changes:
Available Events
// Authentication changes
client.on('authChange', payload => {
console.log('Authentication changed:', payload.token)
})
// Connection state changes
client.on('connectionChange', ({ state, reason }) => {
console.log(`Connection: ${state} - ${reason}`)
})
// Inventory changes
client.on('inventoryChange', payload => {
console.log(`Item ${payload.itemId}: ${payload.delta} (total: ${payload.newTotal})`)
})
// Experience gained
client.on('xpGained', payload => {
console.log(`Gained ${payload.amount} XP (total: ${payload.totalXP})`)
})
// Level up notifications
client.on('levelUp', payload => {
console.log(`Level up! ${payload.oldLevel} → ${payload.newLevel}`)
console.log('Credits awarded:', payload.creditsAwarded)
})Disconnect Handling
For convenience, use the onDisconnect method to handle only offline/degraded states:
const cleanup = client.onDisconnect(({ state, reason, displayAlert }) => {
console.log(`Disconnect detected: ${state}`)
displayAlert?.(`Connection ${state}: ${reason}`, { type: 'warning' })
})
// Later: cleanup() to unregisterEvent-Driven UI Updates
// Update UI in response to platform events
client.on('inventoryChange', payload => {
updateInventoryDisplay(payload.itemId, payload.newTotal)
})
client.on('levelUp', payload => {
showLevelUpAnimation(payload.newLevel)
showCreditsAwarded(payload.creditsAwarded)
})
client.on('xpGained', payload => {
updateXPBar(payload.totalXP, payload.leveledUp)
})Advanced Usage
Manual Initialization
For server-side applications or custom environments:
import { PlaycademyClient } from '@playcademy/sdk'
import type { LoginResponse } from '@playcademy/sdk'
// Step 1: Authenticate
const loginData: LoginResponse = await PlaycademyClient.login(
'https://api.playcademy.com',
'user@example.com',
'password',
)
// Step 2: Initialize client
const client = new PlaycademyClient({
baseUrl: 'https://api.playcademy.com',
token: loginData.token,
gameId: 'your-game-id', // Optional: enables automatic session management
})Custom Configuration
const client = new PlaycademyClient({
baseUrl: 'https://api.playcademy.com',
token: 'your-auth-token',
gameId: 'your-game-id',
// Additional options
timeout: 10000, // Request timeout in milliseconds
retries: 3, // Number of retry attempts
})Error Handling
import { PlaycademyError } from '@playcademy/sdk'
try {
const user = await client.users.me()
// Handle success
} catch (error) {
if (error instanceof PlaycademyError) {
console.error('Playcademy API Error:', error.message)
console.error('Status Code:', error.statusCode)
console.error('Error Code:', error.code)
} else {
console.error('Unexpected error:', error)
}
}Development Environment
Integration with Playcademy Vite Plugin
When using the official Playcademy Vite templates, the development environment is automatically configured:
// In your game's main file
import { PlaycademyClient } from '@playcademy/sdk'
// The vite plugin automatically starts the sandbox
const client = await PlaycademyClient.init()
// SDK automatically connects to local sandbox at http://localhost:4321Manual Sandbox Setup
If not using the Vite plugin, start the sandbox manually:
# Start sandbox server (will also start realtime server on port 4322)
bunx @playcademy/sandbox --port 4321 --verbose
# In your application
const client = new PlaycademyClient({
baseUrl: 'http://localhost:4321/api',
realtimeUrl: 'ws://localhost:4322',
token: 'dev-token' // Sandbox provides mock authentication
})Best Practices
Initialization & Setup
- Always use automatic initialization for game development with
PlaycademyClient.init() - Handle initialization errors gracefully with proper try-catch blocks
- Store the client instance for reuse throughout your application lifecycle
State Management
- Use
games.saveState()for transient data (current level, position, temporary status) - Use
users.inventoryfor persistent items and resources that carry between sessions - Save state periodically, not on every frame or minor change
- Load state once at game start, then manage locally
Performance Optimization
- Cache frequently accessed data like user information and inventory
- Batch inventory operations when possible instead of individual API calls
- Use event listeners to update UI reactively rather than polling
- Implement proper loading states for better user experience
Error Handling
- Wrap all SDK calls in appropriate try-catch blocks
- Provide fallback behavior for network errors and API failures
- Show meaningful error messages to users when operations fail
- Implement retry logic for non-critical operations
Development Workflow
- Use the sandbox environment for all local development
- Test both online and offline scenarios to ensure robust error handling
- Enable verbose logging during development for debugging
- Validate API responses and handle edge cases appropriately
Testing
Unit Testing
// Mock the SDK for unit tests
import { jest } from '@jest/globals'
// Mock the entire SDK module
jest.mock('@playcademy/sdk', () => ({
PlaycademyClient: {
init: jest.fn().mockResolvedValue({
users: {
me: jest.fn().mockResolvedValue({ id: 'test-user', name: 'Test User' }),
inventory: {
get: jest.fn().mockResolvedValue([]),
add: jest.fn().mockResolvedValue(undefined),
},
},
}),
},
}))Integration Testing
// Test with real sandbox
import { PlaycademyClient } from '@playcademy/sdk'
describe('Playcademy Integration', () => {
let client: PlaycademyClient
beforeAll(async () => {
// Initialize with sandbox
client = new PlaycademyClient({
baseUrl: 'http://localhost:4321/api',
token: 'test-token',
})
})
test('should fetch user data', async () => {
const user = await client.users.me()
expect(user).toBeDefined()
expect(user.name).toEqual(expect.any(String))
})
})Troubleshooting
Common Issues
SDK Initialization Timeout
Error: PLAYCADEMY_INIT not received within 5000ms- Ensure you're running in the correct environment (development with sandbox, or production with platform)
- Check that the Vite plugin is properly configured
- Verify the sandbox is running on the expected port
Authentication Errors
Error: Unauthorized (401)- Check that your authentication token is valid
- Ensure you have the necessary permissions for the operation
- Try re-authenticating with
PlaycademyClient.login()
Network Connection Issues
Error: Failed to fetch- Verify the API endpoint is accessible
- Check network connectivity
- Ensure CORS is properly configured for cross-origin requests
Debugging
Use these debugging techniques for troubleshooting SDK issues:
// Check initialization process
try {
const client = await PlaycademyClient.init()
console.log('SDK initialized successfully')
} catch (error) {
console.error('SDK initialization failed:', error)
}
// Monitor network requests in browser dev tools (Network tab)
// Check console for SDK error messages
// Verify API responses and error detailsContributing
The SDK is a critical component of the Playcademy platform ecosystem. When contributing:
- Maintain Type Safety: Ensure all new APIs are fully typed
- Update Documentation: Keep this README and JSDoc comments current
- Add Tests: Include both unit and integration tests for new features
- Follow Patterns: Use consistent patterns with existing SDK methods
- Handle Errors: Implement proper error handling and user feedback
For general contribution guidelines, see the monorepo CONTRIBUTING.md.
Enumerations
MessageEvents
Defined in: messaging.ts:51
Enumeration of all message types used in the Playcademy messaging system.
Message Flow Patterns:
Parent → Game (Overworld → Game):
- INIT: Provides game with authentication token and configuration
- TOKEN_REFRESH: Updates game's authentication token before expiry
- PAUSE/RESUME: Controls game execution state
- FORCE_EXIT: Immediately terminates the game
- OVERLAY: Shows/hides UI overlays over the game
Game → Parent (Game → Overworld):
- READY: Game has loaded and is ready to receive messages
- EXIT: Game requests to be closed (user clicked exit, game ended, etc.)
- TELEMETRY: Game reports performance metrics (FPS, memory usage, etc.)
Enumeration Members
AUTH_CALLBACK
AUTH_CALLBACK: "PLAYCADEMY_AUTH_CALLBACK";Defined in: messaging.ts:180
OAuth callback data from popup/new-tab windows. Sent from popup window back to parent after OAuth completes. Payload:
code: string (OAuth authorization code)state: string (OAuth state for CSRF protection)error: string | null (OAuth error if any)
AUTH_STATE_CHANGE
AUTH_STATE_CHANGE: "PLAYCADEMY_AUTH_STATE_CHANGE";Defined in: messaging.ts:170
Notifies about authentication state changes. Can be sent in both directions depending on auth flow. Payload:
authenticated: booleanuser: UserInfo | nullerror: Error | null
CONNECTION_STATE
CONNECTION_STATE: "PLAYCADEMY_CONNECTION_STATE";Defined in: messaging.ts:110
Broadcasts connection state changes to games. Sent by platform when network connectivity changes. Payload:
state: 'online' | 'offline' | 'degraded'reason: string
DISPLAY_ALERT
DISPLAY_ALERT: "PLAYCADEMY_DISPLAY_ALERT";Defined in: messaging.ts:156
Game requests platform to display an alert. Sent when connection issues are detected or other important events occur. Payload:
message: stringoptions:{ type?: 'info' | 'warning' | 'error', duration?: number }
EXIT
EXIT: "PLAYCADEMY_EXIT";Defined in: messaging.ts:128
Game requests to be closed/exited. Sent when user clicks exit button or game naturally ends. Payload: void
FORCE_EXIT
FORCE_EXIT: "PLAYCADEMY_FORCE_EXIT";Defined in: messaging.ts:94
Forces immediate game termination (emergency exit). Game should clean up resources and exit immediately. Payload: void
INIT
INIT: "PLAYCADEMY_INIT";Defined in: messaging.ts:64
Initializes the game with authentication context and configuration. Sent immediately after game iframe loads. Payload:
baseUrl: stringtoken: stringgameId: string
KEY_EVENT
KEY_EVENT: "PLAYCADEMY_KEY_EVENT";Defined in: messaging.ts:147
Game reports key events to parent. Sent when certain keys are pressed within the game iframe. Payload:
key: stringcode?: stringtype: 'keydown' | 'keyup'
OVERLAY
OVERLAY: "PLAYCADEMY_OVERLAY";Defined in: messaging.ts:101
Shows or hides UI overlays over the game. Game may need to pause or adjust rendering accordingly. Payload: boolean (true = show overlay, false = hide overlay)
PAUSE
PAUSE: "PLAYCADEMY_PAUSE";Defined in: messaging.ts:80
Pauses game execution (e.g., when user switches tabs). Game should pause timers, animations, and user input. Payload: void
READY
READY: "PLAYCADEMY_READY";Defined in: messaging.ts:121
Game has finished loading and is ready to receive messages. Sent once after game initialization is complete. Payload: void
RESUME
RESUME: "PLAYCADEMY_RESUME";Defined in: messaging.ts:87
Resumes game execution after being paused. Game should restore timers, animations, and user input. Payload: void
TELEMETRY
TELEMETRY: "PLAYCADEMY_TELEMETRY";Defined in: messaging.ts:137
Game reports performance telemetry data. Sent periodically for monitoring and analytics. Payload:
fps: numbermem: number
TOKEN_REFRESH
TOKEN_REFRESH: "PLAYCADEMY_TOKEN_REFRESH";Defined in: messaging.ts:73
Updates the game's authentication token before it expires. Sent periodically to maintain valid authentication. Payload:
token: stringexp: number
Classes
ApiError
Defined in: core/errors.ts:86
API error thrown when a request fails.
Contains structured error information from the API response:
status- HTTP status code (e.g., 404)code- API error code (e.g., "NOT_FOUND")message- Human-readable error messagedetails- Optional additional error context
Example
try {
await client.games.get('nonexistent')
} catch (error) {
if (error instanceof ApiError) {
console.log(error.status) // 404
console.log(error.code) // "NOT_FOUND"
console.log(error.message) // "Game not found"
console.log(error.details) // { identifier: "nonexistent" }
}
}Extends
Error
Constructors
Constructor
new ApiError(
status,
code,
message,
details?,
rawBody?): ApiError;Defined in: core/errors.ts:107
Parameters
status
number
HTTP status code
code
string
API error code
message
string
Human-readable error message
details?
unknown
Additional error context
rawBody?
unknown
Raw response body
Returns
Overrides
Error.constructorProperties
code
readonly code: string;Defined in: core/errors.ts:91
API error code (e.g., "NOT_FOUND", "VALIDATION_FAILED"). Use this for programmatic error handling.
details
readonly details: unknown;Defined in: core/errors.ts:97
Additional error context from the API. Structure varies by error type (e.g., validation errors include field details).
rawBody
readonly rawBody: unknown;Defined in: core/errors.ts:103
Internal
Raw response body for debugging.
status
readonly status: number;Defined in: core/errors.ts:105
Methods
is()
is(code): boolean;Defined in: core/errors.ts:167
Check if this is a specific error type.
Parameters
code
string
Returns
boolean
Example
if (error.is('NOT_FOUND')) {
// Handle not found
} else if (error.is('VALIDATION_FAILED')) {
// Handle validation error
}isClientError()
isClientError(): boolean;Defined in: core/errors.ts:174
Check if this is a client error (4xx).
Returns
boolean
isRetryable()
isRetryable(): boolean;Defined in: core/errors.ts:189
Check if this error is retryable. Server errors and rate limits are typically retryable.
Returns
boolean
isServerError()
isServerError(): boolean;Defined in: core/errors.ts:181
Check if this is a server error (5xx).
Returns
boolean
fromResponse()
static fromResponse(
status,
statusText,
body): ApiError;Defined in: core/errors.ts:134
Internal
Create an ApiError from an HTTP response. Parses the structured error response from the API.
Parameters
status
number
statusText
string
body
unknown
Returns
ConnectionManager
Defined in: core/connection/manager.ts:48
Manages connection monitoring for the Playcademy client.
The ConnectionManager serves as an integration layer between the low-level ConnectionMonitor and the PlaycademyClient. It handles:
- Event wiring and coordination
- Disconnect callbacks with context
- Platform-level alert integration
- Request success/failure tracking
This class is used internally by PlaycademyClient and typically not instantiated directly by game developers.
See
- ConnectionMonitor for the underlying monitoring implementation
- PlaycademyClient.onDisconnect for the public API
Constructors
Constructor
new ConnectionManager(config): ConnectionManager;Defined in: core/connection/manager.ts:75
Creates a new ConnectionManager instance.
Parameters
config
ConnectionManagerConfig
Configuration options for the manager
Returns
Example
const manager = new ConnectionManager({
baseUrl: 'https://api.playcademy.com',
authContext: { isInIframe: false },
onDisconnect: (context) => {
console.log(`Disconnected: ${context.state}`)
},
onConnectionChange: (state, reason) => {
console.log(`Connection changed: ${state}`)
}
})Methods
checkNow()
checkNow(): Promise<ConnectionState>;Defined in: core/connection/manager.ts:124
Manually triggers a connection check immediately.
Forces a heartbeat ping to verify the current connection status. Useful when you need to check connectivity before a critical operation.
In iframe mode, this returns the last known state from platform.
Returns
Promise<ConnectionState>
Promise resolving to the current connection state
Example
const state = await manager.checkNow()
if (state === 'online') {
await performCriticalOperation()
}getState()
getState(): ConnectionState;Defined in: core/connection/manager.ts:102
Gets the current connection state.
Returns
The current connection state ('online', 'offline', or 'degraded')
Example
const state = manager.getState()
if (state === 'offline') {
console.log('No connection')
}onDisconnect()
onDisconnect(callback): () => void;Defined in: core/connection/manager.ts:185
Registers a callback to be called when connection issues are detected.
The callback only fires for 'offline' and 'degraded' states, not when recovering to 'online'. This provides a clean API for games to handle disconnect scenarios without being notified of every state change.
Works in both iframe and standalone modes transparently.
Parameters
callback
Function to call when connection degrades
Returns
Cleanup function to unregister the callback
(): void;Returns
void
Example
const cleanup = manager.onDisconnect(({ state, reason, displayAlert }) => {
if (state === 'offline') {
displayAlert?.('Connection lost. Saving your progress...', { type: 'error' })
saveGameState()
}
})
// Later: cleanup() to unregisterreportRequestFailure()
reportRequestFailure(error): void;Defined in: core/connection/manager.ts:157
Reports a failed API request to the connection monitor.
Only network errors are tracked (not 4xx/5xx HTTP responses). After consecutive failures exceed the threshold, the state transitions to 'degraded' or 'offline'.
Typically called automatically by the SDK's request wrapper. No-op in iframe mode (platform handles monitoring).
Parameters
error
unknown
The error from the failed request
Returns
void
reportRequestSuccess()
reportRequestSuccess(): void;Defined in: core/connection/manager.ts:141
Reports a successful API request to the connection monitor.
This resets the consecutive failure counter and transitions from 'degraded' to 'online' state if applicable.
Typically called automatically by the SDK's request wrapper. No-op in iframe mode (platform handles monitoring).
Returns
void
stop()
stop(): void;Defined in: core/connection/manager.ts:201
Stops connection monitoring and performs cleanup.
Removes event listeners and clears heartbeat intervals. Should be called when the client is being destroyed.
Returns
void
ConnectionMonitor
Defined in: core/connection/monitor.ts:45
Monitors network connectivity using multiple signals and notifies callbacks of state changes.
The ConnectionMonitor uses a multi-signal approach to detect connection issues:
- navigator.onLine events - Instant detection of hard disconnects
- Heartbeat pings - Periodic checks to detect slow/degraded connections
- Request failure tracking - Piggybacks on actual API calls
This comprehensive approach ensures reliable detection across different network failure modes common in school WiFi environments (hard disconnect, slow connection, intermittent failures).
Example
const monitor = new ConnectionMonitor({
baseUrl: 'https://api.playcademy.com',
heartbeatInterval: 10000, // Check every 10s
failureThreshold: 2 // Trigger after 2 failures
})
monitor.onChange((state, reason) => {
console.log(`Connection: ${state} - ${reason}`)
})
monitor.start()See
ConnectionManagerConfig for configuration options
Constructors
Constructor
new ConnectionMonitor(config): ConnectionMonitor;Defined in: core/connection/monitor.ts:67
Creates a new ConnectionMonitor instance.
The monitor starts in a stopped state. Call start() to begin monitoring.
Parameters
config
Configuration options
Returns
Methods
checkNow()
checkNow(): Promise<ConnectionState>;Defined in: core/connection/monitor.ts:179
Manually triggers an immediate connection check.
Forces a heartbeat ping to verify connectivity right now, bypassing the normal interval. Useful before critical operations.
Returns
Promise<ConnectionState>
Promise resolving to the current connection state after the check
Example
const state = await monitor.checkNow()
if (state !== 'online') {
alert('Please check your internet connection')
}getState()
getState(): ConnectionState;Defined in: core/connection/monitor.ts:159
Gets the current connection state.
Returns
The current state ('online', 'offline', or 'degraded')
onChange()
onChange(callback): () => void;Defined in: core/connection/monitor.ts:148
Registers a callback to be notified of all connection state changes.
The callback fires for all state transitions: online → offline, offline → degraded, degraded → online, etc.
Parameters
callback
ConnectionChangeCallback
Function called with (state, reason) when connection changes
Returns
Cleanup function to unregister the callback
(): void;Returns
void
Example
const cleanup = monitor.onChange((state, reason) => {
console.log(`Connection: ${state}`)
if (state === 'offline') {
showReconnectingUI()
}
})
// Later: cleanup() to unregisterreportRequestFailure()
reportRequestFailure(error): void;Defined in: core/connection/monitor.ts:207
Reports a request failure for tracking.
This should be called from your request wrapper whenever an API call fails. Only network errors are tracked (TypeError, fetch failures) - HTTP error responses (4xx, 5xx) are ignored.
After consecutive failures exceed the threshold, the monitor transitions to 'degraded' or 'offline' state.
Parameters
error
unknown
The error from the failed request
Returns
void
Example
try {
await fetch('/api/data')
} catch (error) {
monitor.reportRequestFailure(error)
throw error
}reportRequestSuccess()
reportRequestSuccess(): void;Defined in: core/connection/monitor.ts:243
Reports a successful request.
This should be called from your request wrapper whenever an API call succeeds. Resets the consecutive failure counter and transitions from 'degraded' to 'online' if the connection has recovered.
Returns
void
Example
try {
const result = await fetch('/api/data')
monitor.reportRequestSuccess()
return result
} catch (error) {
monitor.reportRequestFailure(error)
throw error
}start()
start(): void;Defined in: core/connection/monitor.ts:86
Starts monitoring the connection state.
Sets up event listeners and begins heartbeat checks based on configuration. Idempotent - safe to call multiple times.
Returns
void
stop()
stop(): void;Defined in: core/connection/monitor.ts:109
Stops monitoring the connection state and cleans up resources.
Removes event listeners and clears heartbeat intervals. Idempotent - safe to call multiple times.
Returns
void
PlaycademyClient
Defined in: clients/public.ts:17
Playcademy SDK client for game developers. Provides namespaced access to platform features for games running inside Cademy.
Extends
PlaycademyBaseClient
Constructors
Constructor
new PlaycademyClient(config?): PlaycademyClient;Defined in: clients/base.ts:61
Parameters
config?
Partial<ClientConfig>
Returns
Inherited from
PlaycademyBaseClient.constructorProperties
authContext?
protected optional authContext: {
isInIframe: boolean;
};Defined in: clients/base.ts:41
isInIframe
isInIframe: boolean;Inherited from
PlaycademyBaseClient.authContextauthStrategy
protected authStrategy: AuthStrategy;Defined in: clients/base.ts:36
Inherited from
PlaycademyBaseClient.authStrategybackend
backend: {
delete: Promise<T>;
download: Promise<Response>;
get: Promise<T>;
patch: Promise<T>;
post: Promise<T>;
put: Promise<T>;
request: Promise<T>;
url: string;
};Defined in: clients/public.ts:77
Make requests to your game's custom backend API routes.
get(path),post(path, body),put(),delete()- HTTP methods- Routes are relative to your game's deployment (e.g., '/hello' → your-game.playcademy.gg/api/hello)
delete()
delete<T>(path, headers?): Promise<T>;Makes a DELETE request to your game's backend.
Type Parameters
T
T = unknown
Parameters
path
string
The API route path (e.g., '/cache' or 'cache/clear')
headers?
Record<string, string>
Optional additional headers
Returns
Promise<T>
Promise resolving to the response data
Example
await client.backend.delete('/cache/clear')download()
download(
path,
method,
body?,
headers?): Promise<Response>;Downloads a file or binary data from your game's backend. Returns the raw Response object, allowing you to access blobs, streams, or other binary data.
Parameters
path
string
The API route path (e.g., '/files/download' or 'files/download')
method
Method = 'GET'
HTTP method (defaults to GET)
body?
unknown
Optional request body
headers?
Record<string, string>
Optional additional headers
Returns
Promise<Response>
Promise resolving to the raw fetch Response
Example
// Download a file
const response = await client.backend.download('/files?key=my-file.pdf')
const blob = await response.blob()
const url = URL.createObjectURL(blob)
// Download with POST and custom headers
const response = await client.backend.download('/files/export', 'POST',
{ format: 'pdf' },
{ 'Accept': 'application/pdf' }
)get()
get<T>(path, headers?): Promise<T>;Makes a GET request to your game's backend.
Type Parameters
T
T = unknown
Parameters
path
string
The API route path (e.g., '/hello' or 'hello')
headers?
Record<string, string>
Optional additional headers
Returns
Promise<T>
Promise resolving to the response data
Example
const data = await client.backend.get('/hello')
console.log(data.message)patch()
patch<T>(
path,
body?,
headers?): Promise<T>;Makes a PATCH request to your game's backend.
Type Parameters
T
T = unknown
Parameters
path
string
The API route path (e.g., '/profile' or 'profile')
body?
unknown
The request body data
headers?
Record<string, string>
Optional additional headers
Returns
Promise<T>
Promise resolving to the response data
Example
const result = await client.backend.patch('/profile', {
displayName: 'NewName'
})post()
post<T>(
path,
body?,
headers?): Promise<T>;Makes a POST request to your game's backend.
Type Parameters
T
T = unknown
Parameters
path
string
The API route path (e.g., '/save' or 'save')
body?
unknown
The request body data
headers?
Record<string, string>
Optional additional headers
Returns
Promise<T>
Promise resolving to the response data
Example
const result = await client.backend.post('/save', {
level: 5,
score: 1000
})put()
put<T>(
path,
body?,
headers?): Promise<T>;Makes a PUT request to your game's backend.
Type Parameters
T
T = unknown
Parameters
path
string
The API route path (e.g., '/settings' or 'settings')
body?
unknown
The request body data
headers?
Record<string, string>
Optional additional headers
Returns
Promise<T>
Promise resolving to the response data
Example
const result = await client.backend.put('/settings', {
volume: 0.8
})request()
request<T>(
path,
method,
body?,
headers?): Promise<T>;Makes a custom HTTP request to your game's backend. Use this for non-standard HTTP methods or advanced configurations.
Type Parameters
T
T = unknown
Parameters
path
string
The API route path (e.g., '/custom' or 'custom')
method
Method
HTTP method (GET, POST, PUT, DELETE, PATCH, etc.)
body?
unknown
Optional request body
headers?
Record<string, string>
Optional additional headers
Returns
Promise<T>
Promise resolving to the response data
Example
const result = await client.backend.request('/custom', 'OPTIONS')url()
url(pathOrStrings, ...values): string;Builds a complete URL to your game's backend route. Useful for embedding backend URLs in HTML elements (img, video, audio, anchor tags).
Supports both regular function call and tagged template literal syntax.
Parameters
pathOrStrings
The API route path as a string, or template strings array
string | TemplateStringsArray
values
...unknown[]
Template literal interpolated values (when using tagged template syntax)
Returns
string
Complete URL to the backend route
Example
// Regular function call
const url1 = client.backend.url('/assets/sprite.png')
const url2 = client.backend.url(`/assets/${fileKey}`)
// Tagged template literal (recommended)
const url3 = client.backend.url`/assets/${fileKey}`
// Use in JSX/HTML elements
const imageUrl = client.backend.url`/assets/${sprite.key}`
const videoUrl = client.backend.url('/videos/intro.mp4')
const downloadUrl = client.backend.url`/downloads/${file.key}`baseUrl
baseUrl: string;Defined in: clients/base.ts:34
Inherited from
PlaycademyBaseClient.baseUrlconfig
protected config: Partial<ClientConfig>;Defined in: clients/base.ts:38
Inherited from
PlaycademyBaseClient.configconnectionManager?
protected optional connectionManager: ConnectionManager;Defined in: clients/base.ts:43
Inherited from
PlaycademyBaseClient.connectionManagercredits
credits: {
add: (amount) => Promise<number>;
balance: () => Promise<number>;
spend: (amount) => Promise<number>;
};Defined in: clients/public.ts:58
Playcademy Credits (platform currency) management.
get()- Get user's credit balanceadd(amount)- Award credits to user
add()
add: (amount) => Promise<number>;Adds Playcademy Credits to the user's inventory. This is a convenience method that automatically finds the credits item ID.
Parameters
amount
number
The amount of credits to add (must be positive)
Returns
Promise<number>
Promise resolving to the new total balance
Example
const newBalance = await client.credits.add(100)
console.log('New balance after adding 100 credits:', newBalance)balance()
balance: () => Promise<number>;Gets the current balance of Playcademy Credits for the authenticated user. This is a convenience method that finds the primary currency in the user's inventory.
Returns
Promise<number>
Promise resolving to the current credits balance
Example
const balance = await client.credits.balance()
console.log('Current credits:', balance)spend()
spend: (amount) => Promise<number>;Spends (removes) Playcademy Credits from the user's inventory. This is a convenience method that automatically finds the credits item ID.
Parameters
amount
number
The amount of credits to spend (must be positive)
Returns
Promise<number>
Promise resolving to the new total balance
Example
const newBalance = await client.credits.spend(50)
console.log('New balance after spending 50 credits:', newBalance)gameId?
protected optional gameId: string;Defined in: clients/base.ts:37
Inherited from
PlaycademyBaseClient.gameIdgameUrl?
optional gameUrl: string;Defined in: clients/base.ts:35
Inherited from
PlaycademyBaseClient.gameUrlidentity
identity: {
_getContext: () => {
isInIframe: boolean;
};
connect: (options) => Promise<AuthResult>;
};Defined in: clients/public.ts:26
Connect external identity providers to the user's Playcademy account.
connect(provider)- Link Discord, Google, etc. via OAuth popup
_getContext()
_getContext: () => {
isInIframe: boolean;
};Internal
Gets the current identity connection context (for internal use).
Returns
{
isInIframe: boolean;
}isInIframe
isInIframe: boolean;connect()
connect: (options) => Promise<AuthResult>;Connects an external identity provider to the user's Playcademy account.
For games in iframes: Uses popup flow to avoid navigation issues. For standalone apps: Uses redirect flow for traditional OAuth.
Parameters
options
AuthOptions
Connection options including provider and callback URL
Returns
Promise<AuthResult>
Promise resolving to the connection result
Examples
// Connect TimeBack identity
const result = await client.identity.connect({
provider: AuthProvider.TIMEBACK,
callbackUrl: 'https://myapp.com/api/auth/callback',
onStateChange: (state) => {
console.log('Connection state:', state.message)
}
})
if (result.success) {
console.log('Connected as:', result.user.email)
}// Force popup mode even in standalone context
const result = await client.identity.connect({
provider: AuthProvider.TIMEBACK,
callbackUrl: 'https://myapp.com/api/auth/callback',
mode: 'popup'
})// Provide custom OAuth configuration for external games
const result = await client.identity.connect({
provider: AuthProvider.TIMEBACK,
callbackUrl: 'https://myapp.com/api/auth/callback',
oauth: {
clientId: 'my-oauth-client-id',
// Optional: override default endpoints
authorizationEndpoint: 'https://custom-idp.com/oauth2/authorize',
tokenEndpoint: 'https://custom-idp.com/oauth2/token',
scope: 'openid email profile custom_scope'
}
})// The SDK automatically includes Playcademy user ID in the OAuth state
const result = await client.identity.connect({
provider: AuthProvider.TIMEBACK,
callbackUrl: 'https://myapp.com/api/auth/callback'
})
// On your server callback, parse the state to get the user ID:
import { PlaycademyClient } from '@playcademy/sdk'
app.get('/api/auth/callback', async (req, res) => {
const { csrfToken, data } = PlaycademyClient.identity.parseOAuthState(req.query.state)
const playcademyUserId = data?.playcademy_user_id
const gameId = data?.game_id
// Now you can associate the OAuth user with the Playcademy user
await linkAccounts(oauthUserId, playcademyUserId)
})initPayload?
protected optional initPayload: InitPayload;Defined in: clients/base.ts:42
Inherited from
PlaycademyBaseClient.initPayloadinternalClientSessionId?
protected optional internalClientSessionId: string;Defined in: clients/base.ts:40
Inherited from
PlaycademyBaseClient.internalClientSessionIdlisteners
protected listeners: EventListeners = {};Defined in: clients/base.ts:39
Inherited from
PlaycademyBaseClient.listenersrealtime
realtime: {
token: {
get: () => Promise<RealtimeTokenResponse>;
};
};Defined in: clients/public.ts:70
Realtime multiplayer authentication.
getToken()- Get token for WebSocket/realtime connections
token
token: {
get: () => Promise<RealtimeTokenResponse>;
};Token sub-namespace for realtime token management
token.get()
get: () => Promise<RealtimeTokenResponse>;Gets a realtime JWT token for establishing WebSocket connections. This token is used by the platform's multiplayer WebSocket client for authentication.
Returns
Promise<RealtimeTokenResponse>
Promise resolving to token response
Example
// Get a realtime token for WebSocket authentication
const response = await client.realtime.token.get()
console.log('Realtime token:', response.token)
// Token is used internally by websocketStore for multiplayer connectionsruntime
runtime: {
assets: {
arrayBuffer: (path) => Promise<ArrayBuffer>;
blob: (path) => Promise<Blob>;
fetch: (path, options?) => Promise<Response>;
json: <T>(path) => Promise<T>;
text: (path) => Promise<string>;
url: string;
};
exit: () => Promise<void>;
getGameToken: (gameId, options?) => Promise<GameTokenResponse>;
getListenerCounts: () => Record<string, number>;
onForceExit: (handler) => void;
onInit: (handler) => void;
onOverlay: (handler) => void;
onPause: (handler) => void;
onResume: (handler) => void;
onTokenRefresh: (handler) => void;
ready: () => void;
removeAllListeners: () => void;
removeListener: (eventType, handler) => void;
sendTelemetry: (data) => void;
};Defined in: clients/public.ts:35
Game runtime lifecycle and asset loading.
exit()- Return to Cademy hubgetGameToken()- Get short-lived auth tokenassets.url(),assets.json(),assets.fetch()- Load game assetson('pause'),on('resume')- Handle visibility changes
assets
assets: {
arrayBuffer: (path) => Promise<ArrayBuffer>;
blob: (path) => Promise<Blob>;
fetch: (path, options?) => Promise<Response>;
json: <T>(path) => Promise<T>;
text: (path) => Promise<string>;
url: string;
};Static assets sub-namespace. Helper methods for loading static files bundled with your game. Use these for runtime asset loading that Vite can't analyze at build time.
assets.arrayBuffer()
arrayBuffer: (path) => Promise<ArrayBuffer>;Fetches a file from the CDN as an ArrayBuffer.
Parameters
path
string
Relative path to the file
Returns
Promise<ArrayBuffer>
Promise resolving to an ArrayBuffer
Example
// Load binary data
const buffer = await client.runtime.assets.arrayBuffer('assets/data.bin')
// Load WebAssembly module
const wasmBuffer = await client.runtime.assets.arrayBuffer('game.wasm')
const module = await WebAssembly.compile(wasmBuffer)assets.blob()
blob: (path) => Promise<Blob>;Fetches a file from the CDN as a Blob.
Parameters
path
string
Relative path to the file
Returns
Promise<Blob>
Promise resolving to a Blob
Example
// Load image as blob
const imageBlob = await client.runtime.assets.blob('images/hero.png')
const objectUrl = URL.createObjectURL(imageBlob)
img.src = objectUrlassets.fetch()
fetch: (path, options?) => Promise<Response> = fetchAsset;Fetches a static asset from the game deployment.
Internal fetch helper used by json, blob, text, arrayBuffer methods.
Parameters
path
string
options?
RequestInit
Returns
Promise<Response>
Param
Relative path to the asset
Param
Optional fetch options
Returns
Promise resolving to the fetch Response
Example
const response = await client.runtime.assets.fetch('data/config.json')
const data = await response.json()assets.json()
json: <T>(path) => Promise<T>;Fetches and parses a JSON file from the CDN.
Type Parameters
T
T = unknown
Parameters
path
string
Relative path to the JSON file
Returns
Promise<T>
Promise resolving to the parsed JSON data
Example
// Load dynamic level data
const levelData = await client.runtime.assets.json(`levels/level-${id}.json`)
console.log('Level name:', levelData.name)assets.text()
text: (path) => Promise<string>;Fetches a text file from the CDN.
Parameters
path
string
Relative path to the text file
Returns
Promise<string>
Promise resolving to the file contents as a string
Example
// Load text data
const story = await client.runtime.assets.text('data/story.txt')
console.log(story)assets.url()
url(pathOrStrings, ...values): string;Builds a complete URL to a static asset bundled with your game. Useful for loading files at runtime that Vite can't analyze at build time.
Supports both regular function call and tagged template literal syntax.
Parameters
pathOrStrings
The asset path as a string, or template strings array
string | TemplateStringsArray
values
...unknown[]
Template literal interpolated values (when using tagged template syntax)
Returns
string
Complete URL to the CDN asset
Example
// Regular function call
const url1 = client.runtime.assets.url('levels/level-5.json')
const url2 = client.runtime.assets.url(`badges/${badgeType}.png`)
// Tagged template literal (recommended for dynamic paths)
const url3 = client.runtime.assets.url`levels/level-${levelId}.json`
// Use in JSX/HTML elements
img.src = client.runtime.assets.url`badges/${badgeType}.png`
audio.src = client.runtime.assets.url`sfx/${soundEffect}.wav`exit()
exit: () => Promise<void>;Gracefully exits the game runtime. Automatically ends any active game session and emits an exit event.
Returns
Promise<void>
Promise that resolves when exit is complete
Example
// Clean up and exit the game
await client.runtime.exit()getGameToken()
getGameToken: (gameId, options?) => Promise<GameTokenResponse>;Retrieves a game token for the specified game. Optionally applies the token to the current client instance.
Parameters
gameId
string
The ID of the game to get a token for
options?
Optional configuration
apply?
boolean
Whether to automatically apply the token to this client
Returns
Promise<GameTokenResponse>
Promise resolving to game token response
Example
// Get token without applying it
const tokenResponse = await client.runtime.getGameToken('game-123')
// Get token and apply it to current client
const tokenResponse = await client.runtime.getGameToken('game-123', { apply: true })getListenerCounts()
getListenerCounts: () => Record<string, number>;Gets the count of active listeners for debugging purposes.
Returns
Record<string, number>
Object with listener counts by event type
Example
const counts = client.runtime.getListenerCounts()
console.log(`Active listeners:`, counts)onForceExit()
onForceExit: (handler) => void;Listens for force exit events from the parent. Called when the game must terminate immediately (emergency exit).
Parameters
handler
() => void
Function to call when game must force exit
Returns
void
Example
client.runtime.onForceExit(() => {
game.emergencyCleanup()
// Game should exit immediately after cleanup
})onInit()
onInit: (handler) => void;Listens for game initialization events from the parent. Called when the parent sends initial configuration and auth context.
Parameters
handler
(context) => void
Function to call when initialization occurs
Returns
void
Example
client.runtime.onInit((context) => {
console.log(`Game ${context.gameId} initialized`)
console.log(`API base URL: ${context.baseUrl}`)
})onOverlay()
onOverlay: (handler) => void;Listens for overlay visibility events from the parent. Called when UI overlays are shown/hidden over the game.
Parameters
handler
(isVisible) => void
Function to call when overlay state changes
Returns
void
Example
client.runtime.onOverlay((isVisible) => {
if (isVisible) {
game.showOverlayMode()
} else {
game.hideOverlayMode()
}
})onPause()
onPause: (handler) => void;Listens for pause events from the parent. Called when the game should pause execution (e.g., user switches tabs).
Parameters
handler
() => void
Function to call when game should pause
Returns
void
Example
client.runtime.onPause(() => {
game.pause()
audioManager.pauseAll()
})onResume()
onResume: (handler) => void;Listens for resume events from the parent. Called when the game should resume execution after being paused.
Parameters
handler
() => void
Function to call when game should resume
Returns
void
Example
client.runtime.onResume(() => {
game.resume()
audioManager.resumeAll()
})onTokenRefresh()
onTokenRefresh: (handler) => void;Listens for token refresh events from the parent. Called when the parent updates the authentication token.
Parameters
handler
(data) => void
Function to call when token is refreshed
Returns
void
Example
client.runtime.onTokenRefresh(({ token, exp }) => {
console.log(`Token refreshed, expires at: ${new Date(exp)}`)
// Token is automatically applied to the client
})ready()
ready: () => void;Signals that the game has finished loading and is ready to receive messages. Should be called once after game initialization is complete.
Returns
void
Example
// After game has loaded
await client.runtime.ready()removeAllListeners()
removeAllListeners: () => void;Removes all runtime event listeners. Use this for cleanup when the game is shutting down.
Returns
void
Example
// Clean up all runtime listeners before exit
client.runtime.removeAllListeners()
await client.runtime.exit()removeListener()
removeListener: (eventType, handler) => void;Removes a specific event listener. Use this to stop listening for specific events.
Parameters
eventType
The message event type to stop listening for
handler
RuntimeEventHandler
The exact handler function that was registered
Returns
void
Example
const pauseHandler = () => game.pause()
client.runtime.onPause(pauseHandler)
// Later, remove the specific handler
client.runtime.removeListener(MessageEvents.PAUSE, pauseHandler)sendTelemetry()
sendTelemetry: (data) => void;Sends performance telemetry data to the parent. Used for monitoring game performance and analytics.
Parameters
data
Performance metrics data
fps
number
Current frames per second
mem
number
Current memory usage in MB
Returns
void
Example
// Send current performance metrics
client.runtime.sendTelemetry({
fps: game.getCurrentFPS(),
mem: performance.memory ? performance.memory.usedJSHeapSize / 1024 / 1024 : 0
})scores
scores: {
submit: (gameId, score, metadata?) => Promise<ScoreSubmission>;
};Defined in: clients/public.ts:64
Game score submission and leaderboards.
submit(gameId, score, metadata?)- Record a game score
submit()
submit: (gameId, score, metadata?) => Promise<ScoreSubmission>;Submits a score for a specific game. Note: This still requires a gameId as scores must be associated with a game.
Parameters
gameId
string
The game ID to submit the score for
score
number
The score value to submit
metadata?
Record<string, unknown>
Optional metadata about the score
Returns
Promise<ScoreSubmission>
Promise resolving to the created score record
Example
const scoreRecord = await client.scores.submit('game-123', 1250, {
level: 5,
difficulty: 'hard'
})
console.log('Score submitted:', scoreRecord.id)timeback
timeback: {
endActivity: (data) => Promise<EndActivityResponse>;
pauseActivity: () => void;
resumeActivity: () => void;
startActivity: (metadata) => void;
get user(): TimebackUser;
};Defined in: clients/public.ts:51
TimeBack integration for activity tracking and user context.
User context (cached from init, refreshable):
user.role- User's role (student, parent, teacher, etc.)user.enrollments- Courses the player is enrolled in for this gameuser.organizations- Schools/districts the player belongs touser.fetch()- Refresh user context from server
Activity tracking:
startActivity(metadata)- Begin tracking an activitypauseActivity()/resumeActivity()- Pause/resume timerendActivity(scoreData)- Submit activity results to TimeBack
endActivity()
endActivity: (data) => Promise<EndActivityResponse>;End the current activity and submit results to TimeBack. Calculates duration from startActivity, computes XP based on score and time, and submits both ActivityEvent and TimeSpent events.
Parameters
data
EndActivityScoreData
Score data and optional XP override
Returns
Promise<EndActivityResponse>
Promise resolving to end activity response
Throws
Error if startActivity was not called first
Example
// Auto-calculate XP
await client.timeback.endActivity({
correctQuestions: 8,
totalQuestions: 10
})
// Report mastery only
await client.timeback.endActivity({
correctQuestions: 8,
totalQuestions: 10,
masteredUnits: 1
})
// Override XP
await client.timeback.endActivity({
correctQuestions: 8,
totalQuestions: 10,
xpAwarded: 15
})pauseActivity()
pauseActivity: () => void;Pause the current activity timer. Paused time is not counted toward the activity duration. Must be called after startActivity and before endActivity.
Returns
void
Throws
Error if no activity is in progress or if already paused
Example
client.timeback.startActivity({ activityId: 'math-quiz-1' })
// ... student starts quiz ...
// Student needs a break
client.timeback.pauseActivity()
// ... student returns ...
client.timeback.resumeActivity()resumeActivity()
resumeActivity: () => void;Resume the current activity timer after a pause. Must be called after pauseActivity.
Returns
void
Throws
Error if no activity is in progress or if not currently paused
Example
client.timeback.startActivity({ activityId: 'math-quiz-1' })
client.timeback.pauseActivity()
// ... break time ...
client.timeback.resumeActivity()
// ... student continues quiz ...startActivity()
startActivity: (metadata) => void;Start tracking an activity. Stores activity metadata and start time internally. Must be called before endActivity.
Parameters
metadata
ActivityData
Activity metadata (only activityId required)
Returns
void
Example
// Minimal - most common
client.timeback.startActivity({
activityId: 'level-1-quiz'
})
// Auto-derives: activityName "Level 1 Quiz"
// With custom name override
client.timeback.startActivity({
activityId: 'level-1-quiz',
activityName: 'Advanced Arithmetic Challenge'
})user
Get Signature
get user(): TimebackUser;TimeBack user context with role, enrollments, and organizations.
Access cached data via properties, or call fetch() for fresh data.
Example
// Access cached data (from init)
const role = client.timeback.user.role
const enrollments = client.timeback.user.enrollments
// Fetch fresh data from server (cached for 5 min)
const fresh = await client.timeback.user.fetch()
// Force refresh bypassing cache
const forced = await client.timeback.user.fetch({ force: true })Returns
TimebackUser
users
users: {
inventory: {
add: (identifier, qty) => Promise<InventoryMutationResponse>;
get: () => Promise<InventoryItemWithItem[]>;
has: (identifier, minQuantity) => Promise<boolean>;
quantity: (identifier) => Promise<number>;
remove: (identifier, qty) => Promise<InventoryMutationResponse>;
};
me: () => Promise<AuthenticatedUser>;
};Defined in: clients/base.ts:380
Current user data and inventory management.
me()- Get authenticated user profileinventory.get()- List user's itemsinventory.add(slug, qty)- Award items to user
inventory
inventory: {
add: (identifier, qty) => Promise<InventoryMutationResponse>;
get: () => Promise<InventoryItemWithItem[]>;
has: (identifier, minQuantity) => Promise<boolean>;
quantity: (identifier) => Promise<number>;
remove: (identifier, qty) => Promise<InventoryMutationResponse>;
};Inventory management methods for the current user.
inventory.add()
add: (identifier, qty) => Promise<InventoryMutationResponse>;Adds items to the user's inventory. Accepts either an item UUID or slug. Emits an 'inventoryChange' event when successful.
Parameters
identifier
string
The item UUID or slug
qty
number
The quantity to add (must be positive)
Returns
Promise<InventoryMutationResponse>
Promise resolving to mutation response with new total
Example
// Using slug
const result = await client.users.inventory.add('gold-coin', 100)
// Using UUID
const result = await client.users.inventory.add('550e8400-e29b-41d4-a716-446655440000', 100)
console.log('New total:', result.newTotal)inventory.get()
get: () => Promise<InventoryItemWithItem[]>;Retrieves the user's complete inventory.
Returns
Promise<InventoryItemWithItem[]>
Promise resolving to array of inventory items with item details
Example
const inventory = await client.users.inventory.get()
inventory.forEach(item => {
console.log(`${item.item.name}: ${item.quantity}`)
})inventory.has()
has: (identifier, minQuantity) => Promise<boolean>;Checks if the user has at least the specified quantity of an item. Accepts either an item UUID or slug.
Parameters
identifier
string
The item UUID or slug
minQuantity
number = 1
Minimum quantity required (defaults to 1)
Returns
Promise<boolean>
Promise resolving to true if user has enough of the item
Example
const hasKey = await client.users.inventory.has('gold-coin')
const hasEnoughGold = await client.users.inventory.has('gold-coin', 100)
const hasPotion = await client.users.inventory.has('uuid-123-456', 5)
if (hasKey && hasEnoughGold) {
console.log('Can enter premium dungeon!')
}inventory.quantity()
quantity: (identifier) => Promise<number>;Gets the current quantity of an item. Accepts either an item UUID or slug.
Parameters
identifier
string
The item UUID or slug
Returns
Promise<number>
Promise resolving to the current quantity (0 if not owned)
Example
const qty = await client.users.inventory.quantity('health-potion')
const qty2 = await client.users.inventory.quantity('uuid-123-456')
console.log('Health potions:', qty)inventory.remove()
remove: (identifier, qty) => Promise<InventoryMutationResponse>;Removes items from the user's inventory. Accepts either an item UUID or slug. Emits an 'inventoryChange' event when successful.
Parameters
identifier
string
The item UUID or slug
qty
number
The quantity to remove (must be positive)
Returns
Promise<InventoryMutationResponse>
Promise resolving to mutation response with new total
Example
// Using slug
const result = await client.users.inventory.remove('HEALTH_POTION', 1)
// Using UUID
const result = await client.users.inventory.remove('uuid-456-789', 1)
console.log('Remaining:', result.newTotal)me()
me: () => Promise<AuthenticatedUser>;Retrieves the current user's profile information with authentication context.
Returns
Promise<AuthenticatedUser>
Promise resolving to user profile data including auth provider info
Example
const user = await client.users.me()
console.log('Username:', user.username)
console.log('Email:', user.email)
console.log('Has Timeback Account:', user.hasTimebackAccount)Inherited from
PlaycademyBaseClient.usersidentity
static identity: {
parseOAuthState: (state) => {
csrfToken: string;
data?: Record<string, string>;
};
};Defined in: clients/public.ts:90
Static identity utilities for OAuth operations
parseOAuthState()
parseOAuthState: (state) => {
csrfToken: string;
data?: Record<string, string>;
};Parses an OAuth state parameter to extract CSRF token and custom data. Use this in your server callback to retrieve the data encoded in the state.
Parses an OAuth state parameter to extract CSRF token and any encoded data.
Parameters
state
string
The OAuth state parameter to parse
Returns
{
csrfToken: string;
data?: Record<string, string>;
}Object containing CSRF token and optional decoded data
csrfToken
csrfToken: string;data?
optional data: Record<string, string>;Param
The OAuth state parameter from the callback
Returns
Object containing the CSRF token and optional custom data
Example
// In your server callback endpoint
import { PlaycademyClient } from '@playcademy/sdk'
app.get('/api/auth/callback', async (req, res) => {
const { csrfToken, data } = PlaycademyClient.identity.parseOAuthState(req.query.state)
// Validate CSRF token
if (!isValidCsrf(csrfToken)) {
return res.status(403).send('Invalid state')
}
// Access Playcademy user ID if available
const playcademyUserId = data?.playcademy_user_id
const gameId = data?.game_id
// Exchange code for tokens...
// Link accounts...
})init()
static init: <T>(this, options?) => Promise<T>;Defined in: clients/public.ts:84
Auto-initializes a PlaycademyClient with context from the environment
Auto-initializes a PlaycademyClient with context from the environment. Works in both iframe mode (production/development) and standalone mode (local dev).
This is the recommended way to initialize the SDK as it automatically:
- Detects the runtime environment (iframe vs standalone)
- Configures the client with the appropriate context
- Sets up event listeners for token refresh
- Exposes the client for debugging in development mode
Type Parameters
T
T extends PlaycademyBaseClient = PlaycademyBaseClient
Parameters
this
(config?) => T
options?
Optional configuration overrides
allowedParentOrigins?
string[]
baseUrl?
string
Override the base URL for API requests
enableConnectionMonitoring?
boolean
onDisconnect?
Returns
Promise<T>
Promise resolving to a fully initialized PlaycademyClient
Throws
Error if not running in a browser context
Example
// Default initialization
const client = await PlaycademyClient.init()
// With custom base URL
const client = await PlaycademyClient.init({ baseUrl: 'https://custom.api.com' })login()
static login: (baseUrl, email, password) => Promise<LoginResponse>;Defined in: clients/public.ts:87
Authenticates a user with email and password
Authenticates a user with email and password.
This is a standalone authentication method that doesn't require an initialized client. Use this for login flows before creating a client instance.
Parameters
baseUrl
string
The base URL of the Playcademy API
string
User's email address
password
string
User's password
Returns
Promise<LoginResponse>
Promise resolving to authentication response with token
Deprecated
Use client.auth.login() instead for better error handling and automatic token management
Throws
PlaycademyError if authentication fails or network error occurs
Example
// Preferred approach:
const client = new PlaycademyClient({ baseUrl: '/api' })
const result = await client.auth.login({
email: 'user@example.com',
password: 'password'
})
// Legacy approach (still works):
try {
const response = await PlaycademyClient.login('/api', 'user@example.com', 'password')
const client = new PlaycademyClient({ token: response.token })
} catch (error) {
console.error('Login failed:', error.message)
}Methods
_ensureGameId()
protected _ensureGameId(): string;Defined in: clients/base.ts:295
Ensures a gameId is available, throwing an error if not.
Returns
string
Inherited from
PlaycademyBaseClient._ensureGameId_setAuthContext()
_setAuthContext(context): void;Defined in: clients/base.ts:189
Internal
Sets the authentication context for the client.
Parameters
context
isInIframe
boolean
Returns
void
Inherited from
PlaycademyBaseClient._setAuthContextcheckConnection()
checkConnection(): Promise<ConnectionState | "unknown">;Defined in: clients/base.ts:177
Manually triggers a connection check immediately.
Returns
Promise<ConnectionState | "unknown">
Inherited from
PlaycademyBaseClient.checkConnectionemit()
protected emit<E>(event, payload): void;Defined in: clients/base.ts:208
Emits an event to all registered listeners.
Type Parameters
E
E extends keyof ClientEvents
Parameters
event
E
payload
ClientEvents[E]
Returns
void
Inherited from
PlaycademyBaseClient.emitgetBaseUrl()
getBaseUrl(): string;Defined in: clients/base.ts:81
Gets the effective base URL for API requests.
Returns
string
Inherited from
PlaycademyBaseClient.getBaseUrlgetConnectionState()
getConnectionState(): ConnectionState | "unknown";Defined in: clients/base.ts:170
Gets the current connection state.
Returns
ConnectionState | "unknown"
Inherited from
PlaycademyBaseClient.getConnectionStategetGameBackendUrl()
protected getGameBackendUrl(): string;Defined in: clients/base.ts:93
Gets the effective game backend URL for integration requests.
Returns
string
Inherited from
PlaycademyBaseClient.getGameBackendUrlgetToken()
getToken(): null | string;Defined in: clients/base.ts:138
Gets the current authentication token.
Returns
null | string
Inherited from
PlaycademyBaseClient.getTokengetTokenType()
getTokenType(): TokenType;Defined in: clients/base.ts:131
Gets the current token type.
Returns
TokenType
Inherited from
PlaycademyBaseClient.getTokenTypeisAuthenticated()
isAuthenticated(): boolean;Defined in: clients/base.ts:145
Checks if the client has a valid API token.
Returns
boolean
Inherited from
PlaycademyBaseClient.isAuthenticatedon()
on<E>(event, callback): void;Defined in: clients/base.ts:200
Registers an event listener for client events.
Type Parameters
E
E extends keyof ClientEvents
Parameters
event
E
callback
(payload) => void
Returns
void
Inherited from
PlaycademyBaseClient.ononAuthChange()
onAuthChange(callback): void;Defined in: clients/base.ts:152
Registers a callback to be called when authentication state changes.
Parameters
callback
(token) => void
Returns
void
Inherited from
PlaycademyBaseClient.onAuthChangeonDisconnect()
onDisconnect(callback): () => void;Defined in: clients/base.ts:159
Registers a callback to be called when connection issues are detected.
Parameters
callback
(context) => void | Promise<void>
Returns
(): void;Returns
void
Inherited from
PlaycademyBaseClient.onDisconnectping()
ping(): string;Defined in: clients/base.ts:112
Simple ping method for testing connectivity.
Returns
string
Inherited from
PlaycademyBaseClient.pingrequest()
protected request<T>(
path,
method,
options?): Promise<T>;Defined in: clients/base.ts:221
Makes an authenticated HTTP request to the platform API.
Type Parameters
T
T
Parameters
path
string
method
Method
options?
body?
unknown
headers?
Record<string, string>
raw?
boolean
Returns
Promise<T>
Inherited from
PlaycademyBaseClient.requestrequestGameBackend()
protected requestGameBackend<T>(
path,
method,
body?,
headers?,
raw?): Promise<T>;Defined in: clients/base.ts:257
Makes an authenticated HTTP request to the game's backend Worker.
Type Parameters
T
T
Parameters
path
string
method
Method
body?
unknown
headers?
Record<string, string>
raw?
boolean
Returns
Promise<T>
Inherited from
PlaycademyBaseClient.requestGameBackendsetToken()
setToken(token, tokenType?): void;Defined in: clients/base.ts:123
Sets the authentication token for API requests.
Parameters
token
null | string
tokenType?
TokenType
Returns
void
Inherited from
PlaycademyBaseClient.setTokenPlaycademyError
Defined in: core/errors.ts:4
Base error class for Cademy SDK specific errors.
Extends
Error
Constructors
Constructor
new PlaycademyError(message): PlaycademyError;Defined in: core/errors.ts:5
Parameters
message
string
Returns
Overrides
Error.constructorInterfaces
ApiErrorInfo
Defined in: core/errors.ts:258
Extracted error information for display purposes.
Properties
code
code: string;Defined in: core/errors.ts:262
API error code
details?
optional details: unknown;Defined in: core/errors.ts:266
Additional error context
message
message: string;Defined in: core/errors.ts:264
Human-readable error message
status
status: number;Defined in: core/errors.ts:260
HTTP status code
ConnectionMonitorConfig
Defined in: core/connection/types.ts:21
Configuration options for ConnectionMonitor.
See
ConnectionMonitor for usage
Properties
baseUrl
baseUrl: string;Defined in: core/connection/types.ts:23
Base URL for heartbeat pings (e.g., 'https://api.playcademy.com')
enableHeartbeat?
optional enableHeartbeat: boolean;Defined in: core/connection/types.ts:31
Enable periodic heartbeat monitoring (default: true)
enableOfflineEvents?
optional enableOfflineEvents: boolean;Defined in: core/connection/types.ts:33
Enable browser online/offline event listeners (default: true)
failureThreshold?
optional failureThreshold: number;Defined in: core/connection/types.ts:29
Number of consecutive failures before triggering disconnect (default: 2)
heartbeatInterval?
optional heartbeatInterval: number;Defined in: core/connection/types.ts:25
How often to send heartbeat pings in milliseconds (default: 10000)
heartbeatTimeout?
optional heartbeatTimeout: number;Defined in: core/connection/types.ts:27
How long to wait for heartbeat response in milliseconds (default: 5000)
ConnectionStatePayload
Defined in: types/events.ts:96
Connection state payload. Broadcast from platform to games when connection changes.
Properties
reason
reason: string;Defined in: types/events.ts:98
state
state: "online" | "offline" | "degraded";Defined in: types/events.ts:97
DevUploadHooks
Defined in: namespaces/platform/dev.types.ts:58
Properties
onClose()?
optional onClose: () => void;Defined in: namespaces/platform/dev.types.ts:60
Returns
void
onEvent()?
optional onEvent: (e) => void;Defined in: namespaces/platform/dev.types.ts:59
Parameters
e
Returns
void
DisconnectContext
Defined in: types/client.ts:29
Context provided to disconnect handlers
Properties
displayAlert()
displayAlert: (message, options?) => void;Defined in: types/client.ts:37
Utility to display a platform-level alert
Parameters
message
string
options?
duration?
number
type?
"error" | "info" | "warning"
Returns
void
reason
reason: string;Defined in: types/client.ts:33
Reason for the disconnect
state
state: "offline" | "degraded";Defined in: types/client.ts:31
Current connection state
timestamp
timestamp: number;Defined in: types/client.ts:35
Timestamp when disconnect was detected
DisplayAlertPayload
Defined in: types/events.ts:7
Properties
message
message: string;Defined in: types/events.ts:8
options?
optional options: {
duration?: number;
type?: "error" | "info" | "warning";
};Defined in: types/events.ts:9
duration?
optional duration: number;type?
optional type: "error" | "info" | "warning";ErrorResponseBody
Defined in: core/errors.ts:55
Structure of error response bodies returned by API endpoints.
Example
{
"error": {
"code": "NOT_FOUND",
"message": "Item not found",
"details": { "identifier": "abc123" }
}
}Properties
error?
optional error: {
code?: string;
details?: unknown;
message?: string;
};Defined in: core/errors.ts:56
code?
optional code: string;details?
optional details: unknown;message?
optional message: string;Type Aliases
ApiErrorCode
type ApiErrorCode =
| "BAD_REQUEST"
| "UNAUTHORIZED"
| "FORBIDDEN"
| "ACCESS_DENIED"
| "NOT_FOUND"
| "METHOD_NOT_ALLOWED"
| "CONFLICT"
| "ALREADY_EXISTS"
| "GONE"
| "PRECONDITION_FAILED"
| "PAYLOAD_TOO_LARGE"
| "VALIDATION_FAILED"
| "TOO_MANY_REQUESTS"
| "RATE_LIMITED"
| "EXPIRED"
| "INTERNAL"
| "INTERNAL_ERROR"
| "NOT_IMPLEMENTED"
| "SERVICE_UNAVAILABLE"
| "TIMEOUT"
| string;Defined in: core/errors.ts:15
Error codes returned by the API. These map to specific error types and HTTP status codes.
ConnectionState
type ConnectionState = "online" | "offline" | "degraded";Defined in: core/connection/types.ts:14
Possible connection states.
- online: Connection is stable and healthy
- offline: Complete loss of network connectivity
- degraded: Connection is slow or experiencing intermittent issues
DevUploadEvent
type DevUploadEvent =
| {
type: "init";
}
| {
loaded: number;
percent: number;
total: number;
type: "s3Progress";
}
| {
type: "finalizeStart";
}
| {
currentFileLabel?: string;
percent: number;
type: "finalizeProgress";
}
| {
message: string;
type: "finalizeStatus";
}
| {
type: "complete";
}
| {
type: "close";
};Defined in: namespaces/platform/dev.types.ts:49
DisconnectHandler()
type DisconnectHandler = (context) => void | Promise<void>;Defined in: types/client.ts:24
Handler called when connection state changes to offline or degraded. Games can implement this to handle disconnects gracefully.
Parameters
context
Returns
void | Promise<void>
Variables
messaging
const messaging: PlaycademyMessaging;Defined in: messaging.ts:809
Playcademy Messaging Singleton
This is the main messaging instance used throughout the Playcademy platform. It's exported as a singleton to ensure consistent communication across all parts of the application.
Why a Singleton?:
- Ensures all parts of the app use the same messaging instance
- Prevents conflicts between multiple messaging systems
- Simplifies the API - no need to pass instances around
- Maintains consistent event listener management
Usage in Different Contexts:
In Games:
import { messaging, MessageEvents } from '@playcademy/sdk'
// Tell parent we're ready
messaging.send(MessageEvents.READY, undefined)
// Listen for pause/resume
messaging.listen(MessageEvents.PAUSE, () => game.pause())
messaging.listen(MessageEvents.RESUME, () => game.resume())In Parent Shell:
import { messaging, MessageEvents } from '@playcademy/sdk'
// Send initialization data to game
messaging.send(MessageEvents.INIT, { baseUrl, token, gameId })
// Listen for game events
messaging.listen(MessageEvents.EXIT, () => closeGame())
messaging.listen(MessageEvents.READY, () => showGame())Automatic Transport Selection: The messaging system automatically chooses the right transport method:
- Uses postMessage when game is in iframe sending to parent
- Uses CustomEvent for local development and parent-to-game communication
Type Safety: All message sending and receiving is fully type-safe with TypeScript.
Functions
extractApiErrorInfo()
function extractApiErrorInfo(error): null | ApiErrorInfo;Defined in: core/errors.ts:285
Extract useful error information from an API error. Useful for displaying errors to users in a friendly way.
Parameters
error
unknown
Returns
null | ApiErrorInfo
Example
try {
await client.shop.purchase(itemId)
} catch (error) {
const info = extractApiErrorInfo(error)
if (info) {
showToast(`Error: ${info.message}`)
}
}