Server
Server-side SDK for backend routes and API endpoints
Overview
The server SDK (@playcademy/sdk/server) provides server-side APIs for integrating Playcademy into your own backend infrastructure (Express, Next.js, custom Node.js servers, etc.).
Not for Playcademy Custom Routes
If you're using Playcademy's custom routes, these features are already integrated.
This SDK is for developers building their own external backends.
PlaycademyClient
Initialization
Initialize the client with your API key:
import { PlaycademyClient } from '@playcademy/sdk/server'
const client = await PlaycademyClient.init({
apiKey: process.env.PLAYCADEMY_API_KEY,
gameId: 'my-project-id',
})Configuration:
| Option | Type | Description |
|---|---|---|
apiKey | string | Playcademy API key (required) |
gameId | string | Project identifier (required) |
config | object | Config object (optional, skips file loading if provided) |
configPath | string | Path to playcademy.config.js (optional, auto-discovered) |
baseUrl | string | API base URL (defaults to production) |
Config Loading
playcademy.config.js is auto-discovered if config or configPath are not provided
For environments without filesystem access, pass the config directly:
import { PlaycademyClient } from '@playcademy/sdk/server'
import type { PlaycademyConfig } from '@playcademy/sdk/server'
const config: PlaycademyConfig = {
name: 'My Project',
integrations: {
timeback: {
course: {
subjects: ['Math'],
grades: [3, 4, 5],
title: 'Elementary Math',
},
},
},
}
const client = await PlaycademyClient.init({
apiKey: process.env.PLAYCADEMY_API_KEY!,
gameId: 'my-project',
config,
})Client Structure
The server client provides a focused API for backend operations:
const client = await PlaycademyClient.init({ ... })
// Namespaces
client.timeback.* // Timeback integration methods
client.gameId // Project identifier property
client.config // Loaded configuration objectCurrent Namespaces:
| Namespace | Purpose |
|---|---|
timeback | End learning activities and submit to Timeback |
More Coming Soon
Additional namespaces for project management, user operations, and platform integrations will be added in future releases.
Timeback Methods
client.timeback.endActivity
End a learning activity and submit results to Timeback:
// Minimal example
await client.timeback.endActivity('student-123', {
activityData: {
activityId: 'math-quiz-1',
},
scoreData: {
correctQuestions: 8,
totalQuestions: 10,
},
timingData: {
durationSeconds: 300, // 5 minutes
},
})
// With optional overrides
await client.timeback.endActivity('student-123', {
activityData: {
activityId: 'math-quiz-1',
activityName: 'Basic Arithmetic Quiz', // Optional
},
scoreData: {
correctQuestions: 8,
totalQuestions: 10,
},
timingData: {
durationSeconds: 300,
},
xpEarned: 15, // Optional: override automatic XP calculation
})Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
studentId | string | Yes | Student identifier (Timeback ID) |
payload.activityData.activityId | string | Yes | Unique activity identifier |
payload.activityData.activityName | string | No | Human-readable name (auto-derived from ID if empty) |
payload.scoreData.correctQuestions | number | Yes | Number of correct answers |
payload.scoreData.totalQuestions | number | Yes | Total number of questions |
payload.timingData.durationSeconds | number | Yes | Activity duration in seconds |
payload.xpEarned | number | No | XP override (bypasses automatic calculation) |
Auto-filled metadata:
activityName: Prettified fromactivityId("math-quiz-1" → "Math Quiz 1") if not providedsubject,appName,courseName,sensorUrl: From your app config
client.timeback.courseId
Access the Timeback course ID:
const courseId = client.timeback.courseId // string | undefinedWhen Is This Set?
The course ID is automatically fetched from the platform API the first time you call endActivity().
Before that, this property is undefined.
Client Properties
client.gameId
const gameId = client.gameIdclient.config
const config = client.config
console.log(config.name)
console.log(config.integrations?.timeback)verifyGameToken
Verify Playcademy tokens to authenticate users.
import { verifyGameToken } from '@playcademy/sdk/server'
// Extract token from Authorization header
const token = request.headers.get('Authorization')?.split(' ')[1]
const { user, gameId, claims } = await verifyGameToken(token)
// User is authenticated
return new Response(JSON.stringify({ userId: user.sub }), {
headers: { 'Content-Type': 'application/json' },
})Parameters:
| Parameter | Type | Description |
|---|---|---|
token | string | Token to verify |
options.baseUrl | string | API base URL (auto-detected from env) |
Returns:
{
claims: Record<string, unknown> // JWT claims
gameId: string // Game ID
user: {
sub: string // User ID
email: string // Email address
name: string // Display name
email_verified: boolean // Email verification status
given_name?: string // First name (optional)
family_name?: string // Last name (optional)
timeback_id?: string // Timeback student ID (optional)
[key: string]: unknown // Additional attributes
}
}Environment Variables:
The function checks these in order:
options.baseUrl(if provided)PLAYCADEMY_BASE_URLPUBLIC_PLAYCADEMY_BASE_URLNEXT_PUBLIC_PLAYCADEMY_BASE_URL
Usage Examples
Express.js
import express from 'express'
import { verifyGameToken } from '@playcademy/sdk/server'
const app = express()
app.get('/api/user', async (req, res) => {
const token = req.headers.authorization?.split(' ')[1]
const { user } = await verifyGameToken(token)
res.json({ message: `Hello, ${user.email}!` })
})Next.js API Route
import { NextRequest, NextResponse } from 'next/server'
import { verifyGameToken } from '@playcademy/sdk/server'
export async function GET(request: NextRequest) {
const token = request.headers.get('Authorization')?.split(' ')[1]
const { user } = await verifyGameToken(token)
return NextResponse.json({ message: `Hello, ${user.email}!` })
}Timeback Integration
import { PlaycademyClient, verifyGameToken } from '@playcademy/sdk/server'
// Initialize client once
const client = await PlaycademyClient.init({
apiKey: process.env.PLAYCADEMY_API_KEY!,
gameId: 'my-game',
})
// In your route handler
const token = request.headers.get('Authorization')?.split(' ')[1]
const { user } = await verifyGameToken(token)
// Parse request body
const body = await request.json()
const { activityData, scoreData, timingData, xpEarned } = body
// End activity and submit to Timeback
await client.timeback.endActivity(user.timeback_id, {
activityData,
scoreData,
timingData,
xpEarned,
})
return new Response(JSON.stringify({ success: true }), {
headers: { 'Content-Type': 'application/json' },
})Environment Setup
Set your API key in .env:
PLAYCADEMY_API_KEY=your-api-key-here
PLAYCADEMY_BASE_URL=https://hub.playcademy.netGetting Your API Key
Your API key is displayed once after running playcademy login
See CLI Authentication for details
