Starter Templates
| Template | Framework | Description |
|---|---|---|
nextjs-app-router | Next.js | App Router with SSO and activity tracking |
nuxt | Nuxt 3 | Server middleware with Vue composables |
svelte-kit | SvelteKit | Hooks-based integration with stores |
solid-start | SolidStart | Middleware with primitives |
tanstack-start | TanStack Start | File-based routes with React |
express | Express | Standalone API server |
express-auth0 | Express + Auth0 | Custom auth mode with Auth0 |
express-supabase | Express + Supabase | Custom auth mode with Supabase |
bun-react | Bun + React | Minimal SSO example |
bun-react-identity-only | Bun + React | Identity-only mode (no activity tracking) |
bun-react
Source: examples/bun-react
/api/timeback/*) plus a simple UI.
For an example that also demonstrates the direct API escape hatch (timeback.api),
see examples/bun-react-full.
Quick Start
Structure
| File | Purpose |
|---|---|
src/index.ts | Bun server + Timeback routes |
src/lib/timeback.ts | SSO config + session management |
src/App.tsx | SignInButton + activity demo |
src/frontend.tsx | React entry point |
Key Patterns
Native handler — Mount Timeback routes:bun-react-full
Source: examples/bun-react-full
timeback.api
(an escape hatch for calling Timeback services like OneRoster).
Quick Start
Structure
| File | Purpose |
|---|---|
src/index.ts | Bun server + Timeback routes |
src/lib/timeback.ts | SSO config + session management |
src/App.tsx | SignInButton + activity demo |
src/frontend.tsx | React entry point |
Key Patterns
Native handler — Mount Timeback routes:bun-react-gradeless
Source: examples/bun-react-gradeless
courseCode instead of (subject, grade) for course identity.
Quick Start
Grade-less Course Configuration
Apps without grade levels usecourseCode as the course identifier:
Activity Tracking with courseCode
When evaluating examples, it’s often useful to preview what would be sent without actually sending Caliper events. This example uses:- normal
activity.end(...)(production-like) - a server-side hook (
beforeActivitySend) to optionally skip sending and log what would be sent
Demo toggle
In.env:
examples/bun-react):
Structure
| File | Purpose |
|---|---|
src/index.ts | Bun server + Timeback routes |
src/lib/timeback.ts | SSO config + session management |
src/App.tsx | SignInButton + activity demo |
timeback.config.json | Grade-less course configuration |
bun-react-identity-only
Source: examples/bun-react-identity-only
createTimebackIdentity() for apps that only need Timeback SSO without the full SDK.
Quick Start
Structure
| File | Purpose |
|---|---|
src/index.ts | Bun server + Timeback routes |
src/lib/timeback.ts | SSO config + session management |
src/App.tsx | SignInButton + session display |
src/frontend.tsx | React entry point |
Key Differences from Full SDK
- Uses
createTimebackIdentity()instead ofcreateTimeback() - No
apicredentials required - No
timeback.config.jsonrequired - Only identity routes are available (no activity tracking)
Key Patterns
Identity-only server — No API credentials needed:bun-react-resumable
Source: examples/bun-react-resumable
/api/timeback/*) plus a simple UI.
For an example that also demonstrates the direct API escape hatch (timeback.api),
see examples/bun-react-full.
Quick Start
Structure
| File | Purpose |
|---|---|
src/index.ts | Bun server + Timeback routes |
src/lib/timeback.ts | SSO config + session management |
src/App.tsx | SignInButton + activity demo |
src/frontend.tsx | React entry point |
Key Patterns
Native handler — Mount Timeback routes:express
Source: examples/express
Quick Start
Verify Routes
Run the verification script to test all routes:Routes
| Method | Path | Description |
|---|---|---|
| GET | /health | Health check |
| GET | /api/timeback/identity/signin | Initiate SSO sign-in |
| GET | /api/timeback/identity/callback | SSO callback handler |
| GET | /api/timeback/identity/signout | Sign out |
| POST | /api/timeback/activity/heartbeat | Submit time heartbeat |
| POST | /api/timeback/activity/submit | Submit completion |
Testing
Integration Options
The SDK provides two ways to integrate with Express:Option 1: Middleware (recommended)
Option 2: Router mounting
express-auth0
Source: examples/express-auth0
auth0 Node.js SDK.
Architecture
Auth0 Setup
-
Create an Auth0 Application
- Go to Auth0 Dashboard → Applications → Create Application
- Choose “Regular Web Application”
- Note your Domain, Client ID, and Client Secret
-
Enable Password Grant
- In your application settings, go to Advanced Settings → Grant Types
- Enable Password grant type
-
Create a Database Connection (if you don’t have one)
- Go to Authentication → Database → Create Connection
- Enable it for your application
-
Create Test Users
- Go to User Management → Users → Create User
- Create users with email/password
Quick Start
Environment Variables
Create.env from .env.example:
How It Works
1. User Login
User submits email/password → Backend calls Auth0’s password grant → Auth0 validates → Returns user info → Backend creates signed JWT session cookie.2. Session Management
The session cookie contains{ sub, email, name } signed with HS256. It’s httpOnly, secure in production, and lasts 7 days.
3. Timeback Integration
getEmail(req) on each request. We parse the cookie header, verify the JWT, and return the user’s email. The SDK then resolves the Timeback user by email.
4. Frontend Activity Tracking
Project Structure
API Routes
| Method | Route | Description |
|---|---|---|
| GET | /health | Health check |
| POST | /auth/login | Login with email/password |
| POST | /auth/logout | Clear session cookie |
| GET | /api/session | Get current user |
| GET | /api/timeback/user/me | Timeback user profile |
| POST | /api/timeback/activity/heartbeat | Submit time heartbeat |
| POST | /api/timeback/activity/submit | Submit activity completion |
Troubleshooting
”Invalid email or password”
- Verify the user exists in Auth0 (User Management → Users)
- Check that the Password grant type is enabled
- Ensure the database connection is enabled for your app
”Auth0 is not configured”
- Check
AUTH0_DOMAIN,AUTH0_CLIENT_ID,AUTH0_CLIENT_SECRETin.env - Domain should be just
your-tenant.auth0.com(nohttps://)
Session not persisting
- Check browser dev tools → Application → Cookies
- In development, cookies work on
localhost - The Vite proxy ensures same-origin for cookie handling
CORS errors
- The Vite dev server proxies
/authand/apito the backend - No CORS configuration needed in development
express-auth0-launch-gate
Activity tracking demo using Auth0 Universal Login (SPA + PKCE) in the frontend and Bearer-token auth in the backend, with a launch-gated entry URL (/timeback/signin).
Architecture
How It Works
- Auth0 handles authentication - Frontend uses Auth0 Universal Login (Authorization Code + PKCE)
- Frontend calls backend with Bearer token - API requests include
Authorization: Bearer {'<'}access_token{'>'} - Backend validates token - using Auth0 JWKS + issuer + audience
- SDK uses custom identity mode - The
getEmailcallback reads email from the request - SDK resolves Timeback user - When submitting activity, SDK looks up the Timeback user by email
- You have an existing Auth0 SPA
- Your backend is a resource server (Bearer JWT auth)
- You want a dedicated Timeback launch URL (
/timeback/signin) without a separate “Sign in with Timeback” button
Quick Start
1. Auth0 Setup
- Create an API in Auth0 (Applications → APIs) and copy its Identifier (this is the audience)
- Create a Single Page Application in Auth0 (Applications → Applications)
- In the SPA settings, set:
- Allowed Callback URLs:
http://localhost:5173 - Allowed Logout URLs:
http://localhost:5173 - Allowed Web Origins:
http://localhost:5173
- Allowed Callback URLs:
- Ensure the Database connection is enabled for the SPA (email/password hosted by Auth0)
2. Environment Setup
.env with your credentials.
3. Install and Run
Launch-gated URL
Visithttp://localhost:5173/timeback/signin to simulate the Timeback launch URL.
Commands
| Command | Description |
|---|---|
just install | Install backend + frontend |
just dev | Start backend + frontend |
just dev-backend | Start backend only |
just dev-frontend | Start frontend only |
just setup | Copy .env.example → .env |
API Endpoints
Auth Endpoints
This example uses Auth0 Universal Login (SPA + PKCE), so there are no backend/auth/* routes. The backend behaves like a typical resource server: it only
validates Authorization: Bearer {'<'}access_token{'>'}.
Timeback SDK Endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/timeback/activity/heartbeat | POST | Submit time heartbeat |
/api/timeback/activity/submit | POST | Submit activity completion |
/api/timeback/user/me | GET | Get Timeback user profile |
/api/timeback/user/verify | GET | Verify Timeback user status |
Session Endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/health | GET | Health check |
/api/user/me | GET | Auth0 user |
Project Structure
express-supabase
Source: examples/express-supabase
Architecture
Setup
1. Install dependencies
2. Configure environment
.env with your credentials:
- Supabase: Get URL and Publishable key from Supabase Dashboard > Settings > API
- Timeback: Get from Timeback Dashboard
3. Create a Supabase user
In your Supabase Dashboard:- Go to Authentication > Users
- Click “Add user” > “Create new user”
- Enter email and password
4. Start development servers
- Frontend: http://localhost:5173
- Backend: http://localhost:3001
Timeback Integration
The SDK is configured with custom identity mode, reading the user’s email from the session cookie:Available Commands
| Command | Description |
|---|---|
just install | Install all dependencies |
just setup | Create .env from template |
just dev | Start both servers |
just dev-backend | Start backend only |
just dev-frontend | Start frontend only |
nextjs-app-router
Source: examples/nextjs-app-router
Quick Start
Structure
| File | Purpose |
|---|---|
src/app/api/timeback/[...timeback]/ | Timeback request handler |
src/app/api/auth/sso/callback/timeback/ | Custom OAuth callback |
src/lib/timeback.ts | SSO config + session management |
src/app/page.tsx | SignInButton + activity demo |
Key Patterns
API routes — Handle auth routes viatoNextjsHandler:
nuxt
Source: examples/nuxt
Setup
Make sure to install dependencies:Development Server
Start the development server onhttp://localhost:3000:
Production
Build the application for production:qti-with-powerpath
Source: examples/qti-with-powerpath
- Renders questions from PowerPath
- Submits answers to PowerPath
- Displays results from PowerPath
Quick Start
Two Quiz Types
| Type | Lesson Type | Questions | Pass Threshold | Algorithm |
|---|---|---|---|---|
| PowerPath 100 | powerpath-100 | 30 | 80% PP score | Adaptive - adjusts difficulty |
| Static Quiz | quiz | 5 | None | Standard - sequential |
lessonType determines behavior.
What is PowerPath 100?
PowerPath 100 refers to thepowerpath-100 lesson type that uses PowerPath’s adaptive algorithm. The “100” comes from the goal: reaching a PowerPath score of 100.
How the algorithm works:
- Score starts at 0 and the goal is to reach 100
- Question difficulty is selected based on current score:
- 0-49: Easy questions
- 50-89: Medium + Hard mix (75%/25%)
- 90-99: Hard questions only
- Correct answers increase your score; incorrect answers decrease it
- 80% PP score is the passing threshold (but the quiz continues until 100 or questions exhausted)
Architecture
- Which question to show next
- Difficulty adaptation (for PowerPath 100)
- Scoring and pass/fail determination
- Attempt tracking
Project Structure
| File/Folder | Purpose |
|---|---|
src/index.ts | Bun server entry point with route definitions |
src/routes/quiz.ts | API routes that call PowerPath |
src/lib/config.ts | Loads lesson config from OneRoster |
src/lib/timeback.ts | SDK initialization |
src/components/ | React UI (Landing, Quiz, Results, AttemptList, AttemptReview) |
scripts/setup-all.ts | Creates org, course, QTI items, and PowerPath lessons |
samples/questions.json | 30 questions in JSON format |
samples/example.xml | Reference QTI 3.0 XML format |
API Endpoints
Quiz Flow
| Endpoint | Method | Purpose |
|---|---|---|
/api/quiz/start | POST | Start a new quiz attempt |
/api/quiz/next | POST | Get the next question |
/api/quiz/submit | POST | Submit an answer |
/api/quiz/complete | POST | Finalize the attempt and get results |
/api/quiz/progress | POST | Get current progress for an attempt |
Attempt History
| Endpoint | Method | Purpose |
|---|---|---|
/api/quiz/attempts | POST | List all attempts for a lesson |
/api/quiz/attempt-details | POST | Get detailed results for a specific attempt |
Configuration
| Endpoint | Method | Purpose |
|---|---|---|
/api/quiz/config | GET | Get lesson IDs and settings |
/api/quiz/user | GET | Get current user info |
/api/health | GET | Health check with setup status |
/api/timeback/activity | POST | Time tracking (Timeback handler) |
Example: Quiz Flow
Environment Variables
Features
Attempt History
Users can review past completed attempts:- View all previous attempts with scores
- Navigate through each question
- See which answers were correct/incorrect
- Compare PP score vs raw accuracy
Time Tracking
Time tracking uses the Timeback client SDK in time-only mode:- The client starts an activity at quiz start and calls
activity.end({})on completion - The server handler emits a
TimeSpentEvent(no ActivityCompletedEvent) - Results show a “Time tracked” indicator
- Idle detection: When the user switches tabs or minimizes the window, the activity is automatically paused via
activity.pause()and resumed viaactivity.resume()on return, so idle time is excluded from the tracked duration
Key Points
- No local algorithm - PowerPath handles all quiz logic
- Real API calls - Every action goes through PowerPath
- Two lesson types - Same code, different behavior based on
lessonType - Setup required - Must run
bun run setupbefore the app works - PP score ≠ accuracy - PowerPath 100 uses weighted scoring, not raw percent correct
solid-start
Source: examples/solid-start
Quick Start
File Structure
| File | Purpose |
|---|---|
src/lib/timeback.ts | SDK server instance with SSO config |
src/routes/api/timeback/[...path].ts | Catch-all API route for SDK |
src/routes/api/auth/sso/callback/timeback.ts | Custom OAuth callback |
src/routes/api/session.ts | Session endpoint |
src/routes/api/signout.ts | Sign out endpoint |
src/routes/index.tsx | Main page with auth UI |
Key Patterns
Server SDK →toSolidStartHandler(timeback) in API routes
Client → SignInButton, Activity from timeback/solid
Session → Server-side store, fetched via /api/session
svelte-kit
Source: examples/svelte-kit
Quick Start
Structure
| File | Purpose |
|---|---|
src/hooks.server.ts | Timeback request handler |
src/lib/timeback.ts | SSO config + session management |
src/routes/+page.server.ts | Pass session to page |
src/routes/+page.svelte | SignInButton + activity demo |
Key Patterns
Server hooks — Handle auth routes viasvelteKitHandler:
tanstack-start
Source: examples/tanstack-start
Quick Start
File Structure
| File | Purpose |
|---|---|
src/lib/timeback.ts | Server SDK configuration |
src/routes/api/timeback/$.ts | Timeback API routes |
src/routes/api/auth/sso/callback/timeback.ts | Custom OAuth callback |
src/routes/api/session.ts | Session endpoint |
src/routes/api/signout.ts | Sign out endpoint |
src/routes/index.tsx | Main page with SSO demo |
timeback.config.json | Course/app configuration |
Key Patterns
Server config — Configure SSO insrc/lib/timeback.ts:
Next Steps
Quickstart
Get started with integration
SDK Overview
Learn about SDK features
Configuration
Full config reference
Developer Program
Apply for access