# Activity models
Source: https://docs.timeback.com/beta/about-timeback/concepts/activity-models
Single-session vs stateful activities and when to use each
Timeback supports two activity models. The right choice depends on the nature of your app, its architecture, and the learning material: specifically, whether students complete an activity in one sitting or across multiple sessions.
## Single-session
The simplest model: a student starts and completes an activity in one browser session.
```mermaid theme={null}
graph LR
A[Student starts] --> B[Engages] --> C[Ends activity]
style A fill:#e0f2fe,stroke:#0284c7
style C fill:#dcfce7,stroke:#16a34a
```
The browser SDK measures time locally and submits time heartbeats and completion separately. Your app owns the completion decision and supplied metrics; authenticate the user and validate important outcomes on your server.
**Use single-session when:**
* Students complete the activity in one sitting (quizzes, short lessons, drills)
* The frontend has all the data it needs to report completion metrics
* You do not need to persist activity state across page reloads
## Stateful/Resumable
Many apps support activities that span multiple sessions:
```mermaid theme={null}
graph TD
subgraph Monday
A[Student starts] --> B[Answers 10 questions] --> C[Leaves]
end
C -.-> |Tuesday — no activity| D
subgraph Wednesday
D[Student resumes] --> E[Answers 10 more] --> F[Completes]
end
style A fill:#e0f2fe,stroke:#0284c7
style C fill:#fef9c3,stroke:#ca8a04
style D fill:#e0f2fe,stroke:#0284c7
style F fill:#dcfce7,stroke:#16a34a
```
Here, state lives in the **app's database**: progress, accumulated time, and status. The frontend cannot be the sole source of truth because it was not present for all sessions.
**Use stateful when:**
* Activities span multiple days or sessions
* The backend owns progress (server-validated answers, adaptive learning)
* Students need to resume where they left off
## The Key Insight
Timeback emits two types of learning events:
*"Student engaged for N seconds"*
**Per session**: Multiple events per activity run.
*"Student finished with these results"*
**Per activity**: Your application should report completion once. This is a reporting rule, not an exactly-once delivery guarantee.
For single-session activities, the student starts and finishes in one sitting, but time and completion still use separate requests and may succeed or fail independently.
Stateful activities do not. Time-spent should be reported per learning session (e.g., "12 minutes on Monday", "8 minutes on Wednesday"), while activity completion happens once, when the activity is truly done.
The SDK handles this by decoupling time tracking from completion. Time is reported continuously via periodic heartbeats. Completion is reported separately, either from the client or the server.
## How the models differ
| Aspect | Single-session | Stateful |
| ------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| **Sessions** | One | Multiple |
| **Time tracking** | Automatic heartbeats | Automatic heartbeats |
| **Completion** | [`activity.end(...)`](/beta/build-on-timeback/sdk/activity-tracking/single-session#starting-an-activity) | [`timeback.activity.record(...)`](/beta/build-on-timeback/sdk/activity-tracking/stateful#backend) |
| **State ownership** | App (with browser session state) | App database |
| **Resume support** | Not needed | UUID `runId` persisted and reused |
Both models use the same client SDK for time tracking. The difference is in who reports completion and whether state persists across sessions.
Implementation guide for one-sitting activities.
Implementation guide for multi-session activities.
# Evaluating apps
Source: https://docs.timeback.com/beta/about-timeback/concepts/evaluating-apps
How Timeback measures app quality
This page describes product principles and review policy. These thresholds are not universal enforcement in every SDK or receiver. Check the relevant integration contract and confirm current approval requirements with the Timeback team.
Timeback operates on a simple principle: **if students are not learning, it is the system's fault.** Apps are evaluated the same way.
| Dimension | What matters |
| ------------------------------ | -------------------------------------------------- |
| **Granularity** | One teachable unit at a time |
| **Instruction quality** | Clear explanations, worked examples, minimal noise |
| **Mastery truthfulness** | "Completed" means mastered |
| **Coverage and rigor** | Aligned to real external tests |
| **Efficiency** | Fewer hours to reach the same verified outcome |
| **Hole-filling compatibility** | Targeted remediation is possible when gaps show up |
If an app performs well on engagement but poorly on externally validated outcomes, the closed loop forces the conversation back to instruction, mastery, and signal integrity.
The rules every app must follow.
Level 1 vs Level 2 requirements.
# Learning science
Source: https://docs.timeback.com/beta/about-timeback/concepts/learning-science
The hierarchy of learning mechanisms that guide Timeback's design
The tiers below express Timeback's instructional design priorities. They are a product framework, not a universal ranking established by the SDK or a claim that every deployed learning flow enforces these thresholds.
## Tier 0: Non-negotiables
These must be in place before anything else matters.
| Mechanism | What it means |
| --------------------------- | ---------------------------------------------------------------------------------------- |
| **Faultless communication** | Instruction is unambiguous. Examples clearly distinguish what counts from what does not. |
| **Retrieval practice** | Active recall is the primary learning event, not passive consumption. |
| **Mastery gating** | Students do not progress without demonstrating ≥90% accuracy on rigorous assessments. |
## Tier 1: Force multipliers
These amplify Tier 0 once the foundation is solid.
| Mechanism | What it means |
| ------------------- | ---------------------------------------------------------------------- |
| **Spacing** | Distribute practice over time. Cramming creates temporary performance. |
| **Interleaving** | Mix problem types to prevent context-dependency. |
| **Worked examples** | Study complete solutions before attempting problems. |
| **Feedback** | Immediate for basic facts; elaborated (explaining why) for concepts. |
## Tier 2: Context-dependent
These work under specific conditions.
| Mechanism | What it means |
| ---------------- | --------------------------------------------------------------------------------- |
| **Novelty** | Activates attention. Useful for marking practice intervals. |
| **Multimedia** | Combine verbal and visual when both add value. Avoid redundancy. |
| **Gamification** | Can increase engagement if it reinforces learning behaviors, not just completion. |
Tier 0 is binary. You either have faultless communication, retrieval practice, and mastery
gating, or you do not. No amount of Tier 1 or 2 optimizations can compensate for a broken
foundation.
# Non-negotiables
Source: https://docs.timeback.com/beta/about-timeback/concepts/non-negotiables
The rules every Timeback app must follow
This page describes product principles and review policy. These thresholds are not universal enforcement in every SDK or receiver. Check the relevant integration contract and confirm current approval requirements with the Timeback team.
These are the rules every integrated learning app must follow. They protect outcome integrity across the ecosystem.
1. **Teach toward verifiable outcomes.** In-app success must predict performance on credible external assessments.
2. **Enforce mastery gates at ≥90% accuracy.** Do not advance students based on time, completion, or self-report.
3. **Award XP only for verified learning.** No XP for passive activity until learning is verified through retrieval. No XP below 80% accuracy.
4. **Design for cognitive load limits.** Keep granularity tight, reduce noise, avoid bundling multiple new skills in one lesson.
5. **Make misconceptions hard to form.** Use clear examples, non-examples, and fast error correction.
6. **Build in retrieval practice and spaced review.** Practice must require recall, not just recognition.
7. **Prevent gaming.** Treat incentives as adversarial. Make the target cognitive process unavoidable.
8. **Emit learning events and keep outcomes transparent.** Results are surfaced to students, families, and operators. Apps cannot hide poor performance.
Apps that award credit without verified learning, allow progression without mastery, or fail to
emit the required learning signals will not be eligible for integration.
# 1EdTech standards
Source: https://docs.timeback.com/beta/about-timeback/concepts/standards
Standards used by Timeback interfaces and their integration boundaries
Timeback services use 1EdTech data models and service-specific extensions. Consult each API's current paths, schemas, and authorization requirements; the existence of an implementation is not a certification claim or a guarantee that every operation in the standard is supported.
| Standard | Role in Timeback | Integration responsibility |
| ----------- | ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| OneRoster | Users, courses, classes, enrollments, results | Map identities and use supported operations/scopes |
| LTI | Learning-tool launch contracts where configured | Implement the relevant launch flow; SDK OIDC SSO is a separate contract |
| QTI | Assessment content and response-processing interfaces | Render supported interactions and handle processing results |
| Caliper | Learning event vocabulary and payloads | Select the correct receiver/profile, send accurate metrics, and verify downstream processing |
| CASE | Competency frameworks and standards associations | Supply and maintain the content mappings your app needs |
| CLR | Learner credential records | Supply a valid credential body; raw credential storage does not itself sign or verify it |
| Open Badges | Achievement credentials | Use the service's issuance and verification contracts; do not infer revocation or external verification from storage |
A shared data model helps interoperability. It does not automatically make two apps' identifiers, progress scales, or mastery policies interchangeable. See [API reference](/beta/api-reference/overview) and [SDK identity](/beta/build-on-timeback/sdk/identity).
# XP system
Source: https://docs.timeback.com/beta/about-timeback/concepts/xp-system
How Timeback measures learning effort and progress
The XP rules below describe the intended reward policy. Custom Activity SDK calls accept app-supplied XP; they do not calculate a universal time-to-XP conversion, enforce an 80% accuracy gate, or detect cheating. TypeScript accepts negative XP; the reviewed Python activity schema requires nonnegative XP. Agree the formula for your integration and validate the emitted metrics.
## Why XP exists
Education software usually forces a false choice: track **time** (which measures presence, not learning) or track **accuracy** (which ignores how much work was done). A student who spends 30 focused minutes mastering fractions and a student who clicks through the same lesson in 5 minutes can both show "100% complete", yet the learning that happened is vastly different.
XP solves this by combining **effort** with **proof**. It's a single metric that captures both how long a student worked and whether that work produced verified learning. This makes it possible to compare outcomes across apps, content, and students.
## The design
**1 XP = 1 minute of focused learning.**
Anchoring XP to real time creates a universal unit that every app in the ecosystem shares. Interpreting XP as calibrated learning effort depends on each contributing app following the agreed policy; a profile total alone does not verify elapsed minutes or mastery.
Two concepts make this work:
| Concept | Definition |
| --------------- | ------------------------------------------------------------ |
| **Expected XP** | How long a focused student should take |
| **Awarded XP** | What the student actually earns, based on verifiable metrics |
**Expected XP** is calibrated at the content level by the app developer. It represents the time a focused student should need to master the material.
**Awarded XP** is what the student earns after completing the activity. It depends on whether mastery was demonstrated and how efficiently the student worked.
## How XP is awarded
Not all effort is equal. XP reflects the quality of learning, not just the fact that something was done.
| Outcome | Effort quality | XP result |
| ----------------------- | --------------- | ------------------ |
| Mastered | Focused | Full XP |
| Perfect (first attempt) | Focused | Full XP + Bonus XP |
| Not mastered | Focused | 0 XP |
| Mastered | Wasteful | Partial XP |
| Any | Gaming/cheating | Negative XP |
A student who masters the material efficiently earns full XP. A student who gets it right on the first try earns a bonus. A student who works hard but doesn't reach mastery earns nothing, because XP represents *verified* learning, not just participation.
No XP is awarded below 80% accuracy. This discourages students from speed-running content or
guessing until correct.
### Why negative XP?
If XP can be earned without real cognitive effort, the metric loses its meaning. Negative XP exists to discourage behaviors like exploiting answer patterns or using external tools to bypass assessments. Rather than simply awarding zero, an application may report a deduction under its agreed policy. The custom activity SDK does not detect gaming or issue that deduction itself.
## How XP flows through the system
When an activity completes, the app reports the XP earned. Timeback processes activity data through the gradebook pipeline and aggregates it in the student's [profile](/beta/build-on-timeback/sdk/user-profile), where it's surfaced in dashboards alongside XP from other apps.
Because every app reports XP using the same unit, the platform can answer questions that no single app can:
* Did 30 minutes in App A produce more learning than 30 minutes in App B?
* Which content sequences produce faster mastery for which students?
* Is a student's total daily effort on track?
How apps report XP through the SDK
The rules that protect XP integrity
# How It Works
Source: https://docs.timeback.com/beta/about-timeback/how-it-works
The platform architecture that enables measurable learning outcomes
This is a conceptual map. Available launchers, dashboards, analytics, and remediation flows depend on the deployed product and configuration. The SDK and API guides describe the verified integration contracts; the diagrams do not establish a live deployment or universal automation.
Component
What it provides
Timeback APIs
1EdTech standards for rosters, content, events, and more
Desktop App
Student launcher with waste detection and time-on-task
Dashboards
Progress for students; analytics for developers
Closed loop
External tests connect in-app activity to real outcomes
Timeback is a **platform for building educational software where outcomes are measurable**. It provides the data infrastructure, APIs, and feedback loops that let developers focus on learning experiences instead of rebuilding rostering, progress tracking, analytics, and school integrations from scratch.
This page covers the architecture developers interact with. For learning science constraints and the motivation system, see [The Principles](/beta/about-timeback/principles). For integration guides, see [Build on Timeback](/beta/build-on-timeback/introduction).
## Platform architecture
Timeback is built as a three-layer system. Each layer builds on the one below it, and the entire stack is designed around a single goal: **connecting what happens in apps to what students can demonstrate on credible assessments**.
### Layer 1: Standards backbone
The foundation is a set of **APIs based on 1EdTech data models** that implement industry-standard data models. Schools already speak these standards for rostering, assessments, and analytics. Interoperability still requires supported operations, identity mapping, authorization, and app-specific integration.
This layer handles the data you would otherwise need to define and store yourself: students, classes, enrollments, courses, content, results, and learning events.
### Layer 2: Learning system
The middle layer provides **mastery tracking and the closed-loop feedback system**. It takes raw events from apps and turns them into progress signals that are comparable, analyzable, and tied to outcomes.
As a student engages with an activity, the learning system tracks time spent continuously via periodic heartbeats. When the student completes the activity, the system records the result, updates mastery state, and feeds signals into the analytics pipeline. The SDK supports [two activity models](/beta/about-timeback/concepts/activity-models): single-session activities where the client reports everything, and stateful activities where the client tracks time per session while the server records completion. Apps provide XP based on their own reward logic, and the platform aggregates it across the ecosystem. When standardized test results arrive, the system correlates them with in-app behavior to identify what worked and what needs adjustment.
### Layer 3: Student experience
The top layer is where students and educators interact with the platform. This includes **learning apps** (both first-party and third-party), **dashboards** for progress visibility, and the **Timeback Desktop App** for monitoring and launching content.
Developers build at this layer. Your app must still configure identity, validate outcomes, handle failures, and render its learning experience.
## The Timeback stack
Here is what developers actually interact with when building on the platform:
| Component | What it does |
| ------------------------ | --------------------------------------------------------------------------------------- |
| **Timeback APIs** | Endpoints based on 1EdTech models for rosters, launch, content, events, and credentials |
| **Timeback Desktop App** | Student-facing launcher that collects engagement signals (waste, time-on-task) |
| **Student Dashboards** | Progress visibility including XP, mastery state, and time-back tracking |
| **Developer Dashboards** | Analytics and outcome correlation for monitoring app performance |
### Timeback APIs
Rather than inventing proprietary interfaces, the platform implements industry specs directly. For developers, this means:
* **Shared models.** Standard concepts reduce translation work; extensions and service-specific behavior still need explicit mapping.
* **Integration requirements.** Confirm the exact standards, versions, and compliance evidence required by each school.
* **Reusable services.** Use the supported APIs for rostering, events, and analytics while handling your integration's identity and failure modes.
### 1EdTech standards
Timeback services use 1EdTech data models and service-specific extensions. Consult each API's current paths, schemas, and authorization requirements; the existence of an implementation is not a certification claim or a guarantee that every operation in the standard is supported.
| Standard | Role in Timeback | Integration responsibility |
| ----------- | ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| OneRoster | Users, courses, classes, enrollments, results | Map identities and use supported operations/scopes |
| LTI | Learning-tool launch contracts where configured | Implement the relevant launch flow; SDK OIDC SSO is a separate contract |
| QTI | Assessment content and response-processing interfaces | Render supported interactions and handle processing results |
| Caliper | Learning event vocabulary and payloads | Select the correct receiver/profile, send accurate metrics, and verify downstream processing |
| CASE | Competency frameworks and standards associations | Supply and maintain the content mappings your app needs |
| CLR | Learner credential records | Supply a valid credential body; raw credential storage does not itself sign or verify it |
| Open Badges | Achievement credentials | Use the service's issuance and verification contracts; do not infer revocation or external verification from storage |
A shared data model helps interoperability. It does not automatically make two apps' identifiers, progress scales, or mastery policies interchangeable. See [API reference](/beta/api-reference/overview) and [SDK identity](/beta/build-on-timeback/sdk/identity).
### Timeback Desktop App
Students launch learning apps through the Timeback Desktop App. Beyond serving as a launcher, it collects **engagement signals** that feed into the analytics pipeline:
* **Waste detection**: Identifies when students are idle, distracted, or multitasking
* **Time-on-task measurement**: Distinguishes active learning from passive screen time
* **Session context**: Captures which apps are in use and for how long
For developers, these signals answer a question you can't answer on your own: **is the problem your content, or is the student disengaged?**
If a student is struggling, the platform can distinguish between "low effort" (motivation issue) and "content too hard" (curriculum issue). This means you can diagnose problems with your app that would otherwise be invisible. When engagement is high but outcomes are low, you know to fix the instruction. When engagement is low, the problem is upstream of your content.
### Student and developer dashboards
**Student dashboards** expose progress in a way that reinforces motivation: XP earned, mastery achieved, time reclaimed. Students see exactly where they stand and what is left to complete.
**Developer dashboards** show how your app performs across the student population: completion rates, accuracy distributions, time-to-mastery, and correlation with standardized test outcomes. When something is not working, you see it in the data.
## The closed loop
Most edtech products operate in an **open loop**: students use the app, some metrics are collected, but nobody knows if learning actually happened. Engagement is tracked because it can be measured. Outcomes are not tracked because they require external validation.
Timeback operates in a **closed loop**: in-app activity is tied to standardized test performance, and the correlation is used to improve instruction.
### What the loop measures
The platform captures signals at multiple levels:
| Signal | What it indicates |
| ---------------------------- | ---------------------------------- |
| **XP earned** | Volume of productive learning time |
| **Accuracy** | Quality of understanding |
| **Time-to-mastery** | Efficiency of instruction |
| **Waste percentage** | Behavior and motivation issues |
| **Standardized test scores** | Transfer to external assessments |
### Why the loop matters for developers
Most edtech companies can't prove their product works. They show engagement metrics, completion rates, and testimonials. But when a school asks "did students learn more?", the honest answer is usually "we don't know."
The closed loop changes this. For developers, it provides:
**Evidence for evaluation.** Linked activity and assessment data can support analysis. Correlation alone does not establish that an app caused improved learning.
**Diagnosis when things break.** If outcomes aren't improving, you see exactly where the breakdown happens:
* **Motivation issues** (low minutes, high waste, inconsistent effort): the problem is upstream of your content
* **Placement issues** (accuracy too high or too low): students are in the wrong place in the curriculum
* **Curriculum issues** (students complete content but scores don't improve): your instruction needs work
**Faster iteration.** Instead of waiting months for anecdotal feedback, you see the impact of changes in the data. Did the new lesson sequence improve time-to-mastery? Did the redesigned practice set increase transfer to assessments? Evaluate those questions with an appropriate study design; the presence of telemetry alone does not answer them.
### External validation
Timeback ties in-app progress to rigorous external assessments:
* **MAP Growth**: Norm-referenced achievement and growth measurement
* **State assessments**: Criterion-referenced mastery verification
* **SAT/AP**: College readiness and advanced placement
When test results arrive, the analytics pipeline correlates them with in-app behavior. This enables comparisons: Did 30 minutes in App A produce more measurable learning than 30 minutes in App B?
### Hole filling
External validation reveals gaps. Hole filling addresses them.
Supported remediation workflows can use assessment results and missed standards to select targeted content and schedule a retest. Eligibility, score thresholds, grade bands, available content, and progression steps vary by subject, app, and workflow. A score below 90% does not universally or immediately create a hole-filling course.
This is the second half of the closed loop. Validation tells you *what's wrong*. Hole filling *fixes it*.
For developers, hole filling means your content participates in a system that actually responds to failure. If students struggle with specific lessons, the platform can route them to remediation (from your app or another) and bring them back to retry. An adapter or configured workflow must support the required content and routing.
## Where your app fits
Apps integrate with Timeback at different depths depending on what they need:
| Integration level | What you use | What you get |
| ----------------- | -------------------------------------------------------- | --------------------------------------------------- |
| **Basic** | LTI launch, OneRoster enrollments | Single sign-on, roster sync |
| **Events** | Caliper events (heartbeats + completions), progress APIs | Analytics, XP tracking, dashboards |
| **Assessments** | QTI content, adaptive delivery APIs | Adaptive quizzes, mastery gating |
| **Full** | All APIs, standards alignment | Complete curriculum integration, outcome validation |
### What you build versus what the platform provides
| You build | Platform provides |
| -------------------------------- | -------------------------------------------- |
| Learning experiences and content | Rostering and identity management |
| Question items and lesson flows | Authentication and permissions |
| UI and interaction design | Progress tracking and mastery state |
| Game mechanics and motivation | XP tracking, aggregation, and reward systems |
| Subject-specific pedagogy | Analytics and outcome measurement |
| Your unique value proposition | Standards compliance and school integrations |
### Integration paths
**Launch and identity**: LTI launch and SDK OIDC SSO are separate integration contracts. For the SDK, configure SSO or custom identity and own a secure application session. Do not assume every launcher uses LTI or that a launch URL alone authenticates the user.
**Emit events via Caliper**: Activity telemetry is split into continuous time-spent heartbeats and completion submissions. The SDK correlates these events per run, and stateful apps can record completion from the backend while still reporting time from the frontend. The platform captures the stream, updates progress, and feeds analytics.
**Read and write via OneRoster**: Query enrollments to know what content a student should see. Write results to the gradebook so scores appear in dashboards and reports. You do not need to define your own data models for courses and progress.
**Deliver assessments via QTI**: Store questions in QTI format. Use the platform's adaptive delivery APIs for placement tests and mastery-based progression. You do not need to build quiz engines or adaptive algorithms from scratch.
The depth of integration is your choice. Some apps only need launch and events. Others use the
full stack. Start with what you need and add more as your product matures.
## What this enables for developers
Building on Timeback means inheriting infrastructure that would otherwise take years to build:
**Skip the commodity work.** Rostering, identity, progress tracking, analytics, and school integrations are solved. Focus on the learning experience that differentiates your app.
**Know if your app works.** The closed loop validates whether your content produces measurable learning gains. Iterate based on outcomes, not just engagement.
**Reach students through existing channels.** Apps on Timeback reach students through Alpha School, partner schools, and direct-to-consumer channels subject to onboarding, curriculum fit, approval, and deployment availability.
**Compound with the ecosystem.** Apps that follow Timeback's learning science principles work together. A tutoring app can pick up where a lesson app left off because both share the same progress model.
***
This page covers how the platform works. For the learning science principles that guide what
apps should do, see [The Principles](/beta/about-timeback/principles). For integration guides
and API reference, see [Build on Timeback](/beta/build-on-timeback/introduction).
Why current edtech cannot prove outcomes.
What Timeback is building toward.
Learning science constraints that protect outcomes.
What developers get from the platform.
# The Principles
Source: https://docs.timeback.com/beta/about-timeback/principles
The learning constraints, motivation model, and measurement rules that make outcomes provable and improvable
This page presents Timeback's product rationale and goals. Educational efficacy, market-wide comparisons, distribution commitments, and current program availability require evidence beyond repository code. Use the integration guides for implemented API behavior.
Principle
What it means for app builders
Tier 0 first
Communication, retrieval, mastery gating first
Content is the lever
Example selection is product design
Cognitive load
Instruction must fit working memory limits
Transfer via testing
Evaluated on external tests, not in-app metrics
Retrieval and spacing
Retrieval is learning; spacing keeps it
Motivation + rigor
Push through high standards, never lower them
Trustworthy metrics
XP/time-to-mastery are hard to game
Interoperability
Shared events compound rather than fragment progress
Timeback is not a marketplace of "any learning experience goes." It is a platform built around constraints that make learning outcomes **measurable, comparable, and improvable** across apps.
These principles are the reason Timeback can run a real closed loop: apps generate learning signals, the platform aggregates them consistently, and external assessments verify what actually transferred. Builders who align with these constraints get leverage from the ecosystem. Builders who do not align get exposed by the measurement system.
***
## Learning Science Foundations
Timeback uses a strict definition of learning: **a durable change in long-term memory** that shows up later, in new contexts, and on credible assessments.
For developers, this changes product incentives. "High in-app accuracy" is not automatically success. "Kids love it" is not automatically success. "They finished the course" is not automatically success. Success is when students can still do the skill later, under variation, at the rigor demanded by real tests.
### The hierarchy of learning mechanisms
The tiers below express Timeback's instructional design priorities, not a universal research ranking or proof that every deployed app enforces these thresholds.
#### Tier 0: Non-negotiables
These must be in place before anything else matters.
| Mechanism | What it means |
| --------------------------- | ---------------------------------------------------------------------------------------- |
| **Faultless communication** | Instruction is unambiguous. Examples clearly distinguish what counts from what does not. |
| **Retrieval practice** | Active recall is the primary learning event, not passive consumption. |
| **Mastery gating** | Students do not progress without demonstrating ≥90% accuracy on rigorous assessments. |
#### Tier 1: Force multipliers
These amplify Tier 0 once the foundation is solid.
| Mechanism | What it means |
| ------------------- | ---------------------------------------------------------------------- |
| **Spacing** | Distribute practice over time. Cramming creates temporary performance. |
| **Interleaving** | Mix problem types to prevent context-dependency. |
| **Worked examples** | Study complete solutions before attempting problems. |
| **Feedback** | Immediate for basic facts; elaborated (explaining why) for concepts. |
#### Tier 2: Context-dependent
These work under specific conditions.
| Mechanism | What it means |
| ---------------- | --------------------------------------------------------------------------------- |
| **Novelty** | Activates attention. Useful for marking practice intervals. |
| **Multimedia** | Combine verbal and visual when both add value. Avoid redundancy. |
| **Gamification** | Can increase engagement if it reinforces learning behaviors, not just completion. |
Tier 0 is binary. You either have faultless communication, retrieval practice, and mastery gating, or you do not. No amount of Tier 1 or 2 optimizations can compensate for a broken foundation. Features are liabilities; mechanisms are assets.
### Content is the lever
Students infer rules from the patterns you present. If your examples allow multiple interpretations, students will form misconceptions that are *rational given the evidence*. This is why faultless communication sits at Tier 0.
Misconceptions are often rational inferences from ambiguous evidence, not failures of attention or effort. If the learner can logically infer the wrong rule from the examples provided, the fault lies with the instruction, not the learner.
Timeback borrows heavily from Direct Instruction style design:
* **Contrastive examples** that show what counts and what does not
* **Near-misses** that differ only in the critical feature
* **Minimally different examples** that isolate what matters
* **Immediate error correction** that prevents wrong rules from becoming stable memory
This principle has a direct developer implication: **example selection is product design**, not content polish.
**Make the target cognitive process unavoidable.** If students can succeed via pattern matching, shallow guessing strategies, or memorizing repeated items, the app is gameable and its signals are untrustworthy.
### Cognitive load is the constraint
Working memory is severely limited. When instruction overloads it, students do not "try harder and get there." They stall, guess, or memorize surface patterns. This is why the Tier 0 mechanisms exist: they respect cognitive limits while ensuring learning actually happens.
The highest-leverage move is **granularity**. Timeback strongly prefers learning flows that teach one thing at a time, keep steps small enough that errors are diagnosable, and build integration only after components are secure.
**Practical implications:**
* Lessons should target a single concept, skill, or procedure
* Ensure each component is secure before asking students to integrate them
* Use worked examples before independent practice
* Remove extraneous content that consumes cognitive resources without serving learning
### The closed loop validates transfer
Students can appear successful while acquiring knowledge that does not transfer, persist, or show up on meaningful assessments. High in-app accuracy can be driven by pattern matching, memorization of specific items, or shallow strategies that collapse under variation.
| Success pattern | What it indicates |
| -------------------------------------- | -------------------------------------- |
| High in-app accuracy, high test scores | Learning is occurring and transferring |
| High in-app accuracy, low test scores | In-app tasks are not testing transfer |
| Low in-app accuracy, low test scores | Instruction is not working |
**Invisible failure is worse than visible error.** A system that fails visibly can be debugged and fixed. A system that fails invisibly gets mistaken for one that works.
Learning systems face a fundamental asymmetry: success and failure are not equally visible. A learner who fails may produce signals that mimic success: completion without comprehension, correct answers via shortcuts, engagement metrics that track time without cognitive work.
The Timeback closed loop exists specifically to make failure visible. External standardized tests validate what students actually learned. When in-app success diverges from test performance, the gap is exposed, and the conversation returns to instruction, content, and mastery.
Timeback's standard for "did it work?" is transfer on external assessments, not in-app metrics.
***
## The Motivation System
Timeback treats motivation as a core product problem, not UI polish. Consistent effort is a prerequisite for consistent outcomes.
**Time back** is the primary motivator: finish academics with mastery, reclaim the day. Students who complete academics in about two hours reclaim four or more hours for sports, life skills, and creativity. Students who rush through content without mastery do not get their time back. They get remediation.
When time-back is not available, incentives must still push toward mastery, not toward completion theater.
The XP rules below describe the intended reward policy. Custom Activity SDK calls accept app-supplied XP; they do not calculate a universal time-to-XP conversion, enforce an 80% accuracy gate, or detect cheating. TypeScript accepts negative XP; the reviewed Python activity schema requires nonnegative XP. Agree the formula for your integration and validate the emitted metrics.
### XP as a universal progress currency
Timeback uses XP as a shared unit across apps. XP exists because education software usually forces a false choice: track time (which measures presence, not learning) or track accuracy (which ignores how much work was done). XP combines effort with proof.
**The core specification: 1 XP = 1 minute of focused learning.**
| Concept | Definition |
| --------------- | -------------------------------------------------------------------- |
| **Expected XP** | How long a focused student should take (content-level constant) |
| **Awarded XP** | What the student earns based on verified learning and effort quality |
This is one of the core ways Timeback makes apps comparable: time-to-mastery is a legitimate metric only when the unit is consistent.
| Outcome | Effort quality | XP result |
| ----------------------- | --------------- | ----------- |
| Mastered | Focused | Full XP |
| Perfect (first attempt) | Focused | Bonus XP |
| Not mastered | Focused | 0 XP |
| Mastered | Wasteful | Partial XP |
| Any | Gaming/cheating | Negative XP |
### From extrinsic to intrinsic
Timeback uses extrinsic rewards to create enough early success that competence can form. Competence builds confidence. Confidence enables identity change. Identity is what lasts.
**The motivation arc:**
1. **Extrinsic rewards** get students to engage consistently
2. **Consistent engagement** produces **competence**
3. **Competence** builds **confidence**
4. **Confidence** enables **identity change**
5. **Identity** sustains **intrinsic motivation**
This only works if mastery is real. Rewards for fake progress train students to optimize the reward system, not their knowledge. Motivation follows mastery, not the reverse.
### Why gaming must be prevented
Any reward system attracts gaming. Students are not "bad" for doing this; they are optimizing incentives. Timeback assumes adversarial optimization and hardens signals accordingly.
**Common gaming patterns:**
* Tanking placement tests to receive easier content
* Clicking through explanations without reading
* Guessing until correct
* Pattern matching on test items rather than learning concepts
Timeback builds anti-gaming protections into the platform. The Timeback Desktop App collects engagement signals (waste detection, time-on-task) that distinguish active learning from passive screen time. For developers, this means designing apps where the target cognitive process is *unavoidable*.
***
This page describes product principles and review policy. These thresholds are not universal enforcement in every SDK or receiver. Check the relevant integration contract and confirm current approval requirements with the Timeback team.
## How Timeback Evaluates Apps
Timeback operates on a simple principle: **if students are not learning, it is the system's fault.** Apps are evaluated the same way.
| Dimension | What matters |
| ------------------------------ | -------------------------------------------------- |
| **Granularity** | One teachable unit at a time |
| **Instruction quality** | Clear explanations, worked examples, minimal noise |
| **Mastery truthfulness** | "Completed" means mastered |
| **Coverage and rigor** | Aligned to real external tests |
| **Efficiency** | Fewer hours to reach the same verified outcome |
| **Hole-filling compatibility** | Targeted remediation is possible when gaps show up |
If an app performs well on engagement but poorly on externally validated outcomes, the closed loop forces the conversation back to instruction, mastery, and signal integrity.
***
This page describes product principles and review policy. These thresholds are not universal enforcement in every SDK or receiver. Check the relevant integration contract and confirm current approval requirements with the Timeback team.
## The Non-Negotiables
These are the rules every integrated learning app must follow. They protect outcome integrity across the ecosystem.
1. **Teach toward verifiable outcomes.** In-app success must predict performance on credible external assessments.
2. **Enforce mastery gates at ≥90% accuracy.** Do not advance students based on time, completion, or self-report. Timeback treats 90% on rigorous checks as the mastery bar.
3. **Award XP only for verified learning.** No XP for passive activity (reading, watching) until learning is verified through retrieval. No XP below 80% accuracy.
4. **Design for cognitive load limits.** Keep granularity tight, reduce noise, and avoid bundling multiple new skills in one lesson.
5. **Make misconceptions hard to form.** Use clear examples, non-examples, and fast error correction.
6. **Build in retrieval practice and spaced review.** Practice must require recall, not just recognition. Plan for retention across time, not just short-term performance.
7. **Prevent gaming.** Treat incentives as adversarial. Make the target cognitive process unavoidable.
8. **Emit learning events and keep outcomes transparent.** The platform must be able to attribute work to student, content, and attempt. Results are surfaced to students, families, and operators. Apps cannot hide poor performance.
Timeback's closed loop only works if apps play by the same measurement and mastery rules. Apps
that award credit without verified learning, allow progression without mastery, or fail to emit
the required learning signals may be ineligible for
integration.
***
Apps that follow these principles compound each other's effectiveness. A tutoring app can pick up where a lesson app left off because both share the same mastery model. A practice app can reinforce what an instruction app taught because both emit compatible events. Apps that violate these principles will show poor outcomes, and that will be visible.
See how these principles show up in the platform stack and the closed loop.
The developer benefits that come from these constraints.
Choose how deeply your app plugs into events, assessments, and progress.
Why current edtech cannot reliably produce outcomes.
# The Problem
Source: https://docs.timeback.com/beta/about-timeback/problem
Education software can't reliably produce or prove learning outcomes
This page presents Timeback's product rationale and goals. Educational efficacy, market-wide comparisons, distribution commitments, and current program availability require evidence beyond repository code. Use the integration guides for implemented API behavior.
Problem
Summary
Time-based, not mastery
Students advance by age, not mastery
No closed loop
Edtech can't prove outcomes or improve them
Fragmented ecosystem
Every app is a silo with its own data model
No shared metrics
No shared language for effort/progress/efficiency
Wrong incentives
Engagement is rewarded over learning
Invisible failure
Learning problems surface too late
Motivation ignored
The biggest bottleneck is an afterthought
Developer tax
Every team rebuilds the same plumbing
Education is one of the largest software markets on earth, yet most products cannot reliably answer a simple question: **did learning happen, and did it happen efficiently?** Schools, families, and developers are stuck optimizing proxies like usage, completion, and seat time because the ecosystem lacks shared data and shared measurement.
Timeback exists because the core failures are structural. Education is organized around *time*. Edtech is organized around *isolated apps*. And learning is governed by constraints most products don't instrument or respect.
## Measuring time, not mastery
Traditional schooling measures progress by calendars, attendance, and age rather than **demonstrated competence**. Students advance through grades with gaps, and those gaps compound silently until "grade level" becomes a label rather than a description of what a student can actually do.
This "social promotion" model makes it nearly impossible to diagnose *why* a student struggles. Is it the content? The instruction? Missing prerequisites? The system can't tell. Instruction quality varies widely, outcomes remain opaque, and students advance based on **time served** rather than knowledge gained.
## Can't improve what you can't prove
Most education products can show activity (minutes spent, clicks, lessons completed) but cannot prove **causal impact on durable learning**. Even when test scores are available, they're often disconnected from what happened inside the product. Iteration is slow. Arguments about efficacy are endless.
The industry standard is the opposite: **open loops everywhere**.
Everyone hopes learning happened but few systems can verify it. Edtech companies end up optimizing for engagement, retention, and session length because those are the metrics they *can* actually measure.
| What gets tracked | What actually matters |
| ------------------ | ---------------------- |
| Minutes spent | Knowledge retained |
| Lessons completed | Skills transferred |
| Daily streaks | Mastery demonstrated |
| Click-through rate | Test score improvement |
## Every app is a silo
Schools run dozens of tools across rostering, content, assessment, tutoring, analytics, and motivation. But apps rarely share a coherent underlying data model. Each new product must **reinvent the same infrastructure** from scratch:
* Rostering and identity management to sync student and class data
* Authentication and access control for logins and permissions
* Content storage and delivery for lessons and assessments
* Progress and mastery tracking to define what "done" and "learned" mean
* Analytics and event logging to capture what happens in the app
* Standardized test integration to connect to meaningful outcomes
Each app becomes its own "mini platform." Schools become the integration layer, manually reconciling data across dashboards that disagree with each other.
Students use multiple tools that cannot share information.
There is **no shared record of learning**.
## No language for effort, progress, or efficiency
Even when edtech apps work, schools can't compare them. One product reports points, another reports levels, another reports completion percent, another reports time spent. None of these are interoperable, and most aren't tied to **externally verifiable outcomes**.
Developers should be able to answer basic questions in a mature platform ecosystem. Did 30 minutes in Tool A produce more learning than 30 minutes in Tool B? Which content sequences produce faster mastery for which students? Where are students stuck because of missing prerequisites versus confusion versus disengagement?
Today, these questions *can't be answered*. Parents see a jumble of incompatible dashboards. Teachers can't build a coherent picture of student performance. Administrators can't make informed decisions about which products deserve investment.
## Incentives reward engagement, even when it conflicts with learning
Many products are built to maximize retention metrics: time in app, daily streaks, content consumption. But **time spent is not the same as learning**. Systems that reward "doing school" can accidentally reward low-effort behaviors that look productive.
Gamification often rewards *completion* rather than *mastery*. Students learn to optimize for points with minimal cognitive effort: clicking through explanations, guessing until correct, avoiding challenging content. Systems report high engagement while actual learning doesn't happen.
Products that feel good and look busy win procurement cycles. Products that are efficient and rigorous are harder to explain using today's dashboards.
## Learning failure is often invisible
Students can appear successful in a product while learning very little that transfers or persists. High in-app accuracy can be driven by pattern matching, memorization of specific items, or shallow strategies that **collapse under variation**.
Common failure patterns:
* Students advance based on completion, not understanding
* New content is layered on top of gaps, causing compounding failure
* Correct answers in-app don't transfer to real assessment results
* Learners infer wrong rules from poorly designed instruction
* Systems report success while actual learning doesn't happen
When systems don't instrument for transfer, prerequisites, and cognitive load, failure surfaces late: on real assessments, in later units, or in the next grade.
**Invisible failure is the most damaging failure mode** in scalable learning systems.
Learning science calls this the "transfer problem": success in the training environment doesn't
guarantee success elsewhere. Apps that don't test for transfer can't detect this failure.
## Motivation is the bottleneck
Even the best instructional design fails if students won't engage consistently. The industry often treats motivation as UI polish (badges, confetti, streaks) rather than as a **core product problem** with measurable consequences.
Traditional systems don't give students a compelling reason to try. There is no meaningful reward for mastery, no "time back" for finishing early, no visible proof that effort leads to results. The standard motivational model is "work hard for 12 years, then 4 more, then a job."
No adult would accept that. Yet we expect children to.
Effective motivation requires designing systems where effort leads to *visible outcomes*. Time reclaimed. Skills demonstrated. Goals achieved. Surface gamification doesn't cut it.
## Developers pay the infrastructure tax
For builders, fragmentation and lack of standards creates a **compounding tax**:
* Rebuilding primitives: rostering, identity, permissions, observability
* Guessing at data models for courses, content, and results
* No way to validate impact, so iteration is slow and proof is expensive
* No reliable feedback loop to tell you what's actually improving learning
In other software categories, platforms reduce this tax. In education, the lack of shared standards and outcome-linked measurement means every serious team ends up trying to become a platform, whether they want to or not.
Edtech apps are expensive to build, hard to measure, and don't work together. Most fail to move the needle on actual learning. Not because the teams lack talent, but because **the infrastructure to build effective, measurable, interoperable learning products does not exist**.
***
This page describes the problems Timeback is built around. Next, see how the platform approaches
standards, measurement, and interoperability.
See what Timeback is building toward.
The platform architecture designed to close the loop.
What developers get by building on Timeback.
# The Vision
Source: https://docs.timeback.com/beta/about-timeback/vision
A learning platform where outcomes are measurable and improvable
This page presents Timeback's product rationale and goals. Educational efficacy, market-wide comparisons, distribution commitments, and current program availability require evidence beyond repository code. Use the integration guides for implemented API behavior.
Vision pillar
What it enables
Academics are efficient
Master academics faster, reclaim hours daily
A billion kids
Global scale through AI and falling costs
Proven in production
Evaluate outcomes for your own app
Build once, reach many
Reach schools, families, and global markets
Outcomes as product
Measured, verifiable, continuously improved
Standards backbone
Stop rebuilding the same infra from scratch
Learning science
Effective practices become platform defaults
Motivation + rigor
High standards that are engaging and hard to game
Timeback is building an education platform where **learning outcomes are measured**, comparable across tools, and improved through a closed loop. What students do in software connects directly to what they can demonstrate on credible assessments.
## Give kids their time back
The core promise: **academics get radically more efficient**. Students who learn faster reclaim meaningful time every day. That reclaimed time is the point. Space for sports, life skills, projects, creativity, and the rest of childhood.
Time back is earned through **real mastery**. Students finish academics by demonstrating competence, then own their afternoon. The goal is a world where time-based schooling feels as outdated as time-based billing for compute.
## Scale to a billion kids
The long-term ambition is **global scale**. That requires a cost curve falling with software and AI progress. Once the closed loop exists, the system can improve instruction efficiently, localize content, and expand access without reinventing everything for each geography.
AI is the lever. The platform uses AI to generate and personalize content, adapt pacing, close feedback loops, and reduce the marginal cost of instruction. As costs drop, access expands. The target: **more learning per hour** for more students, and more life reclaimed because academics stop consuming the whole day.
## Alpha School: the model in production
**[Alpha School](https://alpha.school)** is the working implementation. Students complete academics in two hours, then spend the rest of the day on sports, life skills workshops, passion projects, and character development. Results are measured continuously against external standardized tests.
The intended operating model is:
| Metric | Alpha School | Traditional |
| ------------------------ | -------------- | ----------- |
| **Daily academics** | 2 hours | 6+ hours |
| **Mastery verification** | Continuous | End-of-term |
| **Time for life** | 4+ hours daily | Homework |
For developers, Alpha School provides **credibility**. An app must establish its own learning outcomes; using the same platform does not transfer evidence of efficacy. The same platform, the same learning engine, the same closed loop that powers Alpha School also powers your app.
## Build once, reach many contexts
Timeback is a platform. The same infrastructure powers multiple deployment contexts:
| Context | Description |
| --------------------------- | ---------------------------------------------------------- |
| **Alpha School** | Flagship premium private education |
| **Sports Academies** | Athletics-focused schools where earned time unlocks sports |
| **GT Schools** | High-achievement tracks for academically driven students |
| **Micro schools** | Small learning communities running the full platform |
| **Homeschool** | Families using mastery-based academics |
| **Charter/public pilots** | Schools adopting the platform via voucher and ESA programs |
| **International expansion** | Students globally as costs come down |
Developers **build once**. Apps reach students across all contexts through a single integration. As Timeback grows, distribution grows with it.
## Learning outcomes as the unit of progress
Education has plenty of metrics, but few hold up under scrutiny. Timeback's default unit is an **outcome**: a demonstrated skill that transfers, persists, and shows up on meaningful assessments.
The platform answers three questions continuously:
| Question | Signals |
| ------------------------------- | ------------------------------------------- |
| **What did the student do?** | Attempts, time, errors, retries, effort |
| **What did the student learn?** | Mastery evidence, retention, transfer |
| **Did it hold up?** | Standardized tests and credible assessments |
Instead of debating efficacy, the system **measures it**, compares approaches, and iterates. Apps improve over time because the feedback signal is shared and outcome-linked.
## A shared, standards-based record
Timeback aims to become the **shared system of record** for the objects education software constantly re-creates: students, rosters, courses, content, results, credentials, and events.
With a shared record, apps can specialize:
* A tutoring app relies on the same student context as an assessment app
* A content tool emits results that a reporting tool can interpret
* Builders stop paying the "rebuild the platform" tax for every new product
The platform builds on the best available education standards (including 1EdTech specs) and extends where necessary. Developers cover real-world school requirements without inventing proprietary substitutes. Layered access for builders who want raw standards fidelity *and* for builders who want higher-level convenience.
## Learning science as product constraints
Learning science should show up as **constraints and defaults in software**, not just in research papers or training decks:
| Constraint | Purpose |
| ----------------------------- | ------------------------------------------------- |
| **Mastery gating** | No advancement without demonstrated understanding |
| **Prerequisite verification** | Gaps are caught before they compound |
| **Retrieval practice** | Active recall strengthens retention |
| **Spaced review** | Protects learning over time |
When apps align on how mastery is defined and how progress is recorded, tools compound rather than conflict. Builders innovate on experiences and content while still participating in a common outcomes loop.
Timeback's learning science stance: make the effective path the default path, and measure the
result.
## Motivation without lowering standards
A platform demanding real mastery must also make sustained effort **emotionally worth it**. Timeback treats motivation as a first-class system problem, with mechanisms that reward behaviors driving learning and guardrails that reduce gaming.
**Time back** is the primary motivator: finish academics efficiently, earn your afternoon. Beyond that:
* **XP** as a universal progress currency
* **Rewards** tied to mastery, not just completion
* **Leaderboards** engineered to create wins
* **Visible proof** that effort leads to results
The intent: help students move from extrinsic motivation to intrinsic motivation through competence, identity, and real progress.
## Integrity and verifiability as defaults
If outcomes matter, **integrity matters**. Timeback is building toward a world where apps can prove what happened during learning. The goal: make measurement trustworthy and improvement possible.
For developers, this means building in an environment where:
* **Effective work** is measurable
* **Anomalies** are detectable
* **Outcomes** can be trusted across tools
When integrity is shared infrastructure, builders spend more time on product differentiation and less time inventing anti-cheat systems in isolation.
***
This page describes where Timeback is headed. For the mechanics and interfaces, continue to the
architecture and principles pages.
Why current edtech can't reliably produce or prove outcomes.
The platform components that enable the closed loop.
The constraints and defaults Timeback optimizes for.
What developers get by building on the platform.
# Why Build Here
Source: https://docs.timeback.com/beta/about-timeback/why-build-here
Distribution, outcomes, and infrastructure — why Timeback is the platform to build on
This page presents Timeback's product rationale and goals. Educational efficacy, market-wide comparisons, distribution commitments, and current program availability require evidence beyond repository code. Use the integration guides for implemented API behavior.
Benefit
What it means
Distribution without BD
Reach every school and context the platform serves
Building effective edtech is hard. Distribution is harder. Proving outcomes is nearly impossible. Timeback aims to reduce those costs through shared infrastructure and better measurement.
**Build the learning experience and integrate with shared services.**
## Distribution without BD
Distribution is subject to onboarding, curriculum fit, deployment readiness, and the team's current program. Approval alone does not guarantee immediate reach.
Timeback powers multiple deployment contexts:
| Context | Students you reach |
| -------------------------- | -------------------------------------- |
| **Alpha School** | Flagship private education |
| **Sports Academies** | Athletics-focused schools |
| **GT Schools** | High-achievement tracks |
| **Micro schools** | Small learning communities |
| **Homeschool** | Families using mastery-based academics |
| **Charter/ public pilots** | Schools via voucher and ESA programs |
| **International** | Global expansion as costs drop |
A reusable integration can support multiple contexts when the team approves and deploys it. Confirm the channels available for your app.
## Skip the infrastructure
Every edtech team rebuilds the same plumbing. On Timeback, you don't.
| What you skip building | What the platform provides |
| ---------------------- | ------------------------------- |
| Rostering & identity | OneRoster sync, LTI launch |
| Progress tracking | Mastery state, prerequisites |
| Analytics & events | Caliper event stream |
| Content delivery | Standards-based packaging |
| Motivation systems | XP, rewards, time-back tracking |
**What you build:** the learning experience itself.
Focus on instruction, content, and outcomes. The commodity infrastructure is shared.
For architecture details, see [How It Works](/beta/about-timeback/how-it-works).
## Know your impact
Most edtech can't prove it works. You can.
Timeback's closed loop connects in-app activity to standardized test performance. Your app emits learning events; the platform correlates them with real assessment gains.
| Instead of | You get |
| ------------------- | ---------------------- |
| Engagement theater | Outcome validation |
| Guessing what works | Data on what transfers |
| Hoping for impact | Measuring it |
When learning fails, the data shows where. Iterate based on evidence, not intuition.
## Proven model
[Alpha School](https://alpha.school) is not a theory. Students complete academics in two hours, then own their afternoon. Outcomes are measured continuously against external standardized tests.
Shared infrastructure may support evaluation, but it does not demonstrate efficacy for your app:
* The same platform
* The same learning engine
* The same closed loop
You're not asking schools to bet on an unproven approach. You're extending a system that already works.
For the full Alpha School story, see [The Vision](/beta/about-timeback/vision) or [watch the
video](https://www.youtube.com/watch?v=a06qSgfccZs).
## AI-first platform and tooling
Timeback is AI-native. The platform uses AI to generate content, personalize instruction, and adapt pacing at scale.
Developer tooling follows the same principle:
| Tool | What it enables |
| ------------------------ | ------------------------------------------------------ |
| **SDK** | Type-safe integration with platform APIs |
| **CLI** | Configuration, resource operations, and API inspection |
| **MCP** | AI-assisted development workflows |
| **Developer dashboards** | Outcome visibility for your app |
Build with AI, not around legacy systems. The platform is designed for how you build now.
## Early mover advantage
The platform is early. That's the opportunity.
* **Influence the roadmap** — your needs shape what gets built
* **Distribution planning** — confirm available channels during onboarding
* **Tight partnership** — direct access to the platform team
* **Set the standard** — early integrations define best practices
Build now for maximum leverage.
## Partner stories
We're collecting early partner stories showing how Timeback integration improves outcomes and reduces integration overhead.
Partner case studies coming soon. Interested in being featured? [Book a call](https://app.cal.com/team/timeback-dev/developer-onboarding).
## Join the community
Connect with other developers building on Timeback. Share what you're working on, get help from the team, and shape the platform roadmap.
Join the Timeback developer community
***
See the onboarding workflow and app lifecycle.
Choose the path that fits your app.
Schedule an onboarding call and get access to staging.
# Create Events
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/caliper/events/create-events
/openapi/beyond-ai/caliper-api.yaml post /caliper/event
Receives and processes Caliper events wrapped in an envelope. Events will be validated against the IMS Caliper Analytics specification and stored for further processing and analysis.
# Get Event by External ID
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/caliper/events/get-event-by-external-id
/openapi/beyond-ai/caliper-api.yaml get /caliper/events/{externalId}
Get Caliper Event by External ID
# List Events
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/caliper/events/list-events
/openapi/beyond-ai/caliper-api.yaml get /caliper/events
List Caliper Events
# Validate Events
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/caliper/events/validate-events
/openapi/beyond-ai/caliper-api.yaml post /caliper/event/validate
Validate Caliper Events
# Get job status
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/caliper/jobs/get-job-status
/openapi/beyond-ai/caliper-api.yaml get /jobs/{jobId}/status
Returns the current status of a job including processing progress and assigned event IDs if completed
# Experimental. Replace a Package
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/case/learning-standards/experimental-replace-a-package
/openapi/beyond-ai/case-api.yaml put /ims/case/v1p1/CFPackages/{sourcedId}
Replace a complete CASE package with given sourcedId with new document, items, and associations.
Adds new CFItems, CFAssociations.
Removes deleted CFItems, CFAssociations.
Updates existing CFItems, CFAssociations.
Updates CFDocument.
# Get All Documents
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/case/learning-standards/get-all-documents
/openapi/beyond-ai/case-api.yaml get /ims/case/v1p1/CFDocuments
Returns a collection of all CASE documents in the system
# Get All Items
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/case/learning-standards/get-all-items
/openapi/beyond-ai/case-api.yaml get /ims/case/v1p1/CFItems
Returns a collection of all CASE Items
# Get Association by ID
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/case/learning-standards/get-association-by-id
/openapi/beyond-ai/case-api.yaml get /ims/case/v1p1/CFAssociations/{sourcedId}
Returns a specific CASE Association identified by its sourcedId
# Get Document by ID
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/case/learning-standards/get-document-by-id
/openapi/beyond-ai/case-api.yaml get /ims/case/v1p1/CFDocuments/{sourcedId}
Returns a specific CASE Document identified by its sourcedId
# Get Item by ID
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/case/learning-standards/get-item-by-id
/openapi/beyond-ai/case-api.yaml get /ims/case/v1p1/CFItems/{sourcedId}
Returns a specific CASE Item identified by its sourcedId
# Get Package by ID
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/case/learning-standards/get-package-by-id
/openapi/beyond-ai/case-api.yaml get /ims/case/v1p1/CFPackages/{sourcedId}
Returns a complete CASE package for the specified sourcedId
# Get Package with Groups by ID
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/case/learning-standards/get-package-with-groups-by-id
/openapi/beyond-ai/case-api.yaml get /ims/case/v1p1/CFPackages/{sourcedId}/groups
Returns a complete CASE package with groups for the specified sourcedId
# Get Standards Document by ID
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/case/learning-standards/get-standards-document-by-id
/openapi/beyond-ai/case-api.yaml get /ims/case/v1p1/standards/CFDocuments/{sourcedId}
Returns a specific CASE Document by sourcedId, excluding Course Sequences
# Get Standards Documents
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/case/learning-standards/get-standards-documents
/openapi/beyond-ai/case-api.yaml get /ims/case/v1p1/standards/CFDocuments
Returns a collection of CASE documents excluding Course Sequences
# Get Standards Item by ID
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/case/learning-standards/get-standards-item-by-id
/openapi/beyond-ai/case-api.yaml get /ims/case/v1p1/standards/CFItems/{sourcedId}
Returns a specific CASE Item by sourcedId, excluding items from Course Sequence documents
# Get Standards Items
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/case/learning-standards/get-standards-items
/openapi/beyond-ai/case-api.yaml get /ims/case/v1p1/standards/CFItems
Returns a collection of CASE Items excluding items from Course Sequence documents
# Get Standards Package by ID
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/case/learning-standards/get-standards-package-by-id
/openapi/beyond-ai/case-api.yaml get /ims/case/v1p1/standards/CFPackages/{sourcedId}
Returns a complete CASE package for the specified sourcedId, excluding Course Sequences
# Upload Package
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/case/learning-standards/upload-package
/openapi/beyond-ai/case-api.yaml post /ims/case/v1p1/CFPackages
Upload a complete CASE package with document, items, and associations
# Validate Item IDs
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/case/learning-standards/validate-item-ids
/openapi/beyond-ai/case-api.yaml post /ims/case/v1p1/CFItems/validate
Validates an array of CFItem sourcedIds. Returns which IDs are valid standards (exist and are not part of a Course Sequence) and which are invalid.
# Upsert a Verifiable Comprehensive Learner Record (CLR)
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/clr/credentials/upsert-a-verifiable-comprehensive-learner-record-clr
/openapi/beyond-ai/clr-api.yaml post /ims/clr/v2p0/credentials/
Validates and upserts a caller-supplied CLR credential by issuer and credential id. The inspected academic API controller stores parsed credential JSON; it does not assemble achievements from gradebook history or sign the CLR. A nonempty credentialSubject.achievement list is required by the handler in addition to schema requirements. Existing-record updates replace data and dateLastModified without refreshing auxiliary columns. See the CLR client guide for input and verification limits.
# Get v2.0 API Discovery Information
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/clr/discovery/get-v20-api-discovery-information
/openapi/beyond-ai/clr-api.yaml get /ims/clr/v2p0/discovery/
Returns the OpenAPI 3.0 specification for the CLR v2.0 API. This endpoint provides
discovery information including available endpoints, OAuth2 flows, and supported scopes. This is a public
endpoint that allows clients to dynamically discover the service's capabilities without prior configuration.
# Bulk create calendar days for a date range
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/calendar-days/bulk-create-calendar-days-for-a-date-range
/openapi/beyond-ai/edubridge-api.yaml post /calendar/orgs/{orgSourcedId}/days/bulk-create
Generate calendar days across a date range and upsert each one (factory-reset semantics: every existing day in the range is fully overwritten, including status reset to active and metadata cleared). Days are classified as instructional weekdays by default; `weekendDays` (default Sat/Sun) are marked non-instructional; `overrides` (single date or range, any reason code) are applied on top, last-wins. The academic session is auto-detected per day when sessionSourcedId is omitted. Use this to bootstrap or reset a school calendar.
# Bulk update calendar days
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/calendar-days/bulk-update-calendar-days
/openapi/beyond-ai/edubridge-api.yaml post /calendar/orgs/{orgSourcedId}/days/bulk-update
Update multiple calendar days in a date range with the same properties. Useful for marking breaks, holidays, or other multi-day events.
# Count instructional days in a date range
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/calendar-days/count-instructional-days-in-a-date-range
/openapi/beyond-ai/edubridge-api.yaml get /calendar/orgs/{orgSourcedId}/count
Count the number of instructional days for a specific organization between two dates. Excludes weekends, holidays, and other non-instructional days.
# Create or update a calendar day
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/calendar-days/create-or-update-a-calendar-day
/openapi/beyond-ai/edubridge-api.yaml post /calendar/orgs/{orgSourcedId}/days/{date}
Create a new calendar day or update it if it already exists (based on org + date). Use this to add new days or ensure a day has specific properties.
# Get a specific calendar day
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/calendar-days/get-a-specific-calendar-day
/openapi/beyond-ai/edubridge-api.yaml get /calendar/orgs/{orgSourcedId}/days/{date}
Retrieve a specific calendar day by organization and date. Returns 404 if the day doesn't exist.
# Get calendar days for an organization
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/calendar-days/get-calendar-days-for-an-organization
/openapi/beyond-ai/edubridge-api.yaml get /calendar/orgs/{orgSourcedId}/days
Retrieve all calendar days for a specific organization. Optionally filter by session, date range, or instructional days only.
# Update a calendar day
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/calendar-days/update-a-calendar-day
/openapi/beyond-ai/edubridge-api.yaml put /calendar/orgs/{orgSourcedId}/days/{date}
Update a specific calendar day. Use this to change instructional status, minutes, reason codes, or add metadata. Returns 404 if the day doesn't exist.
# API Reference
Source: https://docs.timeback.com/beta/api-reference/overview
Choose the API family and contract that owns your integration
Timeback has several API families with different resource models, authentication rules, and event contracts. Match the endpoint, environment, and SDK client to the same family before implementing a workflow.
## API families
| Family | Owns | Integration boundary |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| Academic OneRoster | Courses, classes, enrollments, resources, and assessment results | Uses `/ims/oneroster/...`; course components and PowerPath conventions extend the roster model |
| EduBridge | Purpose-specific roster views, enrollment analytics, reports, and academic workflows | Shares academic data but applies its own filtering, joins, and response shapes |
| PowerPath | Lesson attempts, question delivery, progress, placement, and course sequencing | Behavior depends on lesson type, persisted results, and the applicable progression rules |
| QTI | Assessment items, tests, stimuli, validation, and response processing | Storage, renderer support, and scoring are separate concerns; external grading can be asynchronous |
| Academic CASE | Standards documents, items, associations, and course-sequence representations | Uses `/ims/case/v1p1/...` |
| Legacy Caliper and Timeback events | Event intake, storage, and downstream fanout | Uses a different transport and schema from partner and Platform event intake |
| Open Badges | Achievement definitions, badge issuance, credential ingestion, and verification | Awarding a badge requires an explicit call; callers establish eligibility |
| CLR | Ingestion of caller-supplied learner credentials | The academic handler stores parsed CLR JSON; it does not automatically assemble or sign a portfolio |
| Platform | Platform roster, applications, curriculum, competency tracks, content grading, analytics, XP, and webhooks | Uses its own API paths, schemas, and authorization; it is a separate service from the academic API |
The sidebar includes the complete published Platform OpenAPI document, including operations whose tags are absent from the document's top-level tag list.
## Choose an event contract
Use the [event contract guide](/beta/build-on-timeback/reference/event-contracts) before selecting a payload.
| Submission surface | Contract |
| ------------------------------------------- | -------------------------------------------------------------------------------- |
| Legacy `/caliper/event` | Legacy Caliper envelope and registered profiles, including `TimebackProfile` |
| Academic native `/caliper/v2/caliper/event` | Native batch intake with its own processing topology |
| Partner `/events/1.0` | Six supported partner event types; single-event submission or one-event envelope |
| Platform `/caliper/v1p2` | Platform event union, including native aggregate metrics |
An intake acknowledgment does not prove that XP, gradebook results, or progression were persisted. A valid event type is also not a universal XP rule. Use the producer and consumer contracts to determine the expected result.
## Authentication and environment
Complete [first steps](/beta/build-on-timeback/first-steps) for the intended environment. Server integrations commonly use OAuth client credentials, while user-facing integrations use the SDK's session and identity flow. Public discovery routes and service-specific authentication are exceptions to a blanket client-credentials rule.
Use the security requirements on the selected operation. Client configuration, token issuer, audience, scopes, application identity, and tenant must agree. Use the token response's expiry information rather than assuming every token lasts one hour. See [API client setup](/beta/build-on-timeback/clients/overview) for the MasteryTrack authentication exception.
## Reference freshness
These API files were refreshed from their published schema endpoints on September 11, 2026. The exports document what those endpoints published at that time; they do not establish the deployed source revision or prove every implementation behavior. Source-checked guides identify SDK and service revision boundaries where they differ from generated descriptions.
The CLR operation description has been corrected because the published text claimed automatic signing that is absent from the inspected handler. Read the [CLR client guide](/beta/build-on-timeback/clients/clr) for the full input and persistence limits.
Use the sidebar for operation details and the [integration guide](/beta/build-on-timeback/start-building/existing-apps) to connect those operations into an application workflow.
# AI Skills
Source: https://docs.timeback.com/beta/build-on-timeback/ai/skills
Agent skills for AI-assisted Timeback integration
The [Timeback SDK skills repository](https://github.com/superbuilders/timeback-sdk-skills) contains a set of [agent skills](https://agentskills.io) that guide coding agents through integration tasks. Install the skills into your project, then invoke them from your agent.
## Install
Configure skills using the [Timeback CLI](/beta/build-on-timeback/cli/overview#installation).
```bash theme={null}
timeback skills add
```
If you're setting up a new project, [`timeback init`](/beta/build-on-timeback/cli/init) will
offer to install skills automatically.
## Skills
End-to-end orchestrator. Discovers your app, then runs the setup, server, and client skills
in sequence. **Start here if you're unsure.**
Project setup: environment variables, CLI credentials, `timeback.config.json`. Includes environment and integration-status file setup; resource provisioning is delegated to the interactive CLI.
Installs the SDK, configures identity/auth, and mounts the framework adapter. TypeScript and
Python are both supported.
Wires [browser-side activity tracking](/beta/build-on-timeback/sdk/activity-tracking/intro).
Compatible with popular frameworks like React, Vue, Svelte, and Solid.
Migrates existing, custom-rolled Timeback integrations to the SDK while preserving behavior.
## Getting started
Run `/timeback-integrate` in your preferred agent harness:
Detects your app's language, framework, subjects, and auth system.
Runs `/timeback-init` to configure credentials and create `timeback.config.json`.
For server-backed apps, runs `/timeback-server` to install the SDK and mount the framework
adapter.
For browser apps, runs `/timeback-client` to wire activity tracking.
You can also run `/timeback-init`, `/timeback-server`, or `/timeback-client` individually for
focused work on a specific layer.
Current skills target [Level 1
integration](/beta/build-on-timeback/integration-levels#level-1-minimal-viable). Use
`/timeback-migrate` only when migrating an existing direct-API integration to the SDK.
Skills are implementation guidance, not an API version guarantee. Check generated code against the SDK version you install. An auth check and populated course IDs alone do not demonstrate that completion events reached downstream analytics.
## Related
Apply, get credentials, and prepare for integration
Step-by-step Level 1 integration guide
CLI commands referenced by the skills
Server and client SDK architecture
# API
Source: https://docs.timeback.com/beta/build-on-timeback/cli/api
Query Timeback APIs from the command line
## Overview
The `timeback api` command provides direct access to Timeback APIs from the command line.
## Services
| Service | Description |
| ------------------------- | ----------------------------------------------- |
| [`oneroster`](#oneroster) | OneRoster 1.2 API |
| [`edubridge`](#edubridge) | EduBridge API |
| [`caliper`](#caliper) | Caliper Analytics API |
| [`powerpath`](#powerpath) | PowerPath API |
| [`qti`](#qti) | QTI API |
| `case` | CASE standards and competencies |
| `clr` | Comprehensive Learner Record API |
| `masterytrack` | MasteryTrack API (separate credential provider) |
| `webhooks` | Webhook configuration |
## Discovery
View available resources and operations:
```bash theme={null}
# View API documentation
timeback api docs oneroster
# List available resources
timeback api oneroster --help
```
## Examples
### OneRoster
```bash theme={null}
# List schools
timeback api oneroster schools list
# List users (with limit)
timeback api oneroster users list --limit 100
# List enrollments (active only)
timeback api oneroster enrollments list --active
# Get specific user
timeback api oneroster users get
# Create user from JSON file
timeback api oneroster users create --file user.json
# Filter users by role
timeback api oneroster users list --role student
# Delete a user
timeback api oneroster users delete
```
### EduBridge
```bash theme={null}
# List enrollments for user
timeback api edubridge enrollments list
# Get activity data
timeback api edubridge analytics activity --student-id --start-date 2026-09-01 --end-date 2026-09-07
# Get weekly facts
timeback api edubridge analytics weekly-facts --email user@example.com --week-date 2026-09-07
# Get grade mastery
timeback api edubridge analytics grade-mastery Math
```
### Caliper
```bash theme={null}
# List events
timeback api caliper events list --limit 50
# Get events for a specific actor
timeback api caliper events list --actor-id
# Filter by actor email
timeback api caliper events list --actor-email user@example.com
```
### QTI
```bash theme={null}
# List assessment items
timeback api qti items list
# Get specific item
timeback api qti items get
```
### PowerPath
```bash theme={null}
# List test assignments
timeback api powerpath assignments list --student
# Get placement level
timeback api powerpath placement level --student --subject Math
# Get screening results
timeback api powerpath screening results --student
```
## Environment
Specify the target environment:
```bash theme={null}
# Use production
timeback api oneroster schools list --env production
# Use staging (default)
timeback api oneroster schools list --env staging
```
## Output Formats
```bash theme={null}
# Default format: JSON
timeback api oneroster schools list
# Explicit JSON output
timeback api oneroster schools list --format json
# Table format (human-readable)
timeback api oneroster schools list --format table
# JSON Lines format
timeback api oneroster schools list --format jsonl
```
## Credentials
API commands require credentials. Set them via:
1. **Environment variables** (recommended for CI):
```bash theme={null}
export TIMEBACK_API_CLIENT_ID=...
export TIMEBACK_API_CLIENT_SECRET=...
```
2. **Credentials command**:
```bash theme={null}
timeback credentials add
```
See [CLI: Credentials](/beta/build-on-timeback/cli/credentials) for details.
## Debug Mode
Enable verbose output to see request/response details:
```bash theme={null}
DEBUG=1 timeback api oneroster schools list
```
## Pagination
For large result sets:
```bash theme={null}
# Limit results
timeback api oneroster users list --limit 50
# Offset pagination
timeback api oneroster users list --offset 100 --limit 50
```
## Next Steps
Manage API credentials
Programmatic API access
# Credentials
Source: https://docs.timeback.com/beta/build-on-timeback/cli/credentials
Manage API credentials for Timeback services
## Overview
The `timeback credentials` command manages local API credentials for the Timeback and MasteryTrack providers. Credentials are stored as JSON in `~/.timeback/credentials.json`; the CLI creates the directory with mode `0700` and writes the file with mode `0600`.
If you're just getting started, `timeback init` will automatically prompt for credentials when
needed. Use `timeback credentials` when you need explicit control over credential management.
## Commands
### `add`
Add credentials interactively:
```bash theme={null}
timeback credentials add
```
The default provider is `timeback`. Choose one or more environments, then enter the client ID, client secret, and optional email for each environment. The email is used to look up your OneRoster profile. API and token URLs are resolved from the provider and environment.
For MasteryTrack, select its provider explicitly:
```bash theme={null}
timeback credentials add --provider masterytrack
```
The MasteryTrack wizard collects an API key and registered email for each selected environment.
### `email`
Add or update the email associated with your saved Timeback credentials:
```bash theme={null}
timeback credentials email
```
### `list`
List stored credentials:
```bash theme={null}
timeback credentials list
```
### `remove`
Remove stored credentials:
```bash theme={null}
timeback credentials remove
```
## Credential Priority
The CLI looks for credentials in this order:
1. **Environment variables**
2. **Stored credentials**
For Timeback, use `TIMEBACK_API_CLIENT_ID` and `TIMEBACK_API_CLIENT_SECRET`. The legacy `TIMEBACK_CLIENT_ID` and `TIMEBACK_CLIENT_SECRET` aliases are also accepted. For MasteryTrack, use `TIMEBACK_MASTERYTRACK_API_KEY` and `TIMEBACK_MASTERYTRACK_EMAIL`; the legacy `MASTERYTRACK_API_KEY` and `MASTERYTRACK_EMAIL` names remain supported.
## Next Steps
Use credentials to query APIs
Manage course configurations
# Init
Source: https://docs.timeback.com/beta/build-on-timeback/cli/init
Initialize a new Timeback project
## Overview
The `timeback init` command creates a [`timeback.config.json`](/beta/build-on-timeback/reference/configuration) file in your project. This is the starting point for [integrating with Timeback](/beta/build-on-timeback/start-building/existing-apps).
**API credentials required**
The wizard requires at least one configured environment and offers credential setup if none exists. For a new integration, obtain staging credentials from the [Developer
Portal](https://staging.developer.timeback.com). If you do not have credentials yet, complete
[first steps](/beta/build-on-timeback/first-steps) first.
## Usage
```bash theme={null}
timeback init
```
## Modes
The init wizard offers two setup paths:
First-time setup. Prompts for app name, subjects, grades, and launch URL.
Import existing courses from Timeback. Requires [API
credentials](/beta/build-on-timeback/cli/credentials).
## Interactive Prompts
When initializing a new app, you will be asked to configure:
1. **App name**: Your application's display name
2. **Courses**: Subject and grade combinations to support
3. **Launch URL**: The URL where your app is hosted
## Options
Push the config to Timeback after creating it
Custom path for the config file (default: `timeback.config.json`)
Skip supported confirmation prompts and choose create mode. App name, courses, launch URL, and missing credentials can still require input.
Target environment for optional sync (`staging` or `production`). Use with `--sync`.Skip optional Prettier formatting of the generated config.
## Examples
```bash theme={null}
# Interactive setup
timeback init
# Initialize and sync to Timeback
timeback init --sync
# Custom config path
timeback init --config ./config/timeback.json
# Skip supported confirmations (required app inputs still prompt)
timeback init --yes
```
## What Gets Created
After running `timeback init`, you'll have a `timeback.config.json` file:
```json theme={null}
{
"name": "My Learning App",
"launchUrl": "https://myapp.example.com",
"courses": [
{
"subject": "Math",
"grade": 3,
"ids": null
}
]
}
```
**The `ids` field is `null` until you run `timeback resources push`.**
See [Resources](/beta/build-on-timeback/cli/resources) for more information.
## AI Skills
After creating your config, `timeback init` offers to install [Timeback agent skills](/beta/build-on-timeback/ai/skills). These guide coding agents through SDK setup, server wiring, and client-side activity tracking.
This prompt is skipped when using `--yes` or when skills are already installed.
You can always install skills later with `timeback skills add`.
## Next Steps
Sync your resources
Learn about all config options
Agent skills for AI-assisted integration
# Overview
Source: https://docs.timeback.com/beta/build-on-timeback/cli/overview
Command-line tools for the Timeback platform
The Timeback CLI provides command-line access to:
| Feature | Description |
| ------------------------------------------------------ | ------------------------------------------- |
| [Initialization](/beta/build-on-timeback/cli/init) | Initialize your Timeback project |
| [Resources](/beta/build-on-timeback/cli/resources) | Push, pull, and sync course configurations |
| [API Access](/beta/build-on-timeback/cli/api) | Query and modify data via Timeback services |
| [Credentials](/beta/build-on-timeback/cli/credentials) | Manage local API credentials |
| [Studio](/beta/build-on-timeback/cli/studio) | Start the Timeback Studio server |
## Installation
The easiest way to install the CLI is to run the following:
```bash theme={null}
curl -fsSL https://timeback.dev/cli | bash
```
Or install via a package manager:
```bash npm theme={null}
npm install -g timeback
```
```bash pnpm theme={null}
pnpm add -g timeback
```
```bash yarn theme={null}
yarn global add timeback
```
```bash bun theme={null}
bun add -g timeback
```
## Commands
| Command | Description |
| ------------------------------------- | ---------------------------------------------------------------------- |
| `timeback credentials` | Add, list, remove, or update local credentials |
| `timeback inspect course ` | Inspect course structure; use `--env` and `--json` as needed |
| `timeback init` | Initialize a new Timeback project |
| `timeback resources` | Manage course configurations |
| `timeback api` | Interact with Timeback APIs |
| `timeback skills` | Install or remove [AI agent skills](/beta/build-on-timeback/ai/skills) |
| `timeback studio` | Start Studio development server |
| `timeback upgrade` | Update CLI to latest version |
## What's next?
Push, pull, import, and unlink courses
Query Timeback APIs directly
Manage API credentials
Start the Studio server
Find setup and integration skills to pair with CLI commands
# Resources
Source: https://docs.timeback.com/beta/build-on-timeback/cli/resources
Manage course configurations with push, pull, and unlink
## Overview
The `timeback resources` command manages course configurations between your local `timeback.config.json` and Timeback's remote services. Without `--env`, these commands prompt for staging or production; they do not unconditionally default to staging. All three accept `--config ` to select a different config.
## Commands
| Command | Description |
| -------- | ---------------------------------------------------------- |
| `push` | Push local config to Timeback (local → remote) |
| `pull` | Pull remote changes into local config (remote → local) |
| `unlink` | Unlink courses from Timeback (deletes remote, keeps local) |
## push
Push resources defined in your local `timeback.config.json` to Timeback.
```bash theme={null}
# Push to staging
timeback resources push --env staging
# Push to production
timeback resources push --env production
# Preview changes without applying
timeback resources push --dry-run
```
| Flag | Description |
| ------------- | ---------------------------------------------- |
| `--env ` | Target environment (`staging` or `production`) |
| `--dry-run` | Preview changes without applying |
| `--yes` | Skip confirmation prompt |
## pull
Pull remote course changes into your local `timeback.config.json`.
```bash theme={null}
# Pull from staging
timeback resources pull --env staging
# Pull from production
timeback resources pull --env production
# Pull and apply changes without prompting
timeback resources pull --apply
# Pull and import new resources
timeback resources pull --import
```
| Flag | Description |
| ------------- | ---------------------------------------------- |
| `--env ` | Source environment (`staging` or `production`) |
| `--apply` | Apply changes without prompting |
| `--import` | Run import flow for new courses after pull |
## unlink
Unlink courses from Timeback, deleting the remote but keeping local definitions.
```bash theme={null}
# Unlink from staging (interactive)
timeback resources unlink --env staging
# Unlink from production
timeback resources unlink --env production
# Unlink all courses
timeback resources unlink --all
# Preview without applying
timeback resources unlink --dry-run
```
| Flag | Description |
| ------------- | --------------------------------------------- |
| `--env ` | Target environment (required with `--yes`) |
| `--all` | Unlink all courses (no interactive selection) |
| `--dry-run` | Preview without applying |
| `--yes` | Skip confirmation (requires explicit `--env`) |
Unlink issues remote delete requests for the derived component-resource link, resource, course component, and course, then clears successfully unlinked IDs locally. It does not merely disconnect local configuration. There is no CLI undo command; partial failures can leave some remote resources deleted. Use `--dry-run` to inspect the selection.
## Next Steps
Query APIs directly
Full config reference
# Studio
Source: https://docs.timeback.com/beta/build-on-timeback/cli/studio
Start the Timeback Studio development server
## Overview
The `timeback studio` command starts the local Timeback Studio server, providing a visual interface for managing courses and content.
## Usage
```bash theme={null}
timeback studio
```
## Options
| Option | Description | Default |
| -------------- | ------------------------------------------ | -------------------------------------------------- |
| `--env ` | Explicit target: `staging` or `production` | Resolved from course IDs or selected interactively |
| `--playcademy` | Load a Playcademy config file | Off |
### Examples
```bash theme={null}
# Start studio with default settings
timeback studio
# Start against production
timeback studio --env production
# Start with specific course IDs
timeback studio course-123 course-456
```
## Features
Studio provides:
* **Course Editor**: Visual course structure editing
* **Resource Manager**: Manage learning resources
* **Enrollment Viewer**: View and manage enrollments
* **Analytics Dashboard**: View student progress
* **Config Sync**: Sync with `timeback.config.json`
## Requirements
* Valid API credentials (via `timeback credentials add` or environment variables)
* Course configuration from `timeback.config.json`, explicit course IDs, or the interactive import flow
If `--env` is omitted and the config contains course IDs for exactly one environment, Studio selects that environment. If both environments have IDs, it asks you to choose. Explicit course IDs without `--env` also trigger an environment prompt.
## Alternative: `timeback-studio`
You can also use the `timeback-studio` npm package directly:
```bash theme={null}
npm install -g timeback-studio
# Start Studio
timeback-studio serve
```
## Next Steps
Manage courses via CLI
timeback.config.json reference
# Upgrade
Source: https://docs.timeback.com/beta/build-on-timeback/cli/upgrade
Get the latest version of the Timeback CLI
## Usage
```bash theme={null}
timeback upgrade
```
## Upgrade to Specific Version
```bash theme={null}
# Upgrade to a specific version
timeback upgrade
```
## Next Steps
Full CLI documentation
Manage course configurations
# Caliper
Source: https://docs.timeback.com/beta/build-on-timeback/clients/caliper
Configure the Caliper client for the event contract assigned to your integration
The Caliper client builds and sends learning events. Available routes, payload transformation, response fields, validation, and historical queries depend on the selected platform. Start with [Event Contracts](/beta/build-on-timeback/reference/event-contracts).
## Platform capabilities
| Client platform | Default submission path | Query, validation, and job methods |
| --------------- | ----------------------- | ----------------------------------------------------------------- |
| `BEYOND_AI` | `/caliper/event` | Legacy `validate`, `list`, `get`, `stream`, and job polling paths |
| `LEARNWITH_AI` | `/caliper/v1p2` | Send-only in this client; unsupported methods raise an error |
The default platform is `BEYOND_AI`. `LEARNWITH_AI` applies a destination-specific event transformation before submission. Changing only the base URL does not prove that the selected paths or payload schema match another API. The Partner Events API at `/events/1.0` has a separate contract.
## Installation
```bash TypeScript theme={null}
npm install @timeback/caliper
```
```bash Python theme={null}
pip install timeback-caliper
```
## Send an activity
Run credentialed client code on a trusted backend. These examples explicitly select the legacy contract and supply synthetic identities. Replace them with assigned user, course, and app values.
```typescript TypeScript theme={null}
import { CaliperClient } from '@timeback/caliper'
const client = new CaliperClient({
platform: 'BEYOND_AI',
env: 'staging',
auth: {
clientId: process.env.CALIPER_CLIENT_ID!,
clientSecret: process.env.CALIPER_CLIENT_SECRET!,
},
})
const result = await client.events.sendActivity('https://learning.example.com', {
id: 'urn:uuid:437620ad-f9c9-4a9d-9232-37676bfcf28e',
eventTime: '2026-09-11T15:00:00.000Z',
actor: {
id: 'https://api.example.com/ims/oneroster/rostering/v1p2/users/student-example',
type: 'TimebackUser',
email: 'learner@example.com',
},
object: {
id: 'https://learning.example.com/activities/fractions',
type: 'TimebackActivityContext',
subject: 'Math',
app: { name: 'Example Learning App' },
course: {
id: 'https://api.example.com/ims/oneroster/rostering/v1p2/courses/course-example',
name: 'Fractions',
},
activity: { name: 'Adding fractions' },
process: true,
},
metrics: [
{ type: 'totalQuestions', value: 5 },
{ type: 'correctQuestions', value: 4 },
{ type: 'xpEarned', value: 10 },
{ type: 'masteredUnits', value: 0 },
],
})
if (result.jobId && result.jobId !== '0') {
const job = await client.jobs.waitForCompletion(result.jobId)
console.log(job.state, job.returnValue)
}
```
```python Python theme={null}
import os
from timeback_caliper import CaliperClient, ActivityCompletedInput
client = CaliperClient(
platform="BEYOND_AI",
env="staging",
client_id=os.environ["CALIPER_CLIENT_ID"],
client_secret=os.environ["CALIPER_CLIENT_SECRET"],
)
# Run this code inside your async application.
result = await client.events.send_activity(
"https://learning.example.com",
ActivityCompletedInput(
id="urn:uuid:437620ad-f9c9-4a9d-9232-37676bfcf28e",
event_time="2026-09-11T15:00:00.000Z",
actor={
"id": "https://api.example.com/ims/oneroster/rostering/v1p2/users/student-example",
"type": "TimebackUser",
"email": "learner@example.com",
},
object={
"id": "https://learning.example.com/activities/fractions",
"type": "TimebackActivityContext",
"subject": "Math",
"app": {"name": "Example Learning App"},
"course": {
"id": "https://api.example.com/ims/oneroster/rostering/v1p2/courses/course-example",
"name": "Fractions",
},
"activity": {"name": "Adding fractions"},
"process": True,
},
metrics=[
{"type": "totalQuestions", "value": 5},
{"type": "correctQuestions", "value": 4},
{"type": "xpEarned", "value": 10},
{"type": "masteredUnits", "value": 0},
],
),
)
if result.job_id and result.job_id != "0":
job = await client.jobs.wait_for_completion(result.job_id)
print(job.state, job.return_value)
```
The numeric XP value is supplied by the producer. It is not calculated from elapsed time. See [Timeback activity and time events](/beta/build-on-timeback/reference/timeback-events) for metric fields and [SDK events](/beta/build-on-timeback/reference/events) for SDK-specific construction.
## Build once, retry the same event
`sendActivity` and `sendTimeSpent` create a fresh event ID and timestamp when you omit them. Calling a convenience method again without preserving those fields can create a distinct event.
For durable delivery, build the event once using `createActivityEvent` or `createTimeSpentEvent`, retain the event payload, and send it with `client.events.send(sensor, events)`. The Python factories are `create_activity_event` and `create_time_spent_event`. Preserve the original event and metric collection identities across transport retries.
Do not edit a submitted payload and reuse its event ID as a replacement operation. The legacy event store retains the first content for an external ID. Use your integration's correction procedure.
## Methods
| Operation | TypeScript | Python |
| ------------------------ | ------------------------------------- | ------------------------------------------ |
| Send event array | `events.send(sensor, events)` | `events.send(sensor_id, events)` |
| Send envelope | `events.sendEnvelope(envelope)` | `events.send_envelope(envelope)` |
| Activity helper | `events.sendActivity(sensor, input)` | `events.send_activity(sensor_id, input)` |
| Time helper | `events.sendTimeSpent(sensor, input)` | `events.send_time_spent(sensor_id, input)` |
| Validate without storage | `events.validate(envelope)` | `events.validate(envelope)` |
| List stored events | `events.list(params)` | `events.list(...)` |
| Get external event ID | `events.get(externalId)` | `events.get(external_id)` |
| Iterate results | `events.stream(params)` | `events.stream(...)` |
| Inspect a job | `jobs.getStatus(jobId)` | `jobs.get_status(job_id)` |
| Poll a job | `jobs.waitForCompletion(jobId)` | `jobs.wait_for_completion(job_id)` |
Validation, queries, and polling require a configured path for that operation. The send result's `jobId`/`job_id` can be absent. The batch API's compatibility value `"0"` is not a job to poll.
## Job monitoring
For a legacy job, polling stops when queue state is `completed`, and raises for `failed` or timeout. It does not inspect every semantic error in the worker result. Inspect `returnValue` in TypeScript or `return_value` in Python, including any error or partial-result information.
A completed queue job does not prove that all webhook subscribers or downstream analytics succeeded. Verify the expected persisted result separately.
## Historical queries
Legacy event listing supports limit, offset, sensor, actor ID, actor email, start date, and end date. For example:
```typescript TypeScript theme={null}
const { events } = await client.events.list({
limit: 20,
sensor: 'https://learning.example.com',
startDate: '2026-09-11T00:00:00.000Z',
})
```
```python Python theme={null}
result = await client.events.list(
limit=20,
sensor="https://learning.example.com",
start_date="2026-09-11T00:00:00.000Z",
)
events = result.events
```
The legacy query store is not a complete search of events sent through other ingestion systems. Check the event destination before treating an empty query as a missing send.
## Generic and question events
`events.send` accepts event objects, but the server still enforces its selected schema. Support for an event in one platform does not guarantee support in another, and schema acceptance does not itself award XP.
The client also provides question-seen, question-answered, and question-graded factories. Their destination-specific shapes should be checked against the assigned contract before use. Use the [Partner Event Reference](/beta/build-on-timeback/reference/partner-events) or [Platform Event Reference](/beta/build-on-timeback/reference/platform-events) for those APIs.
## Error handling
The current package does not export an `ApiError` class. Handle failures using the errors provided by your installed client version. A portable error boundary can inspect `error instanceof Error` for a message; avoid logging access tokens or raw student event payloads.
Correct validation and authorization failures before retrying. For ambiguous network or server failures, retain event identity and use bounded retries. Confirm the resulting activity through the destination's supported read APIs.
# CASE
Source: https://docs.timeback.com/beta/build-on-timeback/clients/case
CASE API client for competency frameworks and academic standards
## Overview
The `@timeback/case` package provides a client for the CASE (Competency and Academic Standards Exchange) API, enabling:
* **Documents**: List and retrieve curriculum framework documents
* **Items**: List and retrieve individual competencies and learning objectives
* **Associations**: Retrieve relationships between CASE entities
* **Packages**: Upload, update, and retrieve complete framework bundles
## Installation
```bash npm theme={null}
npm install @timeback/case
```
```bash pnpm theme={null}
pnpm add @timeback/case
```
```bash yarn theme={null}
yarn add @timeback/case
```
```bash bun theme={null}
bun add @timeback/case
```
```bash pip theme={null}
pip install timeback-case
```
```bash uv theme={null}
uv add timeback-case
```
## Quick Start
```typescript TypeScript theme={null}
import { CaseClient } from '@timeback/case'
const client = new CaseClient({
env: 'staging',
auth: {
clientId: process.env.CASE_CLIENT_ID!,
clientSecret: process.env.CASE_CLIENT_SECRET!,
},
})
// List all framework documents
const { CFDocuments } = await client.documents.list()
// Get a specific item
const { CFItem } = await client.items.get('22222222-2222-4222-8222-222222222222')
// Upload a framework package
const result = await client.packages.create(packageInput)
```
```python Python theme={null}
import os
from timeback_case import CaseClient
client = CaseClient(
env="staging",
client_id=os.environ["CASE_CLIENT_ID"],
client_secret=os.environ["CASE_CLIENT_SECRET"],
)
# List all framework documents
documents = await client.documents.list()
# Get a specific item
item = await client.items.get("22222222-2222-4222-8222-222222222222")
# Upload a framework package
result = await client.packages.create(package_input)
```
## Documents
Curriculum framework documents — the top-level containers for standards.
```typescript TypeScript theme={null}
client.documents.list()
client.documents.list({ limit: 50, offset: 0 })
client.documents.get(sourcedId)
```
```python Python theme={null}
await client.documents.list()
await client.documents.list({"limit": 50, "offset": 0})
await client.documents.get(sourced_id)
```
| Method | Returns | Description |
| -------- | ------------------------------- | ---------------------------- |
| `list()` | `{ CFDocuments: CFDocument[] }` | List all framework documents |
| `get()` | `{ CFDocument: CFDocument }` | Get a document by sourcedId |
## Items
Individual competencies, standards, and learning objectives within a framework.
```typescript TypeScript theme={null}
client.items.list()
client.items.list({ limit: 100, offset: 0 })
client.items.get(sourcedId)
```
```python Python theme={null}
await client.items.list()
await client.items.list({"limit": 100, "offset": 0})
await client.items.get(sourced_id)
```
| Method | Returns | Description |
| -------- | ----------------------- | ------------------------ |
| `list()` | `{ CFItems: CFItem[] }` | List all framework items |
| `get()` | `{ CFItem: CFItem }` | Get an item by sourcedId |
## Associations
Relationships between CASE entities (e.g., "is child of", "is related to").
```typescript TypeScript theme={null}
client.associations.get(sourcedId)
```
```python Python theme={null}
await client.associations.get(sourced_id)
```
| Method | Returns | Description |
| ------- | ---------------------------------- | ------------------------------- |
| `get()` | `{ CFAssociation: CFAssociation }` | Get an association by sourcedId |
## Packages
Complete framework bundles containing documents, items, and associations. Identifiers must be UUIDs; package entities require `lastChangeDateTime`. TypeScript validates with Zod and Python with Pydantic before sending. The example uses illustrative IDs; use IDs assigned to your framework. `upsert` tries PUT and falls back to POST only on 404.
### Upload a Package
```typescript TypeScript theme={null}
const result = await client.packages.create({
CFDocument: {
identifier: '11111111-1111-4111-8111-111111111111',
uri: 'https://example.edu/frameworks/math-k12',
title: 'K-12 Math Standards',
creator: 'Example District',
lastChangeDateTime: '2026-09-11T00:00:00Z',
},
CFItems: [
{
identifier: '22222222-2222-4222-8222-222222222222',
uri: 'https://example.edu/frameworks/math-k12/items/1',
fullStatement: 'Understand addition within 20',
lastChangeDateTime: '2026-09-11T00:00:00Z',
},
],
CFAssociations: [
// ... relationships between items
],
})
```
```python Python theme={null}
result = await client.packages.create({
"CFDocument": {
"identifier": "11111111-1111-4111-8111-111111111111",
"uri": "https://example.edu/frameworks/math-k12",
"title": "K-12 Math Standards",
"creator": "Example District",
"lastChangeDateTime": "2026-09-11T00:00:00Z",
},
"CFItems": [
{
"identifier": "22222222-2222-4222-8222-222222222222",
"uri": "https://example.edu/frameworks/math-k12/items/1",
"fullStatement": "Understand addition within 20",
"lastChangeDateTime": "2026-09-11T00:00:00Z",
},
],
"CFAssociations": [
# ... relationships between items
],
})
```
### All Package Methods
```typescript TypeScript theme={null}
client.packages.create(packageInput)
client.packages.update(sourcedId, packageInput)
client.packages.upsert(sourcedId, packageInput)
client.packages.get(sourcedId)
client.packages.getGroups(sourcedId)
```
```python Python theme={null}
await client.packages.create(package_input)
await client.packages.update(sourced_id, package_input)
await client.packages.upsert(sourced_id, package_input)
await client.packages.get(sourced_id)
await client.packages.get_groups(sourced_id)
```
| Method | Returns | Description |
| ------------- | ---------------------------------------------- | -------------------------------------- |
| `create()` | `CFPackageUploadResult` | Upload a complete framework package |
| `update()` | `CFPackageUploadResult` | Replace a package by sourcedId |
| `upsert()` | `CFPackageUploadResult` | Create or replace a package |
| `get()` | `{ CFPackage: CFPackage }` | Get a package by sourcedId |
| `getGroups()` | `{ CFPackageWithGroups: CFPackageWithGroups }` | Get a package with hierarchical groups |
## Standalone vs Composed
The client works standalone or composed into `@timeback/core`:
```typescript TypeScript theme={null}
// Standalone
import { CaseClient } from '@timeback/case'
const client = new CaseClient({ env: 'staging', auth })
// Composed
import { TimebackClient } from '@timeback/core'
const timeback = new TimebackClient({ env: 'staging', auth })
timeback.case.documents.list()
```
```python Python theme={null}
# Standalone
from timeback_case import CaseClient
client = CaseClient(env="staging", client_id=client_id, client_secret=client_secret)
# Composed (note: case_ avoids Python keyword conflict)
from timeback_core import TimebackClient
timeback = TimebackClient(env="staging", client_id=client_id, client_secret=client_secret)
await timeback.case_.documents.list()
```
## Error Handling
```typescript TypeScript theme={null}
import { CaseError, NotFoundError } from '@timeback/case/errors'
try {
await client.documents.get('99999999-9999-4999-8999-999999999999')
} catch (error) {
if (error instanceof NotFoundError) {
console.log('Document not found')
} else if (error instanceof CaseError) {
console.log(error.statusCode)
console.log(error.message)
}
}
```
```python Python theme={null}
from timeback_case import CaseError, NotFoundError
try:
await client.documents.get("99999999-9999-4999-8999-999999999999")
except NotFoundError:
print("Document not found")
except CaseError as error:
print(error)
```
## Configuration
```text TypeScript — API notation (not executable) theme={null}
new CaseClient({
// Environment mode (Timeback APIs)
env: 'staging' | 'production',
auth: {
clientId: string,
clientSecret: string,
},
// OR Explicit mode (custom API)
baseUrl: string,
auth: {
clientId: string,
clientSecret: string,
authUrl: string,
},
// OR Provider mode (shared auth across clients)
provider: TimebackProvider,
// Optional
timeout?: number, // Request timeout in ms (default: 30000)
})
```
```python Python theme={null}
# Environment mode (Timeback APIs)
client = CaseClient(
env="staging", # or "production"
client_id="...",
client_secret="...",
)
# Explicit mode (custom API)
client = CaseClient(
base_url="https://custom.example.com",
auth_url="https://auth.example.com/oauth2/token",
client_id="...",
client_secret="...",
)
# Provider mode (shared auth)
from timeback_common import TimebackProvider
provider = TimebackProvider(env="staging", client_id="...", client_secret="...")
client = CaseClient(provider=provider)
# Optional
# timeout: float = 30.0 (request timeout in seconds)
```
## Next Steps
Comprehensive Learner Records
Rostering and enrollments
CASE type definitions
# CLR
Source: https://docs.timeback.com/beta/build-on-timeback/clients/clr
Submit and discover Comprehensive Learner Record credentials
`@timeback/clr` and `timeback-clr` wrap the authenticated CLR v2.0 API. The client exposes `credentials.upsert()` and `discovery.get()`.
## Install
```bash TypeScript theme={null}
npm install @timeback/clr
```
```bash Python theme={null}
pip install timeback-clr
```
## Credential contract
`credentials.upsert()` sends `POST /ims/clr/v2p0/credentials/`. The receiver creates or updates a record identified by credential `id` and issuer `id`, returning the stored credential with HTTP 201 or 200. This path does **not** add a signature, verify a supplied proof, or aggregate a student's records automatically.
The receiver requires:
* Three ordered contexts: W3C credentials v2, CLR v2, then Open Badges v3.
* `type` containing both `VerifiableCredential` and `ClrCredential`.
* An issuer profile, credential name, URI identifier and ISO date in `validFrom`.
* A `ClrSubject` with at least one nested `verifiableCredential`.
* At least one top-level `credentialSubject.achievement`. This is an additional receiver requirement: an achievement nested only inside a child credential is insufficient. Each achievement needs an ID, type, name, description and criteria.
Optional email identity entries are resolved to a Timeback student; an unknown email fails the request. Existing proofs are data in this upsert path, not evidence that this endpoint verified or issued the credential.
## Example
This example illustrates the accepted structure. Replace the sample issuer, learner, achievement and credential records with your own authorized records before submitting.
```json credential.json theme={null}
{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://purl.imsglobal.org/spec/clr/v2p0/context-2.0.1",
"https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3"
],
"id": "https://example.edu/credentials/transcript-001",
"type": [
"VerifiableCredential",
"ClrCredential"
],
"issuer": {
"id": "https://example.edu",
"type": [
"Profile"
],
"name": "Example University"
},
"name": "Example achievement record",
"validFrom": "2026-01-01T00:00:00Z",
"credentialSubject": {
"id": "https://example.edu/learners/example",
"type": [
"ClrSubject"
],
"achievement": [
{
"id": "https://example.edu/achievements/algebra-1",
"type": [
"Achievement"
],
"name": "Algebra I",
"description": "Example achievement supplied by the issuer.",
"criteria": {
"narrative": "Meets the issuing organization's documented requirements."
},
"achievementType": "Course"
}
],
"verifiableCredential": [
{
"@context": [
"https://www.w3.org/ns/credentials/v2"
],
"id": "https://example.edu/credentials/algebra-001",
"type": [
"VerifiableCredential",
"AchievementCredential"
],
"issuer": {
"id": "https://example.edu",
"type": [
"Profile"
],
"name": "Example University"
},
"validFrom": "2026-01-01T00:00:00Z",
"credentialSubject": {
"id": "https://example.edu/learners/example"
}
}
]
}
}
```
```typescript TypeScript theme={null}
import { readFile } from 'node:fs/promises'
import { ClrClient } from '@timeback/clr'
const client = new ClrClient({
env: 'staging',
auth: {
clientId: process.env.CLR_CLIENT_ID!,
clientSecret: process.env.CLR_CLIENT_SECRET!,
},
})
const credential = JSON.parse(await readFile('credential.json', 'utf8'))
const stored = await client.credentials.upsert(credential)
const discovery = await client.discovery.get()
```
```python Python theme={null}
import json
import os
from pathlib import Path
from timeback_clr import ClrClient
client = ClrClient(
env="staging",
client_id=os.environ["CLR_CLIENT_ID"],
client_secret=os.environ["CLR_CLIENT_SECRET"],
)
credential = json.loads(Path("credential.json").read_text())
stored = await client.credentials.upsert(credential)
discovery = await client.discovery.get()
```
TypeScript validates the input with Zod; Python validates and serializes its input model. The receiver validates again. Successful client validation alone does not satisfy the receiver's achievement requirement.
## Discovery and composition
`discovery.get()` calls the authenticated discovery endpoint and returns its OpenAPI document. Route descriptions may describe intended capabilities; credential signing is not implemented by the reviewed upsert handler.
The same resources are available through `timeback.clr` on the [Core client](/beta/build-on-timeback/clients/core). Standalone configuration supports environment mode, explicit base/auth URLs, or a shared `TimebackProvider`; the optional request timeout is milliseconds in TypeScript and seconds in Python.
## Errors
```typescript TypeScript theme={null}
import { ClrError } from '@timeback/clr/errors'
try {
await client.credentials.upsert(credential)
} catch (error) {
if (error instanceof ClrError) {
console.error(error.statusCode, error.message)
} else {
throw error
}
}
```
```python Python theme={null}
from timeback_clr import ClrError
try:
await client.credentials.upsert(credential)
except ClrError as error:
print(error)
```
Input validation errors can occur before a request; HTTP error handling is separate. Check the response and your stored record rather than assuming an upsert produced a signed credential.
# Core
Source: https://docs.timeback.com/beta/build-on-timeback/clients/core
Unified client for all Timeback APIs with shared authentication
## Overview
The Core client (`@timeback/core` for TypeScript, `timeback-core` for Python) provides a unified client that aggregates all Timeback API clients with shared OAuth authentication:
* **OneRoster**: Rostering and gradebook data
* **EduBridge**: Simplified enrollments and analytics
* **Caliper**: Learning analytics events
* **QTI**: Assessment content management
* **PowerPath**: Placement tests and adaptive learning
* **CASE**: Competencies and standards
* **CLR**: Comprehensive Learner Records
* **Webhooks**: Webhook management and filters
* **Reporting** (TypeScript): Reporting queries
MasteryTrack uses a separate client and authentication configuration; it is not a `TimebackClient` sub-client.
The composed clients share a provider. Tokens are cached for the configured authentication endpoints; overriding an endpoint does not make every service use the same token.
## Installation
```bash npm theme={null}
npm install @timeback/core
```
```bash pnpm theme={null}
pnpm add @timeback/core
```
```bash yarn theme={null}
yarn add @timeback/core
```
```bash bun theme={null}
bun add @timeback/core
```
```bash pip theme={null}
pip install timeback-core
```
```bash uv theme={null}
uv add timeback-core
```
## Quick Start
```typescript TypeScript theme={null}
import { TimebackClient } from '@timeback/core'
const timeback = new TimebackClient({
env: 'staging',
auth: {
clientId: process.env.TIMEBACK_CLIENT_ID!,
clientSecret: process.env.TIMEBACK_CLIENT_SECRET!,
},
})
// Access any API through the unified client
const { data: schools } = await timeback.oneroster.schools.list()
const { data: users } = await timeback.oneroster.users.list({ where: { role: 'student' } })
await timeback.caliper.events.sendActivity(sensorUrl, activityInput)
```
```python Python theme={null}
import os
from timeback_core import TimebackClient
timeback = TimebackClient(
env="staging",
client_id=os.environ["TIMEBACK_CLIENT_ID"],
client_secret=os.environ["TIMEBACK_CLIENT_SECRET"],
)
# Access any API through the unified client
schools = await timeback.oneroster.schools.list()
users = await timeback.oneroster.users.list(where={"role": "student"})
await timeback.caliper.events.send_activity(sensor_url, activity_input)
```
## Configuration
### Environment Mode
Connect to Timeback's hosted APIs:
```typescript TypeScript theme={null}
const timeback = new TimebackClient({
env: 'staging', // or 'production'
auth: {
clientId: process.env.TIMEBACK_CLIENT_ID!,
clientSecret: process.env.TIMEBACK_CLIENT_SECRET!,
},
})
```
```python Python theme={null}
timeback = TimebackClient(
env="staging", # or "production"
client_id=os.environ["TIMEBACK_CLIENT_ID"],
client_secret=os.environ["TIMEBACK_CLIENT_SECRET"],
)
```
### Base URL Mode
Connect to a self-hosted or custom endpoint:
```typescript TypeScript theme={null}
const timeback = new TimebackClient({
baseUrl: 'https://timeback.myschool.edu',
auth: {
clientId: process.env.CLIENT_ID!,
clientSecret: process.env.CLIENT_SECRET!,
authUrl: 'https://timeback.myschool.edu/oauth/token',
},
})
```
```python Python theme={null}
timeback = TimebackClient(
services={
"oneroster": "https://timeback.myschool.edu",
"edubridge": "https://timeback.myschool.edu",
"caliper": "https://timeback.myschool.edu",
},
auth_url="https://timeback.myschool.edu/oauth/token",
client_id=os.environ["CLIENT_ID"],
client_secret=os.environ["CLIENT_SECRET"],
)
```
### Explicit Services Mode
Full control over each service URL. Only configure the services you need:
```typescript TypeScript theme={null}
const timeback = new TimebackClient({
services: {
oneroster: 'https://roster.example.com',
caliper: 'https://analytics.example.com',
// edubridge not configured — accessing it will throw
},
auth: {
clientId: '...',
clientSecret: '...',
authUrl: 'https://auth.example.com/oauth/token',
},
})
```
```python Python theme={null}
timeback = TimebackClient(
services={
"oneroster": "https://roster.example.com",
"caliper": "https://analytics.example.com",
"qti": "https://qti.example.com",
"powerpath": "https://roster.example.com",
"case": "https://roster.example.com",
"clr": "https://roster.example.com",
"webhooks": "https://analytics.example.com",
},
auth_url="https://auth.example.com/oauth/token",
client_id="...",
client_secret="...",
)
```
Each service can optionally override the default token URL (TypeScript):
```typescript theme={null}
const timeback = new TimebackClient({
services: {
oneroster: 'https://roster.example.com',
caliper: {
baseUrl: 'https://analytics.example.com',
authUrl: 'https://analytics-auth.example.com/token',
},
},
auth: {
clientId: '...',
clientSecret: '...',
authUrl: 'https://auth.example.com/oauth/token',
},
})
```
## Accessing APIs
Each API is lazily initialized on first access:
```typescript TypeScript theme={null}
// OneRoster - rostering and gradebook
timeback.oneroster.users.list()
timeback.oneroster.schools.get(schoolId)
timeback.oneroster.enrollments.list({ where: { role: 'student' } })
// EduBridge - simplified enrollments and analytics
timeback.edubridge.enrollments.list({ userId })
timeback.edubridge.analytics.getActivity({ studentId, startDate, endDate })
// Caliper - learning analytics
timeback.caliper.events.list()
timeback.caliper.events.sendActivity(sensor, input)
// QTI - assessment content
timeback.qti.assessmentItems.list()
timeback.qti.assessmentTests.get(testId)
// PowerPath - adaptive learning
timeback.powerpath.placement.getCurrentLevel({ student, subject })
// CASE - competencies and standards
timeback.case.documents.list()
// CLR - comprehensive learner records
timeback.clr.credentials.upsert(credential)
// Webhooks - webhook management
timeback.webhooks.webhooks.list()
```
```python Python theme={null}
# OneRoster - rostering and gradebook
await timeback.oneroster.users.list()
await timeback.oneroster.schools.get(school_id)
await timeback.oneroster.enrollments.list(where={"role": "student"})
# EduBridge - simplified enrollments and analytics
await timeback.edubridge.enrollments.list(user_id=user_id)
await timeback.edubridge.analytics.get_activity(student_id=user_id, start_date="...", end_date="...")
# Caliper - learning analytics
await timeback.caliper.events.list()
await timeback.caliper.events.send_activity(sensor, input)
# QTI - assessment content
await timeback.qti.assessment_items.list()
await timeback.qti.assessment_tests.get(test_id)
# PowerPath - adaptive learning
await timeback.powerpath.placement.get_current_level({"student": student_id, "subject": "Math"})
# CASE - competencies and standards (note: case_ avoids Python keyword conflict)
await timeback.case_.documents.list()
# CLR - comprehensive learner records
await timeback.clr.credentials.upsert(credential_input)
# Webhooks - webhook management
await timeback.webhooks.webhooks.list()
```
## Managing Multiple Clients
For applications that need to manage multiple `TimebackClient` instances, use `TimebackManager`:
```typescript theme={null}
import { TimebackManager } from '@timeback/core'
const manager = new TimebackManager()
.register('alpha', {
env: 'production',
auth: { clientId: '...', clientSecret: '...' },
})
.register('beta', {
env: 'production',
auth: { clientId: '...', clientSecret: '...' },
})
// Target a specific platform
const users = await manager.get('alpha').oneroster.users.list()
// Sync a user across all platforms (uses Promise.allSettled — never throws)
const results = await manager.broadcast(client => client.oneroster.users.create(user))
// Direct property access
if (results.alpha.ok) {
console.log('Created on alpha:', results.alpha.value.id)
}
// Convenience methods
if (results.allSucceeded) {
console.log('Synced to all platforms!')
}
results.succeeded.forEach(([name, user]) => {
console.log(`Created on ${name}:`, user.id)
})
results.failed.forEach(([name, error]) => {
console.error(`Failed on ${name}:`, error.message)
})
```
### Manager API
| Method | Description |
| ------------------------ | -------------------------------------------------------------------- |
| `register(name, config)` | Add a named client |
| `get(name)` | Retrieve a client by name |
| `has(name)` | Check if a client is registered |
| `names` | Get all registered client names |
| `size` | Get number of registered clients |
| `broadcast(fn)` | Execute on all clients (never throws), returns `BroadcastResults` |
| `unregister(name)` | Remove and close a client |
| `close()` | Close all clients |
### BroadcastResults API
| Property/Method | Description |
| --------------- | ------------------------------------------- |
| `succeeded` | Get successful results as `[name, value][]` |
| `failed` | Get failed results as `[name, error][]` |
| `allSucceeded` | `true` if all operations succeeded |
| `anyFailed` | `true` if any operation failed |
| `values()` | Get all values (throws if any failed) |
## Lifecycle
### Check Authentication
Verify OAuth credentials are working:
```typescript TypeScript theme={null}
const result = await timeback.checkAuth()
if (result.ok) {
console.log('Auth OK, latency:', result.latencyMs, 'ms')
} else {
console.error('Auth failed:', result.error)
}
```
```python Python theme={null}
result = await timeback.check_auth()
if result["ok"]:
print("Auth OK, latency:", result["latency_ms"], "ms")
else:
print("Auth failed:", result.get("error"))
```
### Close the Client
Release resources when done:
```typescript TypeScript theme={null}
timeback.close()
// After close(), further API calls will throw
console.log(timeback.closed) // true
```
```python Python theme={null}
await timeback.close()
# Or use as an async context manager
async with TimebackClient(...) as timeback:
schools = await timeback.oneroster.schools.list()
```
## Next Steps
Rostering and gradebook API
Simplified enrollments and analytics
Learning analytics events
Full-stack SDK with SSO and activity tracking
# EduBridge
Source: https://docs.timeback.com/beta/build-on-timeback/clients/edubridge
EduBridge API client for analytics and resource management
## Overview
The EduBridge client provides access to the EduBridge API, offering:
* **Enrollments**: Simplified enrollment management
* **Analytics**: Pre-aggregated student performance data
* **Subject Tracks**: K-12 curriculum sequence management
* **Users**: User management with role-based filtering
* **Applications**: Application-level configuration
* **Learning Reports**: Aggregated learning reports
## Installation
```bash npm theme={null}
npm install @timeback/edubridge
```
```bash pnpm theme={null}
pnpm add @timeback/edubridge
```
```bash yarn theme={null}
yarn add @timeback/edubridge
```
```bash bun theme={null}
bun add @timeback/edubridge
```
```bash pip theme={null}
pip install timeback-edubridge
```
```bash uv theme={null}
uv add timeback-edubridge
```
## Quick Start
```typescript TypeScript theme={null}
import { EdubridgeClient } from '@timeback/edubridge'
const client = new EdubridgeClient({
env: 'staging',
auth: {
clientId: process.env.EDUBRIDGE_CLIENT_ID!,
clientSecret: process.env.EDUBRIDGE_CLIENT_SECRET!,
},
})
const enrollment = await client.enrollments.enroll('user-123', 'course-123')
const enrollments = await client.enrollments.list({ userId: 'user-123' })
const activity = await client.analytics.getActivity({
studentId: 'user-123',
startDate: '2024-01-01',
endDate: '2024-01-31',
})
```
```python Python theme={null}
import os
from timeback_edubridge import EdubridgeClient
client = EdubridgeClient(
env="staging",
client_id=os.environ["EDUBRIDGE_CLIENT_ID"],
client_secret=os.environ["EDUBRIDGE_CLIENT_SECRET"],
)
enrollment = await client.enrollments.enroll("user-123", "course-123")
enrollments = await client.enrollments.list(user_id="user-123")
activity = await client.analytics.get_activity(
student_id="user-123",
start_date="2024-01-01",
end_date="2024-01-31",
)
```
## Enrollments
Course-centric enrollment management.
```text TypeScript — API notation (not executable) theme={null}
client.enrollments.list({ userId })
client.enrollments.enroll(userId, courseId, schoolId?, options?)
client.enrollments.unenroll(userId, courseId, schoolId?)
client.enrollments.resetGoals(courseId)
client.enrollments.resetProgress(userId, courseId)
client.enrollments.getDefaultClass(courseId, schoolId?)
```
```python Python theme={null}
await client.enrollments.list(user_id=user_id)
await client.enrollments.enroll(user_id, course_id, school_id=..., options=...)
await client.enrollments.unenroll(user_id, course_id, school_id=...)
await client.enrollments.reset_goals(course_id)
await client.enrollments.reset_progress(user_id, course_id)
await client.enrollments.get_default_class(course_id, school_id=...)
```
| Method | Returns | Description |
| ------------------- | ------------------ | ----------------------------------------- |
| `list()` | `Enrollment[]` | List enrollments for a user |
| `enroll()` | `Enrollment` | Enroll a user in a course |
| `unenroll()` | `void` | Mark the matching enrollment for deletion |
| `resetGoals()` | `ResetGoalsResult` | Reset goals for all users in course |
| `resetProgress()` | `void` | Reset a user's progress in a course |
| `getDefaultClass()` | `DefaultClass` | Get the default class for a course |
## Analytics
Student activity data and metrics.
```text TypeScript — API notation (not executable) theme={null}
client.analytics.getActivity({ studentId, startDate, endDate, timezone? })
client.analytics.getWeeklyFacts({ studentId, weekDate, timezone? })
client.analytics.getEnrollmentFacts({ enrollmentId, startDate?, endDate?, timezone? })
client.analytics.getHighestGradeMastered(studentId, subject)
```
```python Python theme={null}
await client.analytics.get_activity(student_id=..., start_date=..., end_date=..., timezone=...)
await client.analytics.get_weekly_facts(student_id=..., week_date=..., timezone=...)
await client.analytics.get_enrollment_facts(enrollment_id=..., start_date=..., end_date=..., timezone=...)
await client.analytics.get_highest_grade_mastered(student_id, subject)
```
| Method | Returns | Description |
| --------------------------- | ------------------------- | ---------------------------------------- |
| `getActivity()` | `ActivityResponse` | Activity metrics grouped by date/subject |
| `getWeeklyFacts()` | `WeeklyFacts` | Weekly summary for a student |
| `getEnrollmentFacts()` | `EnrollmentFactsResponse` | Aggregated facts for an enrollment |
| `getHighestGradeMastered()` | `HighestGradeMastered` | Highest grade mastered in a subject |
`getActivity()` returns an envelope with `facts` and `factsByApp`. For TypeScript, read XP with `activity.facts['2024-01-15']?.Math?.activityMetrics.xpEarned`. `getWeeklyFacts()` returns the unwrapped facts array; `getEnrollmentFacts()` retains its response envelope.
## Users
User filtering is exclusive: a user must have only the specified roles. `listStudents()` is a convenience for the student-only filter; it is not every user who also has a student role.
```text TypeScript — API notation (not executable) theme={null}
client.users.list({ roles, ...params })
client.users.listStudents(params?)
client.users.listTeachers(params?)
client.users.search(roles, searchTerm, limit?)
```
```text Python API notation (not executable) theme={null}
await client.users.list(roles=roles, ...)
await client.users.list_students(...)
await client.users.list_teachers(...)
await client.users.search(roles, search_term, limit=...)
```
## Subject Tracks
K-12 curriculum sequence management.
```typescript TypeScript theme={null}
client.subjectTracks.list()
client.subjectTracks.upsert(data)
client.subjectTracks.delete(id)
client.subjectTracks.listGroups()
```
```python Python theme={null}
await client.subject_tracks.list()
await client.subject_tracks.upsert(data)
await client.subject_tracks.delete(id)
await client.subject_tracks.list_groups()
```
## Applications
```typescript TypeScript theme={null}
client.applications.list()
client.applications.getMetrics(applicationSourcedId)
```
```python Python theme={null}
await client.applications.list()
await client.applications.get_metrics(application_sourced_id)
```
| Method | Returns | Description |
| -------------- | --------------- | ------------------------------ |
| `list()` | `Application[]` | List all applications |
| `getMetrics()` | `AppMetrics` | Get metrics for an application |
## Learning Reports
```typescript TypeScript theme={null}
client.learningReports.getMapProfile(userId)
client.learningReports.getTimeSaved(userId)
```
```python Python theme={null}
await client.learning_reports.get_map_profile(user_id)
await client.learning_reports.get_time_saved(user_id)
```
| Method | Returns | Description |
| ----------------- | ------------ | -------------------------- |
| `getMapProfile()` | `MapProfile` | Get MAP profile for a user |
| `getTimeSaved()` | `TimeSaved` | Get time saved for a user |
## Comparison with OneRoster
| Task | OneRoster | EduBridge |
| -------------- | ------------------------------------------ | -------------------------------- |
| Enroll user | Create class, academic session, enrollment | Single `enroll()` call |
| Get activity | Query Caliper separately | `getActivity()` with aggregation |
| User search | Filter with OneRoster syntax | Role-based filtering with search |
| Reset progress | Manage individual gradebook results | Single `resetProgress()` call |
Use EduBridge when you need simplified access to common queries. Use OneRoster when you need full control over rostering operations.
## Next Steps
Full rostering control
Learning event tracking
EduBridge type definitions
# OneRoster
Source: https://docs.timeback.com/beta/build-on-timeback/clients/oneroster
OneRoster 1.2 API client for rostering, enrollments, and gradebook
## Overview
The OneRoster client provides access to the OneRoster 1.2 API, supporting:
* **Rostering**: Users, orgs, schools, courses, classes, enrollments
* **Gradebook**: Categories, line items, results, score scales
* **Assessment**: Assessment line items and results
* **Resources**: Learning resources linked to courses
## Installation
```bash npm theme={null}
npm install @timeback/oneroster
```
```bash pnpm theme={null}
pnpm add @timeback/oneroster
```
```bash yarn theme={null}
yarn add @timeback/oneroster
```
```bash bun theme={null}
bun add @timeback/oneroster
```
```bash pip theme={null}
pip install timeback-oneroster
```
```bash uv theme={null}
uv add timeback-oneroster
```
## Quick Start
```typescript TypeScript theme={null}
import { OneRosterClient } from '@timeback/oneroster'
const client = new OneRosterClient({
env: 'staging',
auth: {
clientId: process.env.ONEROSTER_CLIENT_ID!,
clientSecret: process.env.ONEROSTER_CLIENT_SECRET!,
},
})
const { data: students, hasMore } = await client.users.list({ where: { role: 'student' } })
const user = await client.users.get('user-123')
```
```python Python theme={null}
import os
from timeback_oneroster import OneRosterClient
client = OneRosterClient(
env="staging",
client_id=os.environ["ONEROSTER_CLIENT_ID"],
client_secret=os.environ["ONEROSTER_CLIENT_SECRET"],
)
students = await client.users.list(where={"role": "student"})
user = await client.users.get("user-123")
```
## Rostering
### Users
Payload variables such as `userInput` below must contain all required fields. User creation requires `sourcedId`, `enabledUser`, `givenName`, `familyName`, `email`, and a nonempty `roles` array; a top-level `role` is not the create contract. Python write methods accept one payload dictionary using API field names, not arbitrary keyword arguments.
```typescript TypeScript theme={null}
client.users.list()
client.users.list({ where: { role: 'teacher' } })
client.users.first({ where: { role: 'administrator' } })
client.users.listAll()
client.users.get(userId)
client.users.exists(userId)
client.users.create(userInput)
client.users.update(userId, { givenName })
client.users.upsert(userId, userInput)
client.users.delete(userId)
```
```python Python theme={null}
await client.users.list()
await client.users.list(where={"role": "teacher"})
await client.users.first(where={"role": "administrator"})
await client.users.list_all()
await client.users.get(user_id)
await client.users.create(user_input)
await client.users.update(user_id, {"givenName": given_name})
await client.users.delete(user_id)
```
| Method | Returns | Description |
| ----------- | ------------------- | -------------------------------------- |
| `list()` | `PageResult` | List users (supports `where` filter) |
| `first()` | `User \| undefined` | Get first matching user |
| `listAll()` | `User[]` | Fetch all pages |
| `get()` | `User` | Get user by ID |
| `exists()` | `boolean` | Check if user exists (lightweight) |
| `create()` | `CreateResponse` | Create user (returns `sourcedIdPairs`) |
| `update()` | `void` | Update user (throws if not found) |
| `upsert()` | `void` | Create or update user |
| `delete()` | `void` | Delete user |
`exists()`, `update()`, and `upsert()` are available on all writable OneRoster resources.
* **`update(id, data)`**: strict (throws if the resource doesn't exist)
* **`upsert(id, data)`**: loose (creates the resource if it doesn't exist)
* **`exists(id)`**: lightweight existence check
### Scoped User Operations
Access user-specific data using the callable pattern:
```typescript TypeScript theme={null}
client.users('user-123').demographics()
client.users('user-123').classes()
client.users('user-123').resources()
client.users('user-123').agents()
client.users('user-123').agentFor()
client.users('user-123').addAgent({ sourcedId: 'parent-123', role: 'parent' })
client.users('user-123').removeAgent('parent-123')
client.users('user-123').registerCredential({ applicationName, credentials: { username, password } })
client.users('user-123').decryptCredential('cred-id')
```
```python Python theme={null}
await client.users("user-123").demographics()
await client.users("user-123").classes()
await client.users("user-123").resources()
await client.users("user-123").agents()
await client.users("user-123").agent_for()
await client.users("user-123").add_agent({"sourcedId": "parent-123", "role": "parent"})
await client.users("user-123").remove_agent("parent-123")
await client.users("user-123").register_credential({"applicationName": application_name, "credentials": {"username": username, "password": password}})
await client.users("user-123").decrypt_credential("cred-id")
```
### Students & Teachers
Dedicated read-only resources for role-specific queries:
```typescript TypeScript theme={null}
client.students.list()
client.students.get(studentId)
client.students(studentId).classes()
client.teachers.list()
client.teachers.get(teacherId)
client.teachers(teacherId).classes()
```
```python Python theme={null}
await client.students.list()
await client.students.get(student_id)
await client.students(student_id).classes()
await client.teachers.list()
await client.teachers.get(teacher_id)
await client.teachers(teacher_id).classes()
```
### Schools & Organizations
```typescript TypeScript theme={null}
client.schools.list()
client.schools.get(schoolId)
client.orgs.list()
// Nested resources
client.schools('school-id').classes()
client.schools('school-id').courses()
client.schools('school-id').terms()
client.schools('school-id').teachers()
client.schools('school-id').students()
client.schools('school-id').enrollments()
client.schools('school-id').scoreScales()
client.schools('school-id').lineItems()
// Deeply nested
client.schools('school-id').class('class-id').teachers()
client.schools('school-id').class('class-id').students()
client.schools('school-id').class('class-id').enrollments()
```
```python Python theme={null}
await client.schools.list()
await client.schools.get(school_id)
await client.orgs.list()
# Nested resources
await client.schools("school-id").classes()
await client.schools("school-id").courses()
await client.schools("school-id").terms()
await client.schools("school-id").teachers()
await client.schools("school-id").students()
await client.schools("school-id").enrollments()
await client.schools("school-id").score_scales()
await client.schools("school-id").line_items()
```
### Courses & Classes
```typescript TypeScript theme={null}
client.courses.list()
client.courses(courseId).classes()
client.courses(courseId).resources()
client.courses.components()
client.courses.getComponent(componentId)
client.courses.componentResources()
client.courses.getComponentResource(resourceId)
client.courses.createStructure(courseStructureInput)
client.classes.get(classId)
client.classes(classId).students()
client.classes(classId).teachers()
client.classes(classId).lineItems()
client.classes(classId).results()
client.classes(classId).categories()
client.classes(classId).scoreScales()
client.classes(classId).resources()
client.classes(classId).enroll({ sourcedId: userId, role: 'student' })
// Deeply nested gradebook
client.classes(classId).lineItem(lineItemId).results()
client.classes(classId).student(studentId).results()
```
```python Python theme={null}
await client.courses.list()
await client.courses(course_id).classes()
await client.courses(course_id).resources()
await client.courses(course_id).components()
await client.classes.get(class_id)
await client.classes(class_id).students()
await client.classes(class_id).teachers()
await client.classes(class_id).enrollments()
await client.classes(class_id).line_items()
await client.classes(class_id).results()
await client.classes(class_id).categories()
await client.classes(class_id).score_scales()
await client.classes(class_id).resources()
await client.classes(class_id).enroll({"sourcedId": user_id, "role": "student"})
# Deeply nested gradebook
await client.classes(class_id).line_item(line_item_id).results()
await client.classes(class_id).student(student_id).results()
```
### Enrollments
```typescript TypeScript theme={null}
client.enrollments.list()
client.enrollments.list({ where: { status: 'active' } })
client.enrollments.get(enrollmentId)
client.enrollments.create({ user, class: classRef, role })
client.enrollments.patch(enrollmentId, { status: 'tobedeleted' })
```
```python Python theme={null}
await client.enrollments.list()
await client.enrollments.list(where={"status": "active"})
await client.enrollments.get(enrollment_id)
await client.enrollments.create({"user": user_ref, "class": class_ref, "role": "student"})
await client.enrollments.patch(enrollment_id, {"status": "tobedeleted"})
```
### Terms & Academic Sessions
```typescript TypeScript theme={null}
client.terms.list()
client.terms(termId).classes()
client.terms(termId).gradingPeriods()
client.academicSessions.list()
client.gradingPeriods.list()
```
```python Python theme={null}
await client.terms.list()
await client.terms(term_id).classes()
await client.terms(term_id).grading_periods()
await client.academic_sessions.list()
await client.grading_periods.list()
```
### Demographics
```typescript TypeScript theme={null}
client.demographics.list()
client.demographics.get(demographicId)
```
```python Python theme={null}
await client.demographics.list()
await client.demographics.get(demographic_id)
```
## Gradebook
Gradebook resources are top-level on the client:
```typescript TypeScript theme={null}
// Core reads
await client.categories.list()
await client.lineItems.get(lineItemId)
await client.results.list()
// Gradebook writes return updated entities
const updatedCategory = await client.categories.update(categoryId, { title: 'Homework 2.0' })
const upsertedLineItem = await client.lineItems.upsert(lineItemId, lineItemInput)
// Assessment writes also return updated entities
const upsertedAssessmentResult = await client.assessmentResults.upsert(
assessmentResultId,
assessmentResultInput,
)
```
```python Python theme={null}
await client.categories.list()
await client.categories.get(category_id)
await client.line_items.list()
await client.line_items.get(line_item_id)
await client.line_items.create(line_item_input)
await client.line_items(line_item_id).results()
await client.results.list()
await client.results.create(result_input)
await client.score_scales.list()
# Assessment
await client.assessment_line_items.list()
await client.assessment_results.list()
```
For **gradebook + assessment** resources, write methods return the entity:
* **`update(id, data)`**: strict (throws on 404), returns entity
* **`upsert(id, data)`**: loose (creates/updates), returns entity
For **rostering + resources**, write methods remain `void`.
| Resource family | `update()` / `upsert()` return |
| ---------------------- | ------------------------------ |
| Rostering + Resources | `void` |
| Gradebook + Assessment | Updated entity |
## Resources
```typescript TypeScript theme={null}
client.resources.list()
client.resources.create(resourceInput)
client.resources('resource-id').export()
```
```python Python theme={null}
await client.resources.list()
await client.resources.create(resource_input)
await client.resources("resource-id").export()
```
## Pagination
Top-level paginated resource `list()` methods return `PageResult` with `{ data, hasMore, total, nextOffset }`.
```text TypeScript — API notation (not executable) theme={null}
client.users.list({ offset?, limit? })
client.users.listAll(options?)
client.users.stream(options?)
```
```python Python theme={null}
await client.users.list(offset=..., limit=...)
await client.users.list_all()
async for user in client.users.stream():
...
```
| Method | Returns | Description |
| ----------- | ------------------ | ----------------------------------- |
| `list()` | `PageResult` | Single page with pagination info |
| `listAll()` | `T[]` | Auto-fetches all pages |
| `stream()` | `AsyncIterable` | Memory-efficient streaming iterator |
## Filtering
Use the `where` parameter to filter results:
```typescript TypeScript theme={null}
client.users.list({ where: { role: 'student' } })
client.enrollments.list({ where: { status: 'active' } })
client.users.list({ where: { role: 'student', status: 'active' } })
```
```python Python theme={null}
await client.users.list(where={"role": "student"})
await client.enrollments.list(where={"status": "active"})
await client.users.list(where={"role": "student", "status": "active"})
```
Enrollment PUT requires a full enrollment payload. Use `patch(id, data)` for a partial status/date/metadata change. A scoped user credential registration requires `applicationName` plus both `username` and `password` under `credentials`.
## Error Handling
```typescript theme={null}
try {
const user = await client.users.get('invalid-id')
} catch (error) {
if (error instanceof Error) {
console.error(error.message)
if ('statusCode' in error && typeof error.statusCode === 'number') {
console.error(error.statusCode)
}
}
}
```
## Next Steps
Simplified analytics queries
Learning event tracking
OneRoster type definitions
# Overview
Source: https://docs.timeback.com/beta/build-on-timeback/clients/overview
Direct API access to Timeback services
Timeback provides API clients for direct access to the supported services below in TypeScript and Python. Use these when you need fine-grained control over API interactions or want to build custom integrations.
## Available Clients
| Client | Description | Typescript | Python |
| ------------ | ------------------------------ | :--------: | :----: |
| OneRoster | Rostering, enrollments, grades | ✓ | ✓ |
| EduBridge | Analytics and resources | ✓ | ✓ |
| Caliper | Learning event tracking | ✓ | ✓ |
| QTI | Assessments and questions | ✓ | ✓ |
| PowerPath | Adaptive learning paths | ✓ | ✓ |
| CASE | Competencies and standards | ✓ | ✓ |
| CLR | Comprehensive Learner Records | ✓ | ✓ |
| MasteryTrack | Test inventory and assignments | ✓ | ✓ |
| Webhooks | Webhook management and filters | ✓ | ✓ |
## Installation
Install individual clients as needed:
```bash npm theme={null}
npm install @timeback/oneroster
npm install @timeback/edubridge
npm install @timeback/caliper
npm install @timeback/qti
npm install @timeback/powerpath
npm install @timeback/case
npm install @timeback/clr
npm install @timeback/webhooks
npm install @timeback/masterytrack
```
```bash pnpm theme={null}
pnpm add @timeback/oneroster
pnpm add @timeback/edubridge
pnpm add @timeback/caliper
pnpm add @timeback/qti
pnpm add @timeback/powerpath
pnpm add @timeback/case
pnpm add @timeback/clr
pnpm add @timeback/webhooks
pnpm add @timeback/masterytrack
```
```bash yarn theme={null}
yarn add @timeback/oneroster
yarn add @timeback/edubridge
yarn add @timeback/caliper
yarn add @timeback/qti
yarn add @timeback/powerpath
yarn add @timeback/case
yarn add @timeback/clr
yarn add @timeback/webhooks
yarn add @timeback/masterytrack
```
```bash bun theme={null}
bun add @timeback/oneroster
bun add @timeback/edubridge
bun add @timeback/caliper
bun add @timeback/qti
bun add @timeback/powerpath
bun add @timeback/case
bun add @timeback/clr
bun add @timeback/webhooks
bun add @timeback/masterytrack
```
```bash pip theme={null}
pip install timeback-oneroster
pip install timeback-edubridge
pip install timeback-caliper
pip install timeback-qti
pip install timeback-powerpath
pip install timeback-case
pip install timeback-clr
pip install timeback-webhooks
pip install timeback-masterytrack
```
```bash uv theme={null}
uv add timeback-oneroster
uv add timeback-edubridge
uv add timeback-caliper
uv add timeback-qti
uv add timeback-powerpath
uv add timeback-case
uv add timeback-clr
uv add timeback-webhooks
uv add timeback-masterytrack
```
Or install the composed client (MasteryTrack remains separate; TypeScript also includes Reporting, which is not in the reviewed Python core):
```bash npm theme={null}
npm install @timeback/core
```
```bash pnpm theme={null}
pnpm add @timeback/core
```
```bash yarn theme={null}
yarn add @timeback/core
```
```bash bun theme={null}
bun add @timeback/core
```
```bash pip theme={null}
pip install timeback-core
```
```bash uv theme={null}
uv add timeback-core
```
## Authentication
Most clients use the OAuth2 client credentials flow. MasteryTrack uses its API key and registered email to obtain a JWT from its authorizer. The examples below show environment and explicit URL configuration. Some clients also accept an auth provider or transport; see their specific types.
### Environment Mode (Recommended)
Connect to Timeback platforms with automatic URL resolution:
```typescript TypeScript theme={null}
import { OneRosterClient } from '@timeback/oneroster'
const client = new OneRosterClient({
env: 'staging', // or 'production'
auth: {
clientId: process.env.ONEROSTER_CLIENT_ID!,
clientSecret: process.env.ONEROSTER_CLIENT_SECRET!,
},
})
```
```python Python theme={null}
import os
from timeback_oneroster import OneRosterClient
client = OneRosterClient(
env="staging", # or "production"
client_id=os.environ["ONEROSTER_CLIENT_ID"],
client_secret=os.environ["ONEROSTER_CLIENT_SECRET"],
)
```
### Explicit Mode
Connect to custom OneRoster APIs:
```typescript TypeScript theme={null}
const client = new OneRosterClient({
baseUrl: 'https://api.example.com',
auth: {
clientId: process.env.ONEROSTER_CLIENT_ID!,
clientSecret: process.env.ONEROSTER_CLIENT_SECRET!,
authUrl: 'https://auth.example.com/oauth2/token',
},
})
```
```python Python theme={null}
client = OneRosterClient(
base_url="https://api.example.com",
auth_url="https://auth.example.com/oauth2/token",
client_id=os.environ["ONEROSTER_CLIENT_ID"],
client_secret=os.environ["ONEROSTER_CLIENT_SECRET"],
)
```
Tokens are automatically managed:
* Fetched on first request
* Cached for subsequent requests
* OAuth tokens are refreshed as needed according to the provider's expiry cache. MasteryTrack uses a separate fixed-duration token cache.
## SDK Integration
With a fully configured SDK server, direct clients are available through `timeback.api`. The browser client does not expose server API credentials or the same direct API namespace.
```typescript Inside your server code theme={null}
// `timeback` is your configured SDK server, including identity callbacks.
const { data: users } = await timeback.api.oneroster.users.list()
const enrollments = await timeback.api.edubridge.enrollments.list({ userId })
```
```python Inside your server code theme={null}
# `timeback` is the configured server returned by create_server().
users = await timeback.api.oneroster.users.list()
enrollments = await timeback.api.edubridge.enrollments.list(user_id=user_id)
```
See [server setup](/beta/build-on-timeback/sdk/server/nextjs) or [FastAPI](/beta/build-on-timeback/sdk/server/fastapi) for complete configuration and [Caliper](/beta/build-on-timeback/clients/caliper) for event payloads.
## Common Patterns
Many resource clients provide these operations. This notation is a guide to common patterns, not a guarantee that every service exposes every method:
```text TypeScript API notation (not executable) theme={null}
client.users.list({ limit?, offset?, where? })
client.users.get(id)
client.users.exists(id)
client.users.create({ ...fields })
client.users.update(id, { ...fields })
client.users.upsert(id, { ...fields })
client.users.delete(id)
client.users.stream()
```
```python Python theme={null}
await client.users.list(limit=..., offset=..., where=...)
await client.users.get(id)
await client.users.create(...)
await client.users.update(id, ...)
await client.users.delete(id)
async for user in client.users.stream():
...
```
| Method | Returns | Description |
| ---------- | ------------------ | ------------------------------------------------------------------ |
| `list()` | `PageResult` | List with pagination/filters |
| `get()` | `Resource` | Get by ID |
| `exists()` | `boolean` | Check whether the resource exists; transport depends on the client |
| `create()` | `CreateResponse` | Create new resource |
| `update()` | `Resource \| void` | Update existing resource (throws if missing) |
| `upsert()` | `Resource \| void` | Create or update resource |
| `delete()` | `void` | Delete resource |
| `stream()` | `AsyncIterable` | Stream all results efficiently |
Return values for `update()` and `upsert()` are client-specific:
* Read the resource-specific signature; return shapes vary by operation
* Some APIs return no body on write, so the SDK returns `void`
* In OneRoster specifically: rostering/resources return `void`, while gradebook/assessment return the updated entity
## Environment Configuration
Configure clients via environment variables (when using environment mode, URLs are auto-resolved):
```bash .env theme={null}
# OneRoster
ONEROSTER_CLIENT_ID=your-client-id
ONEROSTER_CLIENT_SECRET=your-client-secret
# EduBridge
EDUBRIDGE_CLIENT_ID=your-client-id
EDUBRIDGE_CLIENT_SECRET=your-client-secret
# Caliper
CALIPER_CLIENT_ID=your-client-id
CALIPER_CLIENT_SECRET=your-client-secret
# QTI
QTI_CLIENT_ID=your-client-id
QTI_CLIENT_SECRET=your-client-secret
# CASE
CASE_CLIENT_ID=your-client-id
CASE_CLIENT_SECRET=your-client-secret
# CLR
CLR_CLIENT_ID=your-client-id
CLR_CLIENT_SECRET=your-client-secret
# MasteryTrack (uses API key + email, not OAuth)
MASTERYTRACK_API_KEY=your-api-key
MASTERYTRACK_EMAIL=your-registered-email
# Webhooks
WEBHOOKS_CLIENT_ID=your-client-id
WEBHOOKS_CLIENT_SECRET=your-client-secret
```
## Client Guides
Rostering, enrollments, and gradebook
Analytics and resource management
Learning analytics events
Assessments and questions
Adaptive learning paths
Competencies and standards
Comprehensive Learner Records
Test inventory and assignments
Webhook management and filters
# PowerPath
Source: https://docs.timeback.com/beta/build-on-timeback/clients/powerpath
PowerPath API client for adaptive learning
## Overview
The `@timeback/powerpath` package provides a client for the PowerPath API, enabling:
* **Assessments**: Create tests, attempts, and submit responses
* **Placement Tests**: Determine appropriate grade level
* **Screening**: Session management and test assignment
* **Lesson Plans**: Course progress and lesson plan operations
* **Test Assignments**: CRUD operations for test assignments
## Installation
```bash npm theme={null}
npm install @timeback/powerpath
```
```bash pnpm theme={null}
pnpm add @timeback/powerpath
```
```bash yarn theme={null}
yarn add @timeback/powerpath
```
```bash bun theme={null}
bun add @timeback/powerpath
```
```bash pip theme={null}
pip install timeback-powerpath
```
```bash uv theme={null}
uv add timeback-powerpath
```
## Quick Start
```typescript TypeScript theme={null}
import { PowerPathClient } from '@timeback/powerpath'
const client = new PowerPathClient({
env: 'staging',
auth: {
clientId: process.env.POWERPATH_CLIENT_ID!,
clientSecret: process.env.POWERPATH_CLIENT_SECRET!,
},
})
const level = await client.placement.getCurrentLevel({ student: 'student-123', subject: 'Math' })
```
```python Python theme={null}
import os
from timeback_powerpath import PowerPathClient
client = PowerPathClient(
env="staging",
client_id=os.environ["POWERPATH_CLIENT_ID"],
client_secret=os.environ["POWERPATH_CLIENT_SECRET"],
)
level = await client.placement.get_current_level({"student": "student-123", "subject": "Math"})
```
## Client Structure
```typescript TypeScript theme={null}
const client = new PowerPathClient(options)
client.assessments // Tests, attempts, and responses
client.lessonPlans // Course lesson plans and progress
client.placement // Placement testing
client.screening // MAP test sessions
client.syllabus // Course structure
client.testAssignments // Test assignment CRUD
```
```python Python theme={null}
client = PowerPathClient(**options)
client.assessments # Tests, attempts, and responses
client.lesson_plans # Course lesson plans and progress
client.placement # Placement testing
client.screening # MAP test sessions
client.syllabus # Course structure
client.test_assignments # Test assignment CRUD
```
## Typed Responses
All methods return typed responses:
```typescript TypeScript theme={null}
import type { ExternalTestCreateResponse, GetNextPlacementTestResponse } from '@timeback/powerpath'
const test: ExternalTestCreateResponse = await client.assessments.createExternalTestOut({
courseId: 'course-123',
lessonType: 'test-out',
toolProvider: 'mastery-track',
grades: [3],
xp: 10,
resourceMetadata: { subject: 'Math' },
})
console.log(test.lessonId, test.resourceId)
const next: GetNextPlacementTestResponse = await client.placement.getNextPlacementTest({
student: 'student-123',
subject: 'Math',
})
console.log(`${next.availableTests} tests available, next: ${next.lesson}`)
```
```python Python theme={null}
from timeback_powerpath import ExternalTestCreateResponse, GetNextPlacementTestResponse
test = await client.assessments.create_external_test_out({
"course_id": "course-123",
"lesson_type": "test-out",
"tool_provider": "mastery-track",
"grades": [3],
"xp": 10,
"resource_metadata": {"subject": "Math"},
})
print(test.lesson_id, test.resource_id)
next_test = await client.placement.get_next_placement_test({
"student": "student-123",
"subject": "Math",
})
print(f"{next_test.available_tests} tests available, next: {next_test.lesson}")
```
## Assessments
Create and manage assessments, attempts, and responses.
### Create Tests
```typescript TypeScript theme={null}
client.assessments.createInternalTest({ courseId, lessonType: 'quiz', testType: 'qti', qti: { url: qtiTestUrl } })
client.assessments.createExternalTestOut({
courseId,
lessonType,
toolProvider,
grades,
xp,
resourceMetadata,
})
client.assessments.createExternalPlacementTest({ courseId, lessonType: 'placement', toolProvider, grades })
```
```python Python theme={null}
await client.assessments.create_internal_test({
"course_id": course_id,
"lesson_type": lesson_type,
"test_type": "qti",
"qti": {"url": qti_test_url},
"grades": grades,
"xp": xp,
})
await client.assessments.create_external_test_out({
"course_id": course_id,
"lesson_type": lesson_type,
"tool_provider": tool_provider,
"grades": grades,
"xp": xp,
"resource_metadata": resource_metadata,
})
await client.assessments.create_external_placement_test({
"course_id": course_id,
"tool_provider": tool_provider,
"grades": grades,
})
```
### Manage Attempts
```typescript TypeScript theme={null}
client.assessments.createNewAttempt({ student: studentId, lesson: lessonId })
client.assessments.getAttempts({ student: studentId, lesson: lessonId })
client.assessments.resetAttempt({ student: studentId, lesson: lessonId })
```
```python Python theme={null}
await client.assessments.create_new_attempt({"student": student_id, "lesson": lesson_id})
await client.assessments.get_attempts({"student": student_id, "lesson": lesson_id})
await client.assessments.reset_attempt({"student": student_id, "lesson": lesson_id})
```
### Questions and Responses
```typescript TypeScript theme={null}
client.assessments.getNextQuestion({ student: studentId, lesson: lessonId })
client.assessments.updateStudentQuestionResponse({ student: studentId, lesson: lessonId, question: questionId, response })
client.assessments.finalStudentAssessmentResponse({ student: studentId, lesson: lessonId })
client.assessments.getAssessmentProgress({ student: studentId, lesson: lessonId })
```
```python Python theme={null}
await client.assessments.get_next_question({"student": student_id, "lesson": lesson_id})
await client.assessments.update_student_question_response({"student": student_id, "question": question_id, "response": response, "lesson": lesson_id})
await client.assessments.final_student_assessment_response({"student": student_id, "lesson": lesson_id})
await client.assessments.get_assessment_progress({"student": student_id, "lesson": lesson_id})
```
## Placement
Manage placement testing and determine appropriate grade levels. All query methods take `{ student, subject }`:
```typescript TypeScript theme={null}
client.placement.getCurrentLevel({ student, subject })
client.placement.getAllPlacementTests({ student, subject })
client.placement.getNextPlacementTest({ student, subject })
client.placement.getSubjectProgress({ student, subject })
client.placement.resetUserPlacement({ student, subject })
```
```python Python theme={null}
await client.placement.get_current_level({"student": student, "subject": subject})
await client.placement.get_all_placement_tests({"student": student, "subject": subject})
await client.placement.get_next_placement_test({"student": student, "subject": subject})
await client.placement.get_subject_progress({"student": student, "subject": subject})
await client.placement.reset_user_placement({"student": student, "subject": subject})
```
| Method | Returns | Description |
| ------------------------ | ------------------------------ | ------------------------------- |
| `getCurrentLevel()` | `GetCurrentLevelResponse` | Current placement grade level |
| `getAllPlacementTests()` | `GetAllPlacementTestsResponse` | All placement tests for student |
| `getNextPlacementTest()` | `GetNextPlacementTestResponse` | Next recommended test |
| `getSubjectProgress()` | `GetSubjectProgressResponse` | Progress in subject |
| `resetUserPlacement()` | `ResetPlacementResponse` | Reset placement to start over |
## Screening
Manage screening sessions and results.
```typescript TypeScript theme={null}
client.screening.getResults(userId)
client.screening.getSession(userId)
client.screening.resetSession({ userId })
client.screening.assignTest({ userId, subject })
```
```python Python theme={null}
await client.screening.get_results(user_id)
await client.screening.get_session(user_id)
await client.screening.reset_session({"user_id": user_id})
await client.screening.assign_test({"user_id": user_id, "subject": subject})
```
## Lesson Plans
Manage lesson plans and course progress. `createOperations` accepts a single `operation` object plus an optional reason; submit separate calls for multiple operations.
```text TypeScript — API notation (not executable) theme={null}
client.lessonPlans.get(courseId, userId)
client.lessonPlans.create({ courseId, userId, classId? })
client.lessonPlans.getCourseProgress(courseId, studentId)
client.lessonPlans.getTree(lessonPlanId)
client.lessonPlans.getStructure(lessonPlanId)
client.lessonPlans.listOperations(lessonPlanId)
client.lessonPlans.createOperations(lessonPlanId, { operation: { type: 'set-skipped', payload: { target: { type: 'resource', id: itemId }, value: true } } })
client.lessonPlans.sync(lessonPlanId)
client.lessonPlans.recreate(lessonPlanId)
client.lessonPlans.deleteAll(courseId)
```
```python Python theme={null}
await client.lesson_plans.get(course_id, user_id)
await client.lesson_plans.create({"course_id": course_id, "user_id": user_id, "class_id": class_id})
await client.lesson_plans.get_course_progress(course_id, student_id)
await client.lesson_plans.get_tree(lesson_plan_id)
await client.lesson_plans.get_structure(lesson_plan_id)
await client.lesson_plans.list_operations(lesson_plan_id)
await client.lesson_plans.create_operations(lesson_plan_id, {"operation": {"type": "set-skipped", "payload": {"target": {"type": "resource", "id": item_id}, "value": True}}})
await client.lesson_plans.sync(lesson_plan_id)
await client.lesson_plans.recreate(lesson_plan_id)
await client.lesson_plans.delete_all(course_id)
```
## Syllabus
```typescript TypeScript theme={null}
client.syllabus.get(courseSourcedId)
```
```python Python theme={null}
await client.syllabus.get(course_sourced_id)
```
## Test Assignments
CRUD operations for test assignments.
```typescript TypeScript theme={null}
client.testAssignments.list({ student: studentId })
client.testAssignments.create({ student: studentId, subject, grade })
client.testAssignments.get(assignmentId)
client.testAssignments.update(assignmentId, { testName })
client.testAssignments.delete(assignmentId)
client.testAssignments.bulk({ items: [{ student: studentId, subject, grade }] })
```
```python Python theme={null}
await client.test_assignments.list({"student": student_id})
await client.test_assignments.create({"student": student_id, "subject": subject, "grade": grade})
await client.test_assignments.get(assignment_id)
await client.test_assignments.update(assignment_id, {"test_name": test_name})
await client.test_assignments.delete(assignment_id)
await client.test_assignments.bulk({"items": [{"student": student_id, "subject": subject, "grade": grade}]})
```
## Error Handling
```typescript TypeScript theme={null}
import { NotFoundError, PowerPathError } from '@timeback/powerpath/errors'
try {
await client.testAssignments.get('missing-id')
} catch (error) {
if (error instanceof NotFoundError) {
console.log('Assignment not found')
} else if (error instanceof PowerPathError) {
console.log(error.statusCode)
console.log(error.message)
}
}
```
```python Python theme={null}
from timeback_powerpath import NotFoundError, PowerPathError
try:
await client.test_assignments.get("missing-id")
except NotFoundError:
print("Assignment not found")
except PowerPathError as error:
print(error)
```
## SDK Integration
After configuring the [TypeScript server SDK](/beta/build-on-timeback/sdk/server/nextjs) or [Python server SDK](/beta/build-on-timeback/sdk/server/fastapi), use its composed client:
```typescript TypeScript theme={null}
const level = await timeback.api.powerpath.placement.getCurrentLevel({
student: studentId,
subject: 'Math',
})
```
```python Python theme={null}
level = await timeback.api.powerpath.placement.get_current_level({
"student": student_id,
"subject": "Math",
})
```
## Next Steps
Standard assessments
Track learning events
PowerPath type definitions
# QTI
Source: https://docs.timeback.com/beta/build-on-timeback/clients/qti
QTI API client for assessments and question items
## Overview
The `@timeback/qti` package provides a client for the QTI (Question and Test Interoperability) API, enabling:
* **Assessment Items**: Query and manage question items
* **Assessment Tests**: Query tests with nested structure
* **Stimuli**: Query shared content for questions
* **Lessons**: Submit and retrieve lesson feedback
* **Validation**: Validate QTI XML content
## Installation
```bash npm theme={null}
npm install @timeback/qti
```
```bash pnpm theme={null}
pnpm add @timeback/qti
```
```bash yarn theme={null}
yarn add @timeback/qti
```
```bash bun theme={null}
bun add @timeback/qti
```
```bash pip theme={null}
pip install timeback-qti
```
```bash uv theme={null}
uv add timeback-qti
```
## Quick Start
```typescript TypeScript theme={null}
import { QtiClient } from '@timeback/qti'
const client = new QtiClient({
env: 'staging',
auth: {
clientId: process.env.QTI_CLIENT_ID!,
clientSecret: process.env.QTI_CLIENT_SECRET!,
},
})
const { items, total, page, pages } = await client.assessmentItems.list()
const item = await client.assessmentItems.get('item-123')
```
```python Python theme={null}
import os
from timeback_qti import QtiClient
client = QtiClient(
env="staging",
client_id=os.environ["QTI_CLIENT_ID"],
client_secret=os.environ["QTI_CLIENT_SECRET"],
)
items = await client.assessment_items.list()
item = await client.assessment_items.get("item-123")
```
## Assessment Items
### List & Get Items
```typescript TypeScript theme={null}
client.assessmentItems.list()
client.assessmentItems.list({ page: 2, limit: 50, sort: 'title', order: 'asc' })
client.assessmentItems.get(itemId)
client.assessmentItems.stream()
client.assessmentItems.stream({ max: 500 }).toArray()
```
```python Python theme={null}
await client.assessment_items.list()
await client.assessment_items.list({"page": 2, "limit": 50, "sort": "title", "order": "asc"})
await client.assessment_items.get(item_id)
async for item in client.assessment_items.stream():
...
```
| Method | Returns | Description |
| ------------ | ------------------------------- | ----------------------------- |
| `list()` | `{ items, total, page, pages }` | List with pagination metadata |
| `get()` | `AssessmentItem` | Get item by ID |
| `stream()` | `AsyncIterable` | Memory-efficient streaming |
| `.toArray()` | `Item[]` | Collect streamed items |
### Create, Update, Upsert & Delete
The calls below show method usage, with app-supplied payload variables. JSON item writes require `title` and `type`; test writes require a `qti-test-part` array; stimulus writes require a `content` string. Update schemas are not arbitrary partial objects. For an XML item, use `createFromXml({ format: 'xml', xml })` with complete QTI XML. The process-response body identifies a response declaration (for example `RESPONSE`), in addition to the item ID in the path.
```typescript TypeScript theme={null}
client.assessmentItems.create({ identifier, title, ...content })
client.assessmentItems.update(itemId, { title, ...content })
client.assessmentItems.upsert(itemId, { title, ...content })
client.assessmentItems.delete(itemId)
client.assessmentItems.processResponse(itemId, { identifier: responseIdentifier, response })
```
```python Python theme={null}
await client.assessment_items.create({"identifier": identifier, "title": title, **content})
await client.assessment_items.update(item_id, {"title": title, **content})
await client.assessment_items.upsert(item_id, {"title": title, **content})
await client.assessment_items.delete(item_id)
await client.assessment_items.process_response(item_id, {"identifier": response_identifier, "response": response})
```
## Assessment Tests
```typescript TypeScript theme={null}
client.assessmentTests.list()
client.assessmentTests.get(testId)
client.assessmentTests.stream()
client.assessmentTests.create({ identifier, title, ...content })
client.assessmentTests.update(testId, { title, ...content })
client.assessmentTests.upsert(testId, { title, ...content })
client.assessmentTests.delete(testId)
client.assessmentTests.updateMetadata(testId, metadata)
client.assessmentTests.getQuestions(testId)
```
```python Python theme={null}
await client.assessment_tests.list()
await client.assessment_tests.get(test_id)
async for test in client.assessment_tests.stream():
...
await client.assessment_tests.create({"identifier": identifier, "title": title, **content})
await client.assessment_tests.update(test_id, {"title": title, **content})
await client.assessment_tests.upsert(test_id, {"title": title, **content})
await client.assessment_tests.delete(test_id)
await client.assessment_tests.update_metadata(test_id, metadata)
await client.assessment_tests.get_questions(test_id)
```
### Test Parts
Assessment tests contain test parts, which contain sections:
```typescript TypeScript theme={null}
client.assessmentTests.testParts(testId).list()
client.assessmentTests.testParts(testId).get(testPartId)
client.assessmentTests.testParts(testId).create({ identifier, ...content })
client.assessmentTests.testParts(testId).update(testPartId, { ...content })
client.assessmentTests.testParts(testId).delete(testPartId)
```
```python Python theme={null}
await client.assessment_tests.test_parts(test_id).list()
await client.assessment_tests.test_parts(test_id).get(test_part_id)
await client.assessment_tests.test_parts(test_id).create({"identifier": identifier, **content})
await client.assessment_tests.test_parts(test_id).update(test_part_id, {**content})
await client.assessment_tests.test_parts(test_id).delete(test_part_id)
```
### Sections
Sections live within a test part:
```typescript TypeScript theme={null}
client.assessmentTests.testParts(testId).sections(testPartId).list()
client.assessmentTests.testParts(testId).sections(testPartId).get(sectionId)
client.assessmentTests.testParts(testId).sections(testPartId).create({ identifier, ...content })
client.assessmentTests.testParts(testId).sections(testPartId).update(sectionId, { ...content })
client.assessmentTests.testParts(testId).sections(testPartId).delete(sectionId)
```
```python Python theme={null}
await client.assessment_tests.test_parts(test_id).sections(test_part_id).list()
await client.assessment_tests.test_parts(test_id).sections(test_part_id).get(section_id)
await client.assessment_tests.test_parts(test_id).sections(test_part_id).create({"identifier": identifier, **content})
await client.assessment_tests.test_parts(test_id).sections(test_part_id).update(section_id, {**content})
await client.assessment_tests.test_parts(test_id).sections(test_part_id).delete(section_id)
```
### Section Items
Manage items within a section:
```typescript TypeScript theme={null}
client.assessmentTests.testParts(testId).sections(testPartId).items(sectionId).add(itemRef)
client.assessmentTests.testParts(testId).sections(testPartId).items(sectionId).remove(itemIdentifier)
client.assessmentTests.testParts(testId).sections(testPartId).items(sectionId).reorder(orderInput)
```
```python Python theme={null}
await client.assessment_tests.test_parts(test_id).sections(test_part_id).items(section_id).add(item_ref)
await client.assessment_tests.test_parts(test_id).sections(test_part_id).items(section_id).remove(item_identifier)
await client.assessment_tests.test_parts(test_id).sections(test_part_id).items(section_id).reorder(order_input)
```
## Stimuli
Stimuli are shared content blocks referenced by multiple items:
```typescript TypeScript theme={null}
client.stimuli.list()
client.stimuli.get(stimulusId)
client.stimuli.stream()
client.stimuli.create({ identifier, title, ...content })
client.stimuli.update(stimulusId, { title, ...content })
client.stimuli.upsert(stimulusId, { title, ...content })
client.stimuli.delete(stimulusId)
```
```python Python theme={null}
await client.stimuli.list()
await client.stimuli.get(stimulus_id)
async for stimulus in client.stimuli.stream():
...
await client.stimuli.create({"identifier": identifier, "title": title, **content})
await client.stimuli.update(stimulus_id, {"title": title, **content})
await client.stimuli.upsert(stimulus_id, {"title": title, **content})
await client.stimuli.delete(stimulus_id)
```
`upsert()` is available on all three writable QTI resources.
* **`update(id, data)`**: strict (throws if the resource doesn't exist)
* **`upsert(id, data)`**: loose (creates the resource if it doesn't exist)
## Lessons
Submit and retrieve lesson feedback:
```typescript TypeScript theme={null}
client.lesson.submitLesson({ lessonId, userId, feedback })
client.lesson.getLesson(lessonId)
client.lesson.submitQuestion({ lessonId, questionId, userId, feedback })
```
```python Python theme={null}
await client.lesson.submit_lesson({"lesson_id": lesson_id, "user_id": user_id, "feedback": feedback})
await client.lesson.get_lesson(lesson_id)
await client.lesson.submit_question({"lesson_id": lesson_id, "question_id": question_id, "user_id": user_id, "feedback": feedback})
```
## Validation
Validate QTI XML content:
```typescript TypeScript theme={null}
client.validate.validate({ schema: 'item', xml: '...' })
client.validate.validate({ schema: 'test', xml: '...' })
client.validate.validate({ schema: 'stimulus', xml: '...' })
client.validate.batch({
xml: ['...', '...'],
schema: 'item',
entityIds: ['item-1', 'item-2'],
})
```
```python Python theme={null}
await client.validate.validate({"schema": "item", "xml": "..."})
await client.validate.validate({"schema": "test", "xml": "..."})
await client.validate.validate({"schema": "stimulus", "xml": "..."})
await client.validate.batch({
"xml": ["...", "..."],
"schema": "item",
"entity_ids": ["item-1", "item-2"],
})
```
| Schema | Description |
| ---------- | ----------------------- |
| `item` | QTI 3.0 assessment item |
| `test` | QTI 3.0 assessment test |
| `stimulus` | QTI 3.0 stimulus |
Returns `{ valid: boolean, errors?: string[] }`
## Item Types
These are QTI interaction concepts, not a guarantee that every renderer and grading mode implements them. Confirm support in your selected player and response processor:
| Type | Description |
| ------------------------- | ------------------------------------------- |
| `choiceInteraction` | Multiple choice (single or multiple select) |
| `textEntryInteraction` | Text input |
| `extendedTextInteraction` | Long text/essay |
| `inlineChoiceInteraction` | Dropdown select |
| `orderInteraction` | Ordering/ranking |
| `matchInteraction` | Matching pairs |
| `gapMatchInteraction` | Fill in the blank |
| `hotspotInteraction` | Image hotspots |
## Next Steps
Adaptive assessments
Link assessments to gradebook
QTI type definitions
# First steps
Source: https://docs.timeback.com/beta/build-on-timeback/first-steps
How to apply for access and get staging credentials
The application, review, and credential steps on this page are onboarding policy. Confirm current program availability and approval requirements with the Timeback team; repository code does not establish review turnaround or access eligibility.
Before you can use the [CLI](/beta/build-on-timeback/cli/overview) or [SDK](/beta/build-on-timeback/sdk/overview), you need **staging credentials**, which are issued after an onboarding call.
Schedule a call to get started
## Two paths
You have a working app and want to bring it to Timeback. Follow the Level 1 integration
guide.
You want to build something new for Timeback. Talk to us first so we can share curriculum
gaps and guide you toward Level 2.
## Before you apply
Make sure you have:
* A working app with learning functionality
* Development resources to complete the integration
* Familiarity with [how we evaluate apps](/beta/about-timeback/concepts/evaluating-apps) and [the non-negotiables](/beta/about-timeback/concepts/non-negotiables)
## How to get access
[Book an onboarding call](https://app.cal.com/team/timeback-dev/developer-onboarding) to
create your organization profile and start the application process.
Accept the developer terms and complete the developer relations call. This call is required
to unlock staging access.
After the call, staging credentials are issued to your organization. You receive a client ID
and secret for the [staging environment](/beta/build-on-timeback/reference/environment).
Implement the [Level 1
requirements](/beta/build-on-timeback/integration-levels#level-1-minimal-viable) and submit
evidence for review.
The Timeback team verifies requirements and approves your app. See [what to expect during
review](/beta/build-on-timeback/integration-levels#what-to-expect-during-review) for
details. If rejected, you receive feedback and can resubmit.
After approval, production credentials are issued for deployment.
## What you get
After completing onboarding, you receive:
* **Staging credentials** (client ID + secret) for development and testing
* Access to the [staging environment](/beta/build-on-timeback/reference/environment) APIs
* Ability to push app configurations via the [CLI](/beta/build-on-timeback/cli/overview)
Production credentials are issued later, after your integration passes [review](/beta/build-on-timeback/integration-levels#what-to-expect-during-review).
## AI skills for setup
If your team uses coding agents, start with `/timeback-integrate`.
`/timeback-integrate` orchestrates `/timeback-init` and `/timeback-server`, and browser apps also
run `/timeback-client`.
`/timeback-init` is the setup skill. It guides the developer through CLI commands such as
`npx timeback credentials add` and `npx timeback init --sync`.
* [`/timeback-integrate`](https://github.com/superbuilders/timeback-sdk-skills/tree/main/skills/timeback/timeback-integrate)
You can still use `/timeback-init`, `/timeback-client`, or `/timeback-server` manually for focused
setup, client, or server tasks.
Current skill workflows are focused on Level 1 integration.
See the full catalog on [AI Skills](/beta/build-on-timeback/ai/skills).
## Need help?
Stuck on onboarding or integration? The Timeback team and developer community are here to help.
## Next steps
Level 1 vs Level 2 requirements and what to expect during review
Step-by-step integration guide for existing apps
Setup, client, and server skills in one place
# Integration levels
Source: https://docs.timeback.com/beta/build-on-timeback/integration-levels
What Level 1 and Level 2 mean, what's required, and how review works
This page describes integration and review policy. It is not an SDK enforcement contract: custom-activity submission accepts application-supplied metrics and does not implement a universal 80% XP gate or calculate XP from minutes. Confirm current approval requirements with the onboarding team.
Timeback supports incremental integration. Start with Level 1 to get approved without rearchitecting your app. Each level unlocks more platform features and deeper integration with Timeback's learning systems.
## Overview
| Feature | Level 1 | Level 2 |
| ---------------------------------- | :-----: | :-----: |
| Launch and runtime support | ✔ | ✔ |
| Core learning events (XP, mastery) | ✔ | ✔ |
| Rostering via OneRoster | ✔ | ✔ |
| Staging and production credentials | ✔ | ✔ |
| Use Timeback placement data | ✗ | ✔ |
| Use PowerPath learning engine | ✗ | ✔ |
| Use QTI content formats | ✗ | ✔ |
| Use Timeback spaced repetition | ✗ | ✔ |
## Level 1: Minimal viable
Level 1 is the minimum viable integration. Your app works on the Timeback platform and follows the [non-negotiables](/beta/about-timeback/concepts/non-negotiables), but you keep your own learning engine, content formats, and algorithms.
### Technical requirements
| Requirement | Description |
| ----------------- | ----------------------------------------------------------------------------------------------------- |
| App registration | Register app via [first steps](/beta/build-on-timeback/first-steps) |
| Rostering | Student rostering via [supported session flows](/beta/build-on-timeback/sdk/identity) |
| Activity tracking | Record learning sessions via [Custom Activities](/beta/build-on-timeback/sdk/activity-tracking/intro) |
### Non-negotiable behaviors
These are tested during review. Apps that violate them will be rejected.
* XP reflects real learning minutes (1 XP = 1 minute of focused learning)
* No XP below 80% accuracy
* Mastery verified before advancement
* Learning events are complete and accurate
Read [the full non-negotiables](/beta/about-timeback/concepts/non-negotiables) for the complete
list.
### Not required at Level 1
You keep your own:
* Learning engine and algorithms (e.g. no [PowerPath](/beta/build-on-timeback/clients/powerpath))
* Content formats (e.g. no [QTI](/beta/build-on-timeback/clients/qti))
* Placement and diagnostic logic
* Spaced repetition implementation
## Level 2: Fully native
Level 2 apps use Timeback's learning systems instead of building their own. You read Timeback's placement data and act on it. You use our learning engine, content formats, and spaced repetition algorithms.
**Building a new app?**
If you are starting from scratch, [talk to us
first](/beta/build-on-timeback/start-building/native-apps). We will share curriculum gaps and
help you build for Level 2 from day one.
### What changes from Level 1
| Capability | What it means |
| ------------------- | ------------------------------------------------------------------------------------------- |
| Placement data | Leverage Timeback's placement data |
| Learning engine | Use [PowerPath](/beta/build-on-timeback/clients/powerpath) for sequencing and mastery logic |
| Content formats | Store and retrieve content in [QTI](/beta/build-on-timeback/clients/qti) format |
| Standards alignment | Align to curriculum standards via [CASE](/beta/api-reference/overview) |
| Spaced repetition | Use Timeback's review scheduling algorithm |
### Why Level 2
* Don't reinvent placement, sequencing, or spaced repetition
* Your app automatically benefits from platform-wide improvements
* Deeper analytics and cross-app progress sharing
## What to expect during review
The Timeback team checks your app against the [non-negotiables](/beta/about-timeback/concepts/non-negotiables) and [Level 1 requirements](#level-1-minimal-viable).
| What we check | How |
| -------------------- | ------------------------------------------------- |
| XP policy adherence | Verify awarded XP matches 1 XP = 1 focused minute |
| Accuracy gating | Confirm no XP below 80% accuracy |
| Mastery verification | Check that advancement requires proof |
| Event emission | Validate Caliper events are complete and accurate |
| Launch compatibility | Test LTI launch and runtime behavior |
If your app does not meet requirements, you receive specific feedback and can fix and resubmit.
# Introduction
Source: https://docs.timeback.com/beta/build-on-timeback/introduction
Everything you need to integrate with the Timeback platform
**Complete first steps**
Start with staging credentials for authenticated integration work. Offline commands such as CLI help and version do not require credentials. See [first
steps](/beta/build-on-timeback/first-steps) to get started.
Apply for access and understand integration levels
Step-by-step integration guides
Command-line tools for managing courses and interacting with APIs
Full-stack SDK with SSO, sessions, Custom Activities, and Managed Lessons
Central skill guide with setup, client, and server integration links
### Framework Adapters
`@timeback/sdk` includes adapters for popular frameworks:
Provider, hooks, and components
Composables and components
Stores and components
Primitives and components
### SDK & Clients
| Package | Description |
| ------------------------------------------------------------------ | ----------------------------------------- |
| [`@timeback/sdk`](/beta/build-on-timeback/sdk/overview) | Full-stack SDK: SSO, activities, lessons |
| [`@timeback/core`](/beta/build-on-timeback/clients/core) | Unified client with shared authentication |
| [`@timeback/oneroster`](/beta/build-on-timeback/clients/oneroster) | OneRoster 1.2 API client |
| [`@timeback/caliper`](/beta/build-on-timeback/clients/caliper) | Caliper Analytics API client |
| [`@timeback/edubridge`](/beta/build-on-timeback/clients/edubridge) | EduBridge analytics client |
| [`@timeback/qti`](/beta/build-on-timeback/clients/qti) | QTI assessment client |
| [`@timeback/powerpath`](/beta/build-on-timeback/clients/powerpath) | PowerPath adaptive learning client |
### Choose Your Path
Talk to us first so we can guide you toward Level 2 integration
Follow the Level 1 integration guide
Use the API clients for full control over your integration
Use identity-only mode for SSO without full SDK features
### Get help
Ask questions, get integration support from the team, and connect with other developers building
on Timeback.
# Changelog
Source: https://docs.timeback.com/beta/build-on-timeback/reference/changelog
New updates and improvements to Timeback SDK packages
This is a historical package release log. Entries describe the named version and may have been superseded. In particular, older automatic-XP, manual browser time-override, and CLI flag descriptions are not the current contract. Use the current guides and installed package version; source changelog entries alone are not proof of registry publication or live deployment.
## @timeback/caliper (v0.3.2)
* Updated dependencies \[8fcb0ff]
* @timeback/types\@0.4.0
## @timeback/case (v0.3.2)
* Updated dependencies \[8fcb0ff]
* @timeback/types\@0.4.0
## @timeback/clr (v0.3.2)
* Updated dependencies \[8fcb0ff]
* @timeback/types\@0.4.0
## @timeback/core (v0.3.2)
* Updated dependencies \[8fcb0ff]
* @timeback/qti\@0.4.0
* @timeback/caliper\@0.3.2
* @timeback/case\@0.3.2
* @timeback/clr\@0.3.2
* @timeback/edubridge\@0.3.2
* @timeback/oneroster\@0.3.2
* @timeback/powerpath\@0.3.2
* @timeback/reporting\@0.2.2
* @timeback/webhooks\@0.3.2
## @timeback/edubridge (v0.3.2)
* Updated dependencies \[8fcb0ff]
* @timeback/types\@0.4.0
## @timeback/masterytrack (v0.3.2)
* Updated dependencies \[8fcb0ff]
* @timeback/types\@0.4.0
## @timeback/oneroster (v0.3.2)
* Updated dependencies \[8fcb0ff]
* @timeback/types\@0.4.0
## @timeback/powerpath (v0.3.2)
* Updated dependencies \[8fcb0ff]
* @timeback/types\@0.4.0
## @timeback/qti (v0.4.0)
* Support server-side filtering on the top-level assessment-item and assessment-test lists. `list()` and `stream()` accept the same type-safe `where` clause the other clients use, typed against new `AssessmentItemFilterFields`/`AssessmentTestFilterFields` (the filterable-fields convention from the OneRoster types) and compiled with `whereToFilter` into the QTI service's `filter` expression. Nested lists (test parts, sections, stimuli) are unchanged: the QTI service ignores `filter` there.
* Updated dependencies \[8fcb0ff]
* @timeback/types\@0.4.0
## @timeback/reporting (v0.2.2)
* Updated dependencies \[8fcb0ff]
* @timeback/types\@0.4.0
## @timeback/sdk (v0.2.6)
* Updated dependencies \[8fcb0ff]
* @timeback/types\@0.4.0
* @timeback/core\@0.3.2
## @timeback/types (v0.4.0)
* Support server-side filtering on the top-level assessment-item and assessment-test lists. `list()` and `stream()` accept the same type-safe `where` clause the other clients use, typed against new `AssessmentItemFilterFields`/`AssessmentTestFilterFields` (the filterable-fields convention from the OneRoster types) and compiled with `whereToFilter` into the QTI service's `filter` expression. Nested lists (test parts, sections, stimuli) are unchanged: the QTI service ignores `filter` there.
## @timeback/webhooks (v0.3.2)
* Updated dependencies \[8fcb0ff]
* @timeback/types\@0.4.0
## timeback-studio (v0.2.6)
* @timeback/core\@0.3.2
## @timeback/caliper (v0.3.1)
* Updated dependencies \[0586a3b]
* @timeback/types\@0.3.1
## @timeback/caliper (v0.3.0)
* Widen the TypeScript peer dependency range from `^5` to `^5 || ^6` across published packages so npm consumers using TypeScript 6 no longer hit peer resolution failures
* Updated dependencies \[85525a1]
* Updated dependencies \[a6d403b]
* Updated dependencies \[5421a54]
* Updated dependencies \[de5efd0]
* @timeback/types\@0.3.0
## @timeback/case (v0.3.1)
* Updated dependencies \[0586a3b]
* @timeback/types\@0.3.1
## @timeback/case (v0.3.0)
* Widen the TypeScript peer dependency range from `^5` to `^5 || ^6` across published packages so npm consumers using TypeScript 6 no longer hit peer resolution failures
* Updated dependencies \[85525a1]
* Updated dependencies \[a6d403b]
* Updated dependencies \[5421a54]
* Updated dependencies \[de5efd0]
* @timeback/types\@0.3.0
## @timeback/clr (v0.3.1)
* Updated dependencies \[0586a3b]
* @timeback/types\@0.3.1
## @timeback/clr (v0.3.0)
* Widen the TypeScript peer dependency range from `^5` to `^5 || ^6` across published packages so npm consumers using TypeScript 6 no longer hit peer resolution failures
* Updated dependencies \[85525a1]
* Updated dependencies \[a6d403b]
* Updated dependencies \[5421a54]
* Updated dependencies \[de5efd0]
* @timeback/types\@0.3.0
## @timeback/core (v0.3.1)
* Updated dependencies \[0586a3b]
* @timeback/qti\@0.3.1
* @timeback/caliper\@0.3.1
* @timeback/case\@0.3.1
* @timeback/clr\@0.3.1
* @timeback/edubridge\@0.3.1
* @timeback/oneroster\@0.3.1
* @timeback/powerpath\@0.3.1
* @timeback/reporting\@0.2.1
* @timeback/webhooks\@0.3.1
## @timeback/core (v0.3.0)
* Widen the TypeScript peer dependency range from `^5` to `^5 || ^6` across published packages so npm consumers using TypeScript 6 no longer hit peer resolution failures
* Updated dependencies \[85525a1]
* Updated dependencies \[a6d403b]
* Updated dependencies \[5421a54]
* Updated dependencies \[de5efd0]
* @timeback/edubridge\@0.3.0
* @timeback/qti\@0.3.0
* @timeback/caliper\@0.3.0
* @timeback/case\@0.3.0
* @timeback/clr\@0.3.0
* @timeback/oneroster\@0.3.0
* @timeback/powerpath\@0.3.0
* @timeback/reporting\@0.2.0
* @timeback/webhooks\@0.3.0
## @timeback/edubridge (v0.3.1)
* Updated dependencies \[0586a3b]
* @timeback/types\@0.3.1
## @timeback/edubridge (v0.3.0)
* Return the full Edubridge analytics responses from `getActivity()` and `getEnrollmentFacts`
* change `getActivity()` to return the full response object, including `facts` and required `factsByApp`
* change `getEnrollmentFacts()` to return the full response object, including `facts` and required `factsByApp`
* Expose all edubridge `User` fields returned by the server
* Add `email`, `phone`, `sms`, `grades`, `identifier`, `pronouns`, `preferredFirstName`, `preferredMiddleName`, `preferredLastName`, and `password` to `User`
* Add `User.demographics` plus a new `Demographics` type and `DemographicsSex` union
* Widen the TypeScript peer dependency range from `^5` to `^5 || ^6` across published packages so npm consumers using TypeScript 6 no longer hit peer resolution failures
* Updated dependencies \[85525a1]
* Updated dependencies \[a6d403b]
* Updated dependencies \[5421a54]
* Updated dependencies \[de5efd0]
* @timeback/types\@0.3.0
## @timeback/masterytrack (v0.3.1)
* Updated dependencies \[0586a3b]
* @timeback/types\@0.3.1
## @timeback/masterytrack (v0.3.0)
* Widen the TypeScript peer dependency range from `^5` to `^5 || ^6` across published packages so npm consumers using TypeScript 6 no longer hit peer resolution failures
* Updated dependencies \[85525a1]
* Updated dependencies \[a6d403b]
* Updated dependencies \[5421a54]
* Updated dependencies \[de5efd0]
* @timeback/types\@0.3.0
## @timeback/oneroster (v0.3.1)
* Updated dependencies \[0586a3b]
* @timeback/types\@0.3.1
## @timeback/oneroster (v0.3.0)
* Widen the TypeScript peer dependency range from `^5` to `^5 || ^6` across published packages so npm consumers using TypeScript 6 no longer hit peer resolution failures
* Updated dependencies \[85525a1]
* Updated dependencies \[a6d403b]
* Updated dependencies \[5421a54]
* Updated dependencies \[de5efd0]
* @timeback/types\@0.3.0
## @timeback/powerpath (v0.3.1)
* Updated dependencies \[0586a3b]
* @timeback/types\@0.3.1
## @timeback/powerpath (v0.3.0)
* Widen the TypeScript peer dependency range from `^5` to `^5 || ^6` across published packages so npm consumers using TypeScript 6 no longer hit peer resolution failures
* Updated dependencies \[85525a1]
* Updated dependencies \[a6d403b]
* Updated dependencies \[5421a54]
* Updated dependencies \[de5efd0]
* @timeback/types\@0.3.0
## @timeback/qti (v0.3.1)
* Support structured JSON assessment item upserts
* Allow `assessmentItems.upsert()` to send `interaction.questionStructure` bodies without caller-provided `rawXml` or `content`
* Preserve XML-created item behavior while enabling server-derived XML for JSON-authored items
* Updated dependencies \[0586a3b]
* @timeback/types\@0.3.1
## @timeback/qti (v0.3.0)
* Support documented QTI list filters and JSON assessment item payloads in the public client
* Add `query` to the shared QTI pagination params and forward it from `stimuli.list()`, `assessmentItems.list()`, `assessmentTests.list()`, `testParts.list()`, and `sections.list()`
* Accept documented JSON assessment item fields like `format: "json"` and `interaction.questionStructure` on create and `createFromMetadata`
* Widen the TypeScript peer dependency range from `^5` to `^5 || ^6` across published packages so npm consumers using TypeScript 6 no longer hit peer resolution failures
* Updated dependencies \[85525a1]
* Updated dependencies \[a6d403b]
* Updated dependencies \[5421a54]
* Updated dependencies \[de5efd0]
* @timeback/types\@0.3.0
## @timeback/reporting (v0.2.1)
* Updated dependencies \[0586a3b]
* @timeback/types\@0.3.1
## @timeback/reporting (v0.2.0)
* Widen the TypeScript peer dependency range from `^5` to `^5 || ^6` across published packages so npm consumers using TypeScript 6 no longer hit peer resolution failures
* Updated dependencies \[85525a1]
* Updated dependencies \[a6d403b]
* Updated dependencies \[5421a54]
* Updated dependencies \[de5efd0]
* @timeback/types\@0.3.0
## @timeback/sdk (v0.2.5)
* Updated dependencies \[0586a3b]
* @timeback/types\@0.3.1
* @timeback/core\@0.3.1
## @timeback/sdk (v0.2.4)
* Widen the TypeScript peer dependency range from `^5` to `^5 || ^6` across published packages so npm consumers using TypeScript 6 no longer hit peer resolution failures
* Updated dependencies \[85525a1]
* Updated dependencies \[a6d403b]
* Updated dependencies \[5421a54]
* Updated dependencies \[de5efd0]
* @timeback/types\@0.3.0
* @timeback/core\@0.3.0
## @timeback/types (v0.3.1)
* Support structured JSON assessment item upserts
* Allow `assessmentItems.upsert()` to send `interaction.questionStructure` bodies without caller-provided `rawXml` or `content`
* Preserve XML-created item behavior while enabling server-derived XML for JSON-authored items
## @timeback/types (v0.3.0)
* Return the full Edubridge analytics responses from `getActivity()` and `getEnrollmentFacts`
* change `getActivity()` to return the full response object, including `facts` and required `factsByApp`
* change `getEnrollmentFacts()` to return the full response object, including `facts` and required `factsByApp`
* Support documented QTI list filters and JSON assessment item payloads in the public client
* Add `query` to the shared QTI pagination params and forward it from `stimuli.list()`, `assessmentItems.list()`, `assessmentTests.list()`, `testParts.list()`, and `sections.list()`
* Accept documented JSON assessment item fields like `format: "json"` and `interaction.questionStructure` on create and `createFromMetadata`
* Expose all edubridge `User` fields returned by the server
* Add `email`, `phone`, `sms`, `grades`, `identifier`, `pronouns`, `preferredFirstName`, `preferredMiddleName`, `preferredLastName`, and `password` to `User`
* Add `User.demographics` plus a new `Demographics` type and `DemographicsSex` union
* Widen the TypeScript peer dependency range from `^5` to `^5 || ^6` across published packages so npm consumers using TypeScript 6 no longer hit peer resolution failures
## @timeback/webhooks (v0.3.1)
* Updated dependencies \[0586a3b]
* @timeback/types\@0.3.1
## @timeback/webhooks (v0.3.0)
* Widen the TypeScript peer dependency range from `^5` to `^5 || ^6` across published packages so npm consumers using TypeScript 6 no longer hit peer resolution failures
* Updated dependencies \[85525a1]
* Updated dependencies \[a6d403b]
* Updated dependencies \[5421a54]
* Updated dependencies \[de5efd0]
* @timeback/types\@0.3.0
## timeback (v0.2.2)
* Fix interactive CLI prompts breaking when log output appears
* Prompt-based commands (`init`, `credentials`, `resources`, `skills`, `upgrade`, `inspect`, `studio`, and interactive `api` flows) keep a clean prompt UI instead of interleaving log lines
* Non-interactive commands still show normal log formatting, including when `DEBUG` or verbose logging is enabled
* Fix email configuration not persisting when using environment variable credentials
* Merge saved email with environment variable credentials so `resources push` no longer reports "Email not configured"
* `credentials email` no longer writes environment-sourced client secrets to disk
* `credentials list` now clearly distinguishes saved credentials from active environment overrides
* Widen the TypeScript peer dependency range from `^5` to `^5 || ^6` across published packages so npm consumers using TypeScript 6 no longer hit peer resolution failures
## timeback-studio (v0.2.5)
* @timeback/core\@0.3.1
## timeback-studio (v0.2.4)
* Fix interactive CLI prompts breaking when log output appears
* Prompt-based commands (`init`, `credentials`, `resources`, `skills`, `upgrade`, `inspect`, `studio`, and interactive `api` flows) keep a clean prompt UI instead of interleaving log lines
* Non-interactive commands still show normal log formatting, including when `DEBUG` or verbose logging is enabled
* Fix email configuration not persisting when using environment variable credentials
* Merge saved email with environment variable credentials so `resources push` no longer reports "Email not configured"
* `credentials email` no longer writes environment-sourced client secrets to disk
* `credentials list` now clearly distinguishes saved credentials from active environment overrides
* Widen the TypeScript peer dependency range from `^5` to `^5 || ^6` across published packages so npm consumers using TypeScript 6 no longer hit peer resolution failures
* Updated dependencies \[de5efd0]
* @timeback/core\@0.3.0
## @timeback/caliper (v0.2.1)
* Publish `@timeback/types` on npm
* Add `@timeback/types` when you need shared config, payloads, and protocol types without installing `@timeback/core` or another client package first
* `@timeback/core` and each API client depend on it so `tsc` resolves the same declarations whether you install one package or several
* Updated dependencies \[de2cc7a]
* @timeback/types\@0.2.0
## @timeback/case (v0.2.1)
* Publish `@timeback/types` on npm
* Add `@timeback/types` when you need shared config, payloads, and protocol types without installing `@timeback/core` or another client package first
* `@timeback/core` and each API client depend on it so `tsc` resolves the same declarations whether you install one package or several
* Updated dependencies \[de2cc7a]
* @timeback/types\@0.2.0
## @timeback/clr (v0.2.1)
* Publish `@timeback/types` on npm
* Add `@timeback/types` when you need shared config, payloads, and protocol types without installing `@timeback/core` or another client package first
* `@timeback/core` and each API client depend on it so `tsc` resolves the same declarations whether you install one package or several
* Updated dependencies \[de2cc7a]
* @timeback/types\@0.2.0
## @timeback/core (v0.2.3)
* Publish `@timeback/types` on npm
* Add `@timeback/types` when you need shared config, payloads, and protocol types without installing `@timeback/core` or another client package first
* `@timeback/core` and each API client depend on it so `tsc` resolves the same declarations whether you install one package or several
* Updated dependencies \[de2cc7a]
* Updated dependencies \[08d64a5]
* @timeback/caliper\@0.2.1
* @timeback/case\@0.2.1
* @timeback/clr\@0.2.1
* @timeback/edubridge\@0.2.2
* @timeback/oneroster\@0.2.1
* @timeback/powerpath\@0.2.2
* @timeback/qti\@0.2.2
* @timeback/reporting\@0.1.1
* @timeback/webhooks\@0.2.1
## @timeback/edubridge (v0.2.2)
* Publish `@timeback/types` on npm
* Add `@timeback/types` when you need shared config, payloads, and protocol types without installing `@timeback/core` or another client package first
* `@timeback/core` and each API client depend on it so `tsc` resolves the same declarations whether you install one package or several
* Updated dependencies \[de2cc7a]
* @timeback/types\@0.2.0
## @timeback/masterytrack (v0.2.1)
* Publish `@timeback/types` on npm
* Add `@timeback/types` when you need shared config, payloads, and protocol types without installing `@timeback/core` or another client package first
* `@timeback/core` and each API client depend on it so `tsc` resolves the same declarations whether you install one package or several
* Updated dependencies \[de2cc7a]
* @timeback/types\@0.2.0
## @timeback/oneroster (v0.2.1)
* Publish `@timeback/types` on npm
* Add `@timeback/types` when you need shared config, payloads, and protocol types without installing `@timeback/core` or another client package first
* `@timeback/core` and each API client depend on it so `tsc` resolves the same declarations whether you install one package or several
* Align OneRoster types and client validation with the Timeback API
* Tighten Zod and protocol shapes for users, courses, resources, line items, and results (including strict `GuidRef` and learning objective ID vs score sets)
* Accept common string labels for grade levels and normalize them to numeric `TimebackGrade` values
* Validate `updateComponent` and `updateComponentResource` payloads with the same create-input schemas used for POST before sending requests
## @timeback/powerpath (v0.2.2)
* Publish `@timeback/types` on npm
* Add `@timeback/types` when you need shared config, payloads, and protocol types without installing `@timeback/core` or another client package first
* `@timeback/core` and each API client depend on it so `tsc` resolves the same declarations whether you install one package or several
* Updated dependencies \[de2cc7a]
* @timeback/types\@0.2.0
## @timeback/qti (v0.2.2)
* Publish `@timeback/types` on npm
* Add `@timeback/types` when you need shared config, payloads, and protocol types without installing `@timeback/core` or another client package first
* `@timeback/core` and each API client depend on it so `tsc` resolves the same declarations whether you install one package or several
* Updated dependencies \[de2cc7a]
* @timeback/types\@0.2.0
## @timeback/reporting (v0.1.1)
* Publish `@timeback/types` on npm
* Add `@timeback/types` when you need shared config, payloads, and protocol types without installing `@timeback/core` or another client package first
* `@timeback/core` and each API client depend on it so `tsc` resolves the same declarations whether you install one package or several
* Updated dependencies \[de2cc7a]
* @timeback/types\@0.2.0
## @timeback/sdk (v0.2.3)
* Publish `@timeback/types` on npm
* Add `@timeback/types` when you need shared config, payloads, and protocol types without installing `@timeback/core` or another client package first
* `@timeback/core` and each API client depend on it so `tsc` resolves the same declarations whether you install one package or several
* Updated dependencies \[de2cc7a]
* @timeback/core\@0.2.3
* @timeback/types\@0.2.0
## @timeback/types (v0.2.0)
* Publish `@timeback/types` on npm
* Add `@timeback/types` when you need shared config, payloads, and protocol types without installing `@timeback/core` or another client package first
* `@timeback/core` and each API client depend on it so `tsc` resolves the same declarations whether you install one package or several
## @timeback/webhooks (v0.2.1)
* Publish `@timeback/types` on npm
* Add `@timeback/types` when you need shared config, payloads, and protocol types without installing `@timeback/core` or another client package first
* `@timeback/core` and each API client depend on it so `tsc` resolves the same declarations whether you install one package or several
* Updated dependencies \[de2cc7a]
* @timeback/types\@0.2.0
## timeback-studio (v0.2.3)
* Updated dependencies \[de2cc7a]
* @timeback/core\@0.2.3
## @timeback/edubridge (v0.2.1)
* Fix bare `endDate` handling so same-day analytics ranges are not a zero-width window.
## @timeback/core (v0.2.2)
* Bundle declaration types into `@timeback/core` so the package is fully self-contained
## @timeback/sdk (v0.2.2)
* Updated dependencies \[1ddecd5]
* @timeback/core\@0.2.2
## timeback-studio (v0.2.2)
* Updated dependencies \[1ddecd5]
* @timeback/core\@0.2.2
## @timeback/core (v0.2.1)
* Add QTI XML parsing utilities
* `@timeback/qti/parse`: Lightweight, dependency-free extractors for prompts, choices, correct responses, inline feedback, modal feedback, interaction attributes, and response declarations from QTI 3.0 XML
* `@timeback/core/qti`: Re-exports parsing utilities from `@timeback/qti/parse`
* `@timeback/sdk/qti`: Re-exports parsing utilities and adds SDK integration helpers (`toQuizQuestion`, `toReviewQuestions`) for Managed Lessons flows
## @timeback/powerpath (v0.2.1)
* Add `RenderConfigResource` to the PowerPath client, exposing upsert, get, and delete operations for custom renderer configurations on courses via `client.renderConfig.*`.
* Add `TestOutResource` to the PowerPath client with `getEligibility` and `createAssignment` methods for self-elected test-out flows. Add `testOutSupported` and `testOutEligible` booleans to EduBridge enrollment types.
## @timeback/qti (v0.2.1)
* Add QTI XML parsing utilities
* `@timeback/qti/parse`: Lightweight, dependency-free extractors for prompts, choices, correct responses, inline feedback, modal feedback, interaction attributes, and response declarations from QTI 3.0 XML
* `@timeback/core/qti`: Re-exports parsing utilities from `@timeback/qti/parse`
* `@timeback/sdk/qti`: Re-exports parsing utilities and adds SDK integration helpers (`toQuizQuestion`, `toReviewQuestions`) for Managed Lessons flows
## @timeback/sdk (v0.2.1)
* Fix `lessons.next()` crashing with a 500 when `lessonType` is omitted. The optional field is now auto-detected from the PowerPath progress response, routing to the correct path for both quiz and adaptive lessons.
* Add QTI XML parsing utilities
* `@timeback/qti/parse`: Lightweight, dependency-free extractors for prompts, choices, correct responses, inline feedback, modal feedback, interaction attributes, and response declarations from QTI 3.0 XML
* `@timeback/core/qti`: Re-exports parsing utilities from `@timeback/qti/parse`
* `@timeback/sdk/qti`: Re-exports parsing utilities and adds SDK integration helpers (`toQuizQuestion`, `toReviewQuestions`) for Managed Lessons flows
* Updated dependencies \[a719d64]
* @timeback/core\@0.2.1
## timeback (v0.2.1)
* Add `timeback skills` command for managing Timeback agent skills
* `timeback skills` (detects installed skills; offers to add or remove)
* `timeback skills add` (install 1+ skills)
* `timeback skills remove`: (remove installed skills)
* `timeback init`: (install skills at end of first-time setup)
* Fix credential setup blocking when no Timeback account exists for the provided email
* Offer to create a new account instead of failing with "contact a Timeback admin"
* Reactivate soft-deleted accounts instead of failing with a 500 error
## timeback-studio (v0.2.1)
* Updated dependencies \[a719d64]
* @timeback/core\@0.2.1
## @timeback/caliper (v0.2.0)
* Question event methods, LearnWith.AI platform support, and stricter input validation
* Add `sendQuestionSeen()`, `sendQuestionAnswered()`, `sendQuestionGraded()` for question lifecycle events
* Auto-fill event boilerplate and auto-inject session context for LearnWith.AI events
* Handle empty response bodies from platforms that don't return a `jobId`
* Fix spread order in event factories to prevent input overriding hardcoded fields
* Default `limit` and `offset` on `events.list()`
* Add `@timeback/caliper/errors` export
* Event IDs require `urn:uuid:` prefix; actor and context IDs require URL format
* `TimebackActivityContext.course` is now required
* String fields trim whitespace before validation
## @timeback/case (v0.2.0)
* LearnWith.AI platform support and stricter input validation
* Enable CASE service for LearnWith.AI platform (was previously blocked)
* `CFPackageWithGroups.structuredContent` is now optional (was incorrectly required)
* `sourcedId` parameters and `identifier` input fields now require UUID format
## @timeback/clr (v0.2.0)
* Stricter CLR credential input validation to match the API contract
* `@context` requires W3C, CLR, and OB context entries
* ID fields require URL format; datetime fields require ISO 8601
* Type arrays enforce required values (`VerifiableCredential`, `ClrCredential`, `Profile`, etc.)
* Profile `.email` validates email format; `.url` validates URL format
## @timeback/core (v0.2.0)
* Smaller published package via bundle splitting and dep externalization.
## @timeback/edubridge (v0.2.0)
* Stricter input validation
* `subjectTrack.delete(id)` requires UUID format
* `enrollOptions.beginDate` requires full ISO datetime (rejects date-only)
* Date parameters are automatically normalized by validation schemas
## @timeback/masterytrack (v0.2.0)
* Smaller published package via bundle splitting and dep externalization.
## @timeback/oneroster (v0.2.0)
* Bug fixes and stricter input validation
* Rename `createCredential()` to `registerCredential()` on `users(id)`
* Fix body wrapping for `createLineItem()`, `createResult()`, `createResults()` on scoped classes
* Fix `update()` on academic sessions, terms, and grading periods to accept partial data
* Fix `createGradingPeriod()` return type to `CreateResponse`
* Add `InputValidationError` to `./errors` export
* `email` required on user creation; `type`, `class`, `scoreScaleValue` required on score scales
* Enrollment dates accept `YYYY-MM-DD` only; assignment/score dates accept both formats
* Validation returns cleaned/transformed data; string fields trim whitespace
* Remove legacy `Filter`/`f` builder — use `where` clause instead
## @timeback/powerpath (v0.2.0)
* Bug fixes and stricter input validation
* Fix `createOperations()` sending an array instead of a single command object
* Widen `QuestionResult.feedback` and `.outcomes` types to match API responses
* Add `rendererOutcomes` and `playerState` to `updateStudentQuestionResponse` input
* `scoreDate` on lesson plan results requires ISO datetime format
## @timeback/qti (v0.2.0)
* Fix request bodies to match the QTI OpenAPI spec
* `processResponse` no longer injects the item identifier; pass the response declaration identifier via `identifier` field
* `update()` on assessment items, tests, and stimuli auto-injects `identifier` into the PUT body
* String input fields (`identifier`, `title`, `href`, etc.) now trim whitespace before validation
## @timeback/sdk (v0.2.0)
* Config comments, attempt numbering fix, and stricter handler validation
* Support comments (`//` and `/* */`) in `timeback.config` files
* Require `lessons.attemptDetails()` to use attempt numbers starting at `1` (lesson attempts are numbered from `1`, not `0`)
* Handler schemas (`lessonId`, `questionId`, `response`, activity `id`/`name`, course `code`) now trim whitespace before validation
* Updated dependencies \[fa6f72c]
* @timeback/core\@0.2.0
## @timeback/webhooks (v0.2.0)
* Error handling export and stricter input validation
* Add `@timeback/webhooks/errors` export for typed error handling
* Webhook input fields (`name`, `secret`, `webhookId`, `filterKey`, `filterValue`) now trim whitespace before validation
## timeback (v0.2.0)
* Config comments, filter flags, and resource sync improvements
* Support comments (`//` and `/* */`) in `timeback.config` files
* Add filter flags to subresource and scoped commands
* Improve `resources pull` diff display with grouped output and symbols
* Fix `grade: 0` on gradeless courses causing phantom diffs on `resources pull`
## timeback-studio (v0.2.0)
* Support comments (`//` and `/* */`) in `timeback.config` files
* Updated dependencies \[fa6f72c]
* @timeback/core\@0.2.0
## @timeback/case (v0.1.3)
* Add `upsert()` for create-or-update semantics
* `upsert()` on assessment items, assessment tests, stimuli, and CASE packages
* Updates existing resources or creates them if they don't exist
## @timeback/oneroster (v0.1.8)
* Add `exists()`, strict `update()`, and create-or-update `upsert()` across all resources
* `exists(id)` checks whether a resource exists (returns boolean, no entity body)
* `update()` throws when the resource doesn't exist; `upsert()` creates it instead
* Gradebook and assessment `update()`/`upsert()` now return the updated entity
## @timeback/powerpath (v0.1.6)
* Fix incorrect return types on `createInternalTest()` and `makeExternalTestAssignment()`
* Return dedicated response types instead of the shared `ExternalTestCreateResponse`
* Export additional types: `ScoreStatus`, `ExternalTestCapableLessonType`, `QuizLikeLessonType`
## @timeback/qti (v0.1.6)
* Add `upsert()` for create-or-update semantics
* `upsert()` on assessment items, assessment tests, stimuli, and CASE packages
* Updates existing resources or creates them if they don't exist
* Export additional types for assessment items, tests, and validation
* `AssessmentSection`, `TestPart`, `QuestionWithItem`, `ProcessResponseResult`, and \~25 more types now available as top-level exports
## @timeback/sdk (v0.1.14)
* Add `lessons` namespace for managed lesson lifecycle
* Discover and start quiz or adaptive lessons via `list()` and `start()`
* Step through questions with `next()` and `submit()`, then finalize with `complete()`
* Review past attempts with `attempts()` and `attemptDetails()`
* Supports all server adapters: Express, Nuxt, SvelteKit, SolidStart, TanStack Start
* Switch mastery completion to `upsert()` since `update()` now throws for missing resources
* Allow negative `xpEarned` values for deduction-based activity reporting
* @timeback/core\@0.1.6
## @timeback/case (v0.1.2)
* Add README with installation, usage, and configuration docs
* Fix request body serialization in packages and credentials resources
## @timeback/clr (v0.1.2)
* Add README with installation, usage, and configuration docs
* Fix request body serialization in packages and credentials resources
## @timeback/masterytrack (v0.1.1)
* Add MasteryTrack API client
* Search test inventory, assign tests, and invalidate assignments
* Support staging and production environments with API key + email authentication
## @timeback/webhooks (v0.1.1)
* Add webhook management client
* Create, list, update, delete, activate, and deactivate webhook registrations
* Manage event filters per webhook
## timeback (v0.1.13)
* Add CLR and CASE API subcommands
* Add `timeback api clr` with credential upsert and discovery commands
* Add `timeback api case` with framework, item, association, and package commands
* Add MasteryTrack subcommands and consolidate credentials
* Add `timeback api masterytrack` namespace
* Consolidate all credentials into `~/.timeback/credentials.json` with provider namespacing
* Unify credential management under `timeback credentials` with `--provider` flag
* Add webhooks API subcommands
* Add `timeback api webhooks` for managing webhook registrations
* Add `timeback api webhooks filters` for managing event filters per webhook
## @timeback/case (v0.1.1)
* Add CASE client for Competency and Academic Standards Exchange
* Manage competency frameworks, items, and associations
* Authenticate automatically when used through `@timeback/core`
* Support env, explicit, provider, and transport configuration modes
## @timeback/clr (v0.1.1)
* Add CLR client for Comprehensive Learner Record credentials
* Manage verifiable credentials, packages, and assertions
* Authenticate automatically when used through `@timeback/core`
* Support env, explicit, provider, and transport configuration modes
## @timeback/core (v0.1.6)
* Add CASE and CLR APIs
* Access Competency and Academic Standards Exchange and Comprehensive Learner Record APIs through the unified core client
* Support all standard configuration modes (env, explicit, provider, transport)
## @timeback/sdk (v0.1.13)
* Fix TypeScript compilation errors when importing the SDK
## timeback-studio (v0.1.10)
* Support CASE and CLR APIs
## timeback-studio (v0.1.9)
* Include app identity in telemetry
## timeback (v0.1.12)
* Accept client IDs of any length
## @timeback/caliper (v0.1.6)
* Fix TypeScript type resolution for package imports
## @timeback/edubridge (v0.1.5)
* Fix TypeScript type resolution for package imports
## @timeback/oneroster (v0.1.7)
* Fix TypeScript type resolution for package imports
## @timeback/powerpath (v0.1.5)
* Add strongly typed response types for assessment endpoints
* Add `GetNextQuestionResponse`, `FinalizeAssessmentResponse`, `TestOutResponse`, and more
* Distinguish PowerPath 100 from standard lesson progress via TypeScript types
## @timeback/powerpath (v0.1.4)
* Fix TypeScript type resolution for package imports
## @timeback/qti (v0.1.5)
* Add `createFromXml()` and improve input ergonomics
* Create assessment items directly from XML content
* Accept plain strings for enum/literal inputs (no `as const` needed)
## @timeback/qti (v0.1.4)
* Fix TypeScript type resolution for package imports
## @timeback/sdk (v0.1.12)
* Re-export error classes and `isApiError` type guard from `@timeback/sdk`
## timeback (v0.1.11)
* Add `--user-id` flag to `api oneroster enrollments list`
## @timeback/caliper (v0.1.5)
* Improve config types and IDE experience
* Enforce `'production' | 'staging'` for `env` config
* Fix JSDoc `@example` rendering in IDE tooltips
## @timeback/core (v0.1.5)
* Improve config types and IDE experience
* Enforce `'production' | 'staging'` for `env` config instead of open string
* Fix JSDoc `@example` rendering in IDE tooltips
## @timeback/edubridge (v0.1.4)
* Improve config types and IDE experience
* Enforce `'production' | 'staging'` for `env` config
* Fix JSDoc `@example` rendering in IDE tooltips
## @timeback/oneroster (v0.1.6)
* Improve config types and IDE experience
* Enforce `'production' | 'staging'` for `env` config
* Fix JSDoc `@example` rendering in IDE tooltips
* Add `user.sourcedId` to enrollment filter fields
## @timeback/powerpath (v0.1.3)
* Improve config types and IDE experience
* Enforce `'production' | 'staging'` for `env` config
* Fix JSDoc `@example` rendering in IDE tooltips
## @timeback/qti (v0.1.3)
* Improve config types and IDE experience
* Enforce `'production' | 'staging'` for `env` config
* Fix JSDoc `@example` rendering in IDE tooltips
* Fix missing type exports for `./errors` and `./types` subpaths
## @timeback/sdk (v0.1.11)
* Make SDK edge-runtime compatible
* Works in Cloudflare Workers, Vercel Edge, Bun, Deno, and Node.js from main entry
* Remove `./edge` subpath — main entry works everywhere
* Access `toNativeHandler` and `ROUTES` directly from main entry
## timeback-studio (v0.1.8)
* Improve live event streaming performance
## @timeback/caliper (v0.1.4)
* Support optional `session` and `edApp` fields in events
## @timeback/sdk (v0.1.10)
* Improve activity and server lifecycle APIs
* Add lifecycle callbacks (`onError`, `onPause`, `onResume`, `onFlush`) and `ActivityErrorContext` for diagnostics
* Support `time: false` to disable client-side heartbeats and opt-in retry via `time.retryAttempts`
* Enforce single-activity-at-a-time — `start()` throws if an activity is already running
* Add server-side `user.verify()` and `user.getProfile()` methods
* Add request lifecycle hooks (`onRequest`, `onSuccess`, `onError`) to server handlers
* Support optional `session` and `edApp` fields in events
* Add continuous time tracking via heartbeats
* Report time spent periodically instead of once at activity end
* Support resumable activities across sessions via `runId`
* Add `beforeTimeSpentSend` hook to intercept events before sending
* **BREAKING:** `xpEarned` is now required for activity completion
* Fix Svelte, Solid, and Vue adapter builds
## timeback (v0.1.10)
* Add account creation flow to CLI authentication
* Prompt for account creation when email is not found
* Support organization search and creation during signup
* Save credentials automatically after successful signup
## timeback-studio (v0.1.7)
* Add account creation flow to authentication
* Prompt for account creation when email is not found
* Support organization search and creation during signup
## @timeback/caliper (v0.1.3)
* Support unified `TIMEBACK_API_*` environment variables
* Configure credentials once instead of per-service
* Priority: `TIMEBACK_API_*` > `TIMEBACK_*` > service-specific
## @timeback/core (v0.1.4)
* Support unified `TIMEBACK_API_*` environment variables
* Configure credentials once instead of per-service
* Priority: `TIMEBACK_API_*` > `TIMEBACK_*` > service-specific
## @timeback/edubridge (v0.1.3)
* Support unified `TIMEBACK_API_*` environment variables
* Configure credentials once instead of per-service
* Priority: `TIMEBACK_API_*` > `TIMEBACK_*` > service-specific
## @timeback/oneroster (v0.1.5)
* Support unified `TIMEBACK_API_*` environment variables
* Configure credentials once instead of per-service
* Priority: `TIMEBACK_API_*` > `TIMEBACK_*` > service-specific
## @timeback/powerpath (v0.1.2)
* Support unified `TIMEBACK_API_*` environment variables
* Configure credentials once instead of per-service
* Priority: `TIMEBACK_API_*` > `TIMEBACK_*` > service-specific
## @timeback/qti (v0.1.2)
* Support unified `TIMEBACK_API_*` environment variables
* Configure credentials once instead of per-service
* Priority: `TIMEBACK_API_*` > `TIMEBACK_*` > service-specific
## @timeback/sdk (v0.1.9)
* Support unified `TIMEBACK_API_*` environment variables
* Configure credentials once instead of per-service
* Priority: `TIMEBACK_API_*` > `TIMEBACK_*` > service-specific
## timeback (v0.1.9)
* Improve `timeback upgrade` reliability
* Fix upgrade reporting success but not updating standalone installs
* Add `timeback upgrade ` for installing a specific version
## timeback-studio (v0.1.6)
* Support unified `TIMEBACK_API_*` environment variables for credentials
## @timeback/edubridge (v0.1.2)
* Fix `SubjectTrack` field names to match API
* Rename `gradeLevel` to `grade` and `targetCourseId` to `courseId`
## @timeback/oneroster (v0.1.4)
* Add type-safe search parameters
* Enable `search` on users, students, and courses endpoints
* Prevent accidental `search` on endpoints that don't support it
## @timeback/sdk (v0.1.8)
* Add mastery completion tracking
* Create completion entries automatically when students achieve 100% mastery
* Detect target environment automatically from course IDs in config
* Improve activity validation and XP calculation
* Validate `totalQuestions`/`correctQuestions` are provided together
* Calculate XP automatically from duration, accuracy, and attempt number
* Derive sensor from `launchUrl` when not explicitly configured
* Add profile hooks to all framework adapters
* `useTimebackProfile` (React), `createTimebackProfile` (Solid), `timebackProfile` (Svelte), `useTimebackProfile` (Vue)
* Rename `pctCompleteApp` to `pctComplete`
* Remove `attemptNumber` parameter from activity end methods
* Add optional time override for manual elapsed/paused specification
## timeback (v0.1.8)
* Infer sensor URL from recent Caliper events during `timeback init`
* Improve CLI polish and consistency
* Add `docs` command with curated documentation (replaces `describe`)
* Normalize date inputs (accept YYYY-MM-DD, auto-convert to ISO 8601)
* Replace enum string inputs with boolean flags (`--student`, `--asc`, `--enabled`)
* Standardize command descriptions and help text
* Handle large OneRoster queries automatically
* Split oversized requests into batches transparently
* Report partial failures with warning logs
## timeback-studio (v0.1.5)
* Support enrolling students by `courseId` (auto-creates classes)
* Handle large OneRoster queries automatically
## @timeback/sdk (v0.1.7)
* Add gradebook management to activity handler with attempt-based result tracking
* Add user verification and bearer auth
* Add `useTimebackVerification` React hook for checking user eligibility
* Add `bearer()` auth plugin for attaching Bearer tokens
* Add `/user/verify` endpoint for lightweight existence checks
## timeback (v0.1.7)
* Make `launchUrl` optional in config
* Infer launch URL from existing course resources during import
* Warn during sync if launch URL is missing
* Improve sync diff display with color-coded operations and environment context
* Restructure CLI commands and simplify configuration
* Reorganize commands under `resources` namespace (`push`, `pull`, `import`, `unlink`)
* Make `timeback init` scaffold-only by default; use `--sync` to push immediately
* Require `totalXp`/`totalLessons` metrics for `resources push`
* Add `timeback upgrade` command for self-updates
## @timeback/caliper (v0.1.2)
* Add `pctCompleteApp` extension for app-reported course progress
## @timeback/core (v0.1.3)
* Add PowerPath APIs for adaptive learning
## @timeback/oneroster (v0.1.3)
* Improve resource input type safety
* Require `metadata.type` when providing typed resource metadata (e.g. `launchUrl`)
* Validate create/update inputs more consistently
## @timeback/powerpath (v0.1.1)
* Add PowerPath client for adaptive learning APIs
* Manage assessments, placement, lesson plans, screening, syllabus, and test assignments
## @timeback/sdk (v0.1.6)
* Switch config format from TypeScript to JSON
* Use `timeback.config.json` instead of `timeback.config.ts`
* Add JSON schema reference for editor autocompletion
## @timeback/sdk (v0.1.5)
* Support grade-less courses and add activity hooks
* Select courses via `{ code }` in addition to `{ subject, grade }`
* Add `hooks.beforeActivitySend` for intercepting events before sending
* Add `_buildPayload()` for inspecting payloads without sending
* Add preview mode via `?preview=1` query param or `x-timeback-preview: 1` header
* Simplify activity tracking API
* Replace `client.activity.new().start()` with `client.activity.start(...)`
* Replace `getUser()` with `getEmail()` in custom identity config
* Add `pctComplete` support for app-reported course progress
* Accept progress percentage (0-100 scale) in activity payloads
## timeback (v0.1.6)
* Switch config format from TypeScript to JSON
* Use `timeback.config.json` instead of `timeback.config.ts`
* Add JSON schema reference for editor autocompletion
## timeback (v0.1.5)
* Prompt for launch URL during `timeback init`
* Derive sensor suggestion from launch URL origin
* Ensure full course structure exists so dashboards can launch the app
* Support per-course and per-environment config overrides
* Override `level`, `sensor`, and `metadata` per environment in config
* Apply environment-specific overrides during `timeback sync`
* Add standalone CLI binary distribution
* Build executables for macOS (arm64), Linux (x64/arm64), and Windows
* Install via `curl -fsSL https://timeback.dev/cli | bash` (no Node.js required)
* Add `timeback api powerpath` subcommands for adaptive learning APIs
## timeback-studio (v0.1.4)
* Switch config format from TypeScript to JSON
* Use `timeback.config.json` instead of `timeback.config.ts`
* Add JSON schema reference for editor autocompletion
## timeback-studio (v0.1.3)
* Filter events automatically by sensor from config
* Support per-course and per-environment sensor overrides
## @timeback/caliper (v0.1.1)
* Improve error messages for invalid inputs
## @timeback/core (v0.1.2)
* Improve error messages for invalid inputs across all clients
## @timeback/edubridge (v0.1.1)
* Improve error messages for invalid inputs
## @timeback/oneroster (v0.1.2)
* Improve error messages for invalid inputs
## @timeback/qti (v0.1.1)
* Improve error messages for invalid inputs
## @timeback/sdk (v0.1.4)
* Improve activity tracking and user profiles
* Use `course: { subject, grade }` instead of `courseCode` string
* Return enriched user profile data (school, grade, courses, goals, XP)
* Add `@timeback/sdk/config` subpath export for typed config files
* Improve error messages for invalid inputs
* **BREAKING:** Rename package from `timeback` to `@timeback/sdk`
```diff theme={null}
- import { createTimeback } from 'timeback'
+ import { createTimeback } from '@timeback/sdk'
```
## timeback (v0.1.4)
* Add post-init sync prompt and new flags
* `timeback init` offers to sync after creating config
* Add `--env`, `-y/--yes`, `--no-sync`, and `--no-format` flags
## timeback-studio (v0.1.2)
* Improve error messages for invalid inputs
## @timeback/core (v0.1.1)
* Add QTI APIs for assessment management
## @timeback/oneroster (v0.1.1)
* Update list methods
* `list()` returns a single page with metadata (`PageResult`)
* `listAll()` fetches all pages and returns an array
* Add `first()` for getting the first matching resource
* Require `sourcedId` when creating users
## @timeback/qti (v0.1.0)
* Add QTI API client
* Assessment items, tests, and stimuli CRUD operations
* XML validation with single and batch modes
* Lesson and question feedback submission
* Automatic pagination with streaming support
## timeback (v0.1.2)
* Improve error messages for invalid inputs
* **BREAKING:** Rename CLI package from `timeback-cli` to `timeback`
```diff theme={null}
- npx timeback-cli init
+ npx timeback init
```
## timeback (v0.1.1)
* Fix API error details not displaying for 422 responses
* Add `inspect` command for analyzing course structure
* Add QTI subcommands for assessment management, XML validation, and feedback
## timeback-studio (v0.1.1)
* Fetch all results by default for list operations
## @timeback/caliper (v0.1.0)
* First release
## @timeback/core (v0.1.0)
* First release
## @timeback/edubridge (v0.1.0)
* First release
## @timeback/oneroster (v0.1.0)
* First release
## timeback (v0.1.0)
* First release
## timeback-studio (v0.1.0)
* First release
## @timeback/sdk (v0.1.3)
* Simplify SDK setup and integration
* Add canonical `TimebackAuthUser` identity type
* Add `timeback.api` for direct API access
* Add `createTimeback`/`createTimebackIdentity` factory functions
* Add edge-safe entrypoints
## @timeback/sdk (v0.1.2)
* Add identity-only mode via `createIdentityServer()`
* Enable SSO without activity tracking or API credentials
* Support across all framework adapters
## @timeback/sdk (v0.1.1)
* Fetch all results by default for list operations
* Add Vue 3 and Nuxt 3 adapters
* Include composables, components, middleware, and route handlers
## @timeback/sdk (v0.1.0)
* First release
# Configuration
Source: https://docs.timeback.com/beta/build-on-timeback/reference/configuration
Complete reference for timeback.config.json
## Overview
The `timeback.config.json` file configures your Timeback integration, defining courses, sensors, and environment-specific overrides.
## Location
This file is generated by [`timeback init`](/beta/build-on-timeback/cli/init) during project setup and typically lives in your project root:
## Schema
Add this property to your complete configuration for editor autocompletion and validation (this excerpt is not a complete config):
```json timeback.config.json theme={null}
{
"$schema": "https://timeback.dev/schema.json"
}
```
## Complete example
```json timeback.config.json theme={null}
{
"$schema": "https://timeback.dev/schema.json",
"name": "My Learning App",
"sensor": "https://my-app.example.com/sensors/default",
"launchUrl": "https://my-app.example.com/start",
"defaults": {
"level": "Elementary"
},
"courses": [
{
"subject": "Math",
"grade": 3,
"courseCode": "MATH-3",
"level": "Elementary",
"sensor": "https://my-app.example.com/sensors/math",
"launchUrl": "https://my-app.example.com/math",
"metadata": {
"courseType": "base",
"publishStatus": "testing",
"contactEmail": "math-team@example.com",
"goals": {
"dailyXp": 100,
"dailyLessons": 3,
"dailyActiveMinutes": 25,
"dailyAccuracy": 80,
"dailyMasteredUnits": 2
},
"metrics": {
"totalXp": 2500,
"totalLessons": 42,
"totalGrades": 1
}
},
"overrides": {
"staging": {
"level": "Staging",
"sensor": "https://staging.my-app.example.com/sensors/math",
"metadata": {
"publishStatus": "draft",
"metrics": {
"totalLessons": 10
}
}
},
"production": {
"metadata": {
"publishStatus": "published"
}
}
}
}
]
}
```
## Root properties
Display name for your application.
Default Caliper sensor identifier URL for activity tracking. Can be overridden per-course. See
[Sensor resolution](#sensor-resolution).
Default app launch URL. Can be overridden per-course.
Default values applied to all courses.
Default course code.
Default course level (e.g., `"Elementary"`, `"AP"`, `"Honors"`).
Default metadata applied to all courses. See [Course metadata](#course-metadata).
Array of course definitions (minimum 1). See [Course properties](#course-properties).
Studio-specific configuration.
Enable anonymous usage telemetry.
## Course properties
Each course must have either `grade` or `courseCode` (or both). All `(subject, grade)` pairs must be unique, and all `courseCode` values must be unique.
Subject area. One of: `"Math"`, `"Reading"`, `"Language"`, `"Vocabulary"`, `"Writing"`,
`"Science"`, `"Social Studies"`, `"FastMath"`, `"None"`, `"Other"`.
Grade level. `-1` for Pre-K, `0` for Kindergarten, `1`--`12` for grades, `13` for AP. Required
if `courseCode` is not provided.
Unique course identifier (e.g., `"MATH-3"`). Required if `grade` is not provided.
Course level description (e.g., `"Elementary"`, `"AP"`, `"Honors"`).
Caliper sensor identifier URL for this course. Overrides the root `sensor`.
App launch URL for this course. Overrides the root `launchUrl`.
Environment-specific course IDs, populated by `timeback resources push`.
Course ID in the staging environment.
Course ID in the production environment.
Course metadata including publication status, goals, and metrics. See [Course
metadata](#course-metadata).
Per-environment overrides. See [Overrides](#overrides).
Overrides applied in the staging environment.
Overrides applied in the production environment.
## Course metadata
All metadata fields are optional. Metadata set in `defaults.metadata` is merged into every course.
Course classification: `"base"`, `"hole-filling"`, or `"optional"`.
Whether this course is supplemental to a base course.
Whether this is a custom course generated for an individual student.
Publication state: `"draft"`, `"testing"`, `"published"`, or `"deactivated"`.
Contact email for course issues.
Primary application identifier.
Daily learning goals for students in this course.
Target XP to earn per day.
Target lessons to complete per day.
Target active learning minutes per day.
Target accuracy percentage per day.
Target units to master per day.
Aggregate course metrics.
Total XP available in the course.
Optional total number of lessons in the course. When configured, used as the denominator when the server
auto-computes `pctCompleteApp` from
[`masteredUnits`](/beta/build-on-timeback/sdk/activity-tracking/course-progress).
Total grade levels covered by this course.
### Course metadata example
```json timeback.config.json (course excerpt) theme={null}
{
"metadata": {
"courseType": "base",
"publishStatus": "published",
"contactEmail": "math-team@example.com",
"goals": {
"dailyXp": 100,
"dailyLessons": 3,
"dailyActiveMinutes": 25
},
"metrics": {
"totalXp": 2500,
"totalLessons": 42,
"totalGrades": 1
}
}
}
```
## Overrides
Each environment override can contain `level`, `sensor`, `launchUrl`, and `metadata`. Metadata in overrides is **merged** with the base metadata — it does not replace it.
```json timeback.config.json (course excerpt) theme={null}
{
"overrides": {
"staging": {
"level": "Staging",
"sensor": "https://staging.example.com/sensors/math",
"metadata": {
"publishStatus": "draft",
"metrics": {
"totalLessons": 10
}
}
},
"production": {
"metadata": {
"publishStatus": "published"
}
}
}
}
```
### Merge behavior
Overrides are merged in this order (highest priority last):
1. `defaults` (lowest priority)
2. Course base values
3. `overrides[env]` (highest priority)
| Field | Merge strategy |
| ----------- | ----------------------------------------------------- |
| `level` | Replaced entirely |
| `sensor` | Replaced entirely |
| `launchUrl` | Replaced entirely |
| `metadata` | Merged — nested `goals` and `metrics` are deep-merged |
## Sensor resolution
Each course must have a resolvable sensor. The SDK checks these sources in order:
1. `course.overrides[env].sensor`
2. `course.sensor`
3. Root `sensor`
4. Derived from the first available launch URL: `course.overrides[env].launchUrl`, `course.launchUrl`, then root `launchUrl`. Only its origin is used.
The sensor identifies the event producer; it is not the HTTP ingestion endpoint. API client configuration selects the receiver.
Provide a root or course sensor, or a launch URL valid in each environment you use. The reviewed config schema does not accept an override-only sensor as sufficient by itself, and its launch-URL check can accept a URL for only one environment. Runtime activity submission can still fail if the selected environment has no resolvable sensor. `local` SDK mode uses staging overrides.
## Next steps
Environment variables and credentials
How `totalLessons` and `masteredUnits` drive `pctCompleteApp`
Push and pull configurations
Caliper event schemas
# Environment
Source: https://docs.timeback.com/beta/build-on-timeback/reference/environment
Select the SDK environment, platform, and credentials explicitly
## SDK setup
Configure credentials on the server. Environment variable names in an application example are inputs you pass to the SDK; your framework must load them. Follow the full [server adapter setup](/beta/build-on-timeback/sdk/server/nextjs), including identity and session callbacks.
```bash .env.local theme={null}
TIMEBACK_API_CLIENT_ID=your-staging-client-id
TIMEBACK_API_CLIENT_SECRET=your-staging-client-secret
```
For SSO, also supply the Cognito credentials required by your identity configuration. Names such as `AWS_COGNITO_CLIENT_ID` and `AWS_COGNITO_CLIENT_SECRET` in these guides are application conventions: pass their values into `identity.clientId` and `identity.clientSecret` explicitly. Custom identity uses your existing authenticated session instead.
Use `env: 'staging'` or `env: 'production'` explicitly. The SDK also supports `local`, which uses staging API configuration and overrides; it is not an offline emulator.
## Direct TypeScript clients
The composed client can resolve the canonical `TIMEBACK_API_CLIENT_ID` and `TIMEBACK_API_CLIENT_SECRET` environment variables, but explicit constructor options make the selected environment and platform clear:
```typescript theme={null}
import { TimebackClient } from '@timeback/core'
const api = new TimebackClient({
env: 'staging',
platform: 'BEYOND_AI',
clientId: process.env.TIMEBACK_API_CLIENT_ID!,
clientSecret: process.env.TIMEBACK_API_CLIENT_SECRET!,
})
```
The reviewed clients default to `BEYOND_AI`; `LEARNWITH_AI` is a separate endpoint selection. Platform support is service-specific. A constructor accepting a platform does not prove every method matches that platform's currently deployed routes. See [API reference](/beta/api-reference/overview) and the [Caliper client](/beta/build-on-timeback/clients/caliper) before changing receivers.
### Credential and URL fallback names
For individual TypeScript OneRoster, EduBridge, QTI, PowerPath, and Caliper clients, canonical names take precedence over shorthand and service-specific legacy names:
| Setting | Canonical | Shorthand | Service-specific legacy |
| --------------- | ---------------------------- | ------------------------ | ------------------------------ |
| Client ID | `TIMEBACK_API_CLIENT_ID` | `TIMEBACK_CLIENT_ID` | e.g. `ONEROSTER_CLIENT_ID` |
| Client secret | `TIMEBACK_API_CLIENT_SECRET` | `TIMEBACK_CLIENT_SECRET` | e.g. `ONEROSTER_CLIENT_SECRET` |
| API base URL | `TIMEBACK_API_BASE_URL` | `TIMEBACK_BASE_URL` | e.g. `ONEROSTER_BASE_URL` |
| OAuth token URL | `TIMEBACK_API_AUTH_URL` | `TIMEBACK_AUTH_URL` | e.g. `ONEROSTER_TOKEN_URL` |
Replace `ONEROSTER` with `EDUBRIDGE`, `QTI`, `POWERPATH`, or `CALIPER` for that client's legacy names. The composed client also reads `TIMEBACK_ENV`. Use each client's constructor documentation for explicit URL overrides; the different services do not share one universal base URL.
The Caliper client uses OAuth client credentials. `CALIPER_API_KEY` and `CALIPER_SENSOR_URL` are not credential/config fallbacks in the reviewed package. An event sensor identifies the producer and is not the HTTP ingestion URL.
The reviewed BeyondAI constants use `api.staging.alpha-1edtech.ai` / `api.alpha-1edtech.ai` for the main API and separate Caliper hosts. Both staging and production QTI constants point to `https://qti.alpha-1edtech.ai/api`. Selecting staging therefore does not by itself establish isolation for every service. Confirm the receiver and credentials before creating or changing data.
## Python
Pass credentials and environment explicitly when creating the Python client or server. Do not assume every TypeScript fallback variable or platform option is identical in Python. See [Core client](/beta/build-on-timeback/clients/core) and [FastAPI setup](/beta/build-on-timeback/sdk/server/fastapi).
## Secret handling
Keep API and SSO client secrets on the server. Exclude local secret files from version control and use your deployment environment's secret storage. Browser providers communicate with your mounted SDK routes; they do not need these client secrets.
The CLI maintains its own credential store. Configuring application environment variables is separate from selecting credentials for [`timeback resources`](/beta/build-on-timeback/cli/resources).
# Caliper Events
Source: https://docs.timeback.com/beta/build-on-timeback/reference/event-contracts
Choose the event contract for your Timeback integration
Timeback exposes different event contracts. Use the endpoint and credentials assigned to your integration. The Caliper version in `dataVersion` does not determine the HTTP path.
| Contract | Endpoint | Request | Acceptance response |
| -------------------- | --------------------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------- |
| Partner Events API | `POST /events/1.0` on your Timeback API base URL | One supported event, or an envelope with exactly one event | `202` with `{"status":"ok"}` |
| Platform Caliper API | `POST /caliper/v1p2` on your Platform API base URL | Platform Caliper envelope | `202` |
| Timeback batch API | `POST /caliper/v2/caliper/event` on its assigned base URL | Batch Caliper envelope | `200`, `jobId: "0"`, and `groups` |
| Caliper service | `POST /caliper/event` on your Caliper base URL | Caliper 1.2 envelope containing one or more events | `200` with `status`, `message`, and `jobId` |
The payload version does not make these endpoints interchangeable. In particular, `/caliper/v1p2/events` is not the submission path listed above. Use only the endpoint assigned to your integration.
## Partner Events API
Send an access token with the `https://purl.imsglobal.org/spec/caliper/v1p2/scope/events.write` scope. Use HTTPS and send events from a trusted backend.
```bash theme={null}
curl --request POST "$TIMEBACK_API_BASE_URL/events/1.0" \
--header "Authorization: Bearer $ACCESS_TOKEN" \
--header "Content-Type: application/json" \
--data-binary @event.json
```
The endpoint accepts `GradeEvent`, `AssessmentEvent`, `AssessmentItemEvent`, `AssignableEvent`, `ViewEvent`, and `SessionEvent`. These have Timeback-specific schemas. It does not accept `ActivityEvent`, `TimeSpentEvent`, `MediaEvent`, or `NavigationEvent` as partner event types.
Use the [partner event reference](/beta/build-on-timeback/reference/partner-events) for actions, identifiers, metrics, timing, and complete payloads.
### Acceptance and processing
`202` means the event passed request validation and was queued. It does not prove that XP, assessment results, or reporting have updated.
| Outcome | Meaning | What to do |
| ---------------------- | ---------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| `400` | Invalid JSON, unsupported event, invalid schema, or invalid reference when reference validation is enabled | Correct the payload before sending it again |
| `401` or `403` | Authentication or access failed | Check the assigned token and scope |
| `202` | Event was queued | Verify the expected result through your integration's result or analytics read API |
| `500` | Request or queue operation failed | Retry with the same event identity after a bounded delay |
| `503` on a login event | The session could not be prepared | Honor `Retry-After` and retry the same event |
For grade and assessment events, the referenced course must exist and belong to the application identified in the event. That check can occur after acceptance. A missing or mismatched course can prevent downstream writes even after a `202` response.
An active enrollment is not a universal prerequisite for recording partner activity. An explicit valid course can receive a grade or assessment event before an enrollment exists. Enrollment and progression rules still affect how activity is used elsewhere.
### Identity and retries
Assign each event a `urn:uuid:` identifier and preserve it, the original `eventTime`, and the attempt or session identity across transport retries. Use a new event ID for a distinct action. Do not change a submitted event's meaning while retaining its ID.
Deduplication and ordering differ across delivery paths. Do not assume that acceptance means exactly-once processing, or that two HTTP requests finish downstream in event-time order. Keep your own durable send record and make downstream callbacks safe to process more than once.
## Direct Caliper integration
The Caliper service requires an envelope with `sensor`, `sendTime`, `dataVersion`, and a nonempty `data` array. The service's event-create scope is `https://purl.imsglobal.org/spec/caliper/v1p1/scope/event.create`. This scope name differs from the Partner Events API scope.
For an integration assigned the Timeback metrics profile, see [Timeback activity and time events](/beta/build-on-timeback/reference/timeback-events). A standard Caliper event that validates is not a promise of XP or progression support for that event.
The Caliper service also exposes `POST /caliper/event/validate`. This validates without storing or sending events. Use synthetic identities in validation examples; schema validation does not confirm the existence of a student, course, or enrollment.
## Platform Caliper API
The Platform endpoint `/caliper/v1p2` requires the Caliper `v1p2/scope/events.write` scope. It validates its own native schemas and a compatibility subset for TimebackProfile metrics. It rejects malformed envelopes and event IDs already present in durable storage. A `202` response acknowledges queuing. Derived results may appear later or require additional processing.
Do not substitute the Partner Events API event catalog for the Platform schema. Use the [Platform event reference](/beta/build-on-timeback/reference/platform-events) for native payloads. Keep event identity stable across an ambiguous retry and reconcile duplicate responses before creating a new event.
## Before launch
1. Confirm the event contract, base URL, application ID, and credentials for your integration.
2. Use real assigned student and course IDs in your staging test, with synthetic learning content.
3. Send one event for each action your app will emit.
4. Verify the corresponding activity, score, time, or session result.
5. Repeat a delivery with the same event identity and verify the intended retry behavior.
6. Confirm which system owns XP and completion decisions. Do not infer those decisions from a Caliper type name.
# Events
Source: https://docs.timeback.com/beta/build-on-timeback/reference/events
Caliper event types emitted by the Timeback SDK
The Timeback SDK emits Caliper-based events to report learning activity. This page describes the SDK's TimebackProfile output before any destination-specific transformation. Choose your assigned endpoint and wire format in [Event Contracts](/beta/build-on-timeback/reference/event-contracts). The Partner Events API and Platform native schemas have separate event references.
See [Activity Models](/beta/about-timeback/concepts/activity-models) for when each event is
emitted.
## ActivityCompletedEvent
Emitted once per activity run when the student completes the activity. Carries completion metrics like questions answered, XP earned, and mastery.
```json theme={null}
{
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "urn:uuid:c51570e4-f8ed-4c18-bb3a-dfe51b2cc594",
"type": "ActivityEvent",
"action": "Completed",
"profile": "TimebackProfile",
"eventTime": "2026-01-27T10:03:00.000Z",
"actor": {
"id": "https://api.example.com/ims/oneroster/rostering/v1p2/users/student-123",
"type": "TimebackUser",
"email": "student@example.com"
},
"object": {
"id": "https://myapp.example.com/activities/Math/g3/lesson-1",
"type": "TimebackActivityContext",
"subject": "Math",
"app": { "name": "My App" },
"course": { "id": "https://api.example.com/ims/oneroster/rostering/v1p2/courses/course-example", "name": "Math Grade 3" },
"activity": { "name": "Fractions lesson" },
"process": true
},
"generated": {
"id": "https://api.example.com/ims/metrics/collections/activity/abc-123",
"type": "TimebackActivityMetricsCollection",
"items": [
{ "type": "xpEarned", "value": 80 },
{ "type": "totalQuestions", "value": 10 },
{ "type": "correctQuestions", "value": 8 },
{ "type": "masteredUnits", "value": 1 }
],
"extensions": {
"pctCompleteApp": 67
}
},
"extensions": {
"runId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"courseId": "course-example"
}
}
```
### Activity metrics
The `generated.items` array contains completion metrics. Each item has a `type` (string) and a `value` (number).
XP supplied by your app for this activity. Follow the XP policy agreed for your integration.
The SDK does not infer XP from elapsed time or question counts.
Total questions in the activity. Optional, but required if `correctQuestions` is provided.
Questions answered correctly. Optional, but required if `totalQuestions` is provided.
Number of **new** units (lessons) the student mastered during this activity. This is an
incremental count, not a cumulative total. When this value is positive, the SDK server can
combine it with historical enrollment facts and a configured positive `totalLessons` to
compute `pctCompleteApp`, unless you supplied `pctComplete` explicitly. If enrollment or
historical facts cannot be resolved, computation can be skipped. Optional. See [Course
progress](/beta/build-on-timeback/sdk/activity-tracking/course-progress) for full details.
Course completion percentage (0--100). Either passed directly via `pctComplete` in the activity
payload, or auto-computed by the server from `masteredUnits` and
[`totalLessons`](/beta/build-on-timeback/reference/configuration#course-progress-config). Sent
via `generated.extensions.pctCompleteApp`. See [Course
progress](/beta/build-on-timeback/sdk/activity-tracking/course-progress).
## TimeSpentEvent
The SDK sends periodic heartbeats every 15 seconds by default and flushes measured time when an activity ends. The server uses that measured time to construct TimeSpentEvents. Each event reports a bounded time window; a heartbeat is not itself proof that a Caliper event or analytics fact was persisted.
```json theme={null}
{
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "urn:uuid:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "TimeSpentEvent",
"action": "SpentTime",
"profile": "TimebackProfile",
"eventTime": "2026-01-27T10:00:15.000Z",
"actor": {
"id": "https://api.example.com/ims/oneroster/rostering/v1p2/users/student-123",
"type": "TimebackUser",
"email": "student@example.com"
},
"object": {
"id": "https://myapp.example.com/activities/Math/g3/lesson-1",
"type": "TimebackActivityContext",
"subject": "Math",
"app": { "name": "My App" },
"course": { "id": "https://api.example.com/ims/oneroster/rostering/v1p2/courses/course-example", "name": "Math Grade 3" },
"activity": { "name": "Fractions lesson" },
"process": true
},
"generated": {
"id": "https://api.example.com/ims/metrics/collections/time-spent/def-456",
"type": "TimebackTimeSpentMetricsCollection",
"items": [
{
"type": "active",
"value": 15,
"startDate": "2026-01-27T10:00:00.000Z",
"endDate": "2026-01-27T10:00:15.000Z"
},
{
"type": "inactive",
"value": 0
}
]
},
"extensions": {
"runId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"courseId": "course-example"
}
}
```
### Time metrics
The `generated.items` array contains time window metrics. Each item has a `type` (string), a `value` in **seconds** (max 86400), and optional fields.
#### Time categories
Seconds the student was actively engaged (tab visible, not paused).
Seconds the student was inactive (paused or tab hidden).
Seconds classified as non-productive.
Seconds that could not be classified.
Seconds flagged as anomalous behavior.
#### Per-item fields
Optional sub-classification providing additional detail on the time category.
ISO 8601 timestamp for the start of the time window.
ISO 8601 timestamp for the end of the time window.
## Acceptance and delivery
An accepted Caliper request does not confirm XP, reporting, or progression completion. Preserve each event ID and the original payload across transport retries. A new event ID can represent new work to a downstream consumer. An activity's `Completed` action does not by itself assert that the whole course is complete.
## Shared structure
Both event types share these top-level fields:
Caliper JSON-LD context. Always `"http://purl.imsglobal.org/ctx/caliper/v1p2"`.
Unique event identifier in `urn:uuid:...` format.
`"ActivityEvent"` for completions, `"TimeSpentEvent"` for time windows.
`"Completed"` for completions, `"SpentTime"` for time windows.
Always `"TimebackProfile"`.
ISO 8601 timestamp of when the event occurred.
The student who performed the action. See [Identity](/beta/build-on-timeback/sdk/identity).
URL identifying the user (e.g. OneRoster user URL).
Always `"TimebackUser"`.
The student's email address.
Display name.
One of `student`, `teacher`, `admin`, or `guide`.
The activity context where the event was recorded.
URL identifying the activity.
Always `"TimebackActivityContext"`.
One of `Reading`, `Language`, `Vocabulary`, `Social Studies`, `Writing`, `Handwriting`, `Science`,
`FastMath`, `Math`, `None`, `Other`.
The application. Must include `name`. Optional `id` (URL) and `extensions`.
The course context. Must include `name`. Optional `id` (URL) and `extensions`.
The specific activity. Must include `name`. Optional `id` (URL) and `extensions`.
Controls TimebackProfile activity processing. The SDK defaults this to true.
When sending direct activity events, include it when your integration expects the activity consumer to process them.
Metrics collection. Shape depends on event type:
Type: `TimebackActivityMetricsCollection`
URL identifying this collection.
Always `"TimebackActivityMetricsCollection"`.
Array of activity metrics. Each item has `type` (`xpEarned`, `totalQuestions`,
`correctQuestions`, `masteredUnits`) and `value` (number). See [Activity
metrics](#activity-metrics).
Additional attributes, e.g. `pctCompleteApp` (course completion percentage,
0–100).
Type: `TimebackTimeSpentMetricsCollection`
URL identifying this collection.
Always `"TimebackTimeSpentMetricsCollection"`.
Array of time metrics. Each item has `type` (`active`, `inactive`, `waste`,
`unknown`, `anti-pattern`), `value` (seconds), and optional
`startDate`/`endDate`. See [Time metrics](#time-metrics).
Additional attributes not defined by the model.
Application context on the event. Its accepted representation depends on the target contract.
Custom attributes including `runId` and `courseId` for event correlation.
The legacy event schema also defines the following optional event-level fields. They are not envelope metadata, and compatibility parsers on another destination may reject them:
Entity representing a particular segment or location within the object.
Entity representing the referring context.
An Organization representing the group context. Can be a URL or an Organization entity object.
The relationship between the actor and the group in terms of roles and status.
The current user session.
If the event occurs within an LTI platform launch, the tool's LtiSession.
How the SDK emits these events
Direct Caliper API access for custom event submission
# Examples
Source: https://docs.timeback.com/beta/build-on-timeback/reference/examples
Versioned example projects and guidance for adapting them
The SDK repository contains example projects for the integrations below. These are demos, with their own setup scripts and shortcuts. Use the linked current API guides when adapting them, and check the SDK version in the example's package file.
## Before running an example
* Read its server configuration and environment template. Several SSO demos use a process-wide in-memory session and placeholder API credentials. Replace those with authenticated per-user sessions and real server credentials before connecting real users.
* In the reviewed Bun/React and framework SSO demos, `TIMEBACK_DEMO_SEND_ACTIVITY` defaults to off. Hooks return `null` and suppress completion and time events. Seeing a preview log is not evidence of ingestion.
* Browser client initialization is not authentication. Start an activity from a user action after authentication and await `end()` before starting another. Automatic mount/cleanup effects can race asynchronous completion, including during development remounts.
* Example setup scripts can create remote resources. Review their environment and operations before running them. A local route check does not validate downstream analytics or durable delivery.
## Choose a project
### bun-react
Bun and React SSO/activity demo. [Source at the reviewed revision](https://github.com/superbuilders/timeback-dev/tree/0936b08fba44ee1ba7967fd7e5161950d867eff8/examples/bun-react) · [Integration guide](/beta/build-on-timeback/sdk/client/react).
### bun-react-full
Bun/React demo with direct API access. [Source at the reviewed revision](https://github.com/superbuilders/timeback-dev/tree/0936b08fba44ee1ba7967fd7e5161950d867eff8/examples/bun-react-full) · [Integration guide](/beta/build-on-timeback/clients/core).
### bun-react-gradeless
Course-code selection without a grade. [Source at the reviewed revision](https://github.com/superbuilders/timeback-dev/tree/0936b08fba44ee1ba7967fd7e5161950d867eff8/examples/bun-react-gradeless) · [Integration guide](/beta/build-on-timeback/reference/configuration).
### bun-react-identity-only
Identity-only SSO without the activity API. [Source at the reviewed revision](https://github.com/superbuilders/timeback-dev/tree/0936b08fba44ee1ba7967fd7e5161950d867eff8/examples/bun-react-identity-only) · [Integration guide](/beta/build-on-timeback/sdk/identity).
### bun-react-resumable
Multi-session activity demo. [Source at the reviewed revision](https://github.com/superbuilders/timeback-dev/tree/0936b08fba44ee1ba7967fd7e5161950d867eff8/examples/bun-react-resumable) · [Integration guide](/beta/build-on-timeback/sdk/activity-tracking/stateful).
### express
Express server adapter demo. [Source at the reviewed revision](https://github.com/superbuilders/timeback-dev/tree/0936b08fba44ee1ba7967fd7e5161950d867eff8/examples/express) · [Integration guide](/beta/build-on-timeback/sdk/server/express).
### express-auth0
Custom identity using an Auth0-backed session. [Source at the reviewed revision](https://github.com/superbuilders/timeback-dev/tree/0936b08fba44ee1ba7967fd7e5161950d867eff8/examples/express-auth0) · [Integration guide](/beta/build-on-timeback/sdk/identity).
### express-auth0-launch-gate
Auth0 bearer-token and launch-entry demo. [Source at the reviewed revision](https://github.com/superbuilders/timeback-dev/tree/0936b08fba44ee1ba7967fd7e5161950d867eff8/examples/express-auth0-launch-gate) · [Integration guide](/beta/build-on-timeback/sdk/identity).
### express-supabase
Custom identity using a Supabase-backed session. [Source at the reviewed revision](https://github.com/superbuilders/timeback-dev/tree/0936b08fba44ee1ba7967fd7e5161950d867eff8/examples/express-supabase) · [Integration guide](/beta/build-on-timeback/sdk/identity).
### nextjs-app-router
Next.js App Router integration. [Source at the reviewed revision](https://github.com/superbuilders/timeback-dev/tree/0936b08fba44ee1ba7967fd7e5161950d867eff8/examples/nextjs-app-router) · [Integration guide](/beta/build-on-timeback/sdk/server/nextjs).
### nuxt
Nuxt server and Vue browser integration. [Source at the reviewed revision](https://github.com/superbuilders/timeback-dev/tree/0936b08fba44ee1ba7967fd7e5161950d867eff8/examples/nuxt) · [Integration guide](/beta/build-on-timeback/sdk/server/nuxt).
### qti-with-powerpath
PowerPath/QTI assessment demonstration. [Source at the reviewed revision](https://github.com/superbuilders/timeback-dev/tree/0936b08fba44ee1ba7967fd7e5161950d867eff8/examples/qti-with-powerpath) · [Integration guide](/beta/build-on-timeback/sdk/managed-lessons/intro).
### solid-start
SolidStart server and Solid browser integration. [Source at the reviewed revision](https://github.com/superbuilders/timeback-dev/tree/0936b08fba44ee1ba7967fd7e5161950d867eff8/examples/solid-start) · [Integration guide](/beta/build-on-timeback/sdk/server/solidstart).
### svelte-kit
SvelteKit hooks and browser stores. [Source at the reviewed revision](https://github.com/superbuilders/timeback-dev/tree/0936b08fba44ee1ba7967fd7e5161950d867eff8/examples/svelte-kit) · [Integration guide](/beta/build-on-timeback/sdk/server/sveltekit).
### tanstack-start
TanStack Start server and React browser integration. [Source at the reviewed revision](https://github.com/superbuilders/timeback-dev/tree/0936b08fba44ee1ba7967fd7e5161950d867eff8/examples/tanstack-start) · [Integration guide](/beta/build-on-timeback/sdk/server/tanstack-start).
## Adapt activity tracking
Use the [single-session guide](/beta/build-on-timeback/sdk/activity-tracking/single-session) for a start → complete flow. For resumable work, persist your own activity state and use [stateful activities](/beta/build-on-timeback/sdk/activity-tracking/stateful). The browser tracker owns timers; the server `activity.record()` method records supplied metrics and optional measured time.
App-supplied `xpEarned` is not automatically calculated from duration or gated at 80% accuracy by the custom activity SDK. Implement the agreed policy in your application and verify the emitted data. The [course progress guide](/beta/build-on-timeback/sdk/activity-tracking/course-progress) explains the separate analytics-based progress calculation and its limits.
## Adapt managed lessons
The `qti-with-powerpath` example has its own setup data, quiz routes, thresholds, and renderer. Its question counts and expected question totals are demonstration configuration, not universal PowerPath guarantees.
Use [Managed Lessons](/beta/build-on-timeback/sdk/managed-lessons/intro) for the current browser/server contract. Linear answer submission may return placeholder correctness until finalization. Completion responses must be inspected; an HTTP success or the `timeTrackingSent` flag alone does not prove mastery or downstream time-event persistence.
## Configure a gradeless course
A course without `grade` requires a unique `courseCode`, a supported subject, and a sensor or launch URL resolvable in the target environment:
```json timeback.config.json theme={null}
{
"name": "CodeQuest",
"launchUrl": "https://codequest.example.com/start",
"courses": [
{
"subject": "Other",
"courseCode": "INTRO-CS-101",
"ids": null
}
]
}
```
Run `timeback resources push --env staging` to provision IDs. In an SDK activity reference use `{ code: 'INTRO-CS-101' }`; the config field is named `courseCode`.
## Verify the integration
1. Verify a real authenticated test user resolves to the expected Timeback user.
2. Check the selected platform, environment, and course IDs.
3. Start and finish an activity; inspect the HTTP results and error callbacks.
4. Verify the raw event in the intended receiver and the resulting downstream analytics separately. Account for processing delay and failures.
5. For managed lessons, test question rendering, scoring, finalization, and attempt history for the lesson types your app uses.
# Glossary
Source: https://docs.timeback.com/beta/build-on-timeback/reference/glossary
Key terms and concepts in the Timeback ecosystem
## A
### Academic Session
A time period for educational activities (semester, quarter, school year). Defined in OneRoster.
### Activity
A learning interaction tracked by Caliper. Includes start/end times, scores, and XP earned.
### Adapter
An integration layer. In SDK docs, framework adapters provide server routing or browser state/context helpers. Third-party edtech adapters are separate services that map app activity into Timeback data.
## C
### Caliper
IMS Global standard for learning analytics. Tracks learning events like activity starts, completions, and time spent.
### Class
A specific instance of a course (e.g., "Math Grade 3 - Fall 2024"). Students enroll in classes, not courses directly.
### Client
TypeScript package for direct API access (`@timeback/oneroster`, `@timeback/edubridge`, etc.).
### Course
An educational offering (e.g., "Math Grade 3"). Contains structure, resources, and learning objectives.
### Course Code
Unique identifier for a course (e.g., "MATH-3").
## E
### EduBridge
Simplified API layer on top of OneRoster. Provides pre-aggregated analytics and convenience endpoints.
### Enrollment
Association between a user and a class. Determines what content a student can access.
## G
### Grade
Educational grade level; Timeback configuration supports Pre-K (-1), Kindergarten (0), grades 1–12, and AP (13). Used to organize courses and match content to students.
### Gradebook
OneRoster service for managing scores, results, and academic records.
## I
### Identity Mode
How the SDK obtains the current identity: SSO uses Timeback as identity provider; custom mode uses your app's trusted authenticated session.
### Identity-Only
Lightweight SDK mode providing SSO without activity tracking or API access.
## L
### Line Item
A gradable assignment or assessment in the gradebook.
## M
### Mastery
Demonstrated competence in a learning objective. Tracked by PowerPath.
### Metadata
Custom key-value data attached to courses or resources.
## O
### OneRoster
IMS Global standard for rostering (users, classes, enrollments) and gradebook data.
### Override
Environment-specific configuration in `timeback.config.json`. Allows different values for staging vs production.
## P
### PowerPath
Adaptive learning system. Adjusts question difficulty based on student performance.
### Provider
React/Vue/Solid context provider that makes Timeback client available to components.
## Q
### QTI
Question and Test Interoperability. IMS Global standard for digital assessments.
## R
### Resource
Learning material (video, quiz, article) linked to a course.
### Result
A student's score or outcome for a line item.
### Rostering
Managing users, classes, and enrollments. Handled by OneRoster.
## S
### SDK
Software Development Kit. The TypeScript `@timeback/sdk` or Python `timeback-sdk` integration package. The npm `timeback` package is the CLI.
### Sensor
A URL identifying a Caliper event producer. Configured per course or globally; distinct from the HTTP ingestion endpoint.
### Session
A learning session tracked by Caliper or PowerPath.
### SSO
Single Sign-On. Using Timeback as the identity provider via OIDC.
### Subject
A configured subject such as Math, Reading, Language, Writing, Vocabulary, Science, or Social Studies. Use the exact supported subject enum; ELA is not a config subject value.
## T
### Timeback
Educational data platform built on 1EdTech standards.
### TimebackId
The `timebackId` field returned in SDK SSO user data. Use the identity contract for its mapping rather than assuming every Cognito subject, OneRoster sourcedId, and external app ID is interchangeable.
### timeback.config.json
Configuration file defining courses, sensors, and environment overrides.
## U
### User
A person in the Timeback system. Can have roles like student, teacher, or administrator.
## X
### XP
Experience points reported for learning activity. SDK custom activities accept app-supplied XP; calculation and accuracy gates depend on the application and reviewed policy.
## Next Steps
Getting started guide
API endpoints
# Partner Event Reference
Source: https://docs.timeback.com/beta/build-on-timeback/reference/partner-events
Fields, actions, and examples for POST /events/1.0
This reference applies to the Partner Events API at `POST /events/1.0`. Send one event per request. An envelope is accepted only when `data` contains exactly one event. The API derives its transport envelope from the event; an outer `sensor` does not override `edApp`.
## Shared fields
| Field | Contract |
| ---------------------- | --------------------------------------------------------------------------------------------------- |
| `id` | Unique event identifier in `urn:uuid:` form; reuse for retries of that event |
| `type` and `action` | A combination from the table below |
| `eventTime` | UTC ISO 8601 timestamp ending in `Z` |
| `@context` | Use `http://purl.imsglobal.org/ctx/caliper/v1p2`; the schema supplies this value when omitted |
| `profile` | Required for assessment, assignable, and grade events; optional for view and session events |
| `edApp` | `SoftwareApplication` with an ID URL whose path is `/applications/1.0/{app_sourced_id}` |
| Student reference | `Person` with an ID URL whose path is `/rostering/1.0/users/{user_sourced_id}` |
| `extensions.subject` | Required for the five non-session event types |
| `extensions.course.id` | Required for grade, assessment, and assessment-item events; optional for assignable and view events |
Use the IDs assigned to you through Timeback's application, roster, and course APIs. In the examples, `api.example.com` and all example IDs are placeholders. Substitute your API base URL and assigned IDs. A bare student ID does not satisfy the URL schema.
The event and many nested objects reject unknown fields. Place only documented extensions in each extension object. `group.id` is not a substitute for `extensions.course.id` in this contract.
## Actions and results
| Type | Profile | Accepted actions | Primary meaning |
| --------------------- | ------------------------- | --------------------------------------------------------------------------- | ---------------------------------------------------------- |
| `AssessmentEvent` | `AssessmentProfile` | `Started`, `Paused`, `Resumed`, `Restarted`, `Submitted` | Assessment attempt lifecycle and timing |
| `AssessmentItemEvent` | `AssessmentProfile` | `Started`, `Skipped`, `Completed` | Question lifecycle and timing |
| `AssignableEvent` | `AssignableProfile` | `Activated`, `Deactivated`, `Started`, `Completed`, `Submitted`, `Reviewed` | Interaction with assigned content |
| `ViewEvent` | Optional | `Viewed` | Time spent viewing a resource |
| `GradeEvent` | `GradingProfile` | `Graded` | XP, mastery, or a question result, selected by `scoreType` |
| `SessionEvent` | Optional `SessionProfile` | `LoggedIn`, `LoggedOut`, `TimedOut` | Session lifecycle |
`AssessmentEvent` does not accept `Completed`; use `Submitted`. A completed question is an `AssessmentItemEvent`. These lifecycle events do not award XP just because they were accepted. Use the agreed scoring contract.
## AssessmentEvent
Set `object.type` to `Assessment` and provide its ID and nonempty `name`. `generated` is an `Attempt` with an ID URL, student `assignee`, resource URL in `assignable`, and `startedAtTime`. Include `endedAtTime` for `Submitted`. `count` is optional; when present it is a positive integer. Keep `assignable` aligned with `object.id`.
The optional `object.isPartOf` is a `DigitalResourceCollection`. Include an explicit course even when the student has one enrollment.
```json theme={null}
{
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "urn:uuid:437620ad-f9c9-4a9d-9232-37676bfcf28e",
"eventTime": "2026-09-11T15:00:00.000Z",
"actor": {
"id": "https://api.example.com/rostering/1.0/users/student-example",
"type": "Person"
},
"edApp": {
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "https://api.example.com/applications/1.0/app-example",
"type": "SoftwareApplication"
},
"extensions": {
"subject": "Math",
"course": {
"id": "https://api.example.com/rostering/1.0/courses/course-example"
}
},
"type": "AssessmentEvent",
"profile": "AssessmentProfile",
"action": "Submitted",
"object": {
"id": "https://learning.example.com/assessments/fractions",
"type": "Assessment",
"name": "Fractions Quiz"
},
"generated": {
"id": "https://learning.example.com/attempts/attempt-example",
"type": "Attempt",
"assignee": {
"id": "https://api.example.com/rostering/1.0/users/student-example",
"type": "Person"
},
"assignable": "https://learning.example.com/assessments/fractions",
"startedAtTime": "2026-09-11T14:55:00.000Z",
"endedAtTime": "2026-09-11T15:00:00.000Z"
}
}
```
## AssessmentItemEvent
Set `object.type` to `AssessmentItem` and include a nonempty `name`. `object.isPartOf` must identify the parent `Assessment`.
For `Completed`, `generated` must be a `Response` containing an `Attempt`. The attempt requires `startedAtTime` and `endedAtTime`. `generated` can be omitted for `Started` or `Skipped`. A response event does not encode correctness; send an agreed grade event for the score.
```json theme={null}
{
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "urn:uuid:437620ad-f9c9-4a9d-9232-37676bfcf28e",
"eventTime": "2026-09-11T15:00:00.000Z",
"actor": {
"id": "https://api.example.com/rostering/1.0/users/student-example",
"type": "Person"
},
"edApp": {
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "https://api.example.com/applications/1.0/app-example",
"type": "SoftwareApplication"
},
"extensions": {
"subject": "Math",
"course": {
"id": "https://api.example.com/rostering/1.0/courses/course-example"
}
},
"type": "AssessmentItemEvent",
"profile": "AssessmentProfile",
"action": "Completed",
"object": {
"id": "https://learning.example.com/questions/fraction-1",
"type": "AssessmentItem",
"name": "Add two fractions",
"isPartOf": {
"id": "https://learning.example.com/assessments/fractions",
"type": "Assessment"
}
},
"generated": {
"id": "https://learning.example.com/responses/response-example",
"type": "Response",
"attempt": {
"id": "https://learning.example.com/attempts/question-example",
"type": "Attempt",
"assignee": {
"id": "https://api.example.com/rostering/1.0/users/student-example",
"type": "Person"
},
"assignable": "https://learning.example.com/questions/fraction-1",
"startedAtTime": "2026-09-11T14:55:00.000Z",
"endedAtTime": "2026-09-11T15:00:00.000Z"
}
}
}
```
## AssignableEvent
Set `object.type` to `AssignableDigitalResource`. Include a nonempty `name` and `mediaType`. `generated` is an `Attempt` with `startedAtTime` for every action; `Completed` also requires `endedAtTime`. Pin `extensions.course.id` when a student can have multiple courses in the same app and subject.
```json theme={null}
{
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "urn:uuid:437620ad-f9c9-4a9d-9232-37676bfcf28e",
"eventTime": "2026-09-11T15:00:00.000Z",
"actor": {
"id": "https://api.example.com/rostering/1.0/users/student-example",
"type": "Person"
},
"edApp": {
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "https://api.example.com/applications/1.0/app-example",
"type": "SoftwareApplication"
},
"extensions": {
"subject": "Math",
"course": {
"id": "https://api.example.com/rostering/1.0/courses/course-example"
}
},
"type": "AssignableEvent",
"profile": "AssignableProfile",
"action": "Completed",
"object": {
"id": "https://learning.example.com/lessons/fractions",
"type": "AssignableDigitalResource",
"name": "Fractions lesson",
"mediaType": "text/html"
},
"generated": {
"id": "https://learning.example.com/attempts/attempt-example",
"type": "Attempt",
"assignee": {
"id": "https://api.example.com/rostering/1.0/users/student-example",
"type": "Person"
},
"assignable": "https://learning.example.com/lessons/fractions",
"startedAtTime": "2026-09-11T14:55:00.000Z",
"endedAtTime": "2026-09-11T15:00:00.000Z"
}
}
```
## ViewEvent
`object.type` may be `DigitalResource`, `AssignableDigitalResource`, `Assessment`, or `AssessmentItem`. Include a nonempty `name`. `generated` is an `Attempt` with both start and end timestamps. `mediaType`, a parent `DigitalResourceCollection`, and `profile` are optional.
```json theme={null}
{
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "urn:uuid:437620ad-f9c9-4a9d-9232-37676bfcf28e",
"eventTime": "2026-09-11T15:00:00.000Z",
"actor": {
"id": "https://api.example.com/rostering/1.0/users/student-example",
"type": "Person"
},
"edApp": {
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "https://api.example.com/applications/1.0/app-example",
"type": "SoftwareApplication"
},
"extensions": {
"subject": "Math",
"course": {
"id": "https://api.example.com/rostering/1.0/courses/course-example"
}
},
"type": "ViewEvent",
"action": "Viewed",
"object": {
"id": "https://learning.example.com/lessons/fractions",
"type": "DigitalResource",
"name": "Fractions explanation"
},
"generated": {
"id": "https://learning.example.com/attempts/attempt-example",
"type": "Attempt",
"assignee": {
"id": "https://api.example.com/rostering/1.0/users/student-example",
"type": "Person"
},
"assignable": "https://learning.example.com/lessons/fractions",
"startedAtTime": "2026-09-11T14:55:00.000Z",
"endedAtTime": "2026-09-11T15:00:00.000Z"
}
}
```
## GradeEvent
The actor is the grading `SoftwareApplication`. The student is `object.assignee`; `object` is the `Attempt`. Its `assignable` is a `DigitalResource` object with an ID and nonempty `mediaType`. Include `object.extensions.activityName` and an explicit course.
`generated` is a `Score` with an ID URL, `scoreGiven`, `maxScore`, and `extensions.scoreType`.
| `scoreType` | Current metric interpretation |
| ----------------- | -------------------------------------------------------------------------------------------------- |
| `XP` | `scoreGiven` is the XP value for the result; it is not divided by `maxScore` |
| `QUESTION_RESULT` | Counts one question; `scoreGiven >= 1` counts as correct |
| `MASTERY` | Records one mastered unit and the supplied score; the event does not calculate a passing threshold |
Only send `MASTERY` after your integration's mastery decision. A low score with this type is still a mastery assertion under the current mapping. Set `scoreType` to match the meaning of the event.
```json theme={null}
{
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "urn:uuid:437620ad-f9c9-4a9d-9232-37676bfcf28e",
"eventTime": "2026-09-11T15:00:00.000Z",
"actor": {
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "https://api.example.com/applications/1.0/app-example",
"type": "SoftwareApplication"
},
"edApp": {
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "https://api.example.com/applications/1.0/app-example",
"type": "SoftwareApplication"
},
"extensions": {
"subject": "Math",
"course": {
"id": "https://api.example.com/rostering/1.0/courses/course-example"
}
},
"type": "GradeEvent",
"profile": "GradingProfile",
"action": "Graded",
"object": {
"id": "https://learning.example.com/attempts/attempt-example",
"type": "Attempt",
"assignee": {
"id": "https://api.example.com/rostering/1.0/users/student-example",
"type": "Person"
},
"assignable": {
"id": "https://learning.example.com/lessons/fractions",
"type": "DigitalResource",
"mediaType": "text/html"
},
"extensions": {
"activityName": "Fractions lesson"
}
},
"generated": {
"id": "https://learning.example.com/scores/score-example",
"type": "Score",
"scoreGiven": 10,
"maxScore": 10,
"extensions": {
"scoreType": "XP"
}
}
}
```
## SessionEvent
| Action | Actor | Object | Required timing |
| ----------- | --------------------- | --------------------- | ----------------------- |
| `LoggedIn` | Student `Person` | `SoftwareApplication` | `session.startedAtTime` |
| `LoggedOut` | Student `Person` | `SoftwareApplication` | `session.endedAtTime` |
| `TimedOut` | `SoftwareApplication` | `Session` | `object.endedAtTime` |
Use a stable session URL and retain the same session identity through login, heartbeats, and logout. A timeout also needs a student identity that the receiver can resolve from the event or session context; validate that path with your integration before relying on it.
```json theme={null}
{
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "urn:uuid:437620ad-f9c9-4a9d-9232-37676bfcf28e",
"eventTime": "2026-09-11T15:00:00.000Z",
"actor": {
"id": "https://api.example.com/rostering/1.0/users/student-example",
"type": "Person"
},
"edApp": {
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "https://api.example.com/applications/1.0/app-example",
"type": "SoftwareApplication"
},
"type": "SessionEvent",
"profile": "SessionProfile",
"action": "LoggedIn",
"object": {
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "https://api.example.com/applications/1.0/app-example",
"type": "SoftwareApplication"
},
"session": {
"id": "https://learning.example.com/sessions/session-example",
"type": "Session",
"startedAtTime": "2026-09-11T15:00:00.000Z"
}
}
```
### Heartbeats
After a successful login event, send a heartbeat to `POST /events/1.0/sessions/{sessionId}/heartbeat` with `{"eventTime":"2026-09-11T15:01:00.000Z"}`. The API also accepts `POST /events/1.0/heartbeat` with `sessionId` and `eventTime` in the body, or `sessionId` in the query string. Use the session identifier expected by your integration.
A successful heartbeat returns `200`, `status`, `sessionId`, and `endedAtTime`. An unknown session returns `404`. A successful login response prepares the session before queuing further processing, so a database error during that preparation returns a retryable `503`.
## Processing boundaries
A grade or assessment event may validate and then be declined because its pinned course is missing or belongs to another application. A valid pinned course does not require an enrollment row to receive these events. Unpinned assignable and view events use enrollment context when available, which can be ambiguous when several courses match.
Payload validation checks structure. It does not prove that timing is plausible, a mastery assertion is deserved, or the intended result has appeared. Verify those outcomes through the result and analytics APIs available to your integration.
# Platform Event Reference
Source: https://docs.timeback.com/beta/build-on-timeback/reference/platform-events
Native event schemas for the Platform Caliper API
This reference applies to `POST /caliper/v1p2` on your assigned Platform base URL and the native Caliper contract version 0.1.28. It does not describe the Partner Events API at `/events/1.0` or the direct TimebackProfile contract.
## Envelope and common fields
Send an envelope with an IRI `sensor`, UTC millisecond `sendTime`, `dataVersion: "http://purl.imsglobal.org/ctx/caliper/v1p2"`, and nonempty `data`. The submission handler validates each item as an event. Although the shared envelope schema includes entity descriptions, do not infer that a standalone entity is accepted by this submission handler.
Each native event requires `@context` with that same context value, a valid UUID URN `id`, `type`, `eventTime`, `actor`, `action`, and `object`. Timestamps must have exactly three fractional digits and end in `Z`, such as `2026-09-11T15:00:00.000Z`.
Many entity references accept an IRI or an embedded entity. Embedded entities require an ID and the matching entity `type`. Use identities assigned to your integration. The parser normalizes UUID URNs for internal use; continue sending the complete `urn:uuid:` form on the wire.
## Native event families
The native event union implements these 12 types. A profile term being defined does not mean every event in that standard profile is implemented.
| Family | Event types |
| ---------------------------- | ---------------------------------------------------------------------------------------- |
| Lifecycle and content | `SessionEvent`, `AssessmentEvent`, `AssessmentItemEvent`, `AssignableEvent`, `ViewEvent` |
| Scoring | `GradeEvent` |
| Media, navigation, and tools | `MediaEvent`, `NavigationEvent`, `ToolUseEvent` |
| Aggregated measurements | `ActivityEvent`, `TimeSpentEvent` |
| Monitoring | `AntiPatternEvent` |
The event-specific schema determines its actions, required nested entities, and timing fields. Do not copy a Partner Events API event solely because its `type` has the same name. For example, native `ViewEvent` can reference its resource by IRI and does not require the Partner API's generated attempt.
## ActivityEvent
Use `action: "Completed"`. The actor is a `Person` or IRI, and the object is an `AssignableDigitalResource` or IRI. The profile is `AggregationProfile` when supplied; it may be omitted or null.
`generated` is an `ActivityMetricsCollection`. Each item is an `ActivityMetric` entity with its own `id`, `metricType`, and numeric `metricValue`.
Known metric types are `TotalQuestions`, `CorrectQuestions`, `XpEarned`, `MasteredUnits`, and `Mastered`. Additional PascalCase metric terms up to 64 characters pass the schema, but need an agreed consumer interpretation. The schema does not impose a nonnegative constraint or a relationship between question metrics.
This is a different wire format from TimebackProfile's `TimebackActivityMetricsCollection` and lowercase `type`/`value` metric pairs.
## TimeSpentEvent
Use `action: "SpentTime"` with the same actor and resource reference shapes. The profile is `AggregationProfile` when supplied.
`generated` is a `TimeSpentMetricsCollection`. Each item is a `TimeSpentMetric` entity with its own `id`, `metricType`, and `metricValue` in seconds. Native time values must be integers between 0 and 86,400 inclusive.
Known time metric types are `Active`, `Inactive`, `Waste`, `Unknown`, and `AntiPattern`. Custom PascalCase terms up to 64 characters are accepted. `metricSubType`, `startDate`, and `endDate` are optional. Keep the measured duration and timestamps consistent; schema acceptance does not prove the underlying measurement is correct.
## Example native envelope
The following synthetic envelope records one activity and five minutes of active time. Replace example IDs with assigned student, content, and application identities. These two events have been checked against the native 0.1.28 event and envelope schemas.
```json theme={null}
{
"sensor": "https://learning.example.com",
"sendTime": "2026-09-11T15:00:00.000Z",
"dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"data": [
{
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "urn:uuid:bd62c8de-7aa3-4c89-868a-a9523d94831f",
"eventTime": "2026-09-11T15:00:00.000Z",
"actor": "https://api.example.com/users/student-example",
"object": "https://learning.example.com/lessons/fractions",
"edApp": "https://api.example.com/applications/app-example",
"type": "ActivityEvent",
"action": "Completed",
"profile": "AggregationProfile",
"generated": {
"id": "https://learning.example.com/metrics/activity-example",
"type": "ActivityMetricsCollection",
"items": [
{
"id": "https://learning.example.com/metrics/xp-example",
"type": "ActivityMetric",
"metricType": "XpEarned",
"metricValue": 10
}
]
}
},
{
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "urn:uuid:70e919f3-5f1b-4056-82b3-d74d4fc45889",
"eventTime": "2026-09-11T15:00:00.000Z",
"actor": "https://api.example.com/users/student-example",
"object": "https://learning.example.com/lessons/fractions",
"edApp": "https://api.example.com/applications/app-example",
"type": "TimeSpentEvent",
"action": "SpentTime",
"profile": "AggregationProfile",
"generated": {
"id": "https://learning.example.com/metrics/time-example",
"type": "TimeSpentMetricsCollection",
"items": [
{
"id": "https://learning.example.com/metrics/active-example",
"type": "TimeSpentMetric",
"metricType": "Active",
"metricValue": 300,
"startDate": "2026-09-11T14:55:00.000Z",
"endDate": "2026-09-11T15:00:00.000Z"
}
]
}
}
]
}
```
## AntiPatternEvent
Use `Started` or `Ended`. The actor is a `Person` or IRI. The object is an `AntiPattern` entity or IRI. When supplied, the profile is `MonitoringProfile`.
An embedded `AntiPattern` includes an ID, `type: "AntiPattern"`, and `antiPatternType`, a PascalCase term up to 64 characters. Its start time, end time, and reason are optional. A custom type can pass schema validation without having a configured reporting rule.
## Acceptance and compatibility
A `202` response acknowledges queue submission. It does not confirm that a derived insight, XP entry, or callback has completed. Preserve the event ID and payload for transport retries. If a retry reports that the ID already exists, reconcile durable state before sending a new identity.
The endpoint also has a separate compatibility parser for selected TimebackProfile activity and time events. Compatibility does not change the native schema. Use the event format assigned to your integration and verify its downstream result.
# Timeback Activity and Time Events
Source: https://docs.timeback.com/beta/build-on-timeback/reference/timeback-events
The TimebackProfile contract for direct Caliper integrations
This page applies to integrations assigned the direct Caliper metrics contract. Send an envelope to `POST /caliper/event` on your Caliper base URL. These event types are not accepted by the Partner Events API at `/events/1.0`.
## Envelope
| Field | Requirement |
| ------------- | ----------------------------------------------------------------- |
| `sensor` | URL identifying your registered event producer |
| `sendTime` | UTC ISO 8601 send timestamp |
| `dataVersion` | Exactly `http://purl.imsglobal.org/ctx/caliper/v1p2` |
| `data` | Nonempty array of events; use a different event ID for each event |
Each event requires `id`, `type`, `profile`, `actor`, `action`, `object`, and `eventTime`. Use `profile: "TimebackProfile"` and an event ID in `urn:uuid:` form. Use the standard Caliper `@context` value; the schema supplies it when absent.
## User and activity context
| Object | Required fields | Optional fields |
| ----------------- | ------------------------------------------------------------- | ------------------------------- |
| `actor` | `id` URL, `type: "TimebackUser"`, valid `email` | `name`, `role`, `extensions` |
| `object` | `id` URL, `type: "TimebackActivityContext"`, `subject`, `app` | `course`, `activity`, `process` |
| `object.app` | `name` | `id` URL, `extensions` |
| `object.course` | `name`, when the course object is supplied | `id` URL, `extensions` |
| `object.activity` | `name`, when the activity object is supplied | `id` URL, `extensions` |
Allowed roles are `student`, `teacher`, `admin`, and `guide`. Allowed subjects are `Reading`, `Language`, `Vocabulary`, `Social Studies`, `Writing`, `Handwriting`, `Science`, `FastMath`, `Math`, `None`, and `Other`.
Use the student's Timeback user URL and the course URL assigned to your integration. The schema permits a course without an ID, but downstream course resolution can require it. For activity events destined for the TimebackProfile analytics worker, set `object.process` to `true`. It is a processing instruction, not a completion percentage. Confirm your assigned consumer before applying it to another platform.
## ActivityEvent
Set `type` to `ActivityEvent` and `action` to `Completed`. `generated` requires an ID URL, type `TimebackActivityMetricsCollection`, and an `items` array.
| Metric type | Meaning |
| ------------------ | ------------------------------------------------------- |
| `xpEarned` | XP supplied by the producer |
| `totalQuestions` | Number of questions represented by the activity |
| `correctQuestions` | Number of correct questions represented by the activity |
| `masteredUnits` | Number of mastered units represented by the activity |
Each item contains a numeric `value`; item and collection `extensions` are optional. The schema does not infer question counts, compute correctness, or verify a mastery decision. Supply values that match the agreed measurement rule for your app.
`action: "Completed"` describes an activity. It does not mean that the course is complete.
## TimeSpentEvent
Set `type` to `TimeSpentEvent` and `action` to `SpentTime`. `generated` requires an ID URL, type `TimebackTimeSpentMetricsCollection`, and an `items` array.
| Field | Meaning |
| --------------------------------- | ----------------------------------------------------------- |
| `items[].type` | `active`, `inactive`, `waste`, `unknown`, or `anti-pattern` |
| `items[].value` | Duration in seconds, at most 86,400 per item |
| `items[].subType` | Optional detail about the measured activity |
| `items[].startDate` and `endDate` | Optional UTC ISO 8601 timestamps |
| `items[].extensions` | Optional metadata |
Send nonnegative durations that match the measured interval. The current schema enforces the upper duration bound; it does not enforce a lower bound, timestamp ordering, or equality between duration and timestamp difference. Validate these conditions in your producer.
Time measurement is separate from XP measurement. Replaying an interval under a new event identity can add time again in downstream processing. Preserve the event identity for a transport retry and retain your own record of sent intervals.
## Example envelope
This synthetic example records an activity and five minutes of active time. Replace its IDs, addresses, sensor, and timestamps with the values assigned to your integration.
```json theme={null}
{
"sensor": "https://learning.example.com",
"sendTime": "2026-09-11T15:00:00.000Z",
"dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"data": [
{
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "urn:uuid:437620ad-f9c9-4a9d-9232-37676bfcf28e",
"eventTime": "2026-09-11T15:00:00.000Z",
"profile": "TimebackProfile",
"actor": {
"id": "https://api.example.com/ims/oneroster/rostering/v1p2/users/student-example",
"type": "TimebackUser",
"email": "learner@example.com",
"role": "student"
},
"object": {
"id": "https://learning.example.com/activities/activity-example",
"type": "TimebackActivityContext",
"subject": "Math",
"process": true,
"app": {
"name": "Example Learning App"
},
"course": {
"id": "https://api.example.com/ims/oneroster/rostering/v1p2/courses/course-example",
"name": "Fractions"
},
"activity": {
"name": "Adding fractions"
}
},
"type": "ActivityEvent",
"action": "Completed",
"generated": {
"id": "https://learning.example.com/metrics/activity-example",
"type": "TimebackActivityMetricsCollection",
"items": [
{
"type": "xpEarned",
"value": 10
},
{
"type": "totalQuestions",
"value": 5
},
{
"type": "correctQuestions",
"value": 4
},
{
"type": "masteredUnits",
"value": 0
}
]
}
},
{
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "urn:uuid:9c4a6261-26ad-4a3c-9329-466d41ebbc4d",
"eventTime": "2026-09-11T15:00:00.000Z",
"profile": "TimebackProfile",
"actor": {
"id": "https://api.example.com/ims/oneroster/rostering/v1p2/users/student-example",
"type": "TimebackUser",
"email": "learner@example.com",
"role": "student"
},
"object": {
"id": "https://learning.example.com/activities/activity-example",
"type": "TimebackActivityContext",
"subject": "Math",
"process": true,
"app": {
"name": "Example Learning App"
},
"course": {
"id": "https://api.example.com/ims/oneroster/rostering/v1p2/courses/course-example",
"name": "Fractions"
},
"activity": {
"name": "Adding fractions"
}
},
"type": "TimeSpentEvent",
"action": "SpentTime",
"generated": {
"id": "https://learning.example.com/metrics/time-example",
"type": "TimebackTimeSpentMetricsCollection",
"items": [
{
"type": "active",
"value": 300,
"startDate": "2026-09-11T14:55:00.000Z",
"endDate": "2026-09-11T15:00:00.000Z"
}
]
}
}
]
}
```
## Validate and confirm
Use `POST /caliper/event/validate` with the same envelope to check its schema without storing it. On success, the validator returns `200` with `status: "success"` and `message: "Request payload is valid"`.
Ingestion returns `200` after validation and queuing. Retain its `jobId`. For the Caliper service, `GET /jobs/{jobId}/status` exposes queue state and the worker's `returnValue`. Check result status and errors as well as the queue state. A completed queue job does not prove that every downstream subscriber succeeded.
Do not replace an event payload by resending a changed payload under the same event ID. The Caliper service keeps the first stored event content for that ID. Corrections need the correction procedure agreed for the integration.
# Course progress
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/activity-tracking/course-progress
Track student progress using masteredUnits or pctComplete
Course progress tells the platform how far a student is through your app's notion of a course. For example, "Student A is 70% through Math Grade 3."
## Two approaches
You can report progress in two ways. Both are passed as part of [completion metrics](/beta/build-on-timeback/sdk/activity-tracking/reference#activity-end-data) (client) or [`timeback.activity.record()`](/beta/build-on-timeback/sdk/activity-tracking/reference#timeback-activity-record-params) (server).
### `masteredUnits` (recommended)
If your app tracks mastery at the lesson level, pass `masteredUnits` and let the server auto-compute `pctComplete` for you. The SDK reads historical EduBridge analytics and uses a positive [`totalLessons`](/beta/build-on-timeback/reference/configuration#course-progress-config) in the selected course configuration as its denominator. It does not maintain an atomic running total.
```typescript TypeScript (client) theme={null}
await activity.end({
xpEarned: 80,
masteredUnits: 1,
})
```
```typescript TypeScript (server) theme={null}
await timeback.activity.record({
user: { email: 'student@example.com' },
activity: {
id: 'lesson-7',
name: 'Decimals',
course: { code: 'MATH-3' },
},
metrics: {
xpEarned: 80,
masteredUnits: 1,
},
})
```
```python Python (server) theme={null}
await timeback.activity.record({
"user": {"email": "student@example.com"},
"activity": {
"id": "lesson-7",
"name": "Decimals",
"course": {"code": "MATH-3"},
},
"metrics": {
"xp_earned": 80,
"mastered_units": 1,
},
})
```
### `pctComplete` directly
If your app already tracks overall course progress, you can pass `pctComplete` (0--100) directly when ending an activity. The SDK skips historical computation for an explicit value. Keep it within 0–100: browser submissions clamp out-of-range numbers, while the TypeScript server API rejects them.
```typescript TypeScript (client) theme={null}
await activity.end({
xpEarned: 80,
pctComplete: 70,
})
```
```typescript TypeScript (server) theme={null}
await timeback.activity.record({
user: { email: 'student@example.com' },
activity: {
id: 'lesson-7',
name: 'Decimals',
course: { code: 'MATH-3' },
},
metrics: {
xpEarned: 80,
pctComplete: 70,
},
})
```
```python Python (server) theme={null}
await timeback.activity.record({
"user": {"email": "student@example.com"},
"activity": {
"id": "lesson-7",
"name": "Decimals",
"course": {"code": "MATH-3"},
},
"metrics": {
"xp_earned": 80,
"pct_complete": 70,
},
})
```
## How `masteredUnits` works
`masteredUnits` is an **incremental** count; it represents how many new units (lessons) a student mastered during **this specific activity**, not a cumulative total.
`masteredUnits` must be incremental. Sending cumulative counts will double-count mastery and
inflate the student's progress percentage.
### What the server does
When `masteredUnits` is provided and `pctComplete` is omitted, the server:
1. Resolves a positive `totalLessons`, preferring environment-specific metadata over base metadata.
2. Finds the student's enrollment for the synced course and reads its EduBridge enrollment facts.
3. Sums historical mastered units. It adds the current count only if weekly facts do not already contain the same activity ID and course/enrollment context. If that weekly check fails, it conservatively omits the current count.
4. Computes an integer percentage and clamps it to 0–100, then emits `pctCompleteApp` in the [`ActivityCompletedEvent`](/beta/build-on-timeback/reference/events#activitycompletedevent).
If the enrollment, denominator, or historical facts cannot be resolved, automatic progress can be omitted. Analytics freshness and concurrent submissions can affect the result. The weekly-fact check is not a transaction or a global exactly-once guarantee. TypeScript uses `Math.round`; Python uses `round`, which differs on exact half ties.
If `pctComplete` is provided alongside `masteredUnits`, the SDK validates the explicit value for the relevant API and
does not run the auto-computation. The explicit value always wins.
## Configuration
Configure `metadata.metrics.totalLessons` for courses that use automatic progress. The fragment below shows the relevant fields; include the other required app/course settings from the configuration reference.
```json Relevant configuration fields theme={null}
{
"courses": [
{
"subject": "Math",
"grade": 3,
"courseCode": "MATH-3",
"metadata": {
"metrics": {
"totalLessons": 10
}
}
}
]
}
```
## Example walkthrough
Assume a course has `totalLessons: 10`, historical facts are up to date, and each activity is new for this enrollment.
A student completes three activities over time:
| Submission | `masteredUnits` | Historical sum | Total mastered | `pctCompleteApp` |
| ---------- | --------------- | -------------- | -------------- | ---------------- |
| 1st | 3 | 0 | 3 | 30 |
| 2nd | 2 | 3 | 5 | 50 |
| 3rd | 2 | 5 | 7 | 70 |
After the third submission, the student is 70% through the course.
## Best practices
When a student masters one lesson, send `masteredUnits: 1`. If they master two lessons in
the same activity session, send `masteredUnits: 2`. The value always represents how many
**new** units were mastered during **this** activity — never a running total.
If a student replays a lesson they already mastered, do not send `masteredUnits` again for
that lesson. Double-counting inflates the student's progress percentage because the server
sums all historical values.
If no lessons were mastered during the activity, omit `masteredUnits` entirely (or send
`0`). The server only runs the auto-computation when `masteredUnits` is a number greater
than zero.
## Next steps
Full API for `activity.end()` and `timeback.activity.record()`
Caliper event schemas including `ActivityCompletedEvent`
`timeback.config.json` reference including `totalLessons`
Multi-session activities where completion is recorded server-side
# Introduction
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/activity-tracking/intro
Track your own learning content with time tracking and completion metrics
Custom Activities let you record learning sessions, measure time spent, and report completion metrics to the Timeback platform. You own your content and learning logic, while the SDK handles time tracking and event reporting.
This section covers the SDK implementation for **Custom Activities**
([Level 1](/beta/build-on-timeback/integration-levels#level-1-minimal-viable)). For the
step-by-step setup guide, see [Integrating existing apps](/beta/build-on-timeback/start-building/existing-apps).
If you want Timeback to manage the lesson engine, see [Managed Lessons](/beta/build-on-timeback/sdk/managed-lessons/intro).
## When to use
| | Custom Activities | [Managed Lessons](/beta/build-on-timeback/sdk/managed-lessons/intro) |
| --------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| **Content** | Your own content and logic | Timeback's course builder |
| **Platform role** | Time and completion reporting | Sequencing, scoring, and XP |
| **Integration level** | [Level 1](/beta/build-on-timeback/integration-levels#level-1-minimal-viable) | [Level 2](/beta/build-on-timeback/integration-levels#level-2-fully-native) |
## Choose your model
The SDK supports two activity models. Both use the same continuous time tracking under the hood; the difference is in how completion is handled.
Completed in one sitting. Client tracks time and reports completion.
Spans multiple sessions. Client tracks time, server records completion.
Not sure which to choose? See [Activity Models](/beta/about-timeback/concepts/activity-models)
for a conceptual comparison.
## How time tracking works
Both models use **continuous heartbeat-based time tracking**. Rather than accumulating time and reporting it once at the end, the SDK sends periodic updates throughout the session. Each update reconciles a time window, reporting how many milliseconds were active and how many were inactive.
Heartbeats begin automatically when you call `activity.start()` and repeat at a regular interval (default 15s), and are sent as [`TimeSpentEvents`](/beta/build-on-timeback/reference/events#timespentevent).
```mermaid theme={null}
graph LR
A["activity.start()"] --> B["Periodic heartbeats"]
B --> C["activity.end()"]
C --> D["Final heartbeat flush"]
style A fill:#3b82f6,stroke:#2563eb,color:#fff
style B fill:#8b5cf6,stroke:#7c3aed,color:#fff
style C fill:#10b981,stroke:#059669,color:#fff
style D fill:#f59e0b,stroke:#d97706,color:#fff
```
### Why this matters
* **Periodic delivery attempts** reduce dependence on a final browser-exit request. They do not guarantee a maximum loss window: network failures and browser shutdown can lose reports.
* **Accurate per-window timestamps**: each event represents the actual engagement window
* **Visibility-aware by default**: visibility changes pause active-time accumulation. This measures visible time, not proof of attention.
### Abandoned tab detection
If a tab stays hidden for an extended period (default 10 minutes), the SDK stops heartbeats entirely. When the student returns, tracking resets from a clean slate. This prevents counting hours of abandoned-tab time.
Heartbeat windows advance before the network request. Failed sends are reported through `onError` and are not durably buffered. Automatic heartbeat retries default to zero. Page-exit delivery is best effort.
## Event correlation with `runId`
Every activity is identified by a `runId`, a unique identifier generated at `activity.start()`.
All heartbeats, as well as the final completion event, share the same `runId`, which allows downstream systems to correlate time-spent data with completion results.
For [stateful activities](/beta/build-on-timeback/sdk/activity-tracking/stateful), your app must persist and reuse the UUID `runId` when the student resumes, so heartbeats from different sessions are still correlated with the same completion event.
## Pause and resume
Both models support pause and resume. Paused time is not counted as active. The SDK distinguishes active and inactive milliseconds in every heartbeat.
When paused, the SDK flushes accumulated time and stops heartbeats. On resume, it starts a fresh tracking window. Optional `onPause` and `onResume` callbacks fire on both explicit calls and automatic state changes like [hidden timeouts](#abandoned-tab-detection).
See [`pause()` and `resume()`](/beta/build-on-timeback/sdk/activity-tracking/reference#methods) in the reference for usage.
## Configuration
Custom activity tracking requires a Caliper sensor URL in your [`timeback.config.json`](/beta/build-on-timeback/reference/configuration).
See the [configuration reference](/beta/build-on-timeback/reference/configuration#sensor-resolution) for sensor resolution rules and environment overrides.
## Next Steps
Client-driven activities completed in one sitting
Multi-session activities with server-side completion
Parameters, properties, methods, and callbacks
Full timeback.config.json reference
Step-by-step setup guide for integrating existing apps
# Reference
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/activity-tracking/reference
Parameters, properties, methods, and callbacks for activity tracking
## Client
### `activity.current`
The currently active activity instance, or `null` if none is running.
### `activity.start(params)`
Creates and starts a new activity tracker. Heartbeats begin immediately unless `time: false` is passed.
Only one activity can be active at a time. Calling `start()` while an activity is already
running throws an error. Await `end()` on the current activity before starting the next one.
Activity slug: a stable, URL-safe identifier for the learning object (e.g.
`"fractions-with-like-denominators"`). Used to construct the canonical activity URL in Caliper
events.
Human-readable display name (e.g. `"Fractions with Like Denominators"`). Sent as
`object.activity.name` in Caliper events.
Course selector: must match a course in `timeback.config.json`. Either `{ subject, grade }` or `{ code }`.
UUID for correlating events across sessions. Arbitrary strings are rejected by the HTTP handlers. If omitted, the SDK generates a new
UUID. Pass the same `runId` when resuming a [stateful
activity](/beta/build-on-timeback/sdk/activity-tracking/stateful) to link heartbeats with the
eventual completion event.
Time tracking configuration. All fields are optional: defaults work well for most apps.
Set to `false` to disable client-side time tracking entirely. When disabled, no heartbeats are
sent, no visibility handlers are registered, and `end()` skips the final time flush. Use this when e.g. time is managed server-side.
Interval in milliseconds between automatic heartbeat flushes.
Pause time tracking when the browser tab is not visible.
Flush accumulated time immediately when the tab becomes hidden.
Attempt a best-effort flush on page unload via `sendBeacon` or `fetch({ keepalive: true })`.
Stop tracking after this many milliseconds of hidden time (default 10 minutes). Prevents counting abandoned-tab time. Set to `Infinity` to disable. In the reviewed implementation, `null` is replaced by the default by a nullish-coalescing expression and does not disable the timeout.
Number of retry attempts for failed heartbeat sends. `0` means no retry (default). Retries follow the configured `retryDelaysMs` schedule.
Delay schedule (in milliseconds) between retry attempts. Each index corresponds to the delay before that attempt. If more attempts than entries, the last value is reused.
Called when a time-spent flush or completion submission fails. For regular heartbeats, fires after the configured retries are exhausted. Completion sends and page-exit sends do not use that retry schedule. Time-spent errors are non-fatal: the SDK continues tracking. Completion errors (from `end()`) are also surfaced here before being re-thrown.
The error that occurred.
Which operation failed. Aligns with Caliper event types:
* `TimeSpentEvent`
* `ActivityCompletedEvent`
The activity slug passed to `activity.start()`.
The `runId` for this activity instance.
Called when the activity is paused, either explicitly via `pause()` or automatically when the
[hidden timeout](/beta/build-on-timeback/sdk/activity-tracking/intro#abandoned-tab-detection)
fires.
Called when the activity resumes, either via `resume()` or when the user returns after a hidden
timeout.
Called after a heartbeat request succeeds, or after the browser accepts a page-exit beacon for delivery. Beacon acceptance does not confirm server storage. The argument is the active milliseconds in that window.
#### Examples
```typescript Default theme={null}
const activity = timeback.activity.start({
id: 'lesson-1',
name: 'Intro to Fractions',
course: { subject: 'Math', grade: 3 },
})
```
```typescript Custom time options theme={null}
const activity = timeback.activity.start({
id: 'lesson-1',
name: 'Intro to Fractions',
course: { subject: 'Math', grade: 3 },
time: {
flushIntervalMs: 30000, // 30 seconds
visibilityAware: false, // track even when tab is hidden
},
})
```
```typescript With callbacks theme={null}
const activity = timeback.activity.start({
id: 'lesson-1',
name: 'Intro to Fractions',
course: { subject: 'Math', grade: 3 },
onError: (err, ctx) => {
console.warn(`Activity ${ctx.type} failed:`, err.message)
},
onPause: () => showPausedOverlay(),
onResume: () => hidePausedOverlay(),
onFlush: ms => updateTimeDisplay(ms),
})
```
```typescript With retry theme={null}
const activity = timeback.activity.start({
id: 'lesson-1',
name: 'Intro to Fractions',
course: { subject: 'Math', grade: 3 },
time: {
retryAttempts: 3,
retryDelaysMs: [100, 300, 1000],
},
onError: (err, ctx) => {
// Only fires after all retries are exhausted
console.warn(`${ctx.type} failed after retries:`, err.message)
},
})
```
```typescript Disabled (server manages time) theme={null}
const activity = timeback.activity.start({
id: 'lesson-1',
name: 'Intro to Fractions',
course: { subject: 'Math', grade: 3 },
time: false,
})
// No heartbeats are sent — end() sends completion only
await activity.end({ xpEarned: 100 })
```
### Activity instance
The object returned by `activity.start()`.
#### Properties
The activity slug passed to `start()`.
When the activity was started.
Whether the activity is currently paused.
Whether `end()` has completed successfully.
Once true, the activity is no longer active and a new one can be started.
Unique identifier correlating heartbeats and completion events for this run.
Cumulative locally measured active time, including windows whose sends failed, plus the current window. This is not a server-confirmed total.
Active time for the current heartbeat window only. Resets to 0 after each flush.
#### Methods
Flushes accumulated time, then stops heartbeats until `resume()` is called. Fires `onPause` if
provided.
Starts a fresh tracking window and restarts heartbeats. Fires `onResume` if provided.
Attempt to flush accumulated time to the server. Failures are reported to `onError` and swallowed, so a resolved promise is not proof of delivery. No-op when time tracking is disabled or the
activity is paused. Serialized — only one flush can be in flight at a time.
```typescript theme={null}
activity.pause() // Flushes time, stops heartbeats
activity.resume() // Fresh window, restarts heartbeats
```
`onPause` and `onResume` callbacks also fire for automatic state changes like [hidden
timeouts](/beta/build-on-timeback/sdk/activity-tracking/intro#abandoned-tab-detection) — use
them to keep your UI in sync without polling `isPaused`.
### `activity.end(data?)`
Ends the activity. Attempts a final time flush when automatic time tracking is enabled. If completion data is provided, also sends an [`ActivityCompletedEvent`](/beta/build-on-timeback/reference/events#activitycompletedevent).
```typescript theme={null}
// Time-only flush (no completion recorded)
await activity.end()
// With completion
await activity.end({
xpEarned: 80,
totalQuestions: 10,
correctQuestions: 8,
pctComplete: 100,
})
```
If the completion call fails, the activity remains usable so the caller can retry. A heartbeat failure alone does not reject `end()`. `onError` fires with `{ type: 'completion' }` before the error is re-thrown.
```typescript theme={null}
const activity = timeback.activity.start({
id: 'lesson-1',
name: 'Fractions',
course: { subject: 'Math', grade: 3 },
onError: (err, ctx) => {
if (ctx.type === 'completion') showRetryButton()
},
})
```
#### Completion data
App-supplied XP for this activity. Follow the agreed [XP policy](/beta/about-timeback/concepts/xp-system); the SDK cannot infer it from time spanning multiple sessions.
Total questions in the activity. Must be paired with `correctQuestions`.
Questions answered correctly. Must be paired with `totalQuestions`.
Number of **new** units (lessons) the student mastered during this activity. This is an
incremental count, not a cumulative total. The server sums these across submissions and divides
by [`totalLessons`](/beta/build-on-timeback/reference/configuration#course-progress-config) to
auto-compute `pctComplete` when it is omitted. See [Course
progress](/beta/build-on-timeback/sdk/activity-tracking/course-progress) for details.
Course completion percentage (0--100). If omitted and `masteredUnits` is provided, the server
[auto-computes this](/beta/build-on-timeback/sdk/activity-tracking/course-progress) from
EduBridge enrollment analytics when available. The browser submission handler clamps explicit values to 0–100; the TypeScript server recording API rejects values outside that range.
The reviewed browser implementation does not read `end({ time: ... })`, even though the exported type contains a time field. Do not use it for offline time imports. Report explicit time through the server `activity.record({ time: ... })` API below, and disable automatic heartbeats for the same time interval.
**`totalQuestions` and `correctQuestions` must be provided together.**
If you provide one, you must provide the other.
***
## Server
### `timeback.activity.record(params)`
Records an activity completion from the backend. Sends an [`ActivityCompletedEvent`](/beta/build-on-timeback/reference/events#activitycompletedevent) to the Caliper API after resolving the user and synced course. Gradebook and [XP](/beta/about-timeback/concepts/xp-system) processing continues downstream; this promise does not acknowledge those outcomes. At 100% progress an additional mastery-completion write is attempted; its failures are logged rather than propagated.
The optional time event is sent after completion, in a separate request. Failure can leave partial success; your app must reconcile retries. A shared `runId` correlates events and does not make these writes one transaction.
See the [server adapter docs](/beta/build-on-timeback/sdk/server/nextjs) for setup.
Student identity. Provide `email` (required) and optionally `timebackId`. See
[Identity](/beta/build-on-timeback/sdk/identity) for how users are resolved.
Activity identity.
Unique activity identifier
Human-readable activity name
Course reference — provide `code` (course code), or `subject` and `grade`
Completion metrics.
App-supplied XP following the agreed [XP policy](/beta/about-timeback/concepts/xp-system). The reviewed TypeScript input permits negative XP deductions; the Python validator rejects negative XP.
Total questions in the activity
Questions answered correctly
Number of **new** units (lessons) mastered during this activity. This is incremental,
not cumulative. See [Course
progress](/beta/build-on-timeback/sdk/activity-tracking/course-progress).
Course completion percentage (0--100). Auto-computed from `masteredUnits` when omitted.
See [Course progress](/beta/build-on-timeback/sdk/activity-tracking/course-progress).
Optional time data. Include when the backend tracks accumulated session time — for example, when
the frontend uses [`time:
false`](/beta/build-on-timeback/sdk/activity-tracking/stateful#server-managed-time), or for
offline sync and batch imports.
ISO 8601 timestamp of when the activity was first started
ISO 8601 timestamp of when the activity was completed
Total active milliseconds across all sessions
Total inactive (paused) milliseconds across all sessions
UUID correlating this completion with frontend heartbeats. Should match the `runId` persisted
when the activity was started on the client.
**`totalQuestions` and `correctQuestions` must be provided together.**
If you provide one, you must provide the other.
#### Examples
```typescript TypeScript theme={null}
await timeback.activity.record({
user: { email: 'student@example.com' },
activity: {
id: 'lesson-1',
name: 'Fractions',
course: { code: 'MATH-3' },
},
metrics: {
totalQuestions: 20,
correctQuestions: 16,
xpEarned: 150,
masteredUnits: 2,
pctComplete: 100,
},
runId: savedProgress.runId,
})
```
```python Python theme={null}
await timeback.activity.record({
"user": {"email": "student@example.com"},
"activity": {
"id": "lesson-1",
"name": "Fractions",
"course": {"code": "MATH-3"},
},
"metrics": {
"total_questions": 20,
"correct_questions": 16,
"xp_earned": 150,
"mastered_units": 2,
"pct_complete": 100,
},
"run_id": saved_progress.run_id,
})
```
#### With time data
Only include `time` when the frontend uses [`time: false`](/beta/build-on-timeback/sdk/activity-tracking/reference#activity-start-params), meaning your server owns time tracking. Do **not** pass `time` here if the frontend is sending heartbeats (the default), or time will be double-counted.
```typescript TypeScript theme={null}
await timeback.activity.record({
user: { email: 'student@example.com' },
activity: {
id: 'lesson-1',
name: 'Fractions',
course: { code: 'MATH-3' },
},
metrics: {
totalQuestions: 20,
correctQuestions: 16,
xpEarned: 150,
},
time: {
activeMs: 1200000, // 20 minutes total active time
inactiveMs: 120000, // 2 minutes total paused time
},
runId: savedProgress.runId,
})
```
```python Python theme={null}
await timeback.activity.record({
"user": {"email": "student@example.com"},
"activity": {
"id": "lesson-1",
"name": "Fractions",
"course": {"code": "MATH-3"},
},
"metrics": {
"total_questions": 20,
"correct_questions": 16,
"xp_earned": 150,
},
"time": {
"active_ms": 1200000, # 20 minutes total active time
"inactive_ms": 120000, # 2 minutes total paused time
},
"run_id": saved_progress.run_id,
})
```
# Single-session activities
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/activity-tracking/single-session
Track activities that students complete in one sitting
Single-session activities are the simplest model: a student starts and completes an activity in one browser session. The client SDK tracks time automatically and reports completion when the student finishes.
For info on shared concepts, see the [Custom Activities
overview](/beta/build-on-timeback/sdk/activity-tracking/intro).
## How it works
```mermaid theme={null}
graph LR
subgraph Client
A["activity.start()"] --> B["Periodic heartbeats"]
B --> C["activity.end(metrics)"]
end
B -- "TimeSpentEvent" --> D["Caliper API"]
C -- "Final TimeSpentEvent" --> D
C -- "ActivityCompletedEvent" --> D
style A fill:#3b82f6,stroke:#2563eb,color:#fff
style C fill:#10b981,stroke:#059669,color:#fff
style D fill:#8b5cf6,stroke:#7c3aed,color:#fff
```
Once started, the SDK sends periodic [TimeSpentEvents](/beta/build-on-timeback/reference/events#timespentevent) throughout the session. When the student finishes, the SDK sends one final [TimeSpentEvent](/beta/build-on-timeback/reference/events#timespentevent) followed by an [ActivityCompletedEvent](/beta/build-on-timeback/reference/events#activitycompletedevent). All events share the same [`runId`](/beta/build-on-timeback/sdk/activity-tracking/intro#event-correlation-with-runid) for correlation.
## Starting an activity
Every single-session activity starts the same way:
```typescript theme={null}
const activity = timeback.activity.start({
id: 'intro-to-fractions',
name: 'Introduction to Fractions',
course: { subject: 'Math', grade: 3 }, // from timeback.config.json
})
// Student engages... (heartbeats emit automatically)
```
See the
[reference](/beta/build-on-timeback/sdk/activity-tracking/reference#activity-start-params) for
full parameter documentation including time options, callbacks, and `runId`.
## Ending an activity
How you *end* an activity depends on whether the student *completed* the activity. In both cases, accumulated time data is flushed. The difference is whether a completion result is also recorded.
### With completion
When a student finishes, call `activity.end()` with completion metrics:
1. Flushes a final heartbeat ([`TimeSpentEvent`](/beta/build-on-timeback/reference/events#timespentevent))
2. Submits the completion ([`ActivityCompletedEvent`](/beta/build-on-timeback/reference/events#activitycompletedevent))
```typescript theme={null}
await activity.end({
xpEarned: 8,
totalQuestions: 10,
correctQuestions: 8,
masteredUnits: 1,
})
```
See the [reference](/beta/build-on-timeback/sdk/activity-tracking/reference#activity-end-data)
for full completion data documentation.
### Without completion
Call `activity.end()` without arguments to flush time data only; in this case, no completion event is recorded.
1. Flushes a final heartbeat ([`TimeSpentEvent`](/beta/build-on-timeback/reference/events#timespentevent))
2. Stops the heartbeat timer
```typescript theme={null}
await activity.end()
```
There are two reasons to end without completion:
1. **Cleanup**: the component unmounts before the student finishes (e.g. navigating away mid-activity). You want to flush accumulated time without recording a result.
2. **Stateful activities**: the activity spans multiple sessions, so the client only tracks time per visit. Completion is [recorded by the server](/beta/build-on-timeback/sdk/activity-tracking/stateful) when the student eventually finishes.
## Framework integration
Start tracking after the browser client is initialized and the current user is verified. Use the [React](/beta/build-on-timeback/sdk/client/react), [Vue](/beta/build-on-timeback/sdk/client/vue), [Svelte](/beta/build-on-timeback/sdk/client/svelte), or [Solid](/beta/build-on-timeback/sdk/client/solid) verification API. A client object alone does not establish authentication.
Keep one activity owner for your lesson screen. Await `end()` before starting the next activity, including navigation and development-mode remounts. Async cleanup is not awaited by framework unmount hooks; starting immediately in the next mount can otherwise throw “An activity is already active.”
This example is a browser event handler after initialization and verification. `timeback` is the initialized SDK client and `showLesson` is your app's rendering function:
```typescript theme={null}
async function beginLesson(lessonId: string, lessonName: string) {
const previous = timeback.activity.current
if (previous) await previous.end()
const activity = timeback.activity.start({
id: lessonId,
name: lessonName,
course: { subject: 'Math', grade: 3 },
onError: (error, context) => console.warn(context.type, error.message),
})
showLesson(activity)
}
```
Serialize calls to this handler in your app. If you load lesson data asynchronously, cancel or ignore that load after the screen unmounts, before calling `start()`. Capture the activity created by that screen in its cleanup; end it without metrics. Handle a rejected completion in your UI and let the student retry before starting another activity.
## Best practices
Start after initialization and user verification, and serialize activity transitions. Register time-only cleanup for the owning screen.
Use `activity.end()` (no args) in cleanup functions. Only call `activity.end(metrics)` when
the student has actually finished the activity.
Check client initialization and [user verification](/beta/build-on-timeback/sdk/identity) separately.
Use stable, unique IDs that identify the specific lesson or content piece.
## Next steps
Multi-session activities with server-side completion
Parameters, properties, methods, and callbacks
# Stateful activities
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/activity-tracking/stateful
Track activities that students complete across multiple sessions
Stateful activities span multiple sessions. The client SDK tracks time per session via heartbeats, and the server records completion when the activity is truly done.
For info on shared concepts, see the [Activity Tracking
overview](/beta/build-on-timeback/sdk/activity-tracking/intro).
## Architecture
Stateful activities split responsibility between frontend and backend:
| Responsibility | Owner | Mechanism |
| ----------------- | ------------------- | -------------------------------------------------------------------------- |
| **Time tracking** | Frontend or Backend | [Heartbeats](#starting-a-session) or [`time: false`](#server-managed-time) |
| **State** | Backend | Your app's database |
| **Completion** | Backend | `timeback.activity.record()` |
A `runId` ties all sessions together. The SDK generates one automatically when you call `activity.start()`, but you can also provide your own. The SDK HTTP handlers and server recording API require a UUID. Reuse an assignment ID only if it is a valid UUID; otherwise store a separate UUID for the attempt. On resume, pass the same `runId` so every heartbeat and the final completion event share the same identifier.
```mermaid theme={null}
graph TD
DB[("Your Database")]
subgraph S1 [Session 1 - Monday]
A["activity.start()"] -->|generates runId| B["Heartbeats"]
B --> C["activity.end()"]
end
C -->|save runId| DB
DB -->|load runId| E
subgraph S2 [Session 2 - Wednesday]
E["activity.start with runId"] --> F["Heartbeats"]
F --> G["activity.end()"]
end
DB -->|load runId| H
subgraph S3 [Server]
H["timeback.activity.record with runId"]
end
B -- TimeSpentEvent --> I["Caliper API"]
C -- TimeSpentEvent --> I
F -- TimeSpentEvent --> I
G -- TimeSpentEvent --> I
H -- ActivityCompletedEvent --> I
style A fill:#3b82f6,stroke:#2563eb,color:#fff
style E fill:#3b82f6,stroke:#2563eb,color:#fff
style H fill:#10b981,stroke:#059669,color:#fff
style I fill:#8b5cf6,stroke:#7c3aed,color:#fff
style DB fill:#f59e0b,stroke:#d97706,color:#fff
```
## Frontend
The frontend tracks time per session and orchestrates the student's workflow (starting, resuming, and submitting answers). But completion must come from the server: across multiple sessions, it's the only part of the system that has the full picture of the student's accumulated progress.
### Starting a session
```typescript theme={null}
const activity = timeback.activity.start({
id: 'lesson-1',
name: 'Fractions',
course: { subject: 'Math', grade: 3 },
})
// Your API — persist the runId so you can pass it back on resume
await saveProgress({ lessonId: 'lesson-1', runId: activity.runId })
```
See the [reference](/beta/build-on-timeback/sdk/activity-tracking/reference#activity-start-params) for full parameter documentation including time options and callbacks.
### Resuming a session
```typescript theme={null}
const progress = await loadProgress({ lessonId: 'lesson-1' })
const activity = timeback.activity.start({
id: 'lesson-1',
name: 'Fractions',
course: { subject: 'Math', grade: 3 },
runId: progress.runId, // reuse the saved runId
})
```
### Ending a session
This is the key difference from [single-session activities](/beta/build-on-timeback/sdk/activity-tracking/single-session): your app saves progress independently of the SDK. Calling `activity.end()` attempts a final time flush and closes this browser tracker without a completion event. To resume, load saved progress from your app and create a new tracker with the same `runId`. Tab closure does not guarantee a successful final save or heartbeat.
```typescript theme={null}
await activity.end() // Flushes time, no completion
```
The frontend **always** calls `activity.end()` without metrics. Completion is recorded by the
[server](#server-recording-completion).
### Loading and lifecycle
Create or load the attempt in your authenticated backend before starting its browser tracker. Persist the UUID with the attempt, and return it with the saved answers. This avoids losing the correlation ID if a browser closes between `start()` and a later save.
Use the lifecycle guidance in [Single-session activities](/beta/build-on-timeback/sdk/activity-tracking/single-session#framework-integration): wait for client initialization and verification, serialize `end()` and `start()`, and cancel pending progress loads when a screen unmounts. The SDK does not provide `saveProgress` or `loadProgress`; both are your application APIs.
### Server-managed time
If your server already tracks time (e.g. from request logs or its own session model), you can disable heartbeats entirely by passing `time: false`. The client still provides the `activity.end()` ergonomics, but no `TimeSpentEvent`s are sent.
```typescript theme={null}
const activity = timeback.activity.start({
id: 'lesson-1',
name: 'Fractions',
course: { subject: 'Math', grade: 3 },
runId: progress.runId,
time: false,
})
// No heartbeats during the session — end() closes the activity without sending time data
await activity.end()
```
When using `time: false`, the server must report time via the `time` parameter in
`timeback.activity.record()`. Do **not** use both heartbeats and server-side time — this will
double-count. See the [server reference](/beta/build-on-timeback/sdk/activity-tracking/reference#server) for details.
## Backend
When a student completes an activity, as determined by your app's logic, the backend records the result using `timeback.activity.record()`. This sends an [`ActivityCompletedEvent`](/beta/build-on-timeback/reference/events#activitycompletedevent) to the Caliper API after resolving the user and synced course. Downstream gradebook and [XP](/beta/about-timeback/concepts/xp-system) processing is asynchronous; a successful SDK call does not prove that it has completed. At 100% progress the SDK also attempts a mastery-completion gradebook entry; failures of that extra write are logged without failing the request.
Pass the same `runId` from the frontend to correlate heartbeats with the completion event.
See the [reference](/beta/build-on-timeback/sdk/activity-tracking/reference#timeback-activity-record-params) for full parameter documentation.
```typescript TypeScript theme={null}
await timeback.activity.record({
user: { email: 'student@example.com' },
activity: {
id: 'lesson-1',
name: 'Fractions',
course: { code: 'MATH-3' },
},
metrics: {
totalQuestions: 20,
correctQuestions: 16,
xpEarned: 150,
masteredUnits: 2,
pctComplete: 100,
},
runId: savedProgress.runId,
})
```
```python Python theme={null}
await timeback.activity.record({
"user": {"email": "student@example.com"},
"activity": {
"id": "lesson-1",
"name": "Fractions",
"course": {"code": "MATH-3"},
},
"metrics": {
"total_questions": 20,
"correct_questions": 16,
"xp_earned": 150,
"mastered_units": 2,
"pct_complete": 100,
},
"run_id": saved_progress.run_id,
})
```
Only include `time` when the frontend uses [`time: false`](#server-managed-time). If heartbeats
are active (the default), the platform already has the time data — passing `time` here would
double-count. See the full [server reference](/beta/build-on-timeback/sdk/activity-tracking/reference#server)
for all parameters.
## Best practices
The `runId` is the correlation key between frontend heartbeats and backend completion. Save
it to your database as soon as the activity starts.
When a student starts a new attempt of a previously completed activity, do not reuse the old
`runId`. Omit it from `activity.start()` to generate a fresh one — otherwise new heartbeats
would be incorrectly correlated with the old completion event.
Completion should come from the backend via `timeback.activity.record()`. The frontend
should always use `activity.end()` (no metrics) to flush time data only.
## Next steps
Simpler model for one-sitting activities
Full parameter and method reference
Authentication setup for resolving users
# React
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/client/react
Client-side React integration with Timeback
## Overview
The Timeback SDK provides React hooks, components, and a context provider for client-side integration.
If you are using coding agents, pair this page with [`timeback-client`](https://github.com/superbuilders/timeback-sdk-skills/tree/main/skills/timeback/timeback-client) from the [AI Skills](/beta/build-on-timeback/ai/skills) catalog.
## Installation
```bash npm theme={null}
npm install @timeback/sdk
```
```bash pnpm theme={null}
pnpm add @timeback/sdk
```
```bash yarn theme={null}
yarn add @timeback/sdk
```
```bash bun theme={null}
bun add @timeback/sdk
```
## Provider Setup
Wrap your app with `TimebackProvider`:
```tsx app/providers.tsx theme={null}
'use client'
import { TimebackProvider } from '@timeback/sdk/react'
export function Providers({ children }: { children: React.ReactNode }) {
return {children}
}
```
```tsx app/layout.tsx theme={null}
import { Providers } from './providers'
export default function RootLayout({ children }) {
return (
{children}
)
}
```
## Hooks
### `useTimeback`
Access the Timeback client. Its presence means the SDK is initialized, not that a user is signed in. Use the verification API below to check the current user:
```tsx theme={null}
import { useTimeback } from '@timeback/sdk/react'
function MyComponent() {
const timeback = useTimeback()
// The provider may not have initialized the client yet.
if (!timeback) {
return
Initializing...
}
return
Welcome!
}
```
### `useTimebackVerification`
Check authentication status:
```tsx theme={null}
import { useTimebackVerification } from '@timeback/sdk/react'
function ProtectedRoute({ children }) {
const { state, refresh } = useTimebackVerification()
if (state.status === 'loading') return
Loading...
if (state.status !== 'verified') return Sign in
return children
}
```
The `state` object has a `status` property that can be:
* `'loading'` - Verification in progress
* `'verified'` - User is verified (includes `timebackId`)
* `'unverified'` - User is not verified
* `'error'` - Verification failed (includes `message`)
### `useTimebackProfile`
Fetch user profile data:
```tsx theme={null}
import { useTimebackProfile } from '@timeback/sdk/react'
// Manual fetch
function ProfileButton() {
const { state, canFetch, fetchProfile } = useTimebackProfile()
if (state.status === 'loaded') {
return (
Welcome, {state.profile.name}
XP Today: {state.profile.xp.today}
Total XP: {state.profile.xp.all}
)
}
return (
)
}
// Auto-fetch when authenticated
function AutoProfile() {
const { state } = useTimebackProfile({ auto: true })
if (state.status === 'loading') return
)
}
```
#### Props
Button size
Button style variant
Show loading spinner on click
Show Timeback logo
Disable the button
Additional CSS classes
Inline styles
Additional click handler
Button text
## Custom Activities
The SDK supports two [activity models](/beta/about-timeback/concepts/activity-models) that capture how students spend time in your app and whether they complete what they started, producing [TimeSpentEvents](/beta/build-on-timeback/reference/events#timespentevent) and [ActivityCompletedEvents](/beta/build-on-timeback/reference/events#activitycompletedevent) that feed into dashboards, XP, and learning analytics.
### Single-session
A quiz, flashcard deck, or short lesson that a student completes in one sitting. The client tracks time and reports completion. Learn more about [single-session activities](/beta/build-on-timeback/sdk/activity-tracking/single-session).
### Stateful
A multi-part course or long-form project where students leave and come back across multiple sessions. The client tracks time per visit while the server records completion. Learn more about [stateful activities](/beta/build-on-timeback/sdk/activity-tracking/stateful).
## Next Steps
Server-side setup
Learn more about tracking
# Solid
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/client/solid
Client-side Solid integration with Timeback
## Overview
The Timeback SDK provides Solid primitives, components, and a context provider for client-side integration.
## Installation
```bash npm theme={null}
npm install @timeback/sdk
```
```bash pnpm theme={null}
pnpm add @timeback/sdk
```
```bash yarn theme={null}
yarn add @timeback/sdk
```
```bash bun theme={null}
bun add @timeback/sdk
```
## Provider Setup
Wrap your app with `TimebackProvider`:
```tsx src/app.tsx theme={null}
import { TimebackProvider } from '@timeback/sdk/solid'
import { Router } from '@solidjs/router'
import { FileRoutes } from '@solidjs/start/router'
import { Suspense } from 'solid-js'
export default function App() {
return (
{props.children}}>
)
}
```
## Primitives
### `useTimeback`
Access the Timeback client. Its presence means the SDK is initialized, not that a user is signed in. Use the verification API below to check the current user:
```tsx theme={null}
import { useTimeback } from '@timeback/sdk/solid'
import { Show } from 'solid-js'
function MyComponent() {
const timeback = useTimeback()
return (
Initializing...}>
Sign in{props.children}
)
}
```
Keep the returned object intact: destructuring its `state` or `canFetch` getters loses reactive updates. `canFetch` is a boolean property, not a function. The `state` getter returns an object with a `status` property that can be:
* `'loading'` - Verification in progress
* `'verified'` - User is verified (includes `timebackId`)
* `'unverified'` - User is not verified
* `'error'` - Verification failed (includes `message`)
### `createTimebackProfile`
Fetch user profile data:
```tsx theme={null}
import { createTimebackProfile } from '@timeback/sdk/solid'
import { Match, Show, Switch } from 'solid-js'
function ProfileButton() {
const profile = createTimebackProfile()
const loaded = () => {
const current = profile.state
return current.status === 'loaded' ? current.profile : undefined
}
return (
)
}
```
#### Props
Button size
Button style variant
Show loading spinner on click
Show Timeback logo
Disable the button
## Custom Activities
The SDK supports two [activity models](/beta/about-timeback/concepts/activity-models) that capture how students spend time in your app and whether they complete what they started, producing [TimeSpentEvents](/beta/build-on-timeback/reference/events#timespentevent) and [ActivityCompletedEvents](/beta/build-on-timeback/reference/events#activitycompletedevent) that feed into dashboards, XP, and learning analytics.
### Single-session
A quiz, flashcard deck, or short lesson that a student completes in one sitting. The client tracks time and reports completion. Learn more about [single-session activities](/beta/build-on-timeback/sdk/activity-tracking/single-session).
### Stateful
A multi-part course or long-form project where students leave and come back across multiple sessions. The client tracks time per visit while the server records completion. Learn more about [stateful activities](/beta/build-on-timeback/sdk/activity-tracking/stateful).
## Next Steps
Server-side setup
Learn more about tracking
# Svelte
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/client/svelte
Client-side Svelte integration with Timeback
## Overview
The Timeback SDK provides Svelte stores, components, and initialization for client-side integration.
## Installation
```bash npm theme={null}
npm install @timeback/sdk
```
```bash pnpm theme={null}
pnpm add @timeback/sdk
```
```bash yarn theme={null}
yarn add @timeback/sdk
```
```bash bun theme={null}
bun add @timeback/sdk
```
## Setup
Initialize Timeback in your root layout:
```svelte src/routes/+layout.svelte theme={null}
{@render children()}
```
## Stores
### `timeback`
Access the Timeback client. Its presence means the SDK is initialized, not that a user is signed in. Use the verification API below to check the current user:
```svelte theme={null}
{#if !$timeback}
{:else if $timebackVerification.status !== 'verified'}
Please sign in
{:else}
{/if}
```
The store value has a `status` property that can be:
* `'loading'` - Verification in progress
* `'verified'` - User is verified (includes `timebackId`)
* `'unverified'` - User is not verified
* `'error'` - Verification failed (includes `message`)
### `timebackProfile`
Fetch user profile data:
```svelte theme={null}
{#if $timebackProfile.status === 'loaded'}
```
#### Props
Button size
Button style variant
Show loading spinner on click
Show Timeback logo
Disable the button
## Custom Activities
The SDK supports two [activity models](/beta/about-timeback/concepts/activity-models) that capture how students spend time in your app and whether they complete what they started, producing [TimeSpentEvents](/beta/build-on-timeback/reference/events#timespentevent) and [ActivityCompletedEvents](/beta/build-on-timeback/reference/events#activitycompletedevent) that feed into dashboards, XP, and learning analytics.
### Single-session
A quiz, flashcard deck, or short lesson that a student completes in one sitting. The client tracks time and reports completion. Learn more about [single-session activities](/beta/build-on-timeback/sdk/activity-tracking/single-session).
### Stateful
A multi-part course or long-form project where students leave and come back across multiple sessions. The client tracks time per visit while the server records completion. Learn more about [stateful activities](/beta/build-on-timeback/sdk/activity-tracking/stateful).
## Next Steps
Server-side setup
Learn more about tracking
# Vue
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/client/vue
Client-side Vue integration with Timeback
## Overview
The Timeback SDK provides Vue composables, components, and a provider for client-side integration.
## Installation
```bash npm theme={null}
npm install @timeback/sdk
```
```bash pnpm theme={null}
pnpm add @timeback/sdk
```
```bash yarn theme={null}
yarn add @timeback/sdk
```
```bash bun theme={null}
bun add @timeback/sdk
```
## Provider Setup
Wrap your app with `TimebackProvider`:
```vue app.vue theme={null}
```
## Composables
### `useTimeback`
Access the Timeback client. Its presence means the SDK is initialized, not that a user is signed in. Use the verification API below to check the current user:
```vue theme={null}
```
The `state` ref has a `status` property that can be:
* `'loading'` - Verification in progress
* `'verified'` - User is verified (includes `timebackId`)
* `'unverified'` - User is not verified
* `'error'` - Verification failed (includes `message`)
### `useTimebackProfile`
Fetch user profile data:
```vue theme={null}
```
#### Props
Button size
Button style variant
Show loading spinner on click
Show Timeback logo
Disable the button
## Custom Activities
The SDK supports two [activity models](/beta/about-timeback/concepts/activity-models) that capture how students spend time in your app and whether they complete what they started, producing [TimeSpentEvents](/beta/build-on-timeback/reference/events#timespentevent) and [ActivityCompletedEvents](/beta/build-on-timeback/reference/events#activitycompletedevent) that feed into dashboards, XP, and learning analytics.
### Single-session
A quiz, flashcard deck, or short lesson that a student completes in one sitting. The client tracks time and reports completion. Learn more about [single-session activities](/beta/build-on-timeback/sdk/activity-tracking/single-session).
### Stateful
A multi-part course or long-form project where students leave and come back across multiple sessions. The client tracks time per visit while the server records completion. Learn more about [stateful activities](/beta/build-on-timeback/sdk/activity-tracking/stateful).
## Next Steps
Server-side setup
Learn more about tracking
# Identity Modes
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/identity
Configure SSO or custom authentication with Timeback
## Overview
The Timeback SDK supports two identity modes.
Use Timeback as your identity provider
Use your existing auth system
## SSO Mode
SSO mode uses Timeback as the identity provider via OIDC. The SDK automatically resolves the Timeback user by email and returns `TimebackAuthUser`.
```typescript TypeScript theme={null}
import { createTimeback } from '@timeback/sdk'
export const timeback = await createTimeback({
env: 'staging',
api: {
clientId: process.env.TIMEBACK_API_CLIENT_ID!,
clientSecret: process.env.TIMEBACK_API_CLIENT_SECRET!,
},
identity: {
mode: 'sso',
clientId: process.env.AWS_COGNITO_CLIENT_ID!,
clientSecret: process.env.AWS_COGNITO_CLIENT_SECRET!,
onCallbackSuccess: async ({ user, idp, state, redirect }) => {
// user.id is the timebackId
// user.email, user.name come from the user's Timeback profile
// user.claims contains IdP data (sub, firstName, lastName, pictureUrl)
await setSession({ id: user.id, email: user.email })
return redirect(state?.returnTo ?? '/')
},
onCallbackError: ({ error, errorCode, redirect }) => {
console.error('SSO Error:', errorCode, error.message)
return redirect('/?error=sso_failed')
},
getUser: () => getCurrentSession(),
},
})
```
```python Python theme={null}
import os
from timeback import TimebackIdentity
from timeback.server import (
create_server, TimebackConfig, ApiCredentials, SsoIdentityConfig,
)
async def get_user(request):
session = await get_session(request)
return TimebackIdentity(id=session.user_id, email=session.email) if session else None
async def on_callback_success(ctx):
# ctx.user.id is the timebackId
# ctx.user.email, ctx.user.name come from the user's Timeback profile
await set_session(id=ctx.user.id, email=ctx.user.email)
return ctx.redirect((ctx.state or {}).get("return_to", "/"))
def on_callback_error(ctx):
return ctx.redirect("/?error=sso_failed")
timeback = create_server(TimebackConfig(
env="staging",
api=ApiCredentials(
client_id=os.environ["TIMEBACK_API_CLIENT_ID"],
client_secret=os.environ["TIMEBACK_API_CLIENT_SECRET"],
),
identity=SsoIdentityConfig(
mode="sso",
client_id=os.environ["TIMEBACK_SSO_CLIENT_ID"],
client_secret=os.environ["TIMEBACK_SSO_CLIENT_SECRET"],
get_user=get_user,
on_callback_success=on_callback_success,
on_callback_error=on_callback_error,
),
))
```
The `user` object in `onCallbackSuccess` has this structure:
```typescript theme={null}
interface TimebackAuthUser {
id: string // Timeback user ID
email: string
name?: string
school?: { id: string; name: string }
grade?: number
claims: {
sub: string
email: string
firstName?: string
lastName?: string
pictureUrl?: string
}
}
```
## Custom Mode
Use custom mode when your app has its own authentication system.
```typescript TypeScript theme={null}
import { createTimeback } from '@timeback/sdk'
export const timeback = await createTimeback({
env: 'staging',
api: {
clientId: process.env.TIMEBACK_API_CLIENT_ID!,
clientSecret: process.env.TIMEBACK_API_CLIENT_SECRET!,
},
identity: {
mode: 'custom',
getEmail: async req => {
const session = await getSession(req)
// Return the email from your existing auth system
return session?.email
},
},
})
```
```python Python theme={null}
import os
from timeback import TimebackIdentity
from timeback.server import (
create_server, TimebackConfig, ApiCredentials, CustomIdentityConfig,
)
async def get_user_email(request):
session = await get_session(request)
# Return the email from your existing auth system
return session.email if session else None
timeback = create_server(TimebackConfig(
env="staging",
api=ApiCredentials(
client_id=os.environ["TIMEBACK_API_CLIENT_ID"],
client_secret=os.environ["TIMEBACK_API_CLIENT_SECRET"],
),
identity=CustomIdentityConfig(
mode="custom",
get_email=get_user_email,
),
))
```
The SDK resolves the Timeback user by email. You only need to provide the authenticated user's email address.
## Identity-Only Mode
If you need only SSO, use `createTimebackIdentity()`:
```typescript lib/timeback.ts theme={null}
import { createTimebackIdentity } from '@timeback/sdk'
export const timeback = createTimebackIdentity({
env: 'production',
identity: {
mode: 'sso',
clientId: process.env.AWS_COGNITO_CLIENT_ID!,
clientSecret: process.env.AWS_COGNITO_CLIENT_SECRET!,
onCallbackSuccess: async ({ user, tokens, redirect }) => {
// user is raw OIDC userInfo (sub, email, name, picture, etc.)
// No Timeback profile enrichment in identity-only mode
await createSession({
sub: user.sub,
email: user.email,
name: user.name,
})
return redirect('/')
},
onCallbackError: ({ error, redirect }) => {
return redirect('/login?error=sso_failed')
},
getUser: req => getSessionFromRequest(req),
},
})
```
## Sign-In Flow
The SDK provides a `SignInButton` component that initiates the SSO flow:
```tsx theme={null}
import { SignInButton } from '@timeback/sdk/react'
function LoginPage() {
return Sign in with Timeback
}
```
```vue theme={null}
Sign in with Timeback
```
```svelte theme={null}
Sign in with Timeback
```
```tsx theme={null}
import { SignInButton } from '@timeback/sdk/solid'
function LoginPage() {
return Sign in with Timeback
}
```
## User Verification
Verify if a user has a Timeback account:
```tsx theme={null}
import { useTimebackVerification } from '@timeback/sdk/react'
function ProtectedContent() {
const { state } = useTimebackVerification()
if (state.status === 'loading') return
{:else if $timebackVerification.status !== 'verified'}
Please sign in
{:else}
Protected content
{/if}
```
```tsx theme={null}
import { Show } from 'solid-js'
import { createTimebackVerification } from '@timeback/sdk/solid'
function ProtectedContent() {
const { state } = createTimebackVerification()
return (
Please sign in}>
Loading...
}>
Protected content
)
}
```
## Next Steps
Track learning sessions
Access user data and XP
Framework integration
Client components
# Attempt history
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/managed-lessons/attempt-history
Review past lesson attempts and per-question results
After a student completes one or more lesson attempts, you can retrieve their history and review per-question results. This is useful for building review screens, progress dashboards, or retry flows.
## List past attempts
Call `lessons.attempts()` to get all attempts for a lesson:
```typescript theme={null}
const attempts = await timeback.lessons.attempts({
lessonId: lesson.id,
})
```
Attempt summaries contain `score` plus optional `attempt`, `startedAt`, `completedAt`, and `scoreStatus`. Fetch attempt details for question counts and finalization/progress information.
See the [reference](/beta/build-on-timeback/sdk/managed-lessons/reference#lessons-attempts) for
the full return type.
## Get attempt details
To see per-question data for a specific attempt, call `lessons.attemptDetails()`:
```typescript theme={null}
const details = await timeback.lessons.attemptDetails({
lessonId: lesson.id,
attempt: 1,
})
```
### Extract questions
Use `getLessonAttemptQuestions()` to get a flat list of questions from the response:
```typescript theme={null}
import { getLessonAttemptQuestions } from '@timeback/sdk/client'
const questions = getLessonAttemptQuestions(details)
```
Each question has an `id`; response fields are optional `response` and/or `responses`, not `studentResponse`. Correctness, result details, and `content.rawXml` may also be absent.
See the
[reference](/beta/build-on-timeback/sdk/managed-lessons/reference#lessons-attemptdetails) for
full type documentation.
The raw response shape differs by lesson type. Adaptive lessons (`powerpath-100`) return
`seenQuestions`, which is only the questions PowerPath actually served (the count varies per
student). Linear lessons (`quiz`, `test-out`, etc.) return `questions`, which is the full
fixed set for the attempt.
`getLessonAttemptQuestions()` normalizes both into a single `LessonAttemptQuestion[]`. If you
need to distinguish the two (e.g., to show "X of Y questions seen" for adaptive lessons),
check `details.lessonType` and access the raw fields directly.
## Build a review screen
A typical review flow:
```typescript theme={null}
// 1. Load attempts
const attempts = await timeback.lessons.attempts({ lessonId })
// 2. Let the student pick an attempt to review
const selected = attempts.find(attempt => attempt.attempt !== undefined)
if (selected?.attempt === undefined) throw new Error("No numbered attempt to review")
// 3. Load details
const details = await timeback.lessons.attemptDetails({
lessonId,
attempt: selected.attempt,
})
// 4. Get questions
const questions = getLessonAttemptQuestions(details)
// 5. Render review cards
for (const q of questions) {
renderReviewCard({
questionXml: q.content?.rawXml,
studentAnswer: q.responses ?? q.response,
wasCorrect: q.correct,
})
}
```
## Next steps
Start a new lesson
Parameters, properties, methods, and return types
# Completion
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/managed-lessons/completion
Finalize a lesson, interpret results, and understand Caliper behavior
When the [question loop](/beta/build-on-timeback/sdk/managed-lessons/question-loop) is done, call `session.complete()` to finalize the lesson. This triggers server-side scoring, flushes the final time tracking data, and returns a comprehensive result object.
## Complete a lesson
```typescript theme={null}
const result = await session.complete()
```
The result includes everything you need to render a results screen: `score`, `accuracy`, `totalQuestions`, `correctQuestions`, `timeSpentSeconds`, and more.
See the [reference](/beta/build-on-timeback/sdk/managed-lessons/reference#lessonsessioncompleteresult) for the full return type.
## What happens under the hood
`session.complete()` does three things in sequence:
```mermaid theme={null}
graph LR
A["session.complete()"] --> B["PowerPath finalize"]
B --> C["Flush time data"]
C --> D["Return merged result"]
```
1. **Server**: reads progress. For an unfinalized linear lesson it attempts `finalStudentAssessmentResponse()`, catches errors, and reads progress again. For an adaptive lesson it uses the current progress without that finalization call.
2. **Client**: calls `activity.end()` to flush the final time tracking window as a `TimeSpentEvent`
3. **Client**: returns a merged result combining the server response with client-side timing data
## Caliper event flow
Managed Lessons split Caliper responsibility between the SDK and PowerPath:
| Concern | Handled by | Event |
| --------------- | ----------------------- | -------------------------------------------------- |
| Time tracking | SDK (automatic) | `TimeSpentEvent` heartbeats throughout the session |
| Completion + XP | PowerPath (server-side) | `ActivityEvent` with calculated XP |
The SDK does **not** send an `ActivityCompletedEvent` for Managed Lessons. PowerPath is the source of truth for scoring, XP calculation, and OneRoster gradebook updates.
This means you do not need to calculate XP or report completion metrics yourself -- PowerPath handles it when `session.complete()` calls the finalization endpoint.
## Handle time tracking errors
The final time flush and scoring are separate. `timeTrackingSent: false` means the tracker's `end()` call rejected. A true value is not delivery confirmation: heartbeat network errors are normally reported internally and swallowed, so they may leave this flag true.
```typescript theme={null}
const result = await session.complete()
if (!result.timeTrackingSent) {
console.warn('Time tracking flush failed:', result.error)
// Inspect result.finalized and the score separately.
// Reconcile analytics separately; there is no fixed maximum loss window.
}
```
These operations delegate to PowerPath and return its progress. For linear lessons, check `finalized` before displaying a completed result. The SDK catches finalization errors and re-reads progress, so a resolved promise can still return `finalized: false`. Adaptive completion returns the current score and marks the SDK result finalized; it does not itself establish that a mastery threshold was reached.
## Render results
Use the completion result to build a results screen:
```typescript theme={null}
const result = await session.complete()
if (!result.finalized) {
showPendingFinalization(result) // Your application handles the retry/review state.
} else {
const payload = { kind: 'completed' as const, ...result }
renderResults(payload)
}
```
## Next steps
Review past attempts and per-question data
Parameters, properties, methods, and return types
# Introduction
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/managed-lessons/intro
Use Timeback's lesson engine to serve adaptive content
Managed Lessons let your app deliver learning experiences powered by Timeback's lesson engine. Instead of building your own content pipeline, sequencing logic, and scoring system, you call `timeback.lessons.*` and the SDK handles the rest.
This section covers the SDK implementation for **Managed Lessons** ([Level
2](/beta/build-on-timeback/integration-levels#level-2-fully-native)). For the onboarding guide,
see [Building native apps](/beta/build-on-timeback/start-building/native-apps). If you own your
content, see [Custom Activities](/beta/build-on-timeback/sdk/activity-tracking/intro).
## When to use
| | [Custom Activities](/beta/build-on-timeback/sdk/activity-tracking/intro) | Managed Lessons |
| --------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| **Content** | Your own content and logic | Timeback's course builder |
| **Platform role** | Time and completion reporting | Sequencing, scoring, and XP |
| **Integration level** | [Level 1](/beta/build-on-timeback/integration-levels#level-1-minimal-viable) | [Level 2](/beta/build-on-timeback/integration-levels#level-2-fully-native) |
## How it works
Your app renders the UI. The SDK manages everything else: discovering available lessons, serving questions, accepting answers, scoring, and tracking time.
```mermaid theme={null}
graph TB
subgraph browser [Your App]
UI["UI Layer"] --> SDK["@timeback/sdk client"]
end
SDK -->|"POST /lessons/*"| Server["@timeback/sdk server"]
Server --> OR["OneRoster API"]
Server --> PP["PowerPath API"]
SDK -->|"Activity heartbeats"| Server
Server -->|"TimeSpentEvent"| Caliper["Caliper API"]
```
Under the hood, the SDK orchestrates two Timeback systems:
| System | Purpose | What it provides |
| ------------- | ------------------ | ---------------------------------- |
| **OneRoster** | Lesson discovery | Courses, components, and resources |
| **PowerPath** | Assessment runtime | Sequencing, questions, scoring |
Time tracking is handled automatically. When a lesson starts, the SDK creates an activity tracker that sends periodic `TimeSpentEvent` heartbeats.
## The lesson lifecycle
A typical Managed Lesson flow has five phases:
| Phase | SDK method | What happens |
| ----------------- | --------------------------------------------------------------------------------------------------------- | ---------------------------- |
| **Discover** | [`lessons.list()`](/beta/build-on-timeback/sdk/managed-lessons/reference#lessons-list) | List available lessons |
| **Start** | [`lessons.start()`](/beta/build-on-timeback/sdk/managed-lessons/reference#lessons-start) | Begin or resume an attempt |
| **Next question** | [`session.next()`](/beta/build-on-timeback/sdk/managed-lessons/reference#lessonquestion) | Fetch the next question |
| **Submit answer** | [`session.submit()`](/beta/build-on-timeback/sdk/managed-lessons/reference#lessonsubmitresult) | Score and record a response |
| **Complete** | [`session.complete()`](/beta/build-on-timeback/sdk/managed-lessons/reference#lessonsessioncompleteresult) | Finalize scoring, flush time |
The `LessonSession` object returned by `lessons.start()` is the central runtime. It holds the starting attempt metadata, updates `score` after submission, and updates `finalized` after `complete()`.
## Caliper behavior
| Event | Sent by | Purpose |
| ---------------- | --------- | ------------------------------------ |
| `TimeSpentEvent` | SDK | Time tracking heartbeats (every 15s) |
| `ActivityEvent` | PowerPath | Scoring, XP, and gradebook writes |
The SDK does **not** send an [`ActivityCompletedEvent`](/beta/build-on-timeback/reference/events#activitycompletedevent) for Managed Lessons. PowerPath is the source of truth for completion, XP, and gradebook updates.
These operations delegate to PowerPath and return its progress. For linear lessons, check `finalized` before displaying a completed result. The SDK catches finalization errors and re-reads progress, so a resolved promise can still return `finalized: false`. Adaptive completion returns the current score and marks the SDK result finalized; it does not itself establish that a mastery threshold was reached.
## Configuration
Managed Lessons require a Caliper sensor URL in your [`timeback.config.json`](/beta/build-on-timeback/reference/configuration).
See the [configuration
reference](/beta/build-on-timeback/reference/configuration#sensor-resolution) for sensor
resolution rules and environment overrides.
## Next steps
List available lessons and start a session
Fetch questions and submit answers
Finalize scoring and interpret results
Review past attempts and per-question data
Parameters, properties, methods, and return types
Full timeback.config.json reference
Onboarding guide for building native apps on Timeback
# Lesson discovery
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/managed-lessons/lesson-discovery
List available lessons and start a session
This guide covers how to discover available lessons and start a lesson session. By the end, you will have a `LessonSession` object ready for the [question loop](/beta/build-on-timeback/sdk/managed-lessons/question-loop).
## List available lessons
Lessons are resolved from the courses defined in your [config file](/beta/build-on-timeback/reference/configuration).
Call `lessons.list()` to fetch lessons across all configured courses:
```typescript theme={null}
const lessons = await timeback.lessons.list()
```
Each lesson includes `id`, `name`, `type`, and `courseId`. `questionCount` and metadata are optional.
See the [reference](/beta/build-on-timeback/sdk/managed-lessons/reference#lessons-list) for the
full return type.
If your app configures multiple courses, you can narrow to a specific one:
```typescript theme={null}
const mathLessons = await timeback.lessons.list({
course: { subject: 'Math', grade: 5 },
})
```
The `course` parameter matches against your configured courses; in other words, it's a filter, not an override.
### Where lessons come from
Lessons are derived from your course structure in OneRoster:
1. The SDK resolves course IDs from [`timeback.config.json`](/beta/build-on-timeback/reference/configuration)
2. It lists active **course components**
3. For each component, it lists active **component resources**
4. Each resource maps to a `Lesson` object
This structure is created during the [setup phase](/beta/build-on-timeback/start-building/native-apps). Course builder tooling to manage it is in development.
Courses must be set up before `lessons.list()` returns results. See the [onboarding
guide](/beta/build-on-timeback/start-building/native-apps) for how to get started.
## Start a lesson
Call `lessons.start()` with the lesson you want to begin:
```typescript theme={null}
const session = await timeback.lessons.start({
lessonId: lesson.id,
lessonType: lesson.type,
course: { subject: 'Math', grade: 5 },
name: lesson.name,
forceNew: true,
})
```
This returns a `LessonSession`, a stateful object that manages the entire lesson lifecycle.
See the [reference](/beta/build-on-timeback/sdk/managed-lessons/reference#lessons-start) for
full parameter documentation.
### What happens under the hood
When you call `lessons.start()`, two things happen:
1. **Server**: the SDK calls PowerPath to check for an existing attempt or create a new one
2. **Client**: the SDK starts an activity tracker automatically, so `TimeSpentEvent` heartbeats begin immediately
The returned `LessonSession` retains the starting attempt metadata. Its score updates after submission and its finalized flag after completion. Provide a course or client-level `defaultCourse`; the SDK needs it for heartbeat attribution. The single-activity limit also applies to lessons.
See the [reference](/beta/build-on-timeback/sdk/managed-lessons/reference#lesson-session) for
all properties and methods.
### Resume vs new attempt
By default, `lessons.start()` resumes an existing in-progress attempt if one exists. Set `forceNew: true` to request a fresh attempt. The reviewed SDK catches errors while creating the attempt and can return existing progress; inspect the returned attempt before relying on this transition:
```typescript Resume existing attempt (default) theme={null}
const session = await timeback.lessons.start({
lessonId: lesson.id,
course: { subject: 'Math', grade: 5 },
})
```
```typescript Force new attempt theme={null}
const session = await timeback.lessons.start({
lessonId: lesson.id,
course: { subject: 'Math', grade: 5 },
forceNew: true,
})
```
## Framework integration
Use your framework's [client adapter](/beta/build-on-timeback/sdk/client/react) to initialize the client and verify the user. Load lessons from an event handler or a cancellable effect. Keep the selected course aligned with the course returned by discovery, and disable the start control while a start request is in progress.
Retain the returned session in component state. Serialize transitions with the [single-activity lifecycle](/beta/build-on-timeback/sdk/activity-tracking/single-session#framework-integration); starting another managed lesson while a tracker remains active throws. The `LessonSession` is a mutable object, not framework-reactive state: trigger a render when an awaited operation returns. Your application supplies its question renderer and persistence/navigation behavior.
## Next steps
Fetch questions and submit answers
Parameters, properties, methods, and return types
# Question loop
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/managed-lessons/question-loop
Fetch questions and submit answers during a lesson session
Once you have a `LessonSession` from [`lessons.start()`](/beta/build-on-timeback/sdk/managed-lessons/lesson-discovery#start-a-lesson), the question loop is how your app drives the lesson forward. Call `session.next()` to get a question, render it, then call `session.submit()` with the student's answer.
## The basic loop
```typescript theme={null}
let question = await session.next()
while (question) {
// Render the question, collect an answer from the student
const answer = await renderQuestion(question)
const result = await session.submit({
question: question.id,
response: answer,
})
// result.score, result.correct, result.complete are available
question = await session.next()
}
// No more questions -- complete the lesson
const completion = await session.complete()
```
## Fetch a question
`session.next()` returns the next `LessonQuestion`, or `null` when there are no more questions:
```typescript theme={null}
const question = await session.next()
if (!question) {
// Lesson is complete, call session.complete()
}
```
Each normalized question includes an `id`; `content` and `content.rawXml` are optional in the contract. Check for XML before rendering and handle unsupported or missing content in your UI. Your app supplies a QTI renderer.
See the [reference](/beta/build-on-timeback/sdk/managed-lessons/reference#lessonquestion) for the full type.
Select a renderer that supports the item interaction and response format. The managed-session `submit()` accepts a string and the server sends it as `responses.RESPONSE`; it does not expose every multi-response QTI contract. Use the returned grading result for feedback.
## Submit an answer
After the student answers, submit their response:
```typescript theme={null}
const result = await session.submit({
question: question.id,
response: selectedAnswer,
})
```
The result tells you whether the answer was `correct`, the updated `score`, and whether the lesson is now `complete`. After each submission, `score` updates and a complete result stops subsequent `next()` calls. The public `finalized` property updates only after `complete()`.
See the [reference](/beta/build-on-timeback/sdk/managed-lessons/reference#lessonsubmitresult) for full parameter and return type documentation.
```typescript theme={null}
const result = await session.submit({ question: question.id, response: 'A' })
console.log(result.correct) // Whether the answer was correct
console.log(session.score) // Updated cumulative score
console.log(result.complete) // Whether the answer ended the question loop
```
## Adaptive vs linear delivery
The SDK handles two delivery modes transparently. Your code uses the same `next()` / `submit()` loop regardless of mode.
You do not need to detect which mode is active. The `LessonSession` handles it based on the
`lessonType` set during
[`lessons.start()`](/beta/build-on-timeback/sdk/managed-lessons/lesson-discovery#start-a-lesson).
### Adaptive (`powerpath-100`)
Each `next()` call hits the server, which asks PowerPath for the next question based on the student's performance so far. Questions are served one at a time, and difficulty adapts.
### Linear (`quiz`, `test-out`, etc.)
On the first `next()` call, the SDK fetches **all** questions at once and buffers them locally. Subsequent `next()` calls return from the local buffer without a network round trip. Already-answered questions are skipped automatically.
## Track progress during the loop
The [session properties](/beta/build-on-timeback/sdk/managed-lessons/reference#lesson-session) update in real time as the student progresses. Use the returned `result.complete` and `session.score` to render progress. These are mutable object properties; copy returned values into your framework state when each request resolves. For linear lessons, submit returns `correct: false` and `score: 0` before finalization; do not show that placeholder as a scored wrong answer.
## Next steps
Finalize scoring and interpret results
Parameters, properties, methods, and return types
# Reference
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/managed-lessons/reference
Parameters, properties, methods, and return types for Managed Lessons
## Client
### `lessons.list()`
Fetches available lessons from the courses configured in [`timeback.config.json`](/beta/build-on-timeback/reference/configuration).
Filter to a specific configured course. If omitted, returns lessons across all configured courses.
Course subject (e.g. `"Math"`, `"Reading"`).
Grade level (e.g. `5`).
Unique course code (e.g. `"CS-101"`). Use for grade-less apps.
#### Return type
```text Response shape (not executable) theme={null}
{
id: string // Unique lesson identifier
name: string // Display name
type: LessonType // 'powerpath-100' | 'quiz' | 'test-out' | ...
courseId: string // Parent course ID
questionCount?: number // May be absent
metadata?: Record // Additional lesson metadata
}
```
#### Examples
```typescript All courses theme={null}
const lessons = await timeback.lessons.list()
```
```typescript Filtered by course theme={null}
const mathLessons = await timeback.lessons.list({
course: { subject: 'Math', grade: 5 },
})
```
```typescript By course code theme={null}
const lessons = await timeback.lessons.list({
course: { code: 'CS-101' },
})
```
### `lessons.start()`
Starts or resumes a lesson session. Returns a [`LessonSession`](#lesson-session) that manages the question loop and time tracking. An activity tracker starts automatically, so `TimeSpentEvent` heartbeats begin immediately.
The lesson to start.
Course reference for time tracking. Required unless a client-level `defaultCourse` is configured. Must match a course in [`timeback.config.json`](/beta/build-on-timeback/reference/configuration). Use either subject + grade or a course code.
Course subject (e.g. `"Math"`, `"Reading"`).
Grade level (e.g. `5`).
Unique course code (e.g. `"CS-101"`). Use for grade-less apps.
Determines how the SDK handles the question loop and completion. Pass `lesson.type` from the discovery result. The two primary types behave differently:
* `powerpath-100`: adaptive (questions served one at a time, difficulty adjusts, session completes when score reaches 100)
* `quiz`: static (all questions fetched in a batch, session completes via explicit finalization)
Other quiz-like types (`test-out`, `placement`, `unit-test`) follow the same static pattern as `quiz`.
Display name for the activity tracker. Defaults to the server lesson type, then the lesson ID; pass `name: lesson.name` for the discovery title.
Request a fresh attempt instead of reusing progress. The SDK catches errors from finalization/creation; inspect the returned attempt because this option does not guarantee that a new attempt was created. Without it, existing progress can include an already finalized attempt.
When `true`, the behavior depends on the lesson type:
* **Quiz-like types** (`quiz`, `test-out`, etc.): the current attempt is finalized first (scored with whatever was answered), then a new attempt is created
* **`powerpath-100`**: PowerPath attempts cannot be finalized via the API, so a new attempt is created directly
#### Examples
```typescript Default (resume existing) theme={null}
const session = await timeback.lessons.start({
lessonId: lesson.id,
lessonType: lesson.type,
course: { subject: 'Math', grade: 5 },
})
```
```typescript Force new attempt theme={null}
const session = await timeback.lessons.start({
lessonId: lesson.id,
lessonType: lesson.type,
course: { subject: 'Math', grade: 5 },
name: lesson.name,
forceNew: true,
})
```
```typescript By course code theme={null}
const session = await timeback.lessons.start({
lessonId: lesson.id,
lessonType: lesson.type,
course: { code: 'CS-101' },
})
```
### Lesson session
The object returned by `lessons.start()`. Manages the question loop, scoring state, and time tracking for a single lesson attempt.
#### Properties
Most properties are set once when the session is created. `score` updates after `submit()` and `complete()`; `finalized` updates only after `complete()`. These mutations do not automatically trigger framework rendering.
The lesson identifier passed to `lessons.start()`.
The delivery mode for this session. Resolved from the server response, falling back to the `lessonType` you passed to `lessons.start()`, then to `'quiz'`.
Controls how the SDK fetches questions (`next()` hits the server each time for adaptive, fetches all at once for linear) and how completion works (adaptive auto-completes at score 100, linear requires explicit finalization).
The PowerPath attempt number (1, 2, 3, ...). `undefined` on first-ever access when no prior
attempt data exists.
Cumulative score for the current attempt. Starts at the value returned by PowerPath (0 for new attempts, or the saved score when resuming).
**Updates live** after each `submit()` call and again after `complete()` with the final score. For `powerpath-100`, this is a 0–100 mastery scale where reaching 100 means the lesson auto-completes. For quiz types, the score typically updates at finalization.
Total questions in the lesson, as reported by PowerPath. For linear lessons (`quiz`, `test-out`,
etc.), this is the fixed question count. For adaptive lessons (`powerpath-100`), this may be
`undefined` since the total varies based on the student's performance.
Number of questions the student had already seen when the session started. For `powerpath-100`,
reflects how many questions were served before the session was resumed. For linear lesson types,
always `0` (linear quizzes track answered questions differently via their local buffer).
Whether the lesson attempt has been finalized. This is the SDK/PowerPath progress flag, not an acknowledgement of all downstream gradebook or event delivery.
Initial value depends on lesson type: for `powerpath-100`, always starts `false` (adaptive sessions are never pre-finalized). For quiz types, reflects whether the attempt was already completed when resuming.
`submit()` updates the private question-loop completion state, not this public flag. `complete()` updates the flag from the server result.
#### Methods
Returns the next question, or `null` when there are no more questions.
Behavior depends on lesson type:
* **`powerpath-100`** (adaptive): each call makes a server round-trip. PowerPath selects the next question based on the student's performance so far. Returns `null` when PowerPath signals completion (score reached 100).
* **`quiz` and other linear types**: the first call fetches **all** questions from the server in a single batch and buffers them locally. Subsequent calls return the next unanswered question from the buffer with no network request. Already-answered questions (from a resumed attempt) are skipped automatically.
Returns `null` immediately if the session is already completed.
Submits the student's answer to PowerPath and returns the scored result.
After each call, `session.score` is updated with the server's response. If the server signals that the lesson is complete (e.g., `powerpath-100` reaching score 100), the private question-loop completion state is set and subsequent `next()` calls return `null`; public `session.finalized` is unchanged until `complete()`.
For `powerpath-100`, `correct` reflects real-time scoring (the server evaluates immediately). For quiz types, `correct` is always `false` during the quiz. Evaluation happens at finalization.
The question ID being answered (from `LessonQuestion.id`).
The student's selected answer.
Finalizes the lesson in two steps:
1. **Server**: reads progress and, for unfinalized linear lessons, attempts finalization then re-reads progress. Errors in that finalization call are caught; the result can still have `finalized: false`. For adaptive lessons the SDK returns current progress with `finalized: true` without checking a mastery threshold.
2. **Client**: calls `activity.end()` to flush the final time tracking heartbeat. If `end()` rejects, `timeTrackingSent` is false. Heartbeat network failures are usually swallowed inside the tracker, so true does not confirm server delivery.
Updates `session.score` and `session.finalized` with the server's final values.
Flushes any accumulated time to the server, then stops heartbeats until `resume()` is called.
No-op if already paused or if the session has ended. Paused time is not counted as active
learning time.
Starts a fresh time tracking window and restarts heartbeats. No-op if not currently paused or if
the session has ended.
#### `LessonQuestion`
The object returned by `session.next()`:
```text Response shape (not executable) theme={null}
{
id: string // Unique question identifier
title?: string // Optional display title
difficulty?: string // Optional difficulty level
content?: {
rawXml?: string // QTI XML content for rendering
}
}
```
The `content.rawXml` field contains [QTI](https://www.imsglobal.org/question/index.html) XML. Your app is responsible for parsing and rendering this.
Your app supplies a renderer for supported interactions. The session submission API sends a single string in `responses.RESPONSE`; confirm that this matches your item before using this wrapper.
#### `LessonSubmitResult`
The object returned by `session.submit()`:
```text Response shape (not executable) theme={null}
{
correct: boolean // Whether the answer was correct
score: number // Updated cumulative score
complete: boolean // Whether the lesson is now complete
}
```
#### `LessonSessionCompleteResult`
The object returned by `session.complete()`:
```text Response shape (not executable) theme={null}
{
score: number // Final score
totalQuestions?: number // May be absent
correctQuestions?: number // May be absent
accuracy?: number // May be absent
finalized: boolean // Whether PowerPath finalized the attempt
lessonType: LessonType // The lesson type
timeSpentSeconds: number // Active learning time
timeTrackingSent: boolean // Whether tracker cleanup resolved
error?: string // Error message if time flush failed
}
```
`timeTrackingSent` reports whether tracker cleanup threw. It is not delivery confirmation. Inspect `finalized` separately and reconcile missing analytics; data loss is not limited to the last heartbeat interval. See [Handle time tracking
errors](/beta/build-on-timeback/sdk/managed-lessons/completion#handle-time-tracking-errors).
### `lessons.attempts()`
Lists all attempts for a lesson.
The lesson to list attempts for.
#### Return type
```text Response shape (not executable) theme={null}
{
attempt?: number
score: number
startedAt?: string
completedAt?: string
scoreStatus?: string
}
```
### `lessons.attemptDetails()`
Gets per-question data for a specific attempt. The response is a discriminated union based on lesson type.
The lesson to review. Same `id` from the discovery result or the session's `lessonId`.
Which attempt to review (1, 2, 3, ...). Get available attempt numbers from
[`lessons.attempts()`](#lessons-attempts).
#### Return type
**Adaptive lessons (`powerpath-100`)**:
```text Response shape (not executable) theme={null}
{
lessonType: 'powerpath-100'
seenQuestions: LessonAttemptQuestion[]
score: number
totalQuestions: number
correctQuestions: number
}
```
**Linear lessons (`quiz`, `test-out`, etc.)**:
```text Response shape (not executable) theme={null}
{
lessonType: 'quiz' | 'test-out' | 'placement' | 'unit-test'
questions: LessonAttemptQuestion[]
score: number
totalQuestions: number
correctQuestions: number
}
```
#### `LessonAttemptQuestion`
```text Response shape (not executable) theme={null}
{
id: string // Question identifier
correct?: boolean // May be absent
response?: string | string[]
responses?: Record
content?: {
rawXml?: string // QTI XML for re-rendering
}
}
```
### `getLessonAttemptQuestions()`
Helper that normalizes the discriminated `attemptDetails()` response into a flat `LessonAttemptQuestion[]`:
```typescript theme={null}
import { getLessonAttemptQuestions } from '@timeback/sdk/client'
const details = await timeback.lessons.attemptDetails({
lessonId: lesson.id,
attempt: 1,
})
const questions = getLessonAttemptQuestions(details)
// Always returns LessonAttemptQuestion[] regardless of lesson type
```
***
## Server
The server-side lessons namespace exposes the same operations without session management or automatic time tracking. Each operation is a standalone async function call — your backend controls the lifecycle directly.
See the [FastAPI](/beta/build-on-timeback/sdk/server/fastapi) or [Next.js](/beta/build-on-timeback/sdk/server/nextjs) adapter docs for server setup.
The server namespace uses `lesson` and `student` identifiers directly, unlike the client API which uses `lessonId` and resolves the student from the authenticated session.
### `timeback.lessons.list(input?)`
Lists lessons from the configured courses. Same behavior as the [client `lessons.list()`](#lessons-list).
Filter to a specific configured course. Accepts `{ subject, grade }` or `{ code }`. If omitted, returns lessons across all configured courses.
#### Examples
```typescript TypeScript theme={null}
const lessons = await timeback.lessons.list()
const mathLessons = await timeback.lessons.list({ course: { subject: 'Math', grade: 5 } })
```
```python Python theme={null}
lessons = await timeback.lessons.list()
math_lessons = await timeback.lessons.list({"course": {"subject": "Math", "grade": 5}})
```
### `timeback.lessons.start(input)`
Starts or resumes a lesson attempt. Unlike the client's `lessons.start()`, this returns a plain result object — no `LessonSession` or time tracking.
The lesson identifier (from `lessons.list()`).
The student's Timeback ID.
Start a fresh attempt instead of resuming. Behaves the same as the [client `forceNew`](#lessons-start) option.
#### Return type
```text Response shape (not executable) theme={null}
{
lessonId: string
lessonType: LessonType
attempt?: number
score: number
questionCount?: number
seenQuestions: number
finalized: boolean
}
```
#### Examples
```typescript TypeScript theme={null}
const result = await timeback.lessons.start({ lesson: 'lesson-1', student: 'tb_abc' })
const fresh = await timeback.lessons.start({ lesson: 'lesson-1', student: 'tb_abc', forceNew: true })
```
```python Python theme={null}
result = await timeback.lessons.start({"lesson": "lesson-1", "student": "tb_abc"})
fresh = await timeback.lessons.start({"lesson": "lesson-1", "student": "tb_abc", "force_new": True})
```
### `timeback.lessons.next(input)`
Returns the next question(s) for a lesson attempt. The return type depends on lesson type:
* **`powerpath-100`**: returns a single [`LessonQuestion`](#lessonquestion)
* **All other types**: returns a `LessonQuestionBatch` with all questions
The lesson identifier.
The student's Timeback ID.
Pass `"powerpath-100"` for adaptive single-question delivery. If omitted, the server inspects progress and routes adaptive lessons accordingly; a contradictory explicit type is rejected.
#### Return type
```text Response shape (not executable) theme={null}
// Single question (powerpath-100)
LessonQuestion
// Batch (quiz, test-out, etc.)
{
questions: LessonQuestion[]
answeredIds: string[]
score: number
finalized: boolean
complete: boolean
}
```
#### Examples
```typescript TypeScript theme={null}
// Quiz — returns all questions as a batch
const batch = await timeback.lessons.next({ lesson: 'lesson-1', student: 'tb_abc' })
// Adaptive — returns one question at a time
const question = await timeback.lessons.next({
lesson: 'lesson-1',
student: 'tb_abc',
lessonType: 'powerpath-100',
})
```
```python Python theme={null}
# Quiz — returns all questions as a batch
batch = await timeback.lessons.next({"lesson": "lesson-1", "student": "tb_abc"})
# Adaptive — returns one question at a time
question = await timeback.lessons.next({
"lesson": "lesson-1",
"student": "tb_abc",
"lesson_type": "powerpath-100",
})
```
### `timeback.lessons.submit(input)`
Submits a student's answer to a lesson question.
The lesson identifier.
The student's Timeback ID.
The question ID being answered (from `LessonQuestion.id`).
The student's selected answer.
#### Return type
```text Response shape (not executable) theme={null}
{
correct: boolean
score: number
complete: boolean
questionResult?: unknown
}
```
#### Examples
```typescript TypeScript theme={null}
const result = await timeback.lessons.submit({
lesson: 'lesson-1',
student: 'tb_abc',
question: 'q-42',
response: 'B',
})
```
```python Python theme={null}
result = await timeback.lessons.submit({
"lesson": "lesson-1",
"student": "tb_abc",
"question": "q-42",
"response": "B",
})
```
### `timeback.lessons.complete(input)`
Attempts linear finalization and returns progress, with the limitations described for `session.complete()` above. Unlike the client method, it does not flush time tracking.
The lesson identifier.
The student's Timeback ID.
#### Return type
```text Response shape (not executable) theme={null}
{
lessonType: LessonType
attempt?: number
score: number
totalQuestions?: number
correctQuestions?: number
accuracy?: number
finalized: boolean
}
```
#### Examples
```typescript TypeScript theme={null}
const result = await timeback.lessons.complete({ lesson: 'lesson-1', student: 'tb_abc' })
```
```python Python theme={null}
result = await timeback.lessons.complete({"lesson": "lesson-1", "student": "tb_abc"})
```
### `timeback.lessons.attempts(input)`
Lists all attempts for a lesson/student pair. Same return type as the [client `lessons.attempts()`](#lessons-attempts).
The lesson identifier.
The student's Timeback ID.
#### Examples
```typescript TypeScript theme={null}
const attempts = await timeback.lessons.attempts({ lesson: 'lesson-1', student: 'tb_abc' })
```
```python Python theme={null}
attempts = await timeback.lessons.attempts({"lesson": "lesson-1", "student": "tb_abc"})
```
### `timeback.lessons.attemptDetails(input)`
Returns raw progress data for a specific attempt. See [client `lessons.attemptDetails()`](#lessons-attemptdetails) for the discriminated return shape.
The lesson identifier.
The student's Timeback ID.
Which attempt to review (1, 2, 3, ...).
#### Examples
```typescript TypeScript theme={null}
const details = await timeback.lessons.attemptDetails({
lesson: 'lesson-1',
student: 'tb_abc',
attempt: 1,
})
```
```python Python theme={null}
details = await timeback.lessons.attempt_details({
"lesson": "lesson-1",
"student": "tb_abc",
"attempt": 1,
})
```
# Observability
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/observability
Configure logging and request lifecycle hooks in the server SDK
## Logging
The SDK's built-in logger only emits warnings and errors. You can customize this with two options on `createTimeback()`:
Custom logger instance. Compatible with console, Pino, Winston, Bunyan, and any logger whose
methods accept a string message followed by optional arguments.
Minimum level for the built-in logger. Ignored when a custom `logger` is provided.
### Custom logger
Pass any logger whose methods accept a string message followed by optional arguments. The SDK prefixes each message with the internal scope, for example `[timeback:handlers:user]`.
```typescript Pino theme={null}
import { createTimeback } from '@timeback/sdk'
import pino from 'pino'
const timeback = await createTimeback({
logger: pino({ level: 'debug' }),
// ...
})
```
```typescript Winston theme={null}
import { createTimeback } from '@timeback/sdk'
import winston from 'winston'
const timeback = await createTimeback({
logger: winston.createLogger({
level: 'debug',
transports: [new winston.transports.Console()],
}),
// ...
})
```
```typescript console theme={null}
import { createTimeback } from '@timeback/sdk'
const timeback = await createTimeback({
logger: console,
// ...
})
```
When a custom logger is provided, the SDK forwards **all** log calls to it. Level filtering is the logger's responsibility.
### Log level
When using the built-in logger, `logLevel` controls the minimum severity:
```typescript theme={null}
const timeback = await createTimeback({
logLevel: 'debug', // 'debug' | 'info' | 'warn' | 'error' | 'silent'
// ...
})
```
| Level | What you see |
| -------- | -------------------------------------- |
| `debug` | Everything, including request details |
| `info` | Operational messages and above |
| `warn` | Warnings and errors only **(default)** |
| `error` | Errors only |
| `silent` | Nothing |
Set the `DEBUG=1` environment variable to enable debug-level output without changing code. This
takes effect when no explicit `logLevel` is configured.
## Request lifecycle hooks
Track request timing and status with the `onRequestStart` and `onRequestEnd` hooks:
```typescript theme={null}
const timeback = await createTimeback({
hooks: {
onRequestStart({ handler }) {
console.log(`[${handler}] started`)
},
onRequestEnd({ handler, durationMs, status }) {
console.log(`[${handler}] ${status} in ${durationMs}ms`)
},
},
// ...
})
```
These wrap the HTTP handlers listed below. They do not wrap direct namespace calls such as `activity.record()`, and the identity sign-out handler is not wrapped:
| Handler | Trigger |
| -------------------- | ----------------------- |
| `activity.submit` | Activity completion |
| `activity.heartbeat` | Time-spent heartbeat |
| `user.me` | User profile lookup |
| `user.verify` | User session check |
| `identity.signIn` | SSO sign-in initiation |
| `identity.callback` | SSO callback processing |
\| `lessons.list`, `lessons.start` | Lesson discovery and attempt start |
\| `lessons.next`, `lessons.submit` | Question requests and answers |
\| `lessons.complete` | Lesson completion request |
\| `lessons.attempts`, `lessons.attemptDetails` | Attempt history |
Logging configuration is global to this SDK module. Creating another server instance replaces the effective logger/settings for existing scoped loggers. The snippets above show logging options; merge them into your complete server configuration.
# Overview
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/overview
Full-stack SDK for integrating Timeback into your application
The `@timeback/sdk` package provides server and browser integration primitives for Timeback:
* **Server adapters** for handling authentication and API routes
* **Client adapters** for React, Vue, Svelte, and Solid
* **Custom Activities** for tracking your own learning content
* **Managed Lessons** for delivering Timeback-powered assessments
* **Direct API access** through the [composed clients](/beta/build-on-timeback/clients/core) included in your SDK version
## AI-assisted implementation
You can pair the SDK docs with focused skills for faster, reviewable implementation.
Use the [AI Skills page](/beta/build-on-timeback/ai/skills) as the canonical skill catalog. In most cases, run `/timeback-integrate` and let it invoke layer skills as needed.
For focused tasks, you can still run:
* [`/timeback-client`](https://github.com/superbuilders/timeback-sdk-skills/tree/main/skills/timeback/timeback-client) for client-side implementation
* [`/timeback-server`](https://github.com/superbuilders/timeback-sdk-skills/tree/main/skills/timeback/timeback-server) for server-side implementation
## Architecture
The SDK has two main components:
Handles authentication, SSO callbacks, and API proxying. Runs on your backend.
Provides contexts, hooks, stores, and activity/lesson APIs. Your app owns the UI and content renderer. Runs in the browser.
## Choose your path
The SDK supports two integration paths for learning content. Which one you use depends on who owns the content and learning logic.
You own your content. The SDK tracks time and reports completion metrics to Timeback.
Timeback runs the lesson engine. You render the UI and the SDK handles questions, scoring, and time tracking.
Not sure which to choose? See [integration levels](/beta/build-on-timeback/integration-levels) for a detailed comparison.
## Next steps
Set up server-side integration
Add client-side components
Configure authentication
Track your own learning content
Use setup, client, and server skills from one canonical page
# Express
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/server/express
Integrate Timeback with Express.js
## Overview
The Timeback SDK provides an Express middleware adapter.
The catch-all route below uses [Express 5 path syntax](https://expressjs.com/en/guide/migrating-5/#path-route-matching-syntax).
## Installation
```bash npm theme={null}
npm install @timeback/sdk express
```
```bash pnpm theme={null}
pnpm add @timeback/sdk express
```
```bash yarn theme={null}
yarn add @timeback/sdk express
```
```bash bun theme={null}
bun add @timeback/sdk express
```
## Server Setup
Create a server-only Timeback instance. The session helpers below belong to your application: persist the callback user in your authenticated session, and return that session user (including `email`) or `null` from `getUser`. These helpers are not SDK exports. Keep API and SSO secrets on the server:
```typescript lib/timeback.ts theme={null}
import { createTimeback } from '@timeback/sdk'
export const timeback = await createTimeback({
env: 'staging',
api: {
clientId: process.env.TIMEBACK_API_CLIENT_ID!,
clientSecret: process.env.TIMEBACK_API_CLIENT_SECRET!,
},
identity: {
mode: 'sso',
clientId: process.env.AWS_COGNITO_CLIENT_ID!,
clientSecret: process.env.AWS_COGNITO_CLIENT_SECRET!,
redirectUri: 'http://localhost:3000/api/timeback/identity/callback',
onCallbackSuccess: async ({ user, state, redirect }) => {
await setSession({ id: user.id, email: user.email })
return redirect(state?.returnTo ?? '/')
},
onCallbackError: ({ error, redirect }) => {
return redirect('/?error=sso_failed')
},
getUser: req => getSessionFromRequest(req),
},
})
```
## Middleware
Add the Timeback middleware to your Express app:
```typescript server.ts theme={null}
import { toExpressMiddleware } from '@timeback/sdk/express'
import express from 'express'
import { timeback } from './lib/timeback'
const app = express()
// Parse JSON bodies
app.use(express.json())
// Mount Timeback routes
app.use('/api/timeback', toExpressMiddleware(timeback))
// Your other routes...
app.get('/', (req, res) => {
res.send('Hello World')
})
app.listen(3000, () => {
console.log('Server running on port 3000')
})
```
## Alternative: Mount on Router
For more control, you can mount routes on an Express Router:
```typescript server.ts theme={null}
import { mountExpressRoutes } from '@timeback/sdk/express'
import express from 'express'
import { timeback } from './lib/timeback'
const app = express()
const router = express.Router()
app.use(express.json())
// Mount Timeback routes on router
mountExpressRoutes(timeback, router)
// Use the router
app.use('/api/timeback', router)
```
## Routes
The middleware handles these routes under `/api/timeback`:
| Route | Method | Purpose |
| --------------------- | ------ | --------------------------- |
| `/identity/signin` | GET | Initiates SSO flow |
| `/identity/callback` | GET | Handles SSO callback |
| `/identity/signout` | GET | Initiates Timeback sign-out |
| `/user/verify` | GET | Verifies user session |
| `/user/me` | GET | Fetches user profile |
| `/activity/heartbeat` | POST | Time-spent heartbeat |
| `/activity/submit` | POST | Activity completion submit |
## Usage
Beyond the HTTP routes, you can use the Timeback instance directly for server-side operations like verifying users, fetching profiles, or recording activity completions:
```typescript server.ts theme={null}
import { timeback } from './lib/timeback'
// Verify a user exists in Timeback
const result = await timeback.user.verify('student@example.com')
if (result.verified) {
console.log(result.timebackId)
}
// Fetch full enriched profile
const profile = await timeback.user.getProfile('student@example.com')
console.log(profile.xp?.today, profile.courses)
```
See [user profile](/beta/build-on-timeback/sdk/user-profile) for full documentation on
`verify()` and `getProfile()`.
## With Frontend Framework
When using Express with a frontend framework like React:
```typescript server.ts theme={null}
import path from 'path'
import { toExpressMiddleware } from '@timeback/sdk/express'
import express from 'express'
import { timeback } from './lib/timeback'
const app = express()
app.use(express.json())
app.use('/api/timeback', toExpressMiddleware(timeback))
// Serve static files from React build
app.use(express.static(path.join(__dirname, 'dist')))
// Handle client-side routing
// Express 5 catch-all, including the root path
app.get('/{*path}', (req, res) => {
res.sendFile(path.join(__dirname, 'dist', 'index.html'))
})
app.listen(3000)
```
## Next Steps
Client-side React integration
Authentication options
Track learning sessions
# FastAPI
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/server/fastapi
Integrate Timeback with FastAPI
## Overview
The Timeback Python SDK provides `TimebackFastAPI`, a ready-made FastAPI integration that creates an `APIRouter` with all Timeback routes.
## Installation
```bash pip theme={null}
pip install "timeback-sdk[fastapi]"
```
```bash uv theme={null}
uv add "timeback-sdk[fastapi]"
```
## Server Setup
### Timeback instance
Create a module that configures the Timeback instance. `TimebackFastAPI` is callable as a FastAPI dependency via `Depends(timeback)`:
```python SSO (app/timeback.py) theme={null}
import os
from timeback import TimebackIdentity
from timeback.server import (
TimebackConfig,
ApiCredentials,
SsoIdentityConfig,
)
from timeback.server.adapters.fastapi import TimebackFastAPI
def get_user(request):
session = get_session(request) # Your session logic
return TimebackIdentity(id=session.user_id, email=session.email) if session else None
def on_callback_success(ctx):
set_session(id=ctx.user.id, email=ctx.user.email)
return ctx.redirect((ctx.state or {}).get("return_to", "/"))
def on_callback_error(ctx):
return ctx.redirect("/?error=sso_failed")
timeback = TimebackFastAPI(TimebackConfig(
env="staging",
api=ApiCredentials(
client_id=os.environ["TIMEBACK_API_CLIENT_ID"],
client_secret=os.environ["TIMEBACK_API_CLIENT_SECRET"],
),
identity=SsoIdentityConfig(
mode="sso",
client_id=os.environ["TIMEBACK_SSO_CLIENT_ID"],
client_secret=os.environ["TIMEBACK_SSO_CLIENT_SECRET"],
redirect_uri="http://localhost:8000/api/timeback/identity/callback",
get_user=get_user,
on_callback_success=on_callback_success,
on_callback_error=on_callback_error,
),
))
```
```python Custom Auth (app/timeback.py) theme={null}
import os
from timeback.server import (
TimebackConfig,
ApiCredentials,
CustomIdentityConfig,
)
from timeback.server.adapters.fastapi import TimebackFastAPI
def get_user_email(request):
"""Return the authenticated user's email from your auth system."""
session_id = request.cookies.get("session_id")
session = get_session(session_id) # Your session logic
return session.email if session else None
timeback = TimebackFastAPI(TimebackConfig(
env="staging",
api=ApiCredentials(
client_id=os.environ["TIMEBACK_API_CLIENT_ID"],
client_secret=os.environ["TIMEBACK_API_CLIENT_SECRET"],
),
identity=CustomIdentityConfig(
mode="custom",
get_email=get_user_email,
),
))
```
### App initialization
Mount the Timeback router on your FastAPI app:
```python app/main.py theme={null}
from fastapi import FastAPI
from app.timeback import timeback
app = FastAPI()
app.include_router(timeback.router, prefix="/api/timeback")
```
No lifespan or async setup needed — `TimebackFastAPI` is fully initialized at construction time.
## Usage
Use `Depends(timeback)` in your own routes for server-side operations like recording activity completion or fetching user data:
```python app/routes/activities.py theme={null}
from fastapi import APIRouter, Depends
from timeback.server import TimebackInstance
from app.timeback import timeback
router = APIRouter(prefix="/api/activities")
@router.put("/{activity_id}/complete")
async def complete_activity(
activity_id: str,
tb: TimebackInstance = Depends(timeback),
):
activity = await load_activity(activity_id)
progress = await load_progress(activity_id)
await tb.activity.record({
"user": {"email": progress.student_email},
"activity": {
"id": activity.slug,
"name": activity.name,
"course": {"code": activity.course_code},
},
"metrics": {
"xp_earned": progress.xp_earned,
"total_questions": progress.total_questions,
"correct_questions": progress.correct_questions,
},
"run_id": progress.run_id,
})
return {"status": "completed"}
```
You can also use the `timeback.user` namespace to verify users or fetch profiles without going through the HTTP handlers:
```python app/routes/users.py theme={null}
from fastapi import APIRouter, Depends
from timeback.server import TimebackInstance
from app.timeback import timeback
router = APIRouter(prefix="/api/users")
@router.get("/check/{email}")
async def check_user(
email: str,
tb: TimebackInstance = Depends(timeback),
):
result = await tb.user.verify(email)
return result
@router.get("/profile/{email}")
async def get_profile(
email: str,
tb: TimebackInstance = Depends(timeback),
):
profile = await tb.user.get_profile(email)
return profile
```
See [stateful activities](/beta/build-on-timeback/sdk/activity-tracking/stateful) for the full
server-side completion workflow, and [user profile](/beta/build-on-timeback/sdk/user-profile)
for details on `verify()` and `get_profile()`.
The lessons namespace is also available via `Depends(timeback)`:
```python app/routes/lessons.py theme={null}
from fastapi import APIRouter, Depends
from timeback.server import TimebackInstance
from app.timeback import timeback
router = APIRouter(prefix="/api/lessons")
@router.get("/")
async def list_lessons(tb: TimebackInstance = Depends(timeback)):
return await tb.lessons.list()
@router.post("/{lesson_id}/start")
async def start_lesson(
lesson_id: str,
student_id: str,
tb: TimebackInstance = Depends(timeback),
):
return await tb.lessons.start({"student": student_id, "lesson": lesson_id})
```
See the [Managed Lessons reference](/beta/build-on-timeback/sdk/managed-lessons/reference#server)
for all server-side operations (`list`, `start`, `next`, `submit`, `complete`, `attempts`, `attempt_details`),
parameters, and return types.
## Next Steps
Server-side lessons API reference
Track learning sessions
Frontend setup (React, Vue, Svelte, Solid)
Authentication options
# Next.js
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/server/nextjs
Integrate Timeback with Next.js App Router
## Overview
The Timeback SDK provides a Next.js adapter for the App Router with route handlers.
If you are using coding agents, pair this page with [`timeback-server`](https://github.com/superbuilders/timeback-sdk-skills/tree/main/skills/timeback/timeback-server) from the [AI Skills](/beta/build-on-timeback/ai/skills) catalog.
## Installation
```bash npm theme={null}
npm install @timeback/sdk
```
```bash pnpm theme={null}
pnpm add @timeback/sdk
```
```bash yarn theme={null}
yarn add @timeback/sdk
```
```bash bun theme={null}
bun add @timeback/sdk
```
## Server Setup
Create a server-only Timeback instance. The session helpers below belong to your application: persist the callback user in your authenticated session, and return that session user (including `email`) or `null` from `getUser`. These helpers are not SDK exports. Keep API and SSO secrets on the server:
```typescript lib/timeback.ts theme={null}
import { createTimeback } from '@timeback/sdk'
export const timeback = await createTimeback({
env: 'staging',
api: {
clientId: process.env.TIMEBACK_API_CLIENT_ID!,
clientSecret: process.env.TIMEBACK_API_CLIENT_SECRET!,
},
identity: {
mode: 'sso',
clientId: process.env.AWS_COGNITO_CLIENT_ID!,
clientSecret: process.env.AWS_COGNITO_CLIENT_SECRET!,
redirectUri: 'http://localhost:3000/api/timeback/identity/callback',
onCallbackSuccess: async ({ user, state, redirect }) => {
await setSession({ id: user.id, email: user.email })
return redirect(state?.returnTo ?? '/')
},
onCallbackError: ({ error, redirect }) => {
return redirect('/?error=sso_failed')
},
getUser: () => getSession(),
},
})
```
## Route Handler
Create a catch-all route handler:
```typescript app/api/timeback/[...timeback]/route.ts theme={null}
import { toNextjsHandler } from '@timeback/sdk/nextjs'
import { timeback } from '@/lib/timeback'
export const { GET, POST, PUT, DELETE, PATCH } = toNextjsHandler(timeback)
```
This handles all Timeback routes:
| Route | Method | Purpose |
| ---------------------------------- | ------ | --------------------------- |
| `/api/timeback/identity/signin` | GET | Initiates SSO flow |
| `/api/timeback/identity/callback` | GET | Handles SSO callback |
| `/api/timeback/identity/signout` | GET | Initiates Timeback sign-out |
| `/api/timeback/user/verify` | GET | Verifies user session |
| `/api/timeback/user/me` | GET | Fetches user profile |
| `/api/timeback/activity/heartbeat` | POST | Time-spent heartbeat |
| `/api/timeback/activity/submit` | POST | Activity completion submit |
## Client Provider
Wrap your app with the TimebackProvider:
```tsx app/providers.tsx theme={null}
'use client'
import { TimebackProvider } from '@timeback/sdk/react'
export function Providers({ children }: { children: React.ReactNode }) {
return {children}
}
```
```tsx app/layout.tsx theme={null}
import { Providers } from './providers'
export default function RootLayout({ children }) {
return (
{children}
)
}
```
## Usage
For the full client-side API, see the [React client
adapter](/beta/build-on-timeback/sdk/client/react).
Use hooks in client components:
```tsx components/UserStatus.tsx theme={null}
'use client'
import { SignInButton, useTimebackVerification } from '@timeback/sdk/react'
export function UserStatus() {
const { state } = useTimebackVerification()
if (state.status === 'loading') return
Loading...
if (state.status === 'error') return
{state.message}
if (state.status !== 'verified') {
return Sign In
}
return
Welcome!
}
```
## Next Steps
Client-side React integration
Authentication options
Track learning sessions
# Nuxt
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/server/nuxt
Integrate Timeback with Nuxt 3
## Overview
The Timeback SDK provides a Nuxt adapter using server middleware.
## Installation
```bash npm theme={null}
npm install @timeback/sdk
```
```bash pnpm theme={null}
pnpm add @timeback/sdk
```
```bash yarn theme={null}
yarn add @timeback/sdk
```
```bash bun theme={null}
bun add @timeback/sdk
```
## Server Setup
Create a server-only Timeback instance. The session helpers below belong to your application: persist the callback user in your authenticated session, and return that session user (including `email`) or `null` from `getUser`. These helpers are not SDK exports. Keep API and SSO secrets on the server:
```typescript server/lib/timeback.ts theme={null}
import { createTimeback } from '@timeback/sdk'
export const timeback = await createTimeback({
env: 'staging',
api: {
clientId: process.env.TIMEBACK_API_CLIENT_ID!,
clientSecret: process.env.TIMEBACK_API_CLIENT_SECRET!,
},
identity: {
mode: 'sso',
clientId: process.env.AWS_COGNITO_CLIENT_ID!,
clientSecret: process.env.AWS_COGNITO_CLIENT_SECRET!,
redirectUri: 'http://localhost:3000/api/timeback/identity/callback',
onCallbackSuccess: async ({ user, state, redirect }) => {
await setSession({ id: user.id, email: user.email })
return redirect(state?.returnTo ?? '/')
},
onCallbackError: ({ error, redirect }) => {
return redirect('/?error=sso_failed')
},
getUser: () => getSession(),
},
})
```
## Server Middleware
Create middleware to handle Timeback routes:
```typescript server/middleware/timeback.ts theme={null}
import { nuxtHandler } from '@timeback/sdk/nuxt'
import { timeback } from '../lib/timeback'
export default defineEventHandler(async event => {
const response = await nuxtHandler({
timeback,
event,
})
if (response) return response
})
```
## Alternative: Route-Based Handler
For more control, you can use a route-based approach instead of middleware:
```typescript server/api/timeback/[...path].ts theme={null}
import { toNuxtHandler } from '@timeback/sdk/nuxt'
import { timeback } from '../../lib/timeback'
const handlers = toNuxtHandler(timeback)
export default defineEventHandler(event => {
const method = event.node.req.method ?? 'GET'
const handler = handlers[method as keyof typeof handlers]
return handler
? handler(event)
: new Response('Method not allowed', { status: 405 })
})
```
## Client Provider
Wrap your app with the TimebackProvider:
```vue app.vue theme={null}
```
## Usage
For the full client-side API, see the [Vue client
adapter](/beta/build-on-timeback/sdk/client/vue).
Use composables in components:
```vue components/UserStatus.vue theme={null}
Sign In
Welcome!
```
## Next Steps
Client-side Vue integration
Authentication options
Track learning sessions
# SolidStart
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/server/solidstart
Integrate Timeback with SolidStart
## Overview
The Timeback SDK provides a SolidStart adapter using middleware.
## Installation
```bash npm theme={null}
npm install @timeback/sdk
```
```bash pnpm theme={null}
pnpm add @timeback/sdk
```
```bash yarn theme={null}
yarn add @timeback/sdk
```
```bash bun theme={null}
bun add @timeback/sdk
```
## Server Setup
Create a server-only Timeback instance. The session helpers below belong to your application: persist the callback user in your authenticated session, and return that session user (including `email`) or `null` from `getUser`. These helpers are not SDK exports. Keep API and SSO secrets on the server:
```typescript src/lib/timeback.ts theme={null}
import { createTimeback } from '@timeback/sdk'
export const timeback = await createTimeback({
env: 'staging',
api: {
clientId: process.env.TIMEBACK_API_CLIENT_ID!,
clientSecret: process.env.TIMEBACK_API_CLIENT_SECRET!,
},
identity: {
mode: 'sso',
clientId: process.env.AWS_COGNITO_CLIENT_ID!,
clientSecret: process.env.AWS_COGNITO_CLIENT_SECRET!,
redirectUri: 'http://localhost:3000/api/timeback/identity/callback',
onCallbackSuccess: async ({ user, state, redirect }) => {
await setSession({ id: user.id, email: user.email })
return redirect(state?.returnTo ?? '/')
},
onCallbackError: ({ error, redirect }) => {
return redirect('/?error=sso_failed')
},
getUser: () => getSession(),
},
})
```
## Middleware
Create middleware to handle Timeback routes:
```typescript src/middleware.ts theme={null}
import { createMiddleware } from '@solidjs/start/middleware'
import { solidStartHandler } from '@timeback/sdk/solid-start'
import { timeback } from '~/lib/timeback'
export default createMiddleware({
onRequest: [
async event => {
const response = await solidStartHandler({
timeback,
event,
})
if (response) return response
},
],
})
```
## Alternative: Route-Based Handler
For more control, you can use a route-based approach instead of middleware:
```typescript src/routes/api/timeback/[...path].ts theme={null}
import { toSolidStartHandler } from '@timeback/sdk/solid-start'
import { timeback } from '~/lib/timeback'
const handlers = toSolidStartHandler(timeback)
export const GET = handlers.GET
export const POST = handlers.POST
```
## Client Provider
Wrap your app with the TimebackProvider:
```tsx src/app.tsx theme={null}
import { TimebackProvider } from '@timeback/sdk/solid'
import { Router } from '@solidjs/router'
import { FileRoutes } from '@solidjs/start/router'
import { Suspense } from 'solid-js'
export default function App() {
return (
{props.children}}>
)
}
```
## Usage
For the full client-side API, see the [Solid client
adapter](/beta/build-on-timeback/sdk/client/solid).
Use primitives in components:
```tsx src/components/UserStatus.tsx theme={null}
import { SignInButton, createTimebackVerification } from '@timeback/sdk/solid'
import { Show } from 'solid-js'
export function UserStatus() {
const verification = createTimebackVerification()
return (
Sign In}>
Welcome!
)
}
```
## Next Steps
Client-side Solid integration
Authentication options
Track learning sessions
# SvelteKit
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/server/sveltekit
Integrate Timeback with SvelteKit
## Overview
The Timeback SDK provides a SvelteKit adapter using hooks.
## Installation
```bash npm theme={null}
npm install @timeback/sdk
```
```bash pnpm theme={null}
pnpm add @timeback/sdk
```
```bash yarn theme={null}
yarn add @timeback/sdk
```
```bash bun theme={null}
bun add @timeback/sdk
```
## Server Setup
Create a server-only Timeback instance. The session helpers below belong to your application: persist the callback user in your authenticated session, and return that session user (including `email`) or `null` from `getUser`. These helpers are not SDK exports. Keep API and SSO secrets on the server:
```typescript src/lib/server/timeback.ts theme={null}
import { createTimeback } from '@timeback/sdk'
export const timeback = await createTimeback({
env: 'staging',
api: {
clientId: process.env.TIMEBACK_API_CLIENT_ID!,
clientSecret: process.env.TIMEBACK_API_CLIENT_SECRET!,
},
identity: {
mode: 'sso',
clientId: process.env.AWS_COGNITO_CLIENT_ID!,
clientSecret: process.env.AWS_COGNITO_CLIENT_SECRET!,
redirectUri: 'http://localhost:3000/api/timeback/identity/callback',
onCallbackSuccess: async ({ user, state, redirect }) => {
await setSession({ id: user.id, email: user.email })
return redirect(state?.returnTo ?? '/')
},
onCallbackError: ({ error, redirect }) => {
return redirect('/?error=sso_failed')
},
getUser: () => getSession(),
},
})
```
## Server Hooks
Add the Timeback handler to your hooks:
```typescript src/hooks.server.ts theme={null}
import { svelteKitHandler } from '@timeback/sdk/svelte-kit'
import { building } from '$app/environment'
import { timeback } from '$lib/server/timeback'
import type { Handle } from '@sveltejs/kit'
export const handle: Handle = ({ event, resolve }) => {
return svelteKitHandler({
timeback,
event,
resolve,
building,
})
}
```
## Alternative: Route-Based Handler
For more control, you can use a route-based approach instead of hooks:
```typescript src/routes/api/timeback/[...path]/+server.ts theme={null}
import { toSvelteKitHandler } from '@timeback/sdk/svelte-kit'
import { timeback } from '$lib/server/timeback'
const handlers = toSvelteKitHandler(timeback)
export const GET = handlers.GET
export const POST = handlers.POST
```
## Client Setup
Initialize Timeback in your root layout:
```svelte src/routes/+layout.svelte theme={null}
{@render children()}
```
## Usage
For the full client-side API, see the [Svelte client
adapter](/beta/build-on-timeback/sdk/client/svelte).
Use stores in components:
```svelte src/routes/+page.svelte theme={null}
{#if $timebackVerification.status === 'loading'}
Loading...
{:else if $timebackVerification.status === 'verified'}
Welcome!
{:else}
Sign In
{/if}
```
## Profile Store
```svelte theme={null}
{#if $timebackProfile.status === 'loaded'}
XP: {$timebackProfile.profile.xp.today}
{/if}
```
## Next Steps
Client-side Svelte integration
Authentication options
Track learning sessions
# TanStack Start
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/server/tanstack-start
Integrate Timeback with TanStack Start
## Overview
The Timeback SDK provides a TanStack Start adapter using file-based route handlers.
## Installation
```bash npm theme={null}
npm install @timeback/sdk
```
```bash pnpm theme={null}
pnpm add @timeback/sdk
```
```bash yarn theme={null}
yarn add @timeback/sdk
```
```bash bun theme={null}
bun add @timeback/sdk
```
## Server Setup
Create a server-only Timeback instance. The session helpers below belong to your application: persist the callback user in your authenticated session, and return that session user (including `email`) or `null` from `getUser`. These helpers are not SDK exports. Keep API and SSO secrets on the server:
```typescript src/lib/timeback.ts theme={null}
import { createTimeback } from '@timeback/sdk'
export const timeback = await createTimeback({
env: 'staging',
api: {
clientId: process.env.TIMEBACK_API_CLIENT_ID!,
clientSecret: process.env.TIMEBACK_API_CLIENT_SECRET!,
},
identity: {
mode: 'sso',
clientId: process.env.AWS_COGNITO_CLIENT_ID!,
clientSecret: process.env.AWS_COGNITO_CLIENT_SECRET!,
redirectUri: 'http://localhost:3000/api/timeback/identity/callback',
onCallbackSuccess: async ({ user, state, redirect }) => {
await setSession({ id: user.id, email: user.email })
return redirect(state?.returnTo ?? '/')
},
onCallbackError: ({ error, redirect }) => {
return redirect('/?error=sso_failed')
},
getUser: () => getSession(),
},
})
```
## Route Handler
Create a catch-all route handler:
```typescript src/routes/api/timeback/$.ts theme={null}
import { createFileRoute } from '@tanstack/react-router'
import { toTanStackStartHandler } from '@timeback/sdk/tanstack-start'
import { timeback } from '@/lib/timeback'
const handlers = toTanStackStartHandler(timeback)
export const Route = createFileRoute('/api/timeback/$')({
server: { handlers },
})
```
## Client Provider
Wrap your app with the TimebackProvider:
```tsx src/routes/__root.tsx theme={null}
import { TimebackProvider } from '@timeback/sdk/react'
import { createRootRoute, Outlet } from '@tanstack/react-router'
export const Route = createRootRoute({
component: () => (
),
})
```
## Usage
For the full client-side API, see the [React client
adapter](/beta/build-on-timeback/sdk/client/react).
Use hooks in components:
```tsx src/routes/index.tsx theme={null}
import { SignInButton, useTimebackVerification } from '@timeback/sdk/react'
function HomePage() {
const { state } = useTimebackVerification()
if (state.status === 'loading') return
Loading...
if (state.status === 'error') return
{state.message}
if (state.status !== 'verified') {
return Sign In
}
return
Welcome!
}
```
## Next Steps
Client-side React integration
Authentication options
Track learning sessions
# User Profile
Source: https://docs.timeback.com/beta/build-on-timeback/sdk/user-profile
Fetch user profile data including XP, enrollments, and progress
## Overview
The user profile contains an enriched view of the current user, including identity information, school, grade, enrolled courses, goals, and XP totals.
You can access user data in several ways:
1. **Server-side**: verify users or fetch profiles directly from your backend
2. **Client-side**: use a framework hook with built-in state and caching
## Server-Side
The `timeback.user` namespace provides programmatic methods that accept an email directly. Use them in your own API routes, server actions, webhooks, cron jobs, or any backend context.
### Verify a user
Use `timeback.user.verify(email)` to check whether a Timeback user exists for a given email. This is a lightweight check — it does not fetch enrollments, analytics, or build an enriched profile.
```typescript TypeScript theme={null}
const result = await timeback.user.verify('student@example.com')
if (result.verified) {
console.log(result.timebackId) // "tb_abc123"
}
```
```python Python theme={null}
result = await timeback.user.verify("student@example.com")
if result.verified:
print(result.timeback_id) # "tb_abc123"
```
This checks whether an email resolves to a Timeback account. It does not authenticate the caller or prove ownership of that email. Use an email from your authenticated session when gating access. Missing accounts return `verified: false`; ambiguous matches and lookup failures reject.
Whether the user exists in Timeback.
The Timeback user ID. Only present when `verified` is `true`.
### Get a user profile
Use `timeback.user.getProfile(email)` to get the full enriched profile — identity, enrollments, courses, goals, and XP. This is the programmatic equivalent of the `/user/me` HTTP handler.
```typescript TypeScript theme={null}
const email = 'student@example.com'
const profile = await timeback.user.getProfile(email)
```
```python Python theme={null}
email = "student@example.com"
profile = await timeback.user.get_profile(email)
```
Throws a `TimebackUserResolutionError` if the user cannot be resolved. If you're not sure whether a user exists, call `verify()` first.
Timeback user ID.
User's email address.
Display name.
School the user belongs to.
School ID.
School name.
Grade level.
Returned enrolled courses. A matching entry in `timeback.config.json` supplies the course code; unmatched courses fall back to their ID.
Course ID.
Course code.
Course name.
Goals from the first returned enrollment whose metadata contains goals. This is not an aggregate across enrollments.
Daily XP target.
Daily lesson target.
Daily active minutes target.
Daily accuracy target.
Daily mastered units target.
XP summed across the returned EduBridge activity facts for this user, without an app filter.
XP earned during the current UTC day.
XP in the queried range from January 1, 2000 through the current UTC day.
## Client-Side
On the client, use the framework-specific profile hook. The hook handles loading state, caching, and session-aware refetching — the hooks call the browser `timeback.user.fetch()` method for you.
```tsx theme={null}
import { useTimebackProfile } from '@timeback/sdk/react'
// Manual fetch
function ProfileButton() {
const { state, canFetch, fetchProfile } = useTimebackProfile()
if (state.status === 'loaded') {
return
XP: {state.profile.xp.today}
}
return (
)
}
// Auto-fetch when verified
function AutoProfile() {
const { state } = useTimebackProfile({ auto: true })
if (state.status === 'loading') return
)
}
```
Timeback user ID.
User's email address.
Display name.
School the user belongs to.
School ID.
School name.
Grade level.
Returned enrolled courses. A matching entry in `timeback.config.json` supplies the course code; unmatched courses fall back to their ID.
Course ID.
Course code.
Course name.
Goals from the first returned enrollment whose metadata contains goals. This is not an aggregate across enrollments.
Daily XP target.
Daily lesson target.
Daily active minutes target.
Daily accuracy target.
Daily mastered units target.
XP summed across the returned EduBridge activity facts for this user, without an app filter.
XP earned during the current UTC day.
XP in the queried range from January 1, 2000 through the current UTC day.
### Hook State
The profile hook returns a state object with these statuses:
| Status | Description |
| --------- | --------------------------------- |
| `idle` | Initial state, no fetch attempted |
| `loading` | Fetch in progress |
| `loaded` | Profile successfully loaded |
| `error` | Fetch failed |
### Caching
The client SDK caches profile data to minimize API calls:
* Profile requests share an in-flight request and a five-second cache per client instance. Verification has a separate 1.5-second cache.
* `fetchProfile()` can reuse the fresh cache; the hook's `refresh()` bypasses it.
* The reviewed shared cache has no explicit sign-out invalidation function. Hook state resets when verification becomes unverified; do not treat cached profile presence as authorization.
The profile XP totals sum the user's returned EduBridge facts across apps, not only this integration. Courses include returned enrollments; configuration supplies a course code when matched, rather than filtering the list. Goals come from the first returned enrollment with goals.
## Next Steps
Track activities to earn XP
Authentication setup
Advanced analytics queries
Query enrollments directly
# Existing apps
Source: https://docs.timeback.com/beta/build-on-timeback/start-building/existing-apps
Level 1 integration for apps that already exist
This guide walks through [Level 1 integration](/beta/build-on-timeback/integration-levels#level-1-minimal-viable): add event tracking and rostering to your existing app. You keep your content and learning logic, and wire authenticated identity, time tracking, and completion reporting into your app.
**Timeback apps must follow strict rules.**
Read [how we evaluate apps](/beta/about-timeback/concepts/evaluating-apps) and [the
non-negotiables](/beta/about-timeback/concepts/non-negotiables) before you start.
If you do not have credentials yet, complete
[first steps](/beta/build-on-timeback/first-steps) first. Confirm you have your staging client ID and secret.
You will need these credentials for the CLI and SDK.
Install the [Timeback CLI](/beta/build-on-timeback/cli/overview):
```bash theme={null}
curl -fsSL https://timeback.dev/cli | bash
```
Or install via a package manager:
```bash npm theme={null}
npm install -g timeback
```
```bash pnpm theme={null}
pnpm add -g timeback
```
```bash yarn theme={null}
yarn global add timeback
```
```bash bun theme={null}
bun add -g timeback
```
Verify the installation:
```bash theme={null}
timeback --version
```
Run the interactive [`timeback init`](/beta/build-on-timeback/cli/init) command in your project root:
```bash theme={null}
timeback init
```
The CLI guides you through setup:
1. **Mode**: Initialize a new app or import an existing one
2. **App name**: Enter your application name
3. **Subjects**: Select subjects your app covers
4. **Grade levels**: Select grade levels
5. **Launch URL**: Your app's entry point
This creates a `timeback.config.json` file in your project:
```json timeback.config.json theme={null}
{
"name": "My Learning App",
"launchUrl": "https://my-app.example.com",
"courses": [
{
"subject": "Math",
"grade": 3,
"ids": null
}
]
}
```
See [Configuration](/beta/build-on-timeback/reference/configuration) for the full schema reference.
Push your configuration to staging:
```bash theme={null}
timeback resources push --env staging
```
This creates or updates your courses in Timeback.
Use `--dry-run` to preview changes:
```bash theme={null}
timeback resources push --env staging --dry-run
```
**See [CLI: Resources](/beta/build-on-timeback/cli/resources) for more commands.**
Start emitting learning events from your app using [Custom Activities](/beta/build-on-timeback/sdk/activity-tracking/intro).
```bash npm theme={null}
npm install @timeback/sdk
```
```bash pnpm theme={null}
pnpm add @timeback/sdk
```
```bash yarn theme={null}
yarn add @timeback/sdk
```
```bash bun theme={null}
bun add @timeback/sdk
```
```bash pip theme={null}
pip install timeback-sdk
```
```bash uv theme={null}
uv add timeback-sdk
```
```typescript Browser completion handler theme={null}
import type { TimebackClient } from '@timeback/sdk/client'
// Obtain this browser client from your framework provider after
// configuring and mounting the SDK server and authenticating the user.
async function runLesson(timeback: TimebackClient) {
const activity = timeback.activity.start({
id: 'lesson-1',
name: 'Introduction to Fractions',
course: { subject: 'Math', grade: 3 },
})
// Replace this demonstration with your actual lesson UI and await its completion.
// Do not end the activity immediately when starting a real lesson.
await activity.end({
totalQuestions: 10,
correctQuestions: 8,
xpEarned: 80,
})
}
```
```python Server-side completion inside your authenticated handler theme={null}
# `timeback` is the configured server returned by create_server().
# See the FastAPI setup guide for credentials, identity and routes.
# Resolve this email from your trusted session, not a request-supplied identity.
await timeback.activity.record({
"user": {"email": authenticated_user.email},
"activity": {
"id": "lesson-1",
"name": "Introduction to Fractions",
"course": {"subject": "Math", "grade": 3},
},
"metrics": {
"total_questions": 10,
"correct_questions": 8,
"xp_earned": 80,
},
})
```
Configure a [server adapter](/beta/build-on-timeback/sdk/server/nextjs) (or [FastAPI](/beta/build-on-timeback/sdk/server/fastapi)) and a [browser provider](/beta/build-on-timeback/sdk/client/react) first. `createTimeback()` creates a server with `activity.record()`; `activity.start()` belongs to the browser client. The TypeScript sketch shows the
[single-session](/beta/build-on-timeback/sdk/activity-tracking/single-session) pattern. If your
activities span multiple sessions, see [stateful
activities](/beta/build-on-timeback/sdk/activity-tracking/stateful) for the multi-session model
with server-side completion.
See [SDK Overview](/beta/build-on-timeback/sdk/overview) for full documentation.
If you prefer lower-level control, you can send events using the [Caliper client](/beta/build-on-timeback/clients/caliper) directly. See [API Clients](/beta/build-on-timeback/clients/overview) for setup and usage.
## What to expect next
After completing these steps:
1. Verify events are flowing using [Studio](/beta/build-on-timeback/cli/studio)
2. Complete the [Level 1 checklist](/beta/build-on-timeback/integration-levels#level-1-minimal-viable)
3. Submit evidence for [review](/beta/build-on-timeback/integration-levels#what-to-expect-during-review)
4. Receive feedback and production credentials upon approval
Manage multiple credential sets
Push, pull, and sync courses
Time tracking and completion metrics for your content
See Level 1 requirements in detail
## Need help?
Get integration support from the Timeback team and other developers.
# Native apps
Source: https://docs.timeback.com/beta/build-on-timeback/start-building/native-apps
Level 2 integration for native learning apps on Timeback
If you want to build a new learning app specifically for Timeback, we want to talk to you first. This is a different path from [integrating an existing app](/beta/build-on-timeback/start-building/existing-apps).
Schedule a conversation about what you want to build
The onboarding and review steps below are team policy. SDK source verifies the integration primitives, not current curriculum priorities, review availability, or approval.
## Why talk to us first
Curriculum gaps exist and must be prioritized.
Before you build, we can tell you:
* Which subjects and grade levels need coverage
* What content standards to align with
* Which learning approaches fit our model
* How to structure your app for [Level 2 integration](/beta/build-on-timeback/integration-levels#level-2-fully-native) from the start
## What this path looks like
[Book a developer onboarding call](https://app.cal.com/team/timeback-dev/developer-onboarding)
to discuss what you want to build. We will share current curriculum priorities and gaps.
Together we define what your app will cover, which standards it aligns to, and how it fits
the Timeback ecosystem.
The Timeback team will help you create your course structure: courses, QTI content, and
PowerPath configuration. Confirm the currently available setup tooling during onboarding.
The SDK's [Managed Lessons](/beta/build-on-timeback/sdk/managed-lessons/intro) handle lesson
discovery, question sequencing, scoring, and time tracking. You build the UI; the SDK
orchestrates PowerPath, QTI, and OneRoster under the hood.
We review progress and provide guidance throughout development.
Deploy directly to Timeback's distribution channels.
## Level 2 Integration
New apps built for Timeback start at [Level 2 integration](/beta/build-on-timeback/integration-levels#level-2-fully-native). You use Timeback's systems instead of building your own:
* **[PowerPath](/beta/build-on-timeback/clients/powerpath)** for learning engine and sequencing
* **[QTI](/beta/build-on-timeback/clients/qti)** for content formats
* **[CASE](/beta/api-reference/overview)** for standards alignment
The SDK's approach to **[Managed Lessons](/beta/build-on-timeback/sdk/managed-lessons/intro)** brings these together into a single lesson runtime, handling lesson requests, sequencing, and activity tracking. Your app renders the returned content and handles the supported QTI interactions.
Full SDK integration guide for delivering Timeback-powered lessons
See [integration levels](/beta/build-on-timeback/integration-levels) for full details on Level 2.
## Get started
Schedule a call to discuss building a new app
Connect with other builders and the Timeback team
# Timeback Documentation
Source: https://docs.timeback.com/index
Build applications on Timeback and find guidance for the Timeback team.
Find the integration guide, API contract, or team workflow you need.
Connect an existing app or build with the SDK, CLI, and supported frameworks.
Find the right API and event contract for your integration.
Learn how Timeback connects learning activities, progress, and outcomes.
Team members: sign in with your Mintlify organization account to read engineering and support guidance.
# Get latest MAP percentiles by subject for a student
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/analytics/get-latest-map-percentiles-by-subject-for-a-student
/openapi/beyond-ai/edubridge-api.yaml get /edubridge/analytics/students/{studentId}/percentiles
Returns the most recent MAP percentile and RIT score for each subject available for the student.
# Get XP leaderboard for an application
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/analytics/get-xp-leaderboard-for-an-application
/openapi/beyond-ai/edubridge-api.yaml get /edubridge/analytics/xp/leaderboard
Returns per-student XP totals for course-attributed XP owned by the requested application. Requires analytics admin scope and is not tenant-scoped.
# Gets the highest grade mastered by a student for a given subject
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/analytics/gets-the-highest-grade-mastered-by-a-student-for-a-given-subject
/openapi/beyond-ai/edubridge-api.yaml get /edubridge/analytics/highestGradeMastered/{studentId}/{subject}
Gets the highest grade mastered by a student for a given subject across different data sources (currently edulastic, placement and test out datasets)
# List all facts for a given date range by email or studentId
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/analytics/list-all-facts-for-a-given-date-range-by-email-or-studentid
/openapi/beyond-ai/edubridge-api.yaml get /edubridge/analytics/activity
Return a list of processed facts for a given date range by email or studentId
# List all facts for a given enrollment
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/analytics/list-all-facts-for-a-given-enrollment
/openapi/beyond-ai/edubridge-api.yaml get /edubridge/analytics/enrollment/{enrollmentId}
Return a list of processed facts for a given enrollment with optional date range filtering
# List all facts for a given week by email or studentId
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/analytics/list-all-facts-for-a-given-week-by-email-or-studentid
/openapi/beyond-ai/edubridge-api.yaml get /edubridge/analytics/facts/weekly
Return a list of processed facts for a given week by email or studentId
# Check missing enrollments for AP readiness courses
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/ap-readiness/check-missing-enrollments-for-ap-readiness-courses
/openapi/beyond-ai/edubridge-api.yaml get /ap-readiness/enrollment-check
Returns the subset of requested courses where the student does not have an active enrollment.
# Create an AP readiness goal
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/ap-readiness/create-an-ap-readiness-goal
/openapi/beyond-ai/edubridge-api.yaml post /ap-readiness/goals
Creates an AP readiness goal for a student. Only one active AP readiness goal is allowed per student and AP subject; creating a new one cancels the previous active goal for that student and subject.
# Get AP readiness dashboard rows for a subject
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/ap-readiness/get-ap-readiness-dashboard-rows-for-a-subject
/openapi/beyond-ai/edubridge-api.yaml get /ap-readiness/dashboard
Returns all active AP readiness goals for the requested AP subject with readiness, coverage, focus-area, and time-remaining metrics.
# Get AP readiness drill-down for a student goal
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/ap-readiness/get-ap-readiness-drill-down-for-a-student-goal
/openapi/beyond-ai/edubridge-api.yaml get /ap-readiness/students/{studentId}/drill-down
Returns the per-unit accuracy breakdown for a student's AP readiness goal. The goalId query parameter must belong to the same student.
# List active AP readiness goals for a subject
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/ap-readiness/list-active-ap-readiness-goals-for-a-subject
/openapi/beyond-ai/edubridge-api.yaml get /ap-readiness/goals
Returns all active AP readiness goals for the requested AP subject. This is the lightweight goal list view without computed dashboard metrics.
# List supported AP exam subjects
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/ap-readiness/list-supported-ap-exam-subjects
/openapi/beyond-ai/edubridge-api.yaml get /ap-readiness/subjects
Returns the supported AP exam subjects and the title-matching patterns used to suggest candidate courses.
# Update an AP readiness goal
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/ap-readiness/update-an-ap-readiness-goal
/openapi/beyond-ai/edubridge-api.yaml patch /ap-readiness/goals/{goalId}
Partially updates the tracked course set and/or exam date for an AP readiness goal.
# Create a new application metric
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/application-metrics/create-a-new-application-metric
/openapi/beyond-ai/edubridge-api.yaml post /edubridge/applicationMetrics/{applicationSourcedId}
Creates a new application metric for tracking.
This endpoint allows you to record metrics associated with an application,
specifying the metric type and source for proper categorization.
# Delete an application metric
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/application-metrics/delete-an-application-metric
/openapi/beyond-ai/edubridge-api.yaml delete /edubridge/applicationMetrics/{applicationSourcedId}
Deletes a specific application metric.
This endpoint removes a specific metric record identified by the application ID,
metric type, and metric source.
# Get all metrics for an application
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/application-metrics/get-all-metrics-for-an-application
/openapi/beyond-ai/edubridge-api.yaml get /edubridge/applicationMetrics/{applicationSourcedId}
Returns all application metrics for a given application.
This endpoint provides a way to retrieve all tracked metrics associated with an application,
including the metric type and source information.
# Get all applications
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/applications/get-all-applications
/openapi/beyond-ai/edubridge-api.yaml get /edubridge/applications/
List all applications available in the system
# Copy a parent org's calendar into all its active children
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/calendar-days/copy-a-parent-orgs-calendar-into-all-its-active-children
/openapi/beyond-ai/edubridge-api.yaml post /calendar/orgs/{orgSourcedId}/days/fan-out
Copy the parent org's (the path param) active calendar days into every active direct child org. One level only; grandchildren are NOT recursed. All child copies run in a single DB transaction so the fan-out is atomic. Each child's academic session is re-detected per copied day when `redetectSession` is true (default). Returns aggregate counts and the affected child org ids.
# Copy one org's calendar days into another org
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/calendar-days/copy-one-orgs-calendar-days-into-another-org
/openapi/beyond-ai/edubridge-api.yaml post /calendar/orgs/{orgSourcedId}/days/copy-from
Copy the source org's active calendar days into the destination org (the path param) by (org, date) upsert with factory-reset semantics. Only dates present in the source (optionally within a date window) are touched on the destination. The destination's academic session is re-detected per copied day when `redetectSession` is true (default); otherwise `sessionSourcedId` is left null.
# Count resolved student calendar availability
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/calendar-days/count-resolved-student-calendar-availability
/openapi/beyond-ai/edubridge-api.yaml get /calendar/orgs/{orgSourcedId}/students/{studentSourcedId}/availability/count
Count available and unavailable resolved calendar days for a student, subject, and date range.
# Create a student calendar day override
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/calendar-days/create-a-student-calendar-day-override
/openapi/beyond-ai/edubridge-api.yaml post /calendar/orgs/{orgSourcedId}/students/{studentSourcedId}/overrides
Create a subject-specific student unavailability range. V1 overrides can only mark otherwise-instructional org calendar days unavailable; overlapping ranges are kept as raw history and resolved by latest write.
# Delete a student calendar day override
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/calendar-days/delete-a-student-calendar-day-override
/openapi/beyond-ai/edubridge-api.yaml delete /calendar/orgs/{orgSourcedId}/students/{studentSourcedId}/overrides/{overrideSourcedId}
Soft-delete a student calendar override by setting status to tobedeleted.
# Get monthly summary of school days
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/calendar-days/get-monthly-summary-of-school-days
/openapi/beyond-ai/edubridge-api.yaml get /calendar/orgs/{orgSourcedId}/summary/monthly
Get a summary of instructional days grouped by month for a specific organization.
# Get resolved student calendar availability
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/calendar-days/get-resolved-student-calendar-availability
/openapi/beyond-ai/edubridge-api.yaml get /calendar/orgs/{orgSourcedId}/students/{studentSourcedId}/availability
Resolve org calendar days plus student overrides into one row per requested date for a student and subject.
# List student calendar day overrides
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/calendar-days/list-student-calendar-day-overrides
/openapi/beyond-ai/edubridge-api.yaml get /calendar/orgs/{orgSourcedId}/students/{studentSourcedId}/overrides
List raw student/subject availability overrides attached to an organization calendar.
# Update a student calendar day override
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/calendar-days/update-a-student-calendar-day-override
/openapi/beyond-ai/edubridge-api.yaml put /calendar/orgs/{orgSourcedId}/students/{studentSourcedId}/overrides/{overrideSourcedId}
Update the subject, date range, metadata, or status for a student calendar override.
# Enroll user in a course
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/enrollments/enroll-user-in-a-course
/openapi/beyond-ai/edubridge-api.yaml post /edubridge/enrollments/enroll/{userId}/{courseId}/{schoolId}?
Enrolls a user in a course with a single API call.
This endpoint handles all necessary background operations: locating or creating a default class for the course,
establishing appropriate academic sessions (school year and term), and creating the enrollment record.
Consumers can simply specify the user, course, and role (default is 'student') without
needing to understand or manage the underlying academic structure that OneRoster requires.
# Get enrollments for a user
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/enrollments/get-enrollments-for-a-user
/openapi/beyond-ai/edubridge-api.yaml get /edubridge/enrollments/user/{userId}
Returns a simplified, course-centric view of a user's enrollments. Active enrollments are returned by default; pass `includeInactive=true` when historical/tobedeleted enrollment evidence is required.
This endpoint abstracts away the complex OneRoster academic hierarchy,
providing a streamlined representation that focuses on what courses the user is enrolled in rather than details about classes, sections, and academic sessions.
The response includes essential course information without the need to navigate multiple relationship levels.
# Get the default class for a course
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/enrollments/get-the-default-class-for-a-course
/openapi/beyond-ai/edubridge-api.yaml get /edubridge/enrollments/defaultClass/{courseId}/{schoolId}?
Retrieves and automatically creates a default class (and other necessary academic entities) for the specified course.
This endpoint simplifies the management of digital learning by abstracting away the need to manually create class structures.
For digital-only courses, this endpoint ensures that the necessary academic structure (school year, term, class) exists
without requiring the consumer to understand or manage this complex hierarchy.
The class is created with appropriate digital-friendly settings.
You do not need to call this endpoint unless you have a use case where you want to manage the underlying academic structure (school year, term, class) for a course.
# Get total time saved for a student this school year
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/enrollments/get-total-time-saved-for-a-student-this-school-year
/openapi/beyond-ai/edubridge-api.yaml get /edubridge/time-saved/user/{userId}
Calculates the total time a student has "got back" during the current school year.
This endpoint compares the personalized learning system's efficient 2-hour daily study time against
traditional school schedules (6 hours class + 1 hour homework = 7 hours total),
resulting in 5 hours saved per school day.
The calculation starts from the student's earliest valid enrollment date within the current school year
(or the school year start date if they enrolled earlier) and counts only actual school days
(excluding weekends, holidays, breaks, and MAP testing days).
**Formula:** Number of school days elapsed × 5 hours saved per day = Total hours saved
The response includes both the total hours saved and equivalent full days saved (hours ÷ 24).
# Reset enrollment goals to current course goals
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/enrollments/reset-enrollment-goals-to-current-course-goals
/openapi/beyond-ai/edubridge-api.yaml post /edubridge/enrollments/resetGoals/{courseId}
Resets the goals for all enrollments in a course to the course's current goals.
# Resets an user's progress in a given course
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/enrollments/resets-an-users-progress-in-a-given-course
/openapi/beyond-ai/edubridge-api.yaml delete /edubridge/enrollments/resetProgress/{userId}/{courseId}
Reset the progress an user has made in a given course.
This endpoint finds all assessment results to associated a given course and user and marks them as 'tobedeleted'.
This endpoint is not responsible for deleting results and progress in third party apps.
# Unenroll a user from a course
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/enrollments/unenroll-a-user-from-a-course
/openapi/beyond-ai/edubridge-api.yaml delete /edubridge/enrollments/unenroll/{userId}/{courseId}/{schoolId}?
Unenrolls a user from a course with a single API call.
This endpoint automatically handles finding the appropriate class enrollment(s) for the course and marking them as 'tobedeleted'.
Consumers don't need to know which specific class the user is enrolled in or manage the enrollment status transitions required by OneRoster.
# Create a learner-facing subject goal
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/goals/create-a-learner-facing-subject-goal
/openapi/beyond-ai/edubridge-api.yaml post /goals/{studentId}/subjects
Creates or replaces the active K-8 learner-facing outcome goal for a subject. The service resolves a stable routed course path at save time and stores that execution snapshot behind the product-shaped response.
# Create an atomic course goal
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/goals/create-an-atomic-course-goal
/openapi/beyond-ai/edubridge-api.yaml post /goals/{studentId}/courses
Creates a 9-12 course-scoped atomic goal for a currently enrolled course. Subject metadata is derived from the course rather than supplied as canonical input.
# Delete an atomic course goal
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/goals/delete-an-atomic-course-goal
/openapi/beyond-ai/edubridge-api.yaml delete /goals/{studentId}/courses/{goalId}
Deletes the active atomic course goal.
# Delete the active learner-facing subject goal
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/goals/delete-the-active-learner-facing-subject-goal
/openapi/beyond-ai/edubridge-api.yaml delete /goals/{studentId}/subjects/{subject}
Deletes the active subject-outcome goal for the subject. Managed minimum baselines remain in place.
# Get course progress for a student
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/goals/get-course-progress-for-a-student
/openapi/beyond-ai/edubridge-api.yaml get /goals/{studentId}/courses/{courseId}/progress
Returns the aggregate Goals course progress snapshot for a single currently enrolled course.
# Get revision history for one goal
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/goals/get-revision-history-for-one-goal
/openapi/beyond-ai/edubridge-api.yaml get /goals/{studentId}/history/{goalId}
Returns paginated immutable learning_goal_revisions snapshots for one goal. Defaults to version order ascending so consumers can replay the goal chain from v1 forward.
# Get subject grade progress for a student
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/goals/get-subject-grade-progress-for-a-student
/openapi/beyond-ai/edubridge-api.yaml get /goals/{studentId}/subjects/{subject}/grades/{grade}/progress
Returns aggregate Goals progress through one subject-grade band using the course sequence for that subject.
# List course goal snapshots for a student
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/goals/list-course-goal-snapshots-for-a-student
/openapi/beyond-ai/edubridge-api.yaml get /goals/{studentId}/courses
Primary 9-12 goals read model. Returns one row per enrolled course with the current active atomic course goal, if any, plus the course progress snapshot used to evaluate it.
# List goal revision history for a student
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/goals/list-goal-revision-history-for-a-student
/openapi/beyond-ai/edubridge-api.yaml get /goals/{studentId}/history
Returns paginated immutable learning_goal_revisions snapshots for a student. Defaults to newest revisions first and supports filtering by captured goal subject, type, record status, and business status.
# List saved goals for a student
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/goals/list-saved-goals-for-a-student
/openapi/beyond-ai/edubridge-api.yaml get /goals/{studentId}
Saved Goals read contract. Returns saved learning_goals rows from the primary TimeBack database without managed-minimum sync, reporting views, progress computation, projections, activity, or freshness metadata. Default results exclude AP Readiness goals; use the AP Readiness API for those workflows.
# List subject goal snapshots for a student
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/goals/list-subject-goal-snapshots-for-a-student
/openapi/beyond-ai/edubridge-api.yaml get /goals/{studentId}/subjects
Primary K-8 goals read model. Returns one product-shaped row per subject with the managed baseline minimum, active learner-facing outcome goal, saved routed execution snapshot, and current computed pace/projection state.
# Preview a learner-facing subject goal
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/goals/preview-a-learner-facing-subject-goal
/openapi/beyond-ai/edubridge-api.yaml post /goals/{studentId}/subjects/preview
Returns the product-shaped subject goal preview with the saved-path equivalent execution snapshot and computed pace/projection state, without creating or updating a row.
# Preview an atomic course goal
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/goals/preview-an-atomic-course-goal
/openapi/beyond-ai/edubridge-api.yaml post /goals/{studentId}/courses/preview
Returns the product-shaped course goal preview without creating or updating a row.
# Read full subject goal snapshots for multiple students
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/goals/read-full-subject-goal-snapshots-for-multiple-students
/openapi/beyond-ai/edubridge-api.yaml post /goals/subjects/batch
Full subject-goals read for up to 100 unique students after trimming and deduplication. Each successful results entry has the same body as `GET /goals/{studentId}/subjects`, including dailyTargetXPTotal, progress, pacing, calendar, and projection data. The existing managed-minimum initialization may create missing goals and revisions. Uses the individual route's tenant visibility policy; callers must restrict the requested roster to authorized students. Per-student not_found, internal_error, and deadline_exceeded errors remain in the HTTP 200 response, including an all-error batch. Retry failed students or fall back to individual reads. Shared preflight failures can still fail the entire request. POST /goals/subjects/daily-targets/batch is an additional compact option for callers that only need saved daily targets; it does not replace this full contract.
# Read saved daily XP targets for multiple students
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/goals/read-saved-daily-xp-targets-for-multiple-students
/openapi/beyond-ai/edubridge-api.yaml post /goals/subjects/daily-targets/batch
Compact saved-target read for up to 100 unique students after trimming and deduplication. Returns results and errors dictionaries keyed by student ID; per-student failures remain HTTP 200, including an all-error batch. Uses the existing tenant visibility policy, including legacy unscoped rows; callers must restrict the requested roster to authorized students. Selects the newest active minimum for each stored subject name and preserves aliases and zero targets. An outcome-only subject has a null target. Missing managed minimums return minimum_goals_missing; use `GET /goals/{studentId}/subjects` for existing initialization. This endpoint creates no goals or revisions and computes no progress, pacing, calendars, or projections. Clients needing the full read model continue to use `GET /goals/{studentId}/subjects` or POST /goals/subjects/batch.
# Update a learner-facing subject goal
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/goals/update-a-learner-facing-subject-goal
/openapi/beyond-ai/edubridge-api.yaml patch /goals/{studentId}/subjects/{subject}
Updates the active subject-outcome goal for one subject. Recomputes and persists the routed course-path snapshot whenever the objective changes.
# Update an atomic course goal
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/goals/update-an-atomic-course-goal
/openapi/beyond-ai/edubridge-api.yaml patch /goals/{studentId}/courses/{goalId}
Updates an atomic course-scoped goal without forcing course clients through subject-oriented payloads.
# Update the managed minimum baseline for a subject
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/goals/update-the-managed-minimum-baseline-for-a-subject
/openapi/beyond-ai/edubridge-api.yaml patch /goals/{studentId}/subjects/{subject}/minimum
Updates the managed minimum daily XP baseline for the subject. Minimums remain simple baseline policy and are patched separately from learner-facing outcome goals.
# Get the MAP profile for a given student
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/learning-reports/get-the-map-profile-for-a-given-student
/openapi/beyond-ai/edubridge-api.yaml get /edubridge/learning-reports/map-profile/{userId}
Return the MAP profile for a given student
# Browse available lessons
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/progression/browse-available-lessons
/openapi/beyond-ai/edubridge-api.yaml get /edubridge/progression/catalog
Browse available lessons for a learning app. Use this to find lesson IDs before creating a custom hole-filling course.
**Getting started:** Query with just `app` (no filters) to discover available subjects and grade levels. The response includes `subject` and `gradeLevel` on each lesson — use those exact values to narrow down.
**Lesson ID formats vary by app:**
- **MobyMax**: `mobymax_reading_1654_g3`
- **Freckle**: UUIDs or standard codes (`FR-CCSS-2-W.6.3.d`)
- **Edia**: dotted paths (`middle_school.factors and multiples.factor_expressions`)
- **Lalilo**: numeric IDs (`168`, `532`)
- **VocabLoco**: structured IDs (`vocabloco_g1171_long_a_words`)
- **Anton**: path-style IDs (`c-nateng-k/yx4m8g`)
**Grade values:** Accepts `PK`, `K`, `1`–`12`. Canonical values `-1` and `0` are also accepted.
**Pagination:** Default 100 lessons per page, max 500. Use `page` and `limit` to paginate. Response includes `pagination.total` and `pagination.totalPages`.
# Create a custom hole-filling course (async)
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/progression/create-a-custom-hole-filling-course-async
/openapi/beyond-ai/edubridge-api.yaml post /edubridge/progression/custom-hole-filling-course
Creates a custom hole-filling course for a student with specific lessons you choose. Returns 202 with a `jobId` immediately — the course is created asynchronously.
## How it works
1. Looks up the student by email
2. Creates lesson assignments in the target app
3. Creates a course in OneRoster with the assigned lessons
4. Removes the student from existing same-subject courses (protected courses like Manual XP are kept) — unless `skipUnenroll` is true
5. Enrolls the student in the new course
If enrollment fails, any removed courses are automatically restored (unless unenrollment was skipped). Progress and failures are reported via Google Chat notifications.
## Picking lessons
For **Bridge apps** (`mobymax`, `lalilo`, `freckle`, `vocabloco`, `anton`, `edia`), use `GET /edubridge/progression/catalog` to browse available lessons, then pass the `lessonId` values into the `lessons` array.
For the **native OneRoster apps** (`timeback`, `timeback_learn`, `alphawrite`, `alpha_read`), `/catalog` does not apply and no external assignments are created. For `timeback_learn`, Math `lessonId` values are source course-component sourcedIds, while Science values are resource sourcedIds. For the other native apps, `lessonId` values are existing OneRoster resource sourcedIds (for `alpha_read`, use the article resource sourcedId, e.g. `article_4100001`; requires subject `Reading`).
## Validation rules
**Grade:** Accepts `PK`, `PreK`, `Pre-K`, `Pre-Kindergarten`, `K`, `KG`, `Kindergarten`, or `0`–`12`. Canonicalized to a numeric string (PK = `-1`, K = `0`). Values like `3rd` or `banana` are rejected.
**`next`:** What happens after the student completes the course. Accepts:
- A test type: `end of course`, `test out`, or `placement` (case-insensitive, flexible spacing)
- A course UUID from an active course sequence — the student will be enrolled in that course next
**`assignmentId`:** Must be a positive integer if provided. Resolved automatically from existing enrollments if omitted.
# Create a hole-filling course and enroll student
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/progression/create-a-hole-filling-course-and-enroll-student
/openapi/beyond-ai/edubridge-api.yaml post /edubridge/progression/create-course
Proxies the request to the AlphaTest Courses API to create a hole-filling course with selected lessons, enroll the student, and configure post-completion behavior.
**Workflow:**
1. Call GET /catalog to browse available lessons and get valid `lessonId` values
2. Call this endpoint with the selected lessons
**The `next` field** controls what happens when the student completes the course:
- `"assessment"` — assign an end-of-course assessment for the same subject/grade
- A valid course UUID — unenroll from this course and enroll in the specified course
**Notes:**
- Grade format is flexible: `"5"`, `5`, `"K"` all work
- The API does NOT deduplicate — calling twice creates two courses
- Course creation can take up to ~90s for adapter apps due to external job polling
- `timeback_learn` must use `POST /edubridge/progression/custom-hole-filling-course`; this legacy proxy cannot link existing native OneRoster resources
# Enqueue a student progression job
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/progression/enqueue-a-student-progression-job
/openapi/beyond-ai/edubridge-api.yaml post /edubridge/progression/enqueue
Called by AlphaTest DCAN after completing gap analysis. The progression-enqueue Lambda places a message on the student-progression SQS queue.
# Ingest a Caliper event
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/progression/ingest-a-caliper-event
/openapi/beyond-ai/edubridge-api.yaml post /edubridge/caliper/event
Receives Caliper events from educational app sensors. The caliper-ingestion Lambda debundles multi-event payloads and enqueues them on the caliper-events FIFO SQS queue.
# Onboard a student into one or more learning apps
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/progression/onboard-a-student-into-one-or-more-learning-apps
/openapi/beyond-ai/edubridge-api.yaml post /edubridge/progression/onboard-student
For most apps, forwards the request to the onboarding orchestrator, which validates the student and each app, then enqueues each app to SQS (3× retry + DLQ). Incept, Mentava, and Happy Numbers are synchronous, in-repo exceptions and must each be requested alone. This endpoint validates the request **structure**; the orchestrator validates the **values** (valid app names, grade ranges, mathacademy course matching).
## Payload
```jsonc
{
"email": "student@alpha.school", // required
"apps": [ // required, non-empty
{ "app": "", "args": { "grade": 5 } }
]
}
```
- Each `apps` entry is an object: `app` (routing key) + optional `args` bag.
- `args.grade` is required for **every app except `google`**.
- Grade ranges below are generally enforced **downstream**. The synchronous in-repo exceptions enforce their own restrictions here before any provider write.
## Supported apps
_Expand an app to see its `args` and a sample entry._
anton — grade: any integer
```json
{ "app": "anton", "args": { "grade": 5 } }
```
membean — grade: 4–12
Class is assigned from the student's campus + level (not all campuses are configured).
```json
{ "app": "membean", "args": { "grade": 7 } }
```
egumpp — grade: any integer
```json
{ "app": "egumpp", "args": { "grade": 6 } }
```
clearfluency — grade: 1–2
```json
{ "app": "clearfluency", "args": { "grade": 1 } }
```
freckle — grade: -1–12 (PreK–12)
Optional `renaissance_class_id` overrides the class assignment.
```json
{ "app": "freckle", "args": { "grade": 3, "renaissance_class_id": "abc-123" } }
```
lalilo — grade: -1–12 (PreK–12)
Optional `renaissance_class_id` overrides the class assignment.
```json
{ "app": "lalilo", "args": { "grade": 1 } }
```
mobymax — grade: 0–8 (K–8)
Optional: `course` (e.g. `"reading"`), `account` (string[]), `subject`.
```json
{ "app": "mobymax", "args": { "grade": 4, "course": "reading", "subject": "math" } }
```
mathacademy — grade: 4–12 (`course_name` optional)
Each grade resolves to a default course (grades 9–12 default to the traditional track: Algebra I, Geometry, Algebra II, Precalculus). `course_name` is an optional override to select a different Math Academy catalog course (e.g. an Integrated Math (Honors) or SAT course).
```json
{ "app": "mathacademy", "args": { "grade": 10 } }
```
incept - grades: 3-4 (Math). Runs the full Incept enrollment flow (same as the progression engine grade-3/4 Math base enrollment): rosters the student in Incept and provisions their individualized TimeBack Math base course.
Unlike the other apps, `incept` is validated and executed by **THIS service** (not the async orchestrator) and runs **synchronously**. It must be the **only** app in the request. Responses: `202` enrolled (or already enrolled), `207` enrollment failed (e.g. Incept/TimeBack desync), `404` no active student for the email, `409` the email matches multiple active users, `503` Incept not configured in this env, or Incept/TimeBack temporarily unavailable (retry).
```json
{ "app": "incept", "args": { "grade": 4 } }
```
mentava — PK by default; non-PK requires Academics Reading approval. Runs synchronously through Bridge API.
`class_id` optionally overrides the configured current Mentava class UUID. The service always uses the approved starting level (`letter_sounds`, shown in Mentava as Letter Sounds only / no blending), sends the student's TimeBack OneRoster sourcedId to Mentava, enrolls the Mentava Basics TimeBack course, and stores the standard learning-app credentials. Set `approval_confirmed: true` for non-PK students and whenever the student's authoritative TimeBack grade is unavailable. The no-approval PK form is accepted only when TimeBack itself identifies the student as PK. The Bridge consumer used by this service must have `mentava:read` and `mentava:roster:students:create` (or `mentava:roster:write`) scopes. Device assignment/app installation remains an operational prerequisite outside this API.
```json
{ "app": "mentava", "args": { "grade": -1 } }
```
```json
{ "app": "mentava", "args": { "grade": -1, "approval_confirmed": true } }
```
```json
{ "app": "mentava", "args": { "grade": 1, "class_id": "00000000-0000-4000-8000-000000000000", "approval_confirmed": true } }
```
edia — grade: 3–8
```json
{ "app": "edia", "args": { "grade": 5 } }
```
vocabloco — grade: any integer
Word lists are auto-assigned by grade: G3 → Vocabulary + Spelling, G4 → Vocabulary + Spelling, G5 → Word List, G6+ → account only (no word lists).
```json
{ "app": "vocabloco", "args": { "grade": 4 } }
```
zearn — grade: 0–3 (K–3)
```json
{ "app": "zearn", "args": { "grade": 2 } }
```
happynumbers — Pre-K only
Resolves the active student roster record, provisions the student into `Alpha`, `Alpha 2`, and so on, rolling to a newly created class when the current class reaches 95 students, and stores the Happy Numbers identity mapping and TimeBack display-name/PIN credentials. It runs synchronously in this service through Bridge and must be the only app in the request. This is provider-roster onboarding only: it does not create or replace a TimeBack course enrollment. Normal progression assigns the TimeBack course separately. The TimeBack service identity must have Bridge `happynumbers:read` and `happynumbers:roster:write` scopes.
```json
{ "app": "happynumbers", "args": { "grade": -1 } }
```
google — grade: optional
`args` may be omitted entirely. Optional `google_password` (system default if omitted); `grade` is only used for OU assignment.
```json
{ "app": "google" }
```
```json
{ "app": "google", "args": { "google_password": "CustomPass1!" } }
```
## Responses
- **200** — Mentava course onboarding or Happy Numbers provider rostering completed synchronously; check `results[].status`.
- **202** — every app was queued.
- **207** — partial success; check each entry's `queued`/`error` field.
- **400** — malformed body (here) or invalid app name / app-specific value validation (orchestrator).
- **404** — student not found for the given email.
Served by the onboard-student Lambda.
# Publish progression status update
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/progression/publish-progression-status-update
/openapi/beyond-ai/edubridge-api.yaml post /edubridge/progression/status
Enqueues a progression status update to the FIFO queue. The consumer Lambda writes it to the student's user metadata in OneRoster. If the studentTimebackId does not exist in OneRoster, the request is accepted but skipped — the response will include `skipped: true` and a `reason` field. Served by the progression-status-enqueue Lambda.
# Get a signed report PDF URL
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/reports/get-a-signed-report-pdf-url
/openapi/beyond-ai/edubridge-api.yaml get /edubridge/reports/get-report/eow/{year}/{period}/{studentId}
Returns a fresh short-lived signed S3 URL for viewing an end-of-week report PDF. The response uses `pdfUrl` so clients can reuse the MAP report PDF viewer pattern.
# Get end-of-week report email status
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/reports/get-end-of-week-report-email-status
/openapi/beyond-ai/edubridge-api.yaml get /edubridge/reports/eow/{year}/{period}/{studentId}/email-status
Returns report existence and the shared email-delivery ledger state for each current parent/guardian. Provider acceptance is not proof of inbox delivery.
# Resolve an encrypted end-of-week report link
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/reports/resolve-an-encrypted-end-of-week-report-link
/openapi/beyond-ai/edubridge-api.yaml get /edubridge/reports/resolve-eow-link/{token}
Resolves an opaque email-link token to its report locator. The token is not authorization; clients must independently authorize the current user for the resolved student.
# Send an end-of-week report email
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/reports/send-an-end-of-week-report-email
/openapi/beyond-ai/edubridge-api.yaml post /edubridge/reports/eow/send
Synchronously requests one idempotent live delivery to each current parent/guardian, or a version-scoped test delivery to 1-5 recipients on an approved company domain. Historical report weeks are allowed when the report exists. Live callers may explicitly retry definitively failed deliveries without reopening successful or uncertain outcomes.
# Store a student report PDF
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/reports/store-a-student-report-pdf
/openapi/beyond-ai/edubridge-api.yaml post /edubridge/reports/store-report
Stores a base64-encoded PDF report in the Progression reports S3 bucket. v1 supports only end-of-week reports (`reportType: eow`).
# Create a subject track group
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/subject-track/create-a-subject-track-group
/openapi/beyond-ai/edubridge-api.yaml post /edubridge/subject-track/groups
Creates a new subject track group. If orgSourcedIds is empty, creates a global track that applies to all organizations. If orgSourcedIds contains organization IDs, creates campus-specific tracks for each organization. All tracks in the group will have the same subject, grade, and course.
# Create or update a subject track
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/subject-track/create-or-update-a-subject-track
/openapi/beyond-ai/edubridge-api.yaml put /edubridge/subject-track/
Creates a new subject track or updates an existing one for the given organization, subject and grade with upsert behavior. If orgSourcedId is provided, creates an organization-specific track. If omitted, creates a global track that applies to all organizations. There can be only one target course per organization, subject and grade level combination.
# Delete a subject track
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/subject-track/delete-a-subject-track
/openapi/beyond-ai/edubridge-api.yaml delete /edubridge/subject-track/{id}
Deletes a subject track by its ID
# Delete a subject track group
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/subject-track/delete-a-subject-track-group
/openapi/beyond-ai/edubridge-api.yaml delete /edubridge/subject-track/groups
Deletes all subject tracks in a group. The group is identified by subject, grade, courseId, and isGlobal. All tracks belonging to this group will be permanently removed.
# Get all subject tracks
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/subject-track/get-all-subject-tracks
/openapi/beyond-ai/edubridge-api.yaml get /edubridge/subject-track/
List all subject tracks: the target course for each organization (school/campus), subject and grade level combination. Includes both organization-specific tracks and global tracks that apply to all organizations.
# List all subject track groups
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/subject-track/list-all-subject-track-groups
/openapi/beyond-ai/edubridge-api.yaml get /edubridge/subject-track/groups
Returns all subject tracks organized into logical groups. Groups are defined by subject, grade, course, and type (global vs campus-specific). Global groups contain a single track that applies to all organizations. Campus groups contain multiple tracks for specific organizations sharing the same subject, grade, and course.
# Update a subject track group
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/subject-track/update-a-subject-track-group
/openapi/beyond-ai/edubridge-api.yaml put /edubridge/subject-track/groups
Updates an existing subject track group by replacing all tracks in the group. The group is identified by subject, grade, currentCourseId, and isGlobal. All existing tracks in the group will be deleted and new tracks created with the specified configuration. Supports changing the course and organization list for the group.
# Get users by exclusive role
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/edubridge/users/get-users-by-exclusive-role
/openapi/beyond-ai/edubridge-api.yaml get /edubridge/users/
Returns all users who have exclusively the specified role.
This endpoint filters users to only return those who have one or more instances of the specified role
but no other types of roles. For example, if a user has both "student" and "guide" roles,
they will not be returned when filtering for "student".
The response uses the same format as the OneRoster users endpoint.
# Get all Line Items
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/line-items-management/get-all-line-items
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/lineItems/
Get all of the Line Items on the service provider.
# Create a Result
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/results-management/create-a-result
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/gradebook/v1p2/results/
To create a new result. The request body must include a `result` object with the following required fields: `lineItem` (with sourcedId), `student` (with sourcedId), `scoreStatus`, and `scoreDate`. The responding system must return the set of sourcedIds that have been allocated to the newly created result records.
# Delete a Result
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/results-management/delete-a-result
/openapi/beyond-ai/oneroster-api.yaml delete /ims/oneroster/gradebook/v1p2/results/{sourcedId}
Perform a soft delete on a specific Result on the service provider. This operation changes the status of the Result to 'tobedeleted'.
# Get a Result
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/results-management/get-a-result
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/results/{sourcedId}
Get a specific result on the service provider. If the corresponding record cannot be located, the api will return a 404 error code and message 'Result not found.'
# Get all Results
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/results-management/get-all-results
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/results/
Get all of the results on the service provider.
# Update or Create a Result
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/results-management/update-or-create-a-result
/openapi/beyond-ai/oneroster-api.yaml put /ims/oneroster/gradebook/v1p2/results/{sourcedId}
To update an existing result or create a new one if it doesn't exist. The sourcedId for the record is supplied by the requesting system.
# Create a new School
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/schools-management/create-a-new-school
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/rostering/v1p2/schools/
To create a new School. The responding system must return the set of sourcedIds that have been allocated to the newly created school record.
# Create Line Items for a School
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/schools-management/create-line-items-for-a-school
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/gradebook/v1p2/schools/{sourcedId}/lineItems
To create a set of lineItems for a specific school. The responding system must return the set of sourcedIds that have been allocated to the newly created lineItem records. If the corresponding record cannot be located, the api will return a 404 error code and message 'School not found.'
# Delete a School
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/schools-management/delete-a-school
/openapi/beyond-ai/oneroster-api.yaml delete /ims/oneroster/rostering/v1p2/schools/{sourcedId}
Perform a soft delete on a specific School on the service provider. This operation changes the status of the School to 'tobedeleted'.
# Get a specific School
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/schools-management/get-a-specific-school
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/schools/{sourcedId}
Get a specific School on the service provider. If the corresponding record cannot be located, the api will return a 404 error code and message 'School not found.'
# Get all Classes for a School
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/schools-management/get-all-classes-for-a-school
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/schools/{schoolSourcedId}/classes
To get all Classes for a School on the service provider. If the specified school cannot be identified within the service provider, the api will return a 404 error code and message 'School not found.'
# Get all Courses for a School
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/schools-management/get-all-courses-for-a-school
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/schools/{schoolSourcedId}/courses
To get all Courses for a School on the service provider. If the specified school cannot be identified within the service provider, the api will return a 404 error code and message 'School not found.'
# Get all Enrollments for a School
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/schools-management/get-all-enrollments-for-a-school
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/schools/{schoolSourcedId}/enrollments
To get all Enrollments for a School on the service provider. If the specified school cannot be identified within the service provider, the api will return a 404 error code and message 'School not found.'
# Get all Schools
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/schools-management/get-all-schools
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/schools/
To get all Schools on the service provider.
# Get all Students for a School
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/schools-management/get-all-students-for-a-school
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/schools/{schoolSourcedId}/students
To get all Students for a School on the service provider.
# Get all teachers for a school
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/schools-management/get-all-teachers-for-a-school
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/schools/{schoolSourcedId}/teachers
To get all Teachers for a School on the service provider. If the specified school cannot be identified within the service provider, the api will return a 404 error code and message 'School not found.'
# Get all Terms for a School
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/schools-management/get-all-terms-for-a-school
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/schools/{schoolSourcedId}/terms
To get all Terms for a School on the service provider. If the specified school cannot be identified within the service provider, the api will return a 404 error code and message 'School not found.'
# Get Enrollments for a specific Class in a School
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/schools-management/get-enrollments-for-a-specific-class-in-a-school
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/schools/{schoolSourcedId}/classes/{classSourcedId}/enrollments
To get all Enrollments for a Class in a School on the service provider. If the specified school and/or class cannot be identified within the service provider, the api will return a 404 error code and message 'School or class not found.'
# Get Line Items for a School
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/schools-management/get-line-items-for-a-school
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/schools/{sourcedId}/lineItems
Get the set of lineItems on the service provider for a specific school. If the corresponding record cannot be located, the api will return a 404 error code and message 'School not found.'
# Get Score Scales for a School
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/schools-management/get-score-scales-for-a-school
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/schools/{sourcedId}/scoreScales
Get the set of scoreScales on the service provider for a specific school. If the corresponding record cannot be located, the api will return a 404 error code and message 'School not found.'
# Get Students for a specific Class in a School
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/schools-management/get-students-for-a-specific-class-in-a-school
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/schools/{schoolSourcedId}/classes/{classSourcedId}/students
To get all Students for a Class in a School on the service provider. If the specified school and/or class cannot be identified within the service provider, the api will return a 404 error code and message 'School or class not found.'
# Get Teachers for a specific Class in a School
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/schools-management/get-teachers-for-a-specific-class-in-a-school
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/schools/{schoolSourcedId}/classes/{classSourcedId}/teachers
To get all Teachers for a Class in a School on the service provider. If the specified school and/or class cannot be identified within the service provider, the api will return a 404 error code and message 'School or class not found.'
# Update a School
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/schools-management/update-a-school
/openapi/beyond-ai/oneroster-api.yaml put /ims/oneroster/rostering/v1p2/schools/{sourcedId}
To update an existing School. The sourcedId for the record to be updated is supplied by the requesting system.
# Create a Score Scale
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/score-scales-management/create-a-score-scale
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/gradebook/v1p2/scoreScales/
To create a new scoreScale. The responding system must return the set of sourcedIds that have been allocated to the newly created scoreScale records.
# Delete a Score Scale
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/score-scales-management/delete-a-score-scale
/openapi/beyond-ai/oneroster-api.yaml delete /ims/oneroster/gradebook/v1p2/scoreScales/{sourcedId}
Perform a soft delete on a specific Score Scale on the service provider. This operation changes the status of the Score Scale to 'tobedeleted'.
# Get a Score Scale
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/score-scales-management/get-a-score-scale
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/scoreScales/{sourcedId}
Get a specific scoreScale on the service provider.
# Get all Score Scales
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/score-scales-management/get-all-score-scales
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/scoreScales/
Get all of the ScoreScales on the service provider.
# Update or Create a Score Scale
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/score-scales-management/update-or-create-a-score-scale
/openapi/beyond-ai/oneroster-api.yaml put /ims/oneroster/gradebook/v1p2/scoreScales/{sourcedId}
To update an existing scoreScale or create a new one if it doesn't exist. The sourcedId for the record is supplied by the requesting system.
# Create an Assessment Line Item
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/assessment-line-items-management/create-an-assessment-line-item
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/gradebook/v1p2/assessmentLineItems/
To create an Assessment Line Item. The responding system must return the set of sourcedIds that have been allocated to the newly created assessmentLineItem record. A 'title' MUST be provided when creating an assessmentLineItem.
# Delete an Assessment Line Item
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/assessment-line-items-management/delete-an-assessment-line-item
/openapi/beyond-ai/oneroster-api.yaml delete /ims/oneroster/gradebook/v1p2/assessmentLineItems/{sourcedId}
Perform a soft delete on a specific Assessment Line Item on the service provider. This operation changes the status of the Assessment Line Item to 'tobedeleted'.
# Get all Assessment Line Items
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/assessment-line-items-management/get-all-assessment-line-items
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/assessmentLineItems/
Get all of the Assessment Line Items on the service provider.
# Get an Assessment Line Item
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/assessment-line-items-management/get-an-assessment-line-item
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/assessmentLineItems/{sourcedId}
Get a specific Assessment Line Item on the service provider. If the corresponding record cannot be located, the api will return a 404 error code and message 'Assessment line item not found.'
# Partially Update an Assessment Line Item
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/assessment-line-items-management/partially-update-an-assessment-line-item
/openapi/beyond-ai/oneroster-api.yaml patch /ims/oneroster/gradebook/v1p2/assessmentLineItems/{sourcedId}
To partially update an existing Assessment Line Item with metadata merging support.
# Update or Create an Assessment Line Item
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/assessment-line-items-management/update-or-create-an-assessment-line-item
/openapi/beyond-ai/oneroster-api.yaml put /ims/oneroster/gradebook/v1p2/assessmentLineItems/{sourcedId}
To update an existing Assessment Line Item or create a new one if it doesn't exist. The sourcedId for the record is supplied by the requesting system.
# Create an Assessment Result
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/assessment-results-management/create-an-assessment-result
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/gradebook/v1p2/assessmentResults/
To create an Assessment Result. The responding system must return the set of sourcedIds that have been allocated to the newly created assessmentResult record. An Assessment Line Item sourcedId and Student sourcedId MUST be provided when creating an assessmentResult.
# Delete an Assessment Result
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/assessment-results-management/delete-an-assessment-result
/openapi/beyond-ai/oneroster-api.yaml delete /ims/oneroster/gradebook/v1p2/assessmentResults/{sourcedId}
Perform a soft delete on a specific Assessment Result on the service provider. This operation changes the status of the Assessment Result to 'tobedeleted'.
# Get all Assessment Results
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/assessment-results-management/get-all-assessment-results
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/assessmentResults/
Get all of the Assessment Results on the service provider.
# Get an Assessment Result
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/assessment-results-management/get-an-assessment-result
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/assessmentResults/{sourcedId}
Get a specific Assessment Result on the service provider. If the corresponding record cannot be located, the api will return a 404 error code and message 'Assessment result not found.'
# Partially Update an Assessment Result
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/assessment-results-management/partially-update-an-assessment-result
/openapi/beyond-ai/oneroster-api.yaml patch /ims/oneroster/gradebook/v1p2/assessmentResults/{sourcedId}
To partially update an existing Assessment Result with metadata merging support.
# Update or Create an Assessment Result
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/assessment-results-management/update-or-create-an-assessment-result
/openapi/beyond-ai/oneroster-api.yaml put /ims/oneroster/gradebook/v1p2/assessmentResults/{sourcedId}
To update an existing Assessment Result or create a new one if it doesn't exist. The sourcedId for the record is supplied by the requesting system.
# Create a Category
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/categories-management/create-a-category
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/gradebook/v1p2/categories/
To create a new Category. The responding system must return the set of sourcedIds that have been allocated to the newly created category record. A 'title' MUST be provided when creating a category.
# Delete a Category
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/categories-management/delete-a-category
/openapi/beyond-ai/oneroster-api.yaml delete /ims/oneroster/gradebook/v1p2/categories/{sourcedId}
Perform a soft delete on a specific Category on the service provider. This operation changes the status of the Category to 'tobedeleted'.
# Get a Category
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/categories-management/get-a-category
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/categories/{sourcedId}
Get a specific category on the service provider. If the corresponding record cannot be located, the api will return a 404 error code and message 'Category not found.'
# Get all Categories
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/categories-management/get-all-categories
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/categories/
Get all of the Line Item Categories on the service provider.
# Update or Create a Category
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/categories-management/update-or-create-a-category
/openapi/beyond-ai/oneroster-api.yaml put /ims/oneroster/gradebook/v1p2/categories/{sourcedId}
To update an existing Category or create a new one if it doesn't exist. The sourcedId for the record is supplied by the requesting system.
# Add a student to a Class
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/classes-management/add-a-student-to-a-class
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/rostering/v1p2/classes/{classSourcedId}/students
Enrolls a student in a specific Class. The responding system must return the set of sourcedIds that have been allocated to the newly created enrollment record.
# Add a teacher to a Class
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/classes-management/add-a-teacher-to-a-class
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/rostering/v1p2/classes/{classSourcedId}/teachers
Enrolls a teacher to a specific Class. The responding system must return the set of sourcedIds that have been allocated to the newly created enrollment record.
# Create a new Class
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/classes-management/create-a-new-class
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/rostering/v1p2/classes/
To create a new Class. The responding system must return the set of sourcedIds that have been allocated to the newly created class record.
# Create Line Items for a Class
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/classes-management/create-line-items-for-a-class
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/gradebook/v1p2/classes/{classSourcedId}/lineItems
To create a set of lineItems for a specific class. The responding system must return the set of sourcedIds that have been allocated to the newly created lineItem records. If the corresponding record cannot be located, the api will return a 404 error code and message 'Class not found.'
# Create Results for an Academic Session for a Class
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/classes-management/create-results-for-an-academic-session-for-a-class
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/gradebook/v1p2/classes/{classSourcedId}/academicSessions/{academicSessionSourcedId}/results
To create a set of results for a specific academic session and specific class. The responding system must return the set of sourcedIds that have been allocated to the newly created result records. If the corresponding record cannot be located, the api will return a 404 error code and message 'Class or academic session not found.'
# Delete a Class
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/classes-management/delete-a-class
/openapi/beyond-ai/oneroster-api.yaml delete /ims/oneroster/rostering/v1p2/classes/{sourcedId}
Perform a soft delete on a specific Class on the service provider. This operation changes the status of the Class to 'tobedeleted'.
# Get a specific class
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/classes-management/get-a-specific-class
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/classes/{sourcedId}
Get a specific Class on the service provider. If the corresponding record cannot be located, the api will return a 404 error code and message 'Class not found.'
# Get all Classes
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/classes-management/get-all-classes
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/classes/
To get all Classes on the service provider.
# Get Categories for a Class
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/classes-management/get-categories-for-a-class
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/classes/{sourcedId}/categories
Get the set of categories on the service provider for a specific class. If the corresponding record cannot be located, the api will return a 404 error code and message 'Class not found.'
# Get Line Items for a Class
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/classes-management/get-line-items-for-a-class
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/classes/{sourcedId}/lineItems
Get the set of lineItems on the service provider for a specific class. If the corresponding record cannot be located, the api will return a 404 error code and message 'Class not found.'
# Get Results for a Class
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/classes-management/get-results-for-a-class
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/classes/{sourcedId}/results
Get the set of results on the service provider for a specific class. If the corresponding record cannot be located, the api will return a 404 error code and message 'Class not found.'
# Get Results for a Line Item for a Class
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/classes-management/get-results-for-a-line-item-for-a-class
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/classes/{classSourcedId}/lineItems/{lineItemSourcedId}/results
Get the set of results on the service provider for a specific lineItem and for a specific class. If the corresponding record cannot be located, the api will return a 404 error code and message 'Class or line item not found.'
# Get Results for a Student for a Class
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/classes-management/get-results-for-a-student-for-a-class
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/classes/{classSourcedId}/students/{studentSourcedId}/results
Get the set of results on the service provider for a specific student and for a specific class. If the corresponding record cannot be located, the api will return a 404 error code and message 'Class or student not found.'
# Get Score Scales for a Class
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/classes-management/get-score-scales-for-a-class
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/classes/{sourcedId}/scoreScales
Get the set of scoreScales on the service provider for a specific class. If the corresponding record cannot be located, the api will return a 404 error code and message 'Class not found.'
# Get students for a Class
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/classes-management/get-students-for-a-class
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/classes/{classSourcedId}/students
To get all students enrolled in a specific Class. If the corresponding record cannot be located, the api will return a 404 error code and message 'Class not found.'
# Get teachers for a Class
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/classes-management/get-teachers-for-a-class
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/classes/{classSourcedId}/teachers
To get all teachers assigned to a specific Class. If the corresponding record cannot be located, the api will return a 404 error code and message 'Class not found.'
# Update a Class
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/classes-management/update-a-class
/openapi/beyond-ai/oneroster-api.yaml put /ims/oneroster/rostering/v1p2/classes/{sourcedId}
To update an existing Class. The sourcedId for the record to be updated is supplied by the requesting system.
# Create a Course
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/courses-management/create-a-course
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/rostering/v1p2/courses/
To create a new Course. The responding system must return the set of sourcedIds that have been allocated to the newly created course record.
# Delete a Course
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/courses-management/delete-a-course
/openapi/beyond-ai/oneroster-api.yaml delete /ims/oneroster/rostering/v1p2/courses/{sourcedId}
Perform a soft delete on a specific Course on the service provider. This operation changes the status of the Course to 'tobedeleted'.
# Get a specific Course
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/courses-management/get-a-specific-course
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/courses/{sourcedId}
Get a specific Course on the service provider. If the corresponding record cannot be located, the api will return a 404 error code and message 'Course not found.'
# Get all Component Resources
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/courses-management/get-all-component-resources
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/courses/component-resources
To get all Component Resources on the service provider.
# Get All Courses
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/courses-management/get-all-courses
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/courses/
To get all Courses on the service provider.
# Get Classes for a Course
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/courses-management/get-classes-for-a-course
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/courses/{courseSourcedId}/classes
To get all Classes associated with a specific Course. If the corresponding record cannot be located, the api will return a 404 error code and message 'Course not found.'
# Update a Course
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/courses-management/update-a-course
/openapi/beyond-ai/oneroster-api.yaml put /ims/oneroster/rostering/v1p2/courses/{sourcedId}
To update an existing Course. The sourcedId for the record to be updated is supplied by the requesting system.
# Create a new Demographic record
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/demographics-management/create-a-new-demographic-record
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/rostering/v1p2/demographics/
To create a new Demographic record. The responding system must return the set of sourcedIds that have been allocated to the newly created demographic record.
# Delete a Demographic record
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/demographics-management/delete-a-demographic-record
/openapi/beyond-ai/oneroster-api.yaml delete /ims/oneroster/rostering/v1p2/demographics/{sourcedId}
Perform a soft delete on a specific Demographic record on the service provider. This operation changes the status of the Demographic record to 'tobedeleted'.
# Get a specific Demographic record
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/demographics-management/get-a-specific-demographic-record
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/demographics/{sourcedId}
Get a specific Demographic record on the service provider. If the corresponding record cannot be located, the api will return a 404 error code and message 'Demographics record not found.'
# Get all Demographic records
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/demographics-management/get-all-demographic-records
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/demographics/
To get all Demographic records on the service provider.
# Update a Demographic record
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/demographics-management/update-a-demographic-record
/openapi/beyond-ai/oneroster-api.yaml put /ims/oneroster/rostering/v1p2/demographics/{sourcedId}
To update an existing Demographic record. The sourcedId for the record to be updated is supplied by the requesting system.
# Create a new Enrollment
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/enrollments-management/create-a-new-enrollment
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/rostering/v1p2/enrollments/
To create a new Enrollment. The responding system must return the set of sourcedIds that have been allocated to the newly created enrollment record.
# Delete an Enrollment
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/enrollments-management/delete-an-enrollment
/openapi/beyond-ai/oneroster-api.yaml delete /ims/oneroster/rostering/v1p2/enrollments/{sourcedId}
Perform a soft delete on a specific Enrollment on the service provider. This operation changes the status of the Enrollment to 'tobedeleted'.
# Get a specific Enrollment
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/enrollments-management/get-a-specific-enrollment
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/enrollments/{sourcedId}
Get a specific Enrollment on the service provider. If the corresponding record cannot be located, the api will return a 404 error code and message 'Enrollment not found.'
# Get all Enrollments
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/enrollments-management/get-all-enrollments
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/enrollments/
To get all Enrollments on the service provider.
# Partially Update an Enrollment
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/enrollments-management/partially-update-an-enrollment
/openapi/beyond-ai/oneroster-api.yaml patch /ims/oneroster/rostering/v1p2/enrollments/{sourcedId}
To partially update an existing Enrollment with metadata merging support. The sourcedId for the record to be updated is supplied by the requesting system. Metadata will be merged with existing values.
# Update an Enrollment
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/enrollments-management/update-an-enrollment
/openapi/beyond-ai/oneroster-api.yaml put /ims/oneroster/rostering/v1p2/enrollments/{sourcedId}
To update an existing Enrollment. The sourcedId for the record to be updated is supplied by the requesting system.
# Create a new Grading Period
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/grading-periods-management/create-a-new-grading-period
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/rostering/v1p2/gradingPeriods/
To create a new Grading Period. The responding system must return the set of sourcedIds that have been allocated to the newly created gradingPeriod record.
# Delete a Grading Period
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/grading-periods-management/delete-a-grading-period
/openapi/beyond-ai/oneroster-api.yaml delete /ims/oneroster/rostering/v1p2/gradingPeriods/{sourcedId}
Perform a soft delete on a specific Grading Period on the service provider. This operation changes the status of the Grading Period to 'tobedeleted'.
# Get a specific Grading Period
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/grading-periods-management/get-a-specific-grading-period
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/gradingPeriods/{sourcedId}
Get a specific Grading Period on the service provider. If the corresponding record cannot be located, the api will return a 404 error code and message 'Grading period not found.'
# Get all Grading Periods
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/grading-periods-management/get-all-grading-periods
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/gradingPeriods/
To get all Grading Periods on the service provider.
# Update a Grading Period
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/grading-periods-management/update-a-grading-period
/openapi/beyond-ai/oneroster-api.yaml put /ims/oneroster/rostering/v1p2/gradingPeriods/{sourcedId}
To update an existing Grading Period. The sourcedId for the record to be updated is supplied by the requesting system.
# Create a Line Item
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/line-items-management/create-a-line-item
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/gradebook/v1p2/lineItems/
To create a new Line Item. The responding system must return the set of sourcedIds that have been allocated to the newly created Line Item records.
# Create a Result for a Line Item
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/line-items-management/create-a-result-for-a-line-item
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/gradebook/v1p2/lineItems/{sourcedId}/results
To create a new result for a specific Line Item. The responding system must return the set of sourcedIds that have been allocated to the newly created result records. If the corresponding record cannot be located, the api will return a 404 error code and message 'Line item not found.'
# Delete a Line Item
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/line-items-management/delete-a-line-item
/openapi/beyond-ai/oneroster-api.yaml delete /ims/oneroster/gradebook/v1p2/lineItems/{sourcedId}
Perform a soft delete on a specific Line Item on the service provider. This operation changes the status of the Line Item to 'tobedeleted'.
# Get a Line Item
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/line-items-management/get-a-line-item
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/gradebook/v1p2/lineItems/{sourcedId}
Get a specific Line Item on the service provider. If the corresponding record cannot be located, the api will return a 404 error code and message 'Line item not found.'
# Update or Create a Line Item
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/line-items-management/update-or-create-a-line-item
/openapi/beyond-ai/oneroster-api.yaml put /ims/oneroster/gradebook/v1p2/lineItems/{sourcedId}
To update an existing Line Item or create a new one if it doesn't exist. The sourcedId for the record is supplied by the requesting system.
# Create an Organization
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/organizations-management/create-an-organization
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/rostering/v1p2/orgs/
To create a new Organization. The responding system must return the set of sourcedIds that have been allocated to the newly created org record.
# Delete an Organization
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/organizations-management/delete-an-organization
/openapi/beyond-ai/oneroster-api.yaml delete /ims/oneroster/rostering/v1p2/orgs/{sourcedId}
Perform a soft delete on a specific Organization on the service provider. This operation changes the status of the Organization to 'tobedeleted'.
# Get a specific Organization
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/organizations-management/get-a-specific-organization
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/orgs/{sourcedId}
Get a specific Organization on the service provider. If the corresponding record cannot be located, the api will return a 404 error code and message 'Organization not found.'
# Get all Organizations
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/organizations-management/get-all-organizations
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/orgs/
To get all Organizations on the service provider.
# Update an Organization
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/organizations-management/update-an-organization
/openapi/beyond-ai/oneroster-api.yaml put /ims/oneroster/rostering/v1p2/orgs/{sourcedId}
To update an existing Organization. The sourcedId for the record to be updated is supplied by the requesting system.
# Create a new Resource
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/resources-management/create-a-new-resource
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/resources/v1p2/resources/
To create a new resource. The responding system must return the set of sourcedIds that have been allocated to the newly created resource record.
# Delete a resource
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/resources-management/delete-a-resource
/openapi/beyond-ai/oneroster-api.yaml delete /ims/oneroster/resources/v1p2/resources/{sourcedId}
Perform a soft delete on a specific resource. This operation changes the status of the resource to 'tobedeleted'.
# Export Resource to Common Cartridge
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/resources-management/export-resource-to-common-cartridge
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/resources/v1p2/resources/export/{sourceId}
Export a resource to Common Cartridge (.imscc) format for import into LMS systems.
# Get a specific Resource
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/resources-management/get-a-specific-resource
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/resources/v1p2/resources/{sourcedId}
To get a specific resource by sourcedId. If the corresponding record cannot be located, the api will return a 404 error code and message 'Resource not found.'
# Get all Resources
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/resources-management/get-all-resources
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/resources/v1p2/resources/
To get a collection of resources that exist on the service provider.
# Get resources for a class
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/resources-management/get-resources-for-a-class
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/resources/v1p2/resources/classes/{classSourcedId}/resources
To get the collection of resources available to a specific class. If the corresponding record cannot be located, the api will return a 404 error code and message 'Class not found.'
# Get resources for a course
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/resources-management/get-resources-for-a-course
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/resources/v1p2/resources/courses/{courseSourcedId}/resources
To get the collection of resources assigned to a specific course. If the corresponding record cannot be located, the api will return a 404 error code and message 'Course not found.'
# Get resources for a user
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/resources-management/get-resources-for-a-user
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/resources/v1p2/resources/users/{userSourcedId}/resources
To get the collection of resources available to a specific user. If the corresponding record cannot be located, the api will return a 404 error code and message 'User not found.'
# Update an existing Resource
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/resources-management/update-an-existing-resource
/openapi/beyond-ai/oneroster-api.yaml put /ims/oneroster/resources/v1p2/resources/{sourcedId}
To update an existing resource. The sourcedId for the record to be updated is supplied by the requesting system.
# Get a specific Student
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/students-management/get-a-specific-student
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/students/{sourcedId}
To get a specific Student on the service provider. If the corresponding record cannot be located, the api will return a 404 error code and message 'Student not found.'
# Get all Students
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/students-management/get-all-students
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/students/
To get all Students on the service provider.
# Get Classes for a Student
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/students-management/get-classes-for-a-student
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/students/{studentSourcedId}/classes
To get the set of Classes related to a specific Student. If the specified student cannot be identified within the service provider, the api will return a 404 error code and message 'Student not found.'
# Get a specific Teacher
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/teachers-management/get-a-specific-teacher
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/teachers/{sourcedId}
To get a specific Teacher on the service provider. If the corresponding record cannot be located, the api will return a 404 error code and message 'Teacher not found.'
# Get all Teachers
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/teachers-management/get-all-teachers
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/teachers/
To get all Teachers on the service provider.
# Get Classes for a Teacher
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/teachers-management/get-classes-for-a-teacher
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/teachers/{teacherSourcedId}/classes
To get the set of Classes a Teacher is enrolled in. If the specified teacher cannot be identified within the service provider, the api will return a 404 error code and message 'Teacher not found.'
# Create a new Grading Period for a Term
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/terms-management/create-a-new-grading-period-for-a-term
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/rostering/v1p2/terms/{termSourcedId}/gradingPeriods
To create a new Grading Period for a Term. A Grading Period is a type of Academic Session. The responding system must return the set of sourcedIds that have been allocated to the newly created academicSession record.
# Get a specific Term
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/terms-management/get-a-specific-term
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/terms/{sourcedId}
To get a specific Term on the service provider.
# Get all Terms
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/terms-management/get-all-terms
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/terms/
To get all Terms on the service provider.
# Get Classes for a Term
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/terms-management/get-classes-for-a-term
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/terms/{termSourcedId}/classes
To get the set of Classes related to a specific Term. If the specified term cannot be identified within the service provider, the api will return a 404 error code and message 'Term not found.'
# Get Grading Periods for a Term
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/terms-management/get-grading-periods-for-a-term
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/terms/{termSourcedId}/gradingPeriods
To get the set of Grading Periods related to a specific Term. If the specified Term cannot be identified within the service provider, the api will return a 404 error code and message 'Term not found.'
# Add an agent for a user
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/users-management/add-an-agent-for-a-user
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/rostering/v1p2/users/{userId}/agents
Add an agent for a user
# Create a new User
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/users-management/create-a-new-user
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/rostering/v1p2/users/
To create a new User on the service provider. The responding system must return the set of sourcedIds that have been allocated to the newly created user record.
# Decrypt a user credential
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/users-management/decrypt-a-user-credential
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/rostering/v1p2/users/{userId}/credentials/{credentialId}/decrypt
Decrypt and return the password for a specific user credential.
# Delete a User
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/users-management/delete-a-user
/openapi/beyond-ai/oneroster-api.yaml delete /ims/oneroster/rostering/v1p2/users/{sourcedId}
Perform a soft delete on a specific User on the service provider. This operation changes the status of the User to 'tobedeleted'.
# Delete an agent for a user
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/users-management/delete-an-agent-for-a-user
/openapi/beyond-ai/oneroster-api.yaml delete /ims/oneroster/rostering/v1p2/users/{userId}/agents/{agentSourcedId}
Delete an agent for a user
# Get a specific User
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/users-management/get-a-specific-user
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/users/{sourcedId}
To get a specific User on the service provider.
# Get a specific User with demographics
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/users-management/get-a-specific-user-with-demographics
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/users/{sourcedId}/demographics
To get a specific User with demographics on the service provider. If the corresponding record cannot be located, the api will return a 404 error code and message 'User not found.'
# Get agents for a user
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/users-management/get-agents-for-a-user
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/users/{userId}/agents
Get agents for a user
# Get all Users
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/users-management/get-all-users
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/users/
To get all Users on the service provider.
# Get Classes for a User
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/users-management/get-classes-for-a-user
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/users/{userSourcedId}/classes
To get the set of Classes a User is enrolled in.
# Get users this user is an agent for
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/users-management/get-users-this-user-is-an-agent-for
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/users/{userId}/agentFor
Get users this user is an agent for (eg. parents getting the children list)
# Register student credentials for third-party applications
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/users-management/register-student-credentials-for-third-party-applications
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/rostering/v1p2/users/{userId}/credentials
Register student credentials for third-party applications
# Update an existing User
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/users-management/update-an-existing-user
/openapi/beyond-ai/oneroster-api.yaml put /ims/oneroster/rostering/v1p2/users/{sourcedId}
To update an existing User on the service provider. The sourcedId for the record to be updated is supplied by the requesting system.
# Create an Academic Session
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/academic-sessions-management/create-an-academic-session
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/rostering/v1p2/academicSessions/
To create a new academic session. The responding system must return the set of sourcedIds that have been allocated to the newly created academicSession record.
# Delete an Academic Session
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/academic-sessions-management/delete-an-academic-session
/openapi/beyond-ai/oneroster-api.yaml delete /ims/oneroster/rostering/v1p2/academicSessions/{sourcedId}
Perform a soft delete on a specific Academic Session on the service provider. This operation changes the status of the Academic Session to 'tobedeleted'.
# Get a specific Academic Session
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/academic-sessions-management/get-a-specific-academic-session
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/academicSessions/{sourcedId}
Get a specific Academic Session on the service provider. If the corresponding record cannot be located, the api will return a 404 error code and message 'Academic session not found.'
# Get all Academic Sessions
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/academic-sessions-management/get-all-academic-sessions
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/academicSessions/
To get all Academic Sessions on the service provider.
# Update an Academic Session
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/academic-sessions-management/update-an-academic-session
/openapi/beyond-ai/oneroster-api.yaml put /ims/oneroster/rostering/v1p2/academicSessions/{sourcedId}
To update an existing Academic Session. The sourcedId for the record to be updated is supplied by the requesting system.
# Create Component Resource
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/courses-management/create-component-resource
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/rostering/v1p2/courses/component-resources
To create a new Component Resource. The responding system must return the set of sourcedIds that have been allocated to the newly created componentResource record.
# Create Course Component
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/courses-management/create-course-component
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/rostering/v1p2/courses/components
Used when creating a new course component or module
# Create Course Structure from QTI Tests
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/courses-management/create-course-structure-from-qti-tests
/openapi/beyond-ai/oneroster-api.yaml post /ims/oneroster/rostering/v1p2/courses/structure
Create a course structure from QTI tests
# Delete a Component Resource
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/courses-management/delete-a-component-resource
/openapi/beyond-ai/oneroster-api.yaml delete /ims/oneroster/rostering/v1p2/courses/component-resources/{sourcedId}
Perform a soft delete on a specific Component Resource on the service provider. This operation changes the status of the Component Resource to 'tobedeleted'.
# Delete a Course Component
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/courses-management/delete-a-course-component
/openapi/beyond-ai/oneroster-api.yaml delete /ims/oneroster/rostering/v1p2/courses/components/{sourcedId}
Perform a soft delete on a specific Course Component on the service provider. This operation changes the status of the Course Component to 'tobedeleted'.
# Get a specific Component Resource
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/courses-management/get-a-specific-component-resource
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/courses/component-resources/{sourcedId}
Get a specific Component Resource on the service provider. If the corresponding record cannot be located, the api will return a 404 error code and message 'Component Resource not found.'
# Get a specific Course Component
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/courses-management/get-a-specific-course-component
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/courses/components/{sourcedId}
Get a specific Course Component on the service provider. If the corresponding record cannot be located, the api will return a 404 error code and message 'Course Component not found.'
# Get all Course Components
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/courses-management/get-all-course-components
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/courses/components
To get all Course Components on the service provider.
# Get component resource identity without backing roster content
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/courses-management/get-component-resource-identity-without-backing-roster-content
/openapi/beyond-ai/oneroster-api.yaml get /ims/oneroster/rostering/v1p2/courses/component-resources/{sourcedId}/identity
# Update a Component Resource
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/courses-management/update-a-component-resource
/openapi/beyond-ai/oneroster-api.yaml put /ims/oneroster/rostering/v1p2/courses/component-resources/{sourcedId}
To update an existing Component Resource. The sourcedId for the record to be updated is supplied by the requesting system.
# Update a Course Component
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/oneroster/courses-management/update-a-course-component
/openapi/beyond-ai/oneroster-api.yaml put /ims/oneroster/rostering/v1p2/courses/components/{sourcedId}
To update an existing Course Component. The sourcedId for the record to be updated is supplied by the requesting system.
# Create an External Placement Test
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/course-mastery/create-an-external-placement-test
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/createExternalPlacementTest
Creates or updates a ComponentResource to act as a Placement Test lesson in a course.
This allows integrating with external test-taking platforms (like Edulastic) for content delivery.
The endpoint creates or updates (if they already exist) the following entities:
- A CourseComponent for the course to hold the Placement Test lesson
- A Resource with lessonType = "placement" and the external service details as metadata
- A ComponentResource acting as the Placement Test lesson
A test assignment is mandatory in order to obtain access credentials for this test on the external platform, as well as to obtain the IDs necessary for fetching test results later on:
- For test assignments, use the "makeExternalTestAssignment" endpoint.
- For test results retrieval, use the "importExternalTestAssignmentResults" endpoint.
If a 'courseIdOnFail' parameter is supplied, its Course's sourcedId will be used to automatically enroll the student when the placement test is completed with a score below 89.5%. When the parameter is omitted (or set to null), no automatic enrollment will happen.
This request fails if:
- The 'course' provided does not exist, or a non-null 'courseIdOnFail' references a non-existent course
- An existing Placement Test lesson in the course, targeting the same grade, has a different toolProvider than the one provided (need to perform an update to the Resource first, altering the "toolProvider", before trying again)
A 'Lesson' in this context is a ComponentResource object which has a Resource object with lessonType = "placement" associated with it.
# Create an External TestOut
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/course-mastery/create-an-external-testout
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/createExternalTestOut
Creates or updates a ComponentResource to act as a TestOut lesson in a course.
This allows integrating with external test-taking platforms (like Edulastic) for content delivery.
The endpoint creates or updates (if they already exist) the following entities:
- A CourseComponent for the course to hold the TestOut lesson
- A Resource with lessonType = "test-out" and the external service details as metadata
- A ComponentResource acting as the TestOut lesson
A test assignment is mandatory in order to obtain access credentials for this test on the external platform, as well as to obtain the IDs necessary for fetching test results later on:
- For test assignments, use the "makeExternalTestAssignment" endpoint.
- For test results retrieval, use the "importExternalTestAssignmentResults" endpoint.
This request fails if:
- The course provided does not exist
- An existing TestOut lesson in the course has a different toolProvider than the one provided (need to perform an update to the Resource first, altering the "toolProvider", before trying again)
A 'Lesson' in this context is a ComponentResource object which has a Resource object with lessonType = "test-out" associated with it.
# Create an Internal Test
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/course-mastery/create-an-internal-test
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/createInternalTest
Creates or updates a ComponentResource to act as an internal test lesson in a course.
This allows creating tests using internal QTI resources or assessment banks with multiple QTI resources.
The endpoint creates or updates (if they already exist) the following entities:
- A CourseComponent for the course to hold the test lesson
- One or more Resources with type = "qti" for individual tests, or type = "assessment-bank" for test banks
- A ComponentResource acting as the test lesson
Supports two test types:
- Regular QTI test: Creates a single QTI resource
- Assessment Bank: Creates multiple QTI resources and wraps them in an assessment bank
For test-out and placement lessons, this will update existing tests of the same type.
For other lesson types (quiz, unit-test, pp-100), it will create new lessons in the course structure.
A 'Lesson' in this context is a ComponentResource object which has a Resource object associated with it.
# Import external test assignment results
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/course-mastery/import-external-test-assignment-results
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/importExternalTestAssignmentResults
Retrieves and stores the results of the external test assignment:
- Applies to 'test-out', 'placement', and 'unit-test' lessons.
This logic changes depending on the stored "toolProvider" of the lesson:
- For "edulastic":
- If the lesson is already finalized, no data import is performed.
- If the lesson is not finalized, this will start populating the test and question results with available data, including question scores and feedback. The test will then be deemed finalized when all questions have been answered and the test grade is "GRADED".
- For "mastery-track":
- If the lesson is already finalized, no data import is performed.
- If the lesson is not finalized and powerpath detects the write-back of results was done, this will process the available test and question results data, including question scores and feedback. The test will then be deemed finalized when the scoreStatus is "fully graded" and the masteryTrackProcessed flag is set to 'true'.
Will fail if:
- The lesson is not an external "test-out", "placement", or "unit-test", or the student does not exist
- Credentials for data consumption are not available in the test result of this student (meaning a previous test assignment was not made)
- Any other problem on the Edulastic or MasteryTrack API being used that may happen
The actual test results can be retrieved by using the "getAssessmentProgress" endpoint.
Notice this may perform a course enrollment for the student if the lesson is a placement test or test-out, and the respective subject and grade are mapped to the Subject Track with a valid course set. The enrollemnt can be skipped by setting the "skipCourseEnrollment" flag in the makeExternalTestAssignment request.
A 'Lesson' in this context is a ComponentResource object which has a Resource object with lessonType = "test-out", "placement", or "unit-test" associated with it.
# Make external test assignment
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/course-mastery/make-external-test-assignment
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/makeExternalTestAssignment
Makes an external test assignment for the given student:
- Applies to 'test-out', 'placement', and 'unit-test' lessons.
This logic changes depending on the provided "toolProvider" value:
- For "edulastic":
- Authenticates the student with their email on Edulastic
- Assigns the test to the student in Edulastic
- Stores the received "assignmentId" and "classId" in the lesson's AssessmentResult
- Returns the test link, credentials, and IDs of the test for later results consumption
- For "mastery-track":
- Authenticates the student with their email on MasteryTrack
- Assigns the test to the student in MasteryTrack (using the testId in the request or subject+grade from the lesson's Resource metadata)
- Stores the received "assignmentId" in the lesson's AssessmentResult
- Returns the test link, credentials, and IDs of the test
- Waits for a test result write-back to be performed by the MasteryTrack on test end
Will fail if:
- The lesson is not an external "test-out", "placement", or "unit-test", or the student does not exist
- External tool (described in the resource.metadata.toolProvider) is not "edulastic" or "mastery-track"
- Any other problem on the Edulastic or MasteryTrack API being used that may happen
A 'Lesson' in this context is a ComponentResource object which has a Resource object with lessonType = "test-out", "placement", or "unit-test" associated with it.
# Test out
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/course-mastery/test-out
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/testOut
Returns the testOut lesson reference for the given student and course.
- TestOut is a lesson that represents the end-of-course test, covering the entire course content.
- The testOut should be specified by a Resource with metadata.lessonType = "test-out".
Details:
- Returns a null lessonId in case no Resource with metadata.lessonType = "test-out" is found in the course.
- In case student has already taken the TestOut, this will return the "finalized" flag set to true.
- In case this is an external TestOut, also return external access credentials, if available (i.e. test was previously assigned to student).
A 'Lesson' in this context is a ComponentResource object which has a Resource object with metadata.lessonType = "test-out" associated with it.
# Get a hole-filling app id
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/course-sequence/get-a-hole-filling-app-id
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/course-sequence/active/stages/hole-filling/learning-app-id
Get a learning app id of the hole-filling stage of the ACTIVE course sequence.
---
A convenience endpoint that gives back the `learningAppId` of the hole-filling stage by the `grade` and the `subject`.
You have to provide both params in query: like `?grade=5&subject=Math`.
# Get a single course sequence by id
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/course-sequence/get-a-single-course-sequence-by-id
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/course-sequence/{id}
Get a single course sequence details.
---
Returns a single course sequence (`CourseSequence` object). Details contain stages.
# Get a stage after the assessment
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/course-sequence/get-a-stage-after-the-assessment
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/course-sequence/{id}/stages/after/assessment
Get a single stage that comes after the specified assessment in a course sequence.
---
Returns a stage (`CourseSequenceStage` object) that comes after the specified assessment in a course sequence.
You specify assessment in query params like `test_grade=5`.
Most sequences have a single assessment stage per grade, so this parameter
is enough to identify the assessment stage after which you want to get the next stage.
You can specify the test type too like `test_type=placement` or `test_type=test_out`.
If you don't provide `test_type`, it will be considered as `end_of_course`.
`score_pct` must be a finite decimal string from `0` through `100`. Default routing uses
the raw value without rounding. Placement and test-out scores below `59.5` select the
assessed-grade base course, scores in `[59.5, 89.5)` select hole-filling, and scores
at or above `89.5` pass. Every standard non-passing end-of-course score selects hole-filling.
Math G3-G12 and Math Pre-K placement and test-out failures select the assessed-grade base course
instead. A Math Pre-K end-of-course failure returns `409 remediation_unavailable`. The temporary
HS Language rule uses the raw `89.5` hold and keeps its lower-grade fallback. The active Math
sequence implements the policy through G11; G12 returns `404` until its assessment stage exists.
A standard test-out pass selects the next-grade base course. A standard end-of-course pass selects
that course and falls back to the next assessment when no next course exists.
---
**Special Input Cases**
1. FastMath sequence
For FastMath sequence you need to be cautious with `test_type` because this sequence contains
Accuracy Assessment (test_out) and Fluency Assessment (end_of_course) for each grade.
So if you omit this parameter in FastMath, you will get the stage after the Fluency Assessment of the specified grade.
Legacy FastMath sequences need `cqpm` (correct questions per minute) when `test_type`
is `end_of_course` or omitted (default). Legacy FastMath uses both the score percentage
and the grade-specific CQPM threshold for this route.
New FastMath sequences may contain multiple fluency assessments in the same grade. In that case,
provide `grade_assessment_tag` to identify the completed operation-specific assessment and
`is_test_passed=true|false` for the assessment provider's pass/fail decision. Course Sequence
uses the sequence data's internal `ruleSetId` to choose the routing rules.
2. Reading sequence
Reading grades K-2 can contain both a MasteryTrack assessment and a Reading screener assessment.
Use `assessment_source=mastery` (or omit it) for the MasteryTrack result. Use
`assessment_source=literably` for the Reading screener result.
`literably` is a compatibility routing key, not a guaranteed provider name. Reading placement
creates a pending AlphaLiteracy result in OneRoster. For other screener types,
`FF_ARF_READING_SCREENERS` selects AlphaLiteracy or Literably. Without
`assessment_source=literably`, the route resolves the MasteryTrack assessment for that grade
instead of the screener stage.
# Get a stage after the course
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/course-sequence/get-a-stage-after-the-course
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/course-sequence/{id}/stages/after/course
Get a single stage that comes after the specified course in a course sequence.
---
Returns a stage (`CourseSequenceStage` object) that comes after the specified course in a course sequence.
You need to provide the `course_id` of the course after which you want to get the next stage.
You can also provide `grade`.
This is not mandatory, but highly recommended, since the same course may appear in multiple grades.
When you got 204 status code - it means the sequence is ended after the course you provided.
# Get course sequences
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/course-sequence/get-course-sequences
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/course-sequence/
Get the course sequences.
---
Returns a list of all course sequences (`CourseSequenceHead` objects) available in the system.
Heads do not contain stages.
You can filter by status and subject.
To get the active sequence for a subject, use `status=active&subject={subject}`.
# Get stages of a single course sequence
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/course-sequence/get-stages-of-a-single-course-sequence
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/course-sequence/{id}/stages
Get stages of a single course sequence.
---
Returns a list of sequence stages (`CourseSequenceStage` objects) of the specified course sequence.
To get the fist stage only, use `limit=1`.
To filter by grade, use `grade={grade}`.
To get the first stage of the specific grade, use `grade={grade}&limit=1`.
# Create new attempt
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-mastery/create-new-attempt
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/createNewAttempt
Creates a new attempt for a student in a lesson if the current attempt is completed.
For Assessment Bank lessons:
- This will also update the state for the student, creating a new entry to associate the new attempt number with a different sub-resource of the test bank.
- If the lesson is taken again by the student, a different test may be served, considering the new resource it points to configures a different test.
- The sub-test is determined using round-robin logic over the sub-resources of the lesson's Assessment Bank Resource object.
- So for example, if a lesson configures 2 sub-tests, the first attempt serves test 1, the second attempt serves test 2, the third attempt serves test 1 again, and so on.
A 'Lesson' in this context is a ComponentResource object which has a Resource object associated with it.
# Create or update render config for one or more courses
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-mastery/create-or-update-render-config-for-one-or-more-courses
/openapi/beyond-ai/powerpath-api.yaml put /powerpath/render-config/
Assigns a custom renderer configuration to one or more courses. If a config already exists for a course, it is updated.
# Delete render config for a course
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-mastery/delete-render-config-for-a-course
/openapi/beyond-ai/powerpath-api.yaml delete /powerpath/render-config/{courseId}
Removes the custom renderer configuration for a course.
# Finalize a test assessments
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-mastery/finalize-a-test-assessments
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/finalStudentAssessmentResponse
Finalize a lesson of type `quiz`, `test-out`, or `placement` after all questions have been answered:
- Evaluates answered questions, attribute scores for each question, and overall lesson score.
- Checks the correctness of the response using the QTI question's `` element and update the score accordingly.
- Creates/updates the AssessmentLineItem and AssessmentResult objects for the student/question pair if it doesn't exist yet.
- When spaced repetition is enabled, fully graded standards-tagged evidence may update retention review state and create or append the student's separate spaced repetition review course. Course authors can opt out with metadata.spacedRepetitionEnabled = false; metadata.spacedRepetitionScheduler accepts "fsrs" or "sm2" and defaults to "fsrs".
Not supported for external test lessons as the 3rd party tool is responsible for finalizing the test. Use the **importExternalTestAssignmentResults** endpoint instead.
Notice this may perform a course enrollment for the student if the lesson is a placement test or test-out, and the respective subject and grade are mapped to the Subject Track with a valid course set.
A 'Lesson' in this context is a ComponentResource object which has a Resource object with metadata.lessonType = "quiz", "test-out", or "placement" associated with it.
# Get all attempts
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-mastery/get-all-attempts
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/getAttempts
Returns a list of all attempts for a student in a lesson
For Assessment Bank lessons, each attempt may represent a different sub test of the bank. Review results with care.
A 'Lesson' in this context is a ComponentResource object which has a Resource object associated with it.
# Get assessment progress
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-mastery/get-assessment-progress
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/getAssessmentProgress
Returns the progress the student has made in the given PowerPath lesson.
A 'Lesson' in this context is a ComponentResource object paired with a Resource object representing an activity.
The optional `view` query parameter controls the runtime projection for internal QTI-backed question content. Omit it, or use `authoring`, to return the full authored content. Use `student-before-answer` before rendering internal QTI-backed content to students; this removes answer-leaking content from the returned question XML/JSON. This sanitization only applies to internal QTI-backed content.
# Get next question
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-mastery/get-next-question
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/getNextQuestion
Returns the next question in the given PowerPath component resource.
Works only with lessons of type 'powerpath-100'.
A 'Lesson' in this context is a ComponentResource object which has a Resource object associated with it.
The optional `view` query parameter controls the runtime projection for internal QTI-backed question content. Omit it, or use `authoring`, to return the full authored content. Use `student-before-answer` before rendering internal QTI-backed content to students; this removes answer-leaking content from the returned question XML/JSON. This sanitization only applies to internal QTI-backed content.
# Get render config for a course
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-mastery/get-render-config-for-a-course
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/render-config/{courseId}
Returns the custom renderer configuration for a course, or 404 if none is configured.
# Get struggling quiz-mastery students
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-mastery/get-struggling-quiz-mastery-students
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/getQuizMasteryDashboard
Returns staff-facing rows for students who reached the configured attempt threshold on a gated quiz.
The effective staff viewer is authorized against the acting user's verified JWT. Administrators
receive tenant-wide results; teachers receive only students who share an active class with them.
Results are paginated by student-quiz cohort so all attempts used for one classification remain on
the same page. Continue with the opaque `nextCursor` value when present.
Client-credential callers must forward the signed Cognito user ID token in `X-ID-Token`.
This route authorizes its effective-viewer parameters directly because its raw cohort CTE cannot
consume the platform visibility filter used by standard ORM-backed cross-user reads.
Students who have not yet met the quiz threshold are categorized as `struggling`. Students whose first passing attempt was at or after the configured minimum are categorized as `mastered_after_struggle`. Passing before the minimum excludes the student from this view.
# Get typed render config for a course
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-mastery/get-typed-render-config-for-a-course
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/render-config/{courseId}/{rendererType}
Returns renderer configuration for a course and renderer type. Use rendererType=qti for the existing AlphaLearn renderer and rendererType=instructional_content for hosted instructional content.
# Reset attempt
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-mastery/reset-attempt
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/resetAttempt
Resets the attempt for the given PowerPath lesson of a student:
- Soft-deletes all previous question responses, resets the test score to 0, and updates its 'scoreStatus' to "not submitted".
- If the lesson is an external test, only resets the test score to 0.
For Assessment Bank lessons, this will keep the user state in the same bank test for the current attempt.
A 'Lesson' in this context is a ComponentResource object which has a Resource object associated with it.
# Update student question response
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-mastery/update-student-question-response
/openapi/beyond-ai/powerpath-api.yaml put /powerpath/updateStudentQuestionResponse
Updates the student's response to a question and returns the updated PowerPath score:
- Checks the correctness of the response using the QTI question `` element and update the score accordingly.
- Creates/updates the AssessmentLineItem and AssessmentResult objects for the student/question pair if it doesn't exist yet.
- When spaced repetition is enabled, fully graded standards-tagged evidence may update retention review state and create or append the student's separate spaced repetition review course. Course authors can opt out with metadata.spacedRepetitionEnabled = false; metadata.spacedRepetitionScheduler accepts "fsrs" or "sm2" and defaults to "fsrs".
A 'Lesson' in this context is a ComponentResource object which has a Resource object associated with it.
# Create a lesson plan
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-plans/create-a-lesson-plan
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/lessonPlans/
Purpose: Create a new lesson plan for a course and student
When to use:
- When a new student is enrolled in a course
- For initial setup of a student's learning path
- When you need to create a lesson plan from scratch
What it does:
- Creates a new lesson plan
- Associates it with the course and student
- Optionally, associates it with a class
- Returns the lesson plan ID
- If the lesson plan already exists, returns the existing lesson plan ID
- If the course, user or class is not found, returns a 404 error
# Delete all lesson plans for a course
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-plans/delete-all-lesson-plans-for-a-course
/openapi/beyond-ai/powerpath-api.yaml delete /powerpath/lessonPlans/{courseId}/deleteAll
Deletes all lesson plans for a course by its ID.
# Get a lesson plan tree by its ID
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-plans/get-a-lesson-plan-tree-by-its-id
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/lessonPlans/tree/{lessonPlanId}
Purpose: Get the complete lesson plan tree for a lesson plan.
When to use:
- When you need to display the full lesson plan to a student
- For rendering the personalized learning path
What it does:
- Returns the lesson plan in a syllabus-like format
- Includes only non-skipped items (visible content)
- Shows the hierarchical structure with components and resources
- Provides all original metadata needed for UI rendering
# Get a lesson plan tree structure by its ID
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-plans/get-a-lesson-plan-tree-structure-by-its-id
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/lessonPlans/tree/{lessonPlanId}/structure
Purpose: Get a simplified structure for inspection and debugging.
When to use:
- For administractive tools and debugging
- When you need to see the internal lesson plan structure without the full metadata
What it does:
- Returns a lightweight view of the lesson plan structure
- Shows both skipped and non-skipped items
- Includes order information and component/resource IDs (alos includes item ids but these should'n be relied on since they are not stable)
- Useful for understanding the current state
# Get course progress
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-plans/get-course-progress
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/lessonPlans/getCourseProgress/{courseId}/student/{studentId}
Get the course progress for a student in a course.
---
Returns a list of **assessment line items** for the **course** and **student**.
A type "**component**" indicates a component of the lesson plan such as a unit or lesson.
A type "**resource**" indicates a resource such as a video, audio,
or document file as well as a quiz or question.
Each **line item** contains a list of assessment results in the **results** attribute, related to student and course.
**Filtering by Lesson**
You can optionally filter the results to only include line items for a specific lesson by providing the `lessonId` query parameter with the component resource ID.
# Get course syllabus
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-plans/get-course-syllabus
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/syllabus/{courseSourcedId}
Get course syllabus
# Get instructional content renderer state
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-plans/get-instructional-content-renderer-state
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/content-renderer/state/{componentResourceId}/student/{studentId}
Returns the latest saved opaque content renderer state for a student resource.
# Get the operations for a lesson plan
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-plans/get-the-operations-for-a-lesson-plan
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/lessonPlans/{lessonPlanId}/operations
Purpose: Get all operations for a lesson plan
When to use:
- For audit trails and history tracking
- When debugging lesson plan issues
- For administrative oversight
What it does:
- Returns all operations in chronological order
- Includes operation type, payload, timestamp, and reason
- Shows who made each change and when
# Recreate a lesson plan from a course and apply all operations
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-plans/recreate-a-lesson-plan-from-a-course-and-apply-all-operations
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/lessonPlans/{lessonPlanId}/recreate
Purpose: Recreate a lesson plan from scratch using it's operation log
When to use:
- When a lesson plan becomes corrupted or out of sync
- For testing or debugging purposes
- After detecting and correcting inconsistencies
What it does:
- Deletes all current lesson plan items
- Rebuilds from the base course structure
- Applies all operations from the operation log in sequence
- Returns the operation results for monitoring and inspection
# Resolve instructional content renderer payload
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-plans/resolve-instructional-content-renderer-payload
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/content-renderer/content
Returns renderer-ready { contentId, title, html, baseUrl } for resources that explicitly provide renderer HTML through QTI stimulus JSON or allowlisted metadata.contentRenderer.htmlUrl.
# Returns the lesson plan tree for a course and student
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-plans/returns-the-lesson-plan-tree-for-a-course-and-student
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/lessonPlans/{courseId}/{userId}
Given a course sourced ID and a user sourced ID, returns the lesson plan tree.
The lesson plan tree is nested object comprised of several lessonPlanItems, which are nodes that contain information about the lesson plan - including which component or component resource is associated with that node, as well as which node is its parent.
A node may reference a component or a componentResource.
A node with no parent is considered at the root level of the lesson plan tree.
A student's lesson plan has a unique ID that can be used instead of the parameters to retrieve it.
# Save instructional content renderer state
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-plans/save-instructional-content-renderer-state
/openapi/beyond-ai/powerpath-api.yaml put /powerpath/content-renderer/state
Debounced state-save endpoint for hosted instructional content. Stores opaque contentState and normalized contentSummary.
# Store an operation on a lesson plan
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-plans/store-an-operation-on-a-lesson-plan
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/lessonPlans/{lessonPlanId}/operations
Purpose: Store a new operation in the lesson plan's operation log
When to use:
- Primary endpoint for all lesson plan modifications
- When students, guides or admins want to customize the learning path
- For any personalization changes
Available Operations:
- set-skipped: Show/hide content for the student
- move-item-before/after: Reorder content relative to other items
- move-item-to-start/end: Move to beginning/end of parent
- add-custom-resource: Add additional resources in the lesson plan
- change-item-parent: Move content to different sections (components) in the lesson plan
# Sync Lesson Plans for a Course
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-plans/sync-lesson-plans-for-a-course
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/lessonPlans/course/{courseId}/sync
Purpose: Bulk synchronization of all lesson plans for a course.
When to use:
- After making significant structural changes to a base course
- When you need to ensure all students have the latest course content
What it does:
- Finds all lesson plans associated with the course
- Recreates each lesson plan from the base course structure
- Applies all historical operations to maintain personalizations
- Return a list of affected lesson plan ID's
# Sync the operations for a lesson plan
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-plans/sync-the-operations-for-a-lesson-plan
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/lessonPlans/{lessonPlanId}/operations/sync
Purpose: Apply pending operations to update the lesson plan
When to use:
- After storing operations, to see the changes take effect
- For incremental updates without full recreation
- When you want to apply only recent changes (e.g after running a script to add a lot of operations)
What it does:
- Finds operations that haven't been applied yet
- Executes them in sequence
- Updates the lesson plan structure
- Returns results of each operation
# Update student item response
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/lesson-plans/update-student-item-response
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/lessonPlans/updateStudentItemResponse
Update the student item response for a student in a course.
---
The item is identified by its **componentResourceId**; the data in the result payload describes the student's response to that specific resource.
**Side effect:** when a result first becomes active and **fully graded**, a durable job is queued to emit a completion Caliper ActivityEvent for the component resource, which feeds course-completion tracking and can trigger course_completion progression once the course reaches 100%. Replayed identical writes reuse the same completion identity. If the durable job cannot be queued, the result write rolls back. Bulk backfills or grade corrections through this endpoint will therefore drive completion recalculation for the affected students.
# Assign test
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/placement/assign-test
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/screening/tests/assign
Assign test to a user
# Get a student's placement state
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/placement/get-a-students-placement-state
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/placement/{studentId}
Returns the current placement state for each subject.
`nextTestId` identifies a native TimeBack assessment. It cannot identify external work because external work has a URL instead of a TimeBack test ID.
Before the service enforced external Reading screeners, clients could skip them and open the next native test. After enforcement, the response for a pending screener was `IN_PROGRESS` with `nextTestId: null`. Clients then had no screener link.
`nextAction` supersedes `nextTestId` for placement actions. `nextTestId` remains in the response for older clients. It carries a value only for native TimeBack assessments.
`nextAction` describes only the next placement action for this response. It does not describe other work for the subject.
- `assign`: A native TimeBack placement test is available. `testId` matches `nextTestId`, and `title` is its display label.
- `launch`: Placement continues in an external application. `nextTestId` is null. `url` is the exact HTTPS destination, and `title` is its display label. A client must open `url` exactly as supplied. It must not add student, grade, result, or battery data.
- `null`: No action object is present in this field.
For K-2 Reading, a `launch` action can point to AlphaLiteracy. Before this response exposes that action, TimeBack has created a pending generic `AlphaLiteracy Assessment` result in OneRoster. AlphaLiteracy discovers and resumes that result after sign-in. Provider-internal test selection is not part of this placement API contract.
# Get all placement tests
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/placement/get-all-placement-tests
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/placement/getAllPlacementTests
Returns all placement tests for a subject, including available results for each.
A 'Lesson' (placement test) in this context is a ComponentResource object which has a Resource object with metadata.lessonType = "placement" associated with it.
# Get current level
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/placement/get-current-level
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/placement/getCurrentLevel
Returns the current level of the student in a placement process:
- The level is determined by the last completed placement test's grade level, starting from the lowest grade level available for the subject's placement tests.
- As the student completes placement tests and attains scores of 89.5 or greater, their level updates to the next level available for the subject.
Also returns the 'onboarded' boolean that indicates if the student completed the onboarding process for the subject:
- A 'onboarded = true' means they either completed and passed all placement tests or they have gotten a score smaller than 89.5 in the last completed placement test.
- A 'onboarded = false' means they haven't completed placement tests yet or have achieved a score of 89.5 or greater in the last completed placement test and there are more tests to take.
# Get next placement test
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/placement/get-next-placement-test
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/placement/getNextPlacementTest
Returns the next placement test for the student in a subject:
- If the student has completed all placement tests for the subject, the next test's lesson ID will be null. Tests will also be marked as exhausted.
- If the student hasn't completed a single placement test, returns the first placement test's lesson ID for the subject.
- If the student has completed some placement tests, it will return null for the next test's lesson ID if the last completed test had a score smaller than 89.5.
- Alternatively, it will return the next available placement test's lesson ID if the score was at least 89.5.
Also returns the 'onboarded' boolean that indicates if the student completed the onboarding process for the subject:
- A 'onboarded = true' means they either completed and passed all placement tests or they have gotten a score smaller than 89.5 in the last completed placement test.
- A 'onboarded = false' means they haven't completed placement tests yet or have achieved a score of 89.5 or greater in the last completed placement test and there are more tests to take.
A 'Lesson' in this context is a ComponentResource object which has a Resource object with metadata.lessonType = "placement" associated with it.
# Get results
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/placement/get-results
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/screening/results/{userId}
Get results for a user
# Get session
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/placement/get-session
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/screening/session/{userId}
Get session for a user
# Get subject progress
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/placement/get-subject-progress
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/placement/getSubjectProgress
Returns the progress the student has made in the given subject
# Get the enabled-subject policy decision
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/placement/get-the-enabled-subject-policy-decision
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/placement/enabledSubjectPolicy
Returns whether automatic progression may act on one subject for one student.
# Look up grade-equivalent bands from a RIT score
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/placement/look-up-grade-equivalent-bands-from-a-rit-score
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/rit-to-grade
Returns the 50th and 90th percentile grade-equivalent mapping for a given subject and RIT score using the seeded PowerPath lookup table.
# Look up target RIT from NWEA norms
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/placement/look-up-target-rit-from-nwea-norms
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/nwea-norms
Returns the target RIT score for a given subject, grade, percentile, and optional testing season using the seeded NWEA norms table.
# Reset session
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/placement/reset-session
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/screening/session/reset
Reset session for a user
# Reset user placement for subject
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/placement/reset-user-placement-for-subject
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/placement/resetUserPlacement
Resets a user's placement progress for a specific subject by:
- Soft deleting all placement assessment results for that subject
- Resetting user onboarding state to "in_progress" and removing completedAt and courseId if existing
This operation is restricted to administrators only and cannot be undone.
# Create an individual test assignment (unlisted test-out)
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/test-assignments/create-an-individual-test-assignment-unlisted-test-out
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/test-assignments
Creates a standalone test-out assignment for a student, generating a Resource and an unlisted ComponentResource (no course link), and registering the assignment record.
# Create multiple test assignments
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/test-assignments/create-multiple-test-assignments
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/test-assignments/bulk
Creates multiple standalone test-out assignments for students. Validates all items and reports all errors before processing. Returns 200 if all succeed, 400 if any validation errors are found. All-or-nothing operation.
# Delete a test assignment
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/test-assignments/delete-a-test-assignment
/openapi/beyond-ai/powerpath-api.yaml delete /powerpath/test-assignments/{id}
Soft deletes a test assignment by ID.
# Get a test assignment
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/test-assignments/get-a-test-assignment
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/test-assignments/{id}
Returns a single test assignment by its ID.
# Import test assignments from Google Sheets
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/test-assignments/import-test-assignments-from-google-sheets
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/test-assignments/import
Fetches a public Google Sheet tab as CSV and creates test assignments in bulk. Requires columns: student, subject, grade (case-insensitive). Student may be email or ID. All-or-nothing; returns 200 if all succeed, 400 if any errors.
# List all test assignments (admin)
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/test-assignments/list-all-test-assignments-admin
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/test-assignments/admin
Returns a paginated list of test assignments across students. Optional filters for student, status, subject, grade.
# List test assignments for a student
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/test-assignments/list-test-assignments-for-a-student
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/test-assignments
Returns a paginated list of test assignments filtered by student with optional filters for status, subject, and grade.
# Update a test assignment
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/test-assignments/update-a-test-assignment
/openapi/beyond-ai/powerpath-api.yaml put /powerpath/test-assignments/{id}
Updates the title of a test assignment.
# Check if a student is eligible to self-request a test-out
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/test-out/check-if-a-student-is-eligible-to-self-request-a-test-out
/openapi/beyond-ai/powerpath-api.yaml get /powerpath/test-out/getTestOutEligibility/{studentId}/{subject}
Checks whether a student can self-request an assessment to test out of their current grade level for a given subject.
The endpoint performs the following checks in order:
1. Verifies the student has an active enrollment for the subject in a non-placeholder course (endDate is null or in the future, and course metadata does not have placementPlaceholder set to true)
2. Fetches the student's highest grade mastered for the subject
3. If the student has no mastery in the requested subject, applies that subject's cross-subject placement prerequisites using all-source prerequisite mastery while FF_TEST_OUT_ENTRY_PLACEMENT_GATE is enabled
4. When advancing from mastered K-2 Reading to the next grade's test-out, requires a passed ORF screener at the mastered grade (summer-experience students retain their configured waiver while its feature flag is enabled)
5. Resolves the target grade from highestGradeMastered + 1; if mastery is unavailable, falls back to the lowest active-enrollment grade and then the active course sequence's lowest assessment grade
6. Returns terminal-grade ineligibility when the resolved target exceeds the Grade 12 self-elected AlphaTest inventory, or the active sequence's highest assessment grade for FastMath
7. For FastMath Kindergarten and above, blocks pending AlphaTest/mastery-track assessments and passed assessments with missing or unknown operation tags, then selects the first canonical operation in active-sequence order that has no passed non-quiz assessment result at the target grade
8. For other subjects, queries the assessment_results table for any existing fully-graded self-elected test-out at the resolved target grade
Returns `eligible: true` with `targetGrade` if no prior test-out exists at the target grade. FastMath Kindergarten-and-above responses also include the required `gradeAssessmentTag`.
Returns `eligible: false` with a reason if the student has no qualifying active enrollment, has no supported higher target grade, or has already tested out. Terminal-grade responses include `code: "terminal_grade"`.
FastMath sequence configuration is cached for up to 30 seconds within the same authorization and tenant context; student evidence is always read fresh. During deployment, an older eligibility service that omits the operation returns temporary ineligibility, and assignment returns 403 until operation selection is available.
Subject matching is case-insensitive. Supported subjects: Math, FastMath, Reading, Language, Science, Writing, Vocabulary.
# Create a self-elected student test-out assignment
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/powerpath/test-out/create-a-self-elected-student-test-out-assignment
/openapi/beyond-ai/powerpath-api.yaml post /powerpath/test-out/makeExternalStudentTestOutAssignment
Creates an AlphaTest test-out assignment for a student on a given subject.
The endpoint performs the following steps:
1. Looks up the student's email using their OneRoster sourcedId
2. Checks eligibility: the student must have an active enrollment in a non-placeholder course for the subject
3. Resolves eligibility and the target grade atomically
4. When advancing from mastered K-2 Reading to the next grade's test-out, requires a passed ORF screener at the mastered grade (summer-experience students retain their configured waiver while its feature flag is enabled)
5. Assigns exactly the target grade returned by the eligibility decision
6. For FastMath Kindergarten and above, selects the first unpassed operation from the active sequence and forwards its exact gradeAssessmentTag
The assignment payload sent to AlphaTest includes:
- `test_type`: "test out"
- `origin`: "self-elected"
- `metadata.gradeAssessmentTag` for FastMath Kindergarten and above (PreK accuracy assignments remain untagged)
Will fail if:
- The student does not exist or has no email
- The student does not have an active enrollment for the subject (or the course has placementPlaceholder set to true)
- The assignment would be the student's entry into a gated subject and the subject's all-source placement prerequisites are unmet while FF_TEST_OUT_ENTRY_PLACEMENT_GATE is enabled
- Advancing from mastered K-2 Reading is still blocked by that grade's ORF gate
- The test-out grade cannot be determined (no highest grade mastered and no active course sequence with assessment stages)
- The AlphaTest API returns an error
# Create a new assessment item
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/assessment-item-management/create-a-new-assessment-item
/openapi/beyond-ai/qti-api.yaml post /assessment-items
Create a QTI 3.0 assessment item, preferably from XML. Send format: 'xml' with the XML string in the body; the XML is validated against IMS QTI XSDs and must conform to the standard. JSON creation is also supported but is experimental and may change.
# Delete an assessment item
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/assessment-item-management/delete-an-assessment-item
/openapi/beyond-ai/qti-api.yaml delete /assessment-items/{identifier}
Permanently delete an assessment item. This operation cannot be undone. Warning: Assessment tests that reference this item may be affected. The item references in test sections will need to be updated separately.
# Get an assessment item with complete question content
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/assessment-item-management/get-an-assessment-item-with-complete-question-content
/openapi/beyond-ai/qti-api.yaml get /assessment-items/{identifier}
Retrieve a specific assessment item. By default this returns the full authoring view, including answer keys, feedback, response-processing rules, and scoring logic. Pass view=student-before-answer when delivering the item to a student before submission; that projection removes answer-leaking content from JSON and XML responses while leaving stored content unchanged. POST /assessment-items/{identifier}/process-response always scores against the stored unsanitized content.
# Process a response for an assessment item
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/assessment-item-management/process-a-response-for-an-assessment-item
/openapi/beyond-ai/qti-api.yaml post /assessment-items/{identifier}/process-response
Process a response for an assessment item. This operation validates the response and returns the result as well as the feedback identifier and the feedback value.
# Search and filter assessment items
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/assessment-item-management/search-and-filter-assessment-items
/openapi/beyond-ai/qti-api.yaml get /assessment-items
Search and retrieve assessment items (questions) with advanced filtering capabilities. Supports text search across titles and identifiers, filtering by type, sorting, and pagination. Assessment items are the core content units that contain questions, answer choices, and scoring logic.
# Update an assessment item's content and configuration
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/assessment-item-management/update-an-assessment-items-content-and-configuration
/openapi/beyond-ai/qti-api.yaml put /assessment-items/{identifier}
Update an assessment item including its question content, interactions, response processing, and scoring logic. This operation regenerates the QTI XML structure and validates all content. Assessment tests that reference this item will automatically use the updated version.
# Update metadata for a list of assessment items
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/assessment-item-management/update-metadata-for-a-list-of-assessment-items
/openapi/beyond-ai/qti-api.yaml post /assessment-items/metadata
Update metadata for a list of assessment items. This operation is used to reset the human approved status for all assessment items.
# Create a new assessment test
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/assessment-test-management/create-a-new-assessment-test
/openapi/beyond-ai/qti-api.yaml post /assessment-tests
Create a new assessment test. Supports both JSON and XML formats.
# Delete an assessment test
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/assessment-test-management/delete-an-assessment-test
/openapi/beyond-ai/qti-api.yaml delete /assessment-tests/{identifier}
Permanently delete an assessment test and all its associated data including test parts, sections, and item references. This operation cannot be undone. The actual assessment items referenced by this test are not deleted.
# Get a complete assessment test with full structure
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/assessment-test-management/get-a-complete-assessment-test-with-full-structure
/openapi/beyond-ai/qti-api.yaml get /assessment-tests/{identifier}
Retrieve a complete assessment test including all its test parts, sections, and assessment item references. This provides the full hierarchical structure needed to understand the test organization and flow. Supports both JSON and XML response formats based on the Content-Type header.
# Get all assessment items referenced by an assessment test
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/assessment-test-management/get-all-assessment-items-referenced-by-an-assessment-test
/openapi/beyond-ai/qti-api.yaml get /assessment-tests/{identifier}/questions
Retrieve all assessment items (questions) that are referenced by an assessment test, along with their structural context (test part and section). This endpoint aggregates items from all sections across all test parts, providing both the item references and the actual assessment item data from the assessment-items collection. By default expanded questions are returned in the full authoring view. Use `student-before-answer` for rendering to student if you want to remove the correct answer hints. This view mode removes correct responses, feedback, response-processing, and unreferenced qti-catalog entries from each expanded question while leaving stored content unchanged.
# Search and filter assessment tests
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/assessment-test-management/search-and-filter-assessment-tests
/openapi/beyond-ai/qti-api.yaml get /assessment-tests
Search and retrieve assessment tests with advanced filtering capabilities. Supports text search across titles and identifiers, filtering by navigation/submission modes, and pagination. Assessment tests are the top-level containers that define complete testing experiences through their test parts and sections.
# Update an entire assessment test
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/assessment-test-management/update-an-entire-assessment-test
/openapi/beyond-ai/qti-api.yaml put /assessment-tests/{identifier}
Update an assessment test by replacing its complete structure. This operation updates the entire assessment test including its test parts, sections, and item references. Supports both JSON and XML formats. The updated XML structure is automatically regenerated.
# Update assessment test metadata only
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/assessment-test-management/update-assessment-test-metadata-only
/openapi/beyond-ai/qti-api.yaml put /assessment-tests/{identifier}/metadata
Update only the metadata fields (title, description, etc.) of an assessment test without affecting its structure, test parts, sections, or assessment items. This is a lightweight operation for administrative changes.
# Create a new stimulus
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/stimulus-management/create-a-new-stimulus
/openapi/beyond-ai/qti-api.yaml post /stimuli
Create a new Stimulus on the service provider. Stimuli can be referenced by Assessment Items.
# Delete a stimulus
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/stimulus-management/delete-a-stimulus
/openapi/beyond-ai/qti-api.yaml delete /stimuli/{identifier}
Permanently delete a stimulus. This operation cannot be undone. Warning: Assessment items that reference this stimulus may be affected. Consider checking for references before deletion.
# Get a stimulus with its complete content
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/stimulus-management/get-a-stimulus-with-its-complete-content
/openapi/beyond-ai/qti-api.yaml get /stimuli/{identifier}
Get a specific Stimulus by identifier from the service provider. Stimuli can be referenced by Assessment Items to display relevant content to the learner.
# Search and filter stimuli
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/stimulus-management/search-and-filter-stimuli
/openapi/beyond-ai/qti-api.yaml get /stimuli
Get all stimuli on the service provider. Search with advanced filtering capabilities. Supports text search across titles and identifiers, sorting, and pagination.
# Update a stimulus and its content
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/stimulus-management/update-a-stimulus-and-its-content
/openapi/beyond-ai/qti-api.yaml put /stimuli/{identifier}
Update a Stimulus on the service provider.
# Create a new test part in an assessment test
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/test-part-management/create-a-new-test-part-in-an-assessment-test
/openapi/beyond-ai/qti-api.yaml post /assessment-tests/{assessmentTestIdentifier}/test-parts
Create a new test part within an assessment test. Test parts organize sections and define navigation behaviors (linear/nonlinear) and submission modes. The assessment test's XML structure is automatically updated to include the new test part.
# Search and filter test parts within an assessment test
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/test-part-management/search-and-filter-test-parts-within-an-assessment-test
/openapi/beyond-ai/qti-api.yaml get /assessment-tests/{assessmentTestIdentifier}/test-parts
Get all test parts within an assessment test with support for filtering by navigation mode, submission mode, and text search. Test parts are organizational units that group sections and define testing behaviors like linear/nonlinear navigation.
# Create a new Achievement
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/openbadges/openbadge--achievements/create-a-new-achievement
/openapi/beyond-ai/openbadges-api.yaml post /ims/ob/v3p0/achievements/
To create a new Achievement. The responding system must return the set of sourcedIds that have been allocated to the newly created achievement record.
# Delete an Achievement
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/openbadges/openbadge--achievements/delete-an-achievement
/openapi/beyond-ai/openbadges-api.yaml delete /ims/ob/v3p0/achievements/{sourcedId}
Perform a soft delete on a specific Achievement on the service provider. This operation changes the status of the Achievement to 'tobedeleted'.
# Get a specific achievement
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/openbadges/openbadge--achievements/get-a-specific-achievement
/openapi/beyond-ai/openbadges-api.yaml get /ims/ob/v3p0/achievements/{sourcedId}
Get a specific Achievement on the service provider. If the corresponding record cannot be located, the api will return a 404 error code and message 'Achievement not found.'
# Get achievements for a Student
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/openbadges/openbadge--achievements/get-achievements-for-a-student
/openapi/beyond-ai/openbadges-api.yaml get /ims/ob/v3p0/achievements/students/{studentSourcedId}
To get all achievements for a specific Student. If the corresponding record cannot be located, the api will return a 404 error code and message 'Student not found.'
# Get all Achievements
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/openbadges/openbadge--achievements/get-all-achievements
/openapi/beyond-ai/openbadges-api.yaml get /ims/ob/v3p0/achievements/
To get all Achievements on the service provider.
# Update an Achievement
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/openbadges/openbadge--achievements/update-an-achievement
/openapi/beyond-ai/openbadges-api.yaml put /ims/ob/v3p0/achievements/{sourcedId}
To update an existing Achievement. The sourcedId for the record to be updated is supplied by the requesting system.
# Upsert a Verifiable Open Badge Credential
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/openbadges/openbadge--credentials/upsert-a-verifiable-open-badge-credential
/openapi/beyond-ai/openbadges-api.yaml post /ims/ob/v3p0/credentials/
Upserts (creates or updates) a Verifiable Open Badge Credential. This endpoint follows the
Open Badges 3.0 specification for credential upsert operations. A credential is considered the same as
another when both the issuer.id and the credential.id are equal.
- If a credential with the same issuer.id and credential.id already exists, it will be updated (HTTP 200)
- If no matching credential is found, a new credential will be created (HTTP 201)
The platform acts as the repository for these credentials, ensuring their authenticity and integrity
through cryptographic proofs. All upserted credentials must be valid according to the Open Badges 3.0
specification and include proper verification methods.
# Get Open Badges v3.0 API Discovery Information
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/openbadges/openbadge--discovery/get-open-badges-v30-api-discovery-information
/openapi/beyond-ai/openbadges-api.yaml get /ims/ob/v3p0/discovery/
Returns the OpenAPI 3.0 specification for the Open Badges v3.0 API. This endpoint provides
discovery information including available endpoints, OAuth2 flows, and supported scopes. This is a public
endpoint that allows clients to dynamically discover the service's capabilities without prior configuration.
# Issue a Verifiable Open Badge Credential
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/openbadges/openbadge--issue-badge/issue-a-verifiable-open-badge-credential
/openapi/beyond-ai/openbadges-api.yaml post /ims/ob/v3p0/issue-badge/
Processes achievement data submitted by an authorized external creator (e.g., university, school)
and issues a new Open Badge v3.0 compliant Verifiable Credential (AchievementCredential).
This platform acts as the 'issuer' of the Verifiable Credential, digitally signing it to ensure
its authenticity and integrity. The 'userId' information provided in the request body will be
embedded within the 'achievement' object of the issued badge. The resulting badge includes a
cryptographic proof (e.g., JsonWebSignature2020) allowing any verifier to confirm that the badge
was issued by this platform and has not been tampered with.
# Verify an Open Badge Credential
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/openbadges/openbadge--issue-badge/verify-an-open-badge-credential
/openapi/beyond-ai/openbadges-api.yaml post /ims/ob/v3p0/issue-badge/verify
Verifies the authenticity and integrity of a presented Open Badge v3.0 (AchievementCredential).
This endpoint checks the cryptographic proof (e.g., signature) embedded within the badge to ensure
it was genuinely issued by the claimed issuer (as identified in the proof's verificationMethod) and
that its contents have not been altered since issuance. It may also perform checks on timeliness
(validFrom/validUntil). This endpoint conforms to the verification principles outlined in the Open Badges v3.0
specification and W3C Verifiable Credentials Data Model.
# Create lesson feedback
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/feedback/create-lesson-feedback
/openapi/beyond-ai/qti-api.yaml post /lesson
Creates a new feedback for a lesson
# Create question feedback
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/feedback/create-question-feedback
/openapi/beyond-ai/qti-api.yaml post /question
Creates a new feedback for a question
# Delete feedback
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/feedback/delete-feedback
/openapi/beyond-ai/qti-api.yaml delete /{id}
Deletes a specific feedback by ID
# Get feedback by lesson ID
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/feedback/get-feedback-by-lesson-id
/openapi/beyond-ai/qti-api.yaml get /lesson/{lessonId}
Retrieves all feedback for a specific lesson
# Add an assessment item reference to a section
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/section-management/add-an-assessment-item-reference-to-a-section
/openapi/beyond-ai/qti-api.yaml post /assessment-tests/{assessmentTestIdentifier}/test-parts/{testPartIdentifier}/sections/{identifier}/items
Add a reference to an existing assessment item in a section. This creates a link between the assessment item and the section without copying the item content. The item must exist in the assessment-items collection.
# Create a new section in a test part
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/section-management/create-a-new-section-in-a-test-part
/openapi/beyond-ai/qti-api.yaml post /assessment-tests/{assessmentTestIdentifier}/test-parts/{testPartIdentifier}/sections
Create a new section within a test part. Sections organize assessment items and define their presentation behavior. The parent assessment test's XML structure is automatically updated to include the new section.
# Delete a section and its item references
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/section-management/delete-a-section-and-its-item-references
/openapi/beyond-ai/qti-api.yaml delete /assessment-tests/{assessmentTestIdentifier}/test-parts/{testPartIdentifier}/sections/{identifier}
Permanently delete a section from a test part. This removes the section and all its assessment item references (but not the underlying assessment items). The assessment test's XML structure is automatically updated.
# Get a section with all its assessment item references
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/section-management/get-a-section-with-all-its-assessment-item-references
/openapi/beyond-ai/qti-api.yaml get /assessment-tests/{assessmentTestIdentifier}/test-parts/{testPartIdentifier}/sections/{identifier}
Retrieve a specific section including all its assessment item references, presentation settings, and configuration. Sections define how groups of assessment items are presented to test takers.
# Remove an assessment item reference from a section
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/section-management/remove-an-assessment-item-reference-from-a-section
/openapi/beyond-ai/qti-api.yaml delete /assessment-tests/{assessmentTestIdentifier}/test-parts/{testPartIdentifier}/sections/{identifier}/items/{itemIdentifier}
Remove an assessment item reference from a section. This only removes the reference link; the actual assessment item remains in the assessment-items collection. The assessment test's XML structure is automatically updated.
# Reorder assessment items within a section
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/section-management/reorder-assessment-items-within-a-section
/openapi/beyond-ai/qti-api.yaml put /assessment-tests/{assessmentTestIdentifier}/test-parts/{testPartIdentifier}/sections/{identifier}/items/order
Update the presentation order of assessment item references within a section. This affects the sequence in which items are presented to test takers. All specified items must exist in the section.
# Search sections within a test part
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/section-management/search-sections-within-a-test-part
/openapi/beyond-ai/qti-api.yaml get /assessment-tests/{assessmentTestIdentifier}/test-parts/{testPartIdentifier}/sections
Get all sections within a specific test part with support for text search, sorting, and pagination. Sections are containers that group related assessment items and define their presentation order.
# Update a section's configuration and item references
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/section-management/update-a-sections-configuration-and-item-references
/openapi/beyond-ai/qti-api.yaml put /assessment-tests/{assessmentTestIdentifier}/test-parts/{testPartIdentifier}/sections/{identifier}
Update a section including its title, presentation settings, and assessment item references. This operation updates the section structure and regenerates the parent assessment test's XML.
# Delete a test part and all its sections
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/test-part-management/delete-a-test-part-and-all-its-sections
/openapi/beyond-ai/qti-api.yaml delete /assessment-tests/{assessmentTestIdentifier}/test-parts/{identifier}
Permanently delete a test part from an assessment test. This removes the test part and all its sections (but not the underlying assessment items). The assessment test's XML structure is automatically updated.
# Get a specific test part with all its sections
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/test-part-management/get-a-specific-test-part-with-all-its-sections
/openapi/beyond-ai/qti-api.yaml get /assessment-tests/{assessmentTestIdentifier}/test-parts/{identifier}
Retrieve a test part by identifier including all its sections and their assessment item references. Test parts define navigation and submission behaviors for groups of sections. Supports both JSON and XML response formats.
# Update a test part's configuration and sections
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/test-part-management/update-a-test-parts-configuration-and-sections
/openapi/beyond-ai/qti-api.yaml put /assessment-tests/{assessmentTestIdentifier}/test-parts/{identifier}
Update a test part including its navigation mode, submission mode, time limits, and sections. This operation updates the entire test part structure and regenerates the parent assessment test's XML.
# Validate a batch of XML strings
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/validation/validate-a-batch-of-xml-strings
/openapi/beyond-ai/qti-api.yaml post /validate/batch
Validate a batch of QTI XML strings against the QTI 3.0 xsd specification.
# Validate a XML string
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/qti/validation/validate-a-xml-string
/openapi/beyond-ai/qti-api.yaml post /validate
Validate a QTI XML file against the QTI 3.0 xsd specification.
# Create Caliper Events
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/timeback-events/caliper-events/create-caliper-events
/openapi/beyond-ai/timeback-events-api.yaml post /caliper/event
Receives and processes Timeback events wrapped in an envelope. Events will be validated against the IMS Caliper Analytics specification and stored for further processing and analysis.
# Validate Caliper Events
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/timeback-events/caliper-events/validate-caliper-events
/openapi/beyond-ai/timeback-events-api.yaml post /caliper/event/validate
This endpoint is useful to prepare the event payload before sending them to the ingestion endpoint.
When you send an event via this endpoint it will only be validated and not stored.
# Create a webhook filter
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/webhooks/webhook-filters/create-a-webhook-filter
/openapi/beyond-ai/webhooks-api.yaml post /webhook-filters/
Creates a new webhook filter
# Delete a webhook filter
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/webhooks/webhook-filters/delete-a-webhook-filter
/openapi/beyond-ai/webhooks-api.yaml delete /webhook-filters/{id}
Deletes a webhook filter by ID
# Get a webhook filter by ID
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/webhooks/webhook-filters/get-a-webhook-filter-by-id
/openapi/beyond-ai/webhooks-api.yaml get /webhook-filters/{id}
Returns a specific webhook filter by ID
# Get all webhook filters
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/webhooks/webhook-filters/get-all-webhook-filters
/openapi/beyond-ai/webhooks-api.yaml get /webhook-filters/
Returns all webhook filters
# Get webhook filters by webhook ID
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/webhooks/webhook-filters/get-webhook-filters-by-webhook-id
/openapi/beyond-ai/webhooks-api.yaml get /webhook-filters/webhook/{webhookId}
Returns all filters for a specific webhook
# Update a webhook filter
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/webhooks/webhook-filters/update-a-webhook-filter
/openapi/beyond-ai/webhooks-api.yaml put /webhook-filters/{id}
Updates an existing webhook filter by ID
# Activate a webhook
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/webhooks/webhooks/activate-a-webhook
/openapi/beyond-ai/webhooks-api.yaml put /webhooks/{id}/activate
Activates a webhook by ID
# Create a webhook
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/webhooks/webhooks/create-a-webhook
/openapi/beyond-ai/webhooks-api.yaml post /webhooks/
Creates a new webhook
# Deactivate a webhook
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/webhooks/webhooks/deactivate-a-webhook
/openapi/beyond-ai/webhooks-api.yaml put /webhooks/{id}/deactivate
Deactivates a webhook by ID
# Delete a webhook
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/webhooks/webhooks/delete-a-webhook
/openapi/beyond-ai/webhooks-api.yaml delete /webhooks/{id}
Deletes a webhook by ID
# Get a specific webhook
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/webhooks/webhooks/get-a-specific-webhook
/openapi/beyond-ai/webhooks-api.yaml get /webhooks/{id}
Returns a specific webhook by ID
# Get for a sensor
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/webhooks/webhooks/get-for-a-sensor
/openapi/beyond-ai/webhooks-api.yaml get /webhooks/
Returns all webhooks for a specific sensor
# Update a webhook
Source: https://docs.timeback.com/beta/api-reference/beyond-ai/webhooks/webhooks/update-a-webhook
/openapi/beyond-ai/webhooks-api.yaml put /webhooks/{id}
Updates an existing webhook by ID
# Get all LTI applications
Source: https://docs.timeback.com/beta/api-reference/platform/applications/get-all-lti-applications
/openapi/learn-with-ai/platform-api.yaml get /applications/1.0
Returns a paginated list of LTI applications with flattened application data
# JSON Web Key Set
Source: https://docs.timeback.com/beta/api-reference/platform/applications/json-web-key-set
/openapi/learn-with-ai/platform-api.yaml get /.well-known/jwks.json
Returns the JSON Web Key Set (JWKS) containing public keys for verifying JWT tokens. Follows RFC 7517 and RFC 8414 standards.
# Register a draft application
Source: https://docs.timeback.com/beta/api-reference/platform/applications/register-a-draft-application
/openapi/learn-with-ai/platform-api.yaml post /applications/1.0/drafts
Registers a new application in draft status and provisions OAuth client
credentials for the client credentials flow.
The response includes `applicationId`, `appUrn`, `publisherId`, the
non-secret `productionCredentials` (and, on the happy path,
`sandboxCredentials`), and a single `secretClaimUrl`. No `clientSecret`
is returned inline (ITD 9): opening `secretClaimUrl` forces a Google-IdP
SSO sign-in and reveals every minted secret once. The accompanying
`securityNote` warns agents not to open the URL on the developer's
behalf.
If your account is linked to more than one publisher, include `publisherId`
in the request body. If you belong to zero or one publisher, omit
`publisherId` and the platform assigns or creates the appropriate publisher.
# Retrieve application OAuth credentials
Source: https://docs.timeback.com/beta/api-reference/platform/applications/retrieve-application-oauth-credentials
/openapi/learn-with-ai/platform-api.yaml get /applications/1.0/{sourcedApplicationId}/credentials
Returns the Cognito `clientId`, `tokenUrl`, and currently-authorized
`scopes` for the App, wrapped under a `productionCredentials` envelope
(and a `sandboxCredentials` envelope when provisioned), plus
`sandboxStatus`. No secret is ever returned by this endpoint, by Cognito
design.
`sandboxStatus` is `pending` when the sandbox side has not been
provisioned, `awaiting_secret_claim` when the sandbox exists but the
developer has not yet collected its secret (call
`POST /applications/1.0/{sourcedApplicationId}/credentials/rotate` to
receive a claim URL without rotating production), and `provisioned`
when the sandbox credentials have been issued.
If you lost a client secret, call
`POST /applications/1.0/{sourcedApplicationId}/credentials/rotate`, which
mints a new client + secret and returns a fresh SSO-gated claim URL.
The `clientId` is stable across draft→active promotion: developers do
NOT need to rotate credentials when their App is approved. Scopes
expand on promotion; client identity does not. (Rotation is a separate,
developer-initiated event where the `clientId` does change.)
The caller must hold the `app:read_credentials` grant on `urn:app:`.
The grant is issued to the App's creator at registration time.
# Request an access token
Source: https://docs.timeback.com/beta/api-reference/platform/auth/request-an-access-token
/openapi/learn-with-ai/platform-api.yaml post /auth/1.0/token
Used to request a new token with the grant_type "client_credentials"
# Create or update a CFItemAssociation
Source: https://docs.timeback.com/beta/api-reference/platform/case/create-or-update-a-cfitemassociation
/openapi/learn-with-ai/platform-api.yaml put /case/1.1/CFItemAssociations/{sourcedId}
Creates a new CFItemAssociation or updates an existing one. The sourcedId in the path must match the identifier in the request body.
# Create or update a document
Source: https://docs.timeback.com/beta/api-reference/platform/case/create-or-update-a-document
/openapi/learn-with-ai/platform-api.yaml put /case/1.1/CFDocuments/{sourcedId}
Creates a new document or updates an existing one with the given sourcedId
# Create or update an item
Source: https://docs.timeback.com/beta/api-reference/platform/case/create-or-update-an-item
/openapi/learn-with-ai/platform-api.yaml put /case/1.1/CFItems/{sourcedId}
Creates a new item or updates an existing one with the given sourcedId
# Create or update an item type
Source: https://docs.timeback.com/beta/api-reference/platform/case/create-or-update-an-item-type
/openapi/learn-with-ai/platform-api.yaml put /case/1.1/CFItemTypes/{sourcedId}
Creates a new item type or updates an existing one with the given sourcedId
# Delete a CFItemAssociation
Source: https://docs.timeback.com/beta/api-reference/platform/case/delete-a-cfitemassociation
/openapi/learn-with-ai/platform-api.yaml delete /case/1.1/CFItemAssociations/{sourcedId}
Deletes an existing CFItemAssociation by its sourcedId. Returns 404 if the association is not found.
# Get a specific association
Source: https://docs.timeback.com/beta/api-reference/platform/case/get-a-specific-association
/openapi/learn-with-ai/platform-api.yaml get /case/1.1/CFAssociations/{sourcedId}
Returns a single association by its sourcedId
# Get a specific association grouping
Source: https://docs.timeback.com/beta/api-reference/platform/case/get-a-specific-association-grouping
/openapi/learn-with-ai/platform-api.yaml get /case/1.1/CFAssociationGroupings/{sourcedId}
Returns a single association grouping by its sourcedId
# Get a specific concept
Source: https://docs.timeback.com/beta/api-reference/platform/case/get-a-specific-concept
/openapi/learn-with-ai/platform-api.yaml get /case/1.1/CFConcepts/{sourcedId}
Returns a single concept by its sourcedId
# Get a specific document
Source: https://docs.timeback.com/beta/api-reference/platform/case/get-a-specific-document
/openapi/learn-with-ai/platform-api.yaml get /case/1.1/CFDocuments/{sourcedId}
Returns a single document by its sourcedId
# Get a specific item
Source: https://docs.timeback.com/beta/api-reference/platform/case/get-a-specific-item
/openapi/learn-with-ai/platform-api.yaml get /case/1.1/CFItems/{sourcedId}
Returns a single item by its sourcedId
# Get a specific item type
Source: https://docs.timeback.com/beta/api-reference/platform/case/get-a-specific-item-type
/openapi/learn-with-ai/platform-api.yaml get /case/1.1/CFItemTypes/{sourcedId}
Returns a single item type by its sourcedId and its children
# Get a specific license
Source: https://docs.timeback.com/beta/api-reference/platform/case/get-a-specific-license
/openapi/learn-with-ai/platform-api.yaml get /case/1.1/CFLicenses/{sourcedId}
Returns a single license by its sourcedId
# Get a specific package
Source: https://docs.timeback.com/beta/api-reference/platform/case/get-a-specific-package
/openapi/learn-with-ai/platform-api.yaml get /case/1.1/CFPackages/{sourcedId}
Returns a single package by its sourcedId
# Get a specific rubric
Source: https://docs.timeback.com/beta/api-reference/platform/case/get-a-specific-rubric
/openapi/learn-with-ai/platform-api.yaml get /case/1.1/CFRubrics/{sourcedId}
Returns a single rubric by its sourcedId
# Get a specific subject
Source: https://docs.timeback.com/beta/api-reference/platform/case/get-a-specific-subject
/openapi/learn-with-ai/platform-api.yaml get /case/1.1/CFSubjects/{sourcedId}
Returns a single subject by its sourcedId
# Get all associations for a specific item
Source: https://docs.timeback.com/beta/api-reference/platform/case/get-all-associations-for-a-specific-item
/openapi/learn-with-ai/platform-api.yaml get /case/1.1/CFItemAssociations/{sourcedId}
Returns the specified CFItem and all of its associations. If the identified record cannot be found then the 'unknownobject' status code must be reported.
# Get all documents
Source: https://docs.timeback.com/beta/api-reference/platform/case/get-all-documents
/openapi/learn-with-ai/platform-api.yaml get /case/1.1/CFDocuments
Returns a paginated list of all documents
# Get all item types
Source: https://docs.timeback.com/beta/api-reference/platform/case/get-all-item-types
/openapi/learn-with-ai/platform-api.yaml get /case/1.1/CFItemTypes
Returns a paginated list of all item types
# Get all subjects
Source: https://docs.timeback.com/beta/api-reference/platform/case/get-all-subjects
/openapi/learn-with-ai/platform-api.yaml get /case/1.1/CFSubjects
Returns a paginated list of all subjects
# Create or update class
Source: https://docs.timeback.com/beta/api-reference/platform/classes/create-or-update-class
/openapi/learn-with-ai/platform-api.yaml put /rostering/1.0/classes/{sourcedId}
Create a new class or update an existing class by its sourcedId
# Delete class
Source: https://docs.timeback.com/beta/api-reference/platform/classes/delete-class
/openapi/learn-with-ai/platform-api.yaml delete /rostering/1.0/classes/{sourcedId}
Delete an existing class by its sourcedId
# Get a class by ID
Source: https://docs.timeback.com/beta/api-reference/platform/classes/get-a-class-by-id
/openapi/learn-with-ai/platform-api.yaml get /rostering/1.0/classes/{sourcedId}
Returns a single class by its sourcedId
# Get all classes
Source: https://docs.timeback.com/beta/api-reference/platform/classes/get-all-classes
/openapi/learn-with-ai/platform-api.yaml get /rostering/1.0/classes
Returns a paginated list of classes
# Get students in a class
Source: https://docs.timeback.com/beta/api-reference/platform/classes/get-students-in-a-class
/openapi/learn-with-ai/platform-api.yaml get /rostering/1.0/classes/{sourcedId}/students
Returns a paginated list of students enrolled in a specific class
# Get teachers in a class
Source: https://docs.timeback.com/beta/api-reference/platform/classes/get-teachers-in-a-class
/openapi/learn-with-ai/platform-api.yaml get /rostering/1.0/classes/{sourcedId}/teachers
Returns a paginated list of teachers assigned to a specific class
# Create a consent record
Source: https://docs.timeback.com/beta/api-reference/platform/consent/create-a-consent-record
/openapi/learn-with-ai/platform-api.yaml post /consent/1.0/students/{studentId}/records
Records a parental consent status for a student in the append-only audit trail. When guardianId is supplied it must belong to a guardian with an active relationship to the student (otherwise 400); guardianId may be null only for a pending record whose metadata.consentMethod is awaiting_guardian.
# Get consent records by student ID
Source: https://docs.timeback.com/beta/api-reference/platform/consent/get-consent-records-by-student-id
/openapi/learn-with-ai/platform-api.yaml get /consent/1.0/students/{studentId}/records
Returns consent history for a specific student, sorted by most recent first
# Initiate the consent flow for a student and guardian
Source: https://docs.timeback.com/beta/api-reference/platform/consent/initiate-the-consent-flow-for-a-student-and-guardian
/openapi/learn-with-ai/platform-api.yaml post /consent/1.0/students/{studentId}/consent-requests
Machine-to-machine entry point for a partner system to start TimeBack's email consent flow for an explicit student and guardian: generates the DocuSign envelope and emails the guardian an HMAC-protected signing link. Gated on the consent.write scope and the caller owning the student's org. Returns 400 if the student's consent is already granted, or if a named guardian has no active relationship to the student.
# Create or update course
Source: https://docs.timeback.com/beta/api-reference/platform/courses/create-or-update-course
/openapi/learn-with-ai/platform-api.yaml put /rostering/1.0/courses/{sourcedId}
Create a new course or update an existing course by its sourcedId
# Delete course
Source: https://docs.timeback.com/beta/api-reference/platform/courses/delete-course
/openapi/learn-with-ai/platform-api.yaml delete /rostering/1.0/courses/{sourcedId}
Delete an existing course by its sourcedId
# Get all courses
Source: https://docs.timeback.com/beta/api-reference/platform/courses/get-all-courses
/openapi/learn-with-ai/platform-api.yaml get /rostering/1.0/courses
Returns a paginated list of all courses
# Get classes for a course
Source: https://docs.timeback.com/beta/api-reference/platform/courses/get-classes-for-a-course
/openapi/learn-with-ai/platform-api.yaml get /rostering/1.0/courses/{sourcedId}/classes
Returns a paginated list of classes for a specific course
# Get course by ID
Source: https://docs.timeback.com/beta/api-reference/platform/courses/get-course-by-id
/openapi/learn-with-ai/platform-api.yaml get /rostering/1.0/courses/{sourcedId}
Retrieve a specific course by its sourcedId
# Get course curriculum tree
Source: https://docs.timeback.com/beta/api-reference/platform/curriculum/get-course-curriculum-tree
/openapi/learn-with-ai/platform-api.yaml get /curriculum/1.0/documents/{documentId}/courses/{courseId}
Retrieve the full hierarchical curriculum tree for a specific course, recursively fetching all children using isChildOf associations
# Get document courses
Source: https://docs.timeback.com/beta/api-reference/platform/curriculum/get-document-courses
/openapi/learn-with-ai/platform-api.yaml get /curriculum/1.0/documents/{documentId}/courses
Retrieve a list of courses (CFItems with itemType.typeCode='course') for a specific document
# Get the demographic for a user
Source: https://docs.timeback.com/beta/api-reference/platform/demographics/get-the-demographic-for-a-user
/openapi/learn-with-ai/platform-api.yaml get /rostering/1.0/users/{sourcedId}/demographics
Returns the demographic record linked to the user identified by `{sourcedId}`. The path parameter is the **user's** `sourcedId` (the value used with `upsertUser` / `upsertStudent`), NOT the demographic record's own `sourcedId`. Returns 404 if no demographic exists for the user.
# Get a specific enrollment
Source: https://docs.timeback.com/beta/api-reference/platform/enrollments/get-a-specific-enrollment
/openapi/learn-with-ai/platform-api.yaml get /rostering/1.0/enrollments/{sourcedId}
Returns a specific enrollment by sourcedId
# Get all enrollments
Source: https://docs.timeback.com/beta/api-reference/platform/enrollments/get-all-enrollments
/openapi/learn-with-ai/platform-api.yaml get /rostering/1.0/enrollments
Returns a list of enrollments
# Create or update an organization
Source: https://docs.timeback.com/beta/api-reference/platform/organizations/create-or-update-an-organization
/openapi/learn-with-ai/platform-api.yaml put /rostering/1.0/orgs/{sourcedId}
Creates or updates an organization in the system
# Get all organizations
Source: https://docs.timeback.com/beta/api-reference/platform/organizations/get-all-organizations
/openapi/learn-with-ai/platform-api.yaml get /rostering/1.0/orgs
Returns a paginated list of organizations
# Get organization by ID
Source: https://docs.timeback.com/beta/api-reference/platform/organizations/get-organization-by-id
/openapi/learn-with-ai/platform-api.yaml get /rostering/1.0/orgs/{sourcedId}
Returns a specific organization by its sourcedId
# Get resolved organization configuration
Source: https://docs.timeback.com/beta/api-reference/platform/organizations/get-resolved-organization-configuration
/openapi/learn-with-ai/platform-api.yaml get /rostering/1.0/orgs/{sourcedId}/config
Returns the resolved configuration for an organization, with values inherited from ancestors when not explicitly set on the org.
# Write organization configuration
Source: https://docs.timeback.com/beta/api-reference/platform/organizations/write-organization-configuration
/openapi/learn-with-ai/platform-api.yaml put /rostering/1.0/orgs/{sourcedId}/config
Write semantics per key in the body:
- Key absent: no-op.
- Value `null`: delete the row (revert to inherited / default).
- Value present: upsert. Registered keys are validated; invalid values return 400.
# Create a new student
Source: https://docs.timeback.com/beta/api-reference/platform/students/create-a-new-student
/openapi/learn-with-ai/platform-api.yaml put /rostering/1.0/students
Creates a new student in the system
# Delete a student agent
Source: https://docs.timeback.com/beta/api-reference/platform/students/delete-a-student-agent
/openapi/learn-with-ai/platform-api.yaml delete /rostering/1.0/students/{sourcedId}/agents/{agentId}
Deletes an agent relationship for a student by agentId
# Get classes for a specific student
Source: https://docs.timeback.com/beta/api-reference/platform/students/get-classes-for-a-specific-student
/openapi/learn-with-ai/platform-api.yaml get /rostering/1.0/students/{sourcedId}/classes
Returns a paginated list of classes for a specific student
# Upsert a student agent
Source: https://docs.timeback.com/beta/api-reference/platform/students/upsert-a-student-agent
/openapi/learn-with-ai/platform-api.yaml put /rostering/1.0/students/{sourcedId}/agents/{agentId}
Creates or updates an agent relationship for a student (e.g., parent, guardian)
# Get agents for a user
Source: https://docs.timeback.com/beta/api-reference/platform/users/get-agents-for-a-user
/openapi/learn-with-ai/platform-api.yaml get /rostering/1.0/users/{sourcedId}/agents
Returns all agents associated with a specific user
# Get all users
Source: https://docs.timeback.com/beta/api-reference/platform/users/get-all-users
/openapi/learn-with-ai/platform-api.yaml get /rostering/1.0/users
Returns a list of users with optional filtering by role
# Get classes for a user
Source: https://docs.timeback.com/beta/api-reference/platform/users/get-classes-for-a-user
/openapi/learn-with-ai/platform-api.yaml get /rostering/1.0/users/{sourcedId}/classes
Returns all classes associated with a specific user
# Get linked users for a user
Source: https://docs.timeback.com/beta/api-reference/platform/users/get-linked-users-for-a-user
/openapi/learn-with-ai/platform-api.yaml get /rostering/1.0/users/{sourcedId}/linked-users
Returns the users currently linked to a specific user through agent relationships. Relationships that have been ended (inactive) are excluded, matching the behavior of the agents endpoint.
# Get profiles for a user
Source: https://docs.timeback.com/beta/api-reference/platform/users/get-profiles-for-a-user
/openapi/learn-with-ai/platform-api.yaml get /rostering/1.0/users/{sourcedId}/profiles
Returns the profiles associated with a specific user, with optional filtering by application, vendor, or profile type.
# Upsert a user
Source: https://docs.timeback.com/beta/api-reference/platform/users/upsert-a-user
/openapi/learn-with-ai/platform-api.yaml put /rostering/1.0/users/{sourcedId}
Creates or updates a user by their sourcedId
# Send session heartbeat
Source: https://docs.timeback.com/beta/api-reference/platform/caliper/send-session-heartbeat
/openapi/learn-with-ai/platform-api.yaml post /events/1.0/sessions/{sessionId}/heartbeat
Lightweight endpoint for browser clients to signal that a session is still active.
Extends the session's endedAtTime to the current time.
Only works for sessions with `requiresHeartbeat: true` in their extensions.
# Submit Caliper Envelope
Source: https://docs.timeback.com/beta/api-reference/platform/caliper/submit-caliper-envelope
/openapi/learn-with-ai/platform-api.yaml post /caliper/v1p2
Receives a Caliper v1.2 Envelope, validates the payload against the Caliper specification, and sends the contained Event and Entity data for processing. The server acknowledges receipt with 202 Accepted per the Caliper sensor transport specification.
# Assign a learning block to a student
Source: https://docs.timeback.com/beta/api-reference/platform/competency-track/assign-a-learning-block-to-a-student
/openapi/learn-with-ai/platform-api.yaml post /competency-track/1.0/assignments
Creates a new student learning block assignment. For non-dynamic blocks, competencies (CFItems) are copied from the learning block definition onto the assignment immediately. For dynamic blocks, the assignment is created with empty CFItems pending placement.
# Cancel a student's learning block assignment
Source: https://docs.timeback.com/beta/api-reference/platform/competency-track/cancel-a-students-learning-block-assignment
/openapi/learn-with-ai/platform-api.yaml delete /competency-track/1.0/assignments/{sourcedId}
Soft-deletes the assignment, deactivates associated assessment profiles and placement records owned by the competency track. Returns 204 No Content.
# Create or update a learning block
Source: https://docs.timeback.com/beta/api-reference/platform/competency-track/create-or-update-a-learning-block
/openapi/learn-with-ai/platform-api.yaml put /competency-track/1.0/learning-blocks/{sourcedId}
Upsert a learning block — if a learning block with the given sourcedId exists, update it; otherwise, create it.
# List learning blocks for a learning app
Source: https://docs.timeback.com/beta/api-reference/platform/competency-track/list-learning-blocks-for-a-learning-app
/openapi/learn-with-ai/platform-api.yaml get /competency-track/1.0/learning-blocks
Returns a paginated list of learning blocks scoped to the given `learningAppId`.
# Map competencies to assessment applications
Source: https://docs.timeback.com/beta/api-reference/platform/competency-track/map-competencies-to-assessment-applications
/openapi/learn-with-ai/platform-api.yaml post /competency-track/1.0/assessments/mappings
Creates or updates mappings between CFItems (competencies) and assessment applications. Each CFItem maps to exactly one assessment app. If a CFItem already has a mapping, the new mapping replaces it.
# Read a learning block
Source: https://docs.timeback.com/beta/api-reference/platform/competency-track/read-a-learning-block
/openapi/learn-with-ai/platform-api.yaml get /competency-track/1.0/learning-blocks/{sourcedId}
Returns the learning block identified by `sourcedId`, including its learning app reference, dynamic flag, CFItems (for non-dynamic blocks), and CFSubject (for dynamic blocks).
# Read a student's learning block assignment
Source: https://docs.timeback.com/beta/api-reference/platform/competency-track/read-a-students-learning-block-assignment
/openapi/learn-with-ai/platform-api.yaml get /competency-track/1.0/assignments/{sourcedId}
Returns the assignment identified by `sourcedId`, including the student, the learning block reference, and the per-assignment CFItem snapshot.
# Trigger a mastery assessment for a student
Source: https://docs.timeback.com/beta/api-reference/platform/competency-track/trigger-a-mastery-assessment-for-a-student
/openapi/learn-with-ai/platform-api.yaml post /competency-track/1.0/assessments
Signal that a student has completed learning and is ready for mastery assessment. Validates the student has an active assignment with resolved CFItems, looks up assessment mappings, and creates OneRosterUserProfile entries linking the student to each assessment app.
# Get external grader
Source: https://docs.timeback.com/beta/api-reference/platform/content-grading/get-external-grader
/openapi/learn-with-ai/platform-api.yaml get /content/1.0/external-graders/{graderId}
Fetches a single external grader by id. Only the registering caller can read it.
# List external graders
Source: https://docs.timeback.com/beta/api-reference/platform/content-grading/list-external-graders
/openapi/learn-with-ai/platform-api.yaml get /content/1.0/external-graders
Lists the external graders registered by the caller. Cross-client visibility is not exposed.
# Register an external grader
Source: https://docs.timeback.com/beta/api-reference/platform/content-grading/register-an-external-grader
/openapi/learn-with-ai/platform-api.yaml post /content/1.0/external-graders
Registers an author-hosted grading endpoint as a Tier 2 external grader.
The platform makes a live test call against the URL; only endpoints that return a coercible grading response are accepted.
Returns the registered grader's stable id, which content items reference to route grading through the platform proxy.
# Revoke external grader
Source: https://docs.timeback.com/beta/api-reference/platform/content-grading/revoke-external-grader
/openapi/learn-with-ai/platform-api.yaml delete /content/1.0/external-graders/{graderId}
Marks the external grader as revoked. The id stays resolvable so bindings on existing items are not orphaned; a revoked grader will fail future grade calls.
# Update external grader
Source: https://docs.timeback.com/beta/api-reference/platform/content-grading/update-external-grader
/openapi/learn-with-ai/platform-api.yaml put /content/1.0/external-graders/{graderId}
Replaces the mutable fields of an external grader. If the incoming url differs from the stored one, the platform re-runs the live test call before persisting; a matching url with only a status flip skips the probe.
# Get student course progress
Source: https://docs.timeback.com/beta/api-reference/platform/curriculum/get-student-course-progress
/openapi/learn-with-ai/platform-api.yaml get /curriculum/1.0/courses/{courseId}/students/{studentId}/progress
Returns the full curriculum tree for a course with per-item completion status for a specific student. Each node contains the item identifier, type code, completion flag, completion timestamp, and children. Completion is determined by Caliper AssignableEvent.Completed events matching the student and curriculum items.
# Get session enforcement state for poll/ack proxying
Source: https://docs.timeback.com/beta/api-reference/platform/insights/get-session-enforcement-state-for-pollack-proxying
/openapi/learn-with-ai/platform-api.yaml get /insights/1.0/sessions/{sessionId}/enforcement-state
Assembles authority, per-key strike counts, active lockout, and in-session notification
insights from the pinned snapshot and platform strike ledger. `sessionId` is the
caliper session id. Requires a pinned session (409 if not).
`sessionContext`, and each notification's `presentationVariant` and `enforcement`, are
always returned. They are additive: a client that reads only the legacy fields sees an
unchanged response.
V2 time labels (`violationTimeLabel`, `suspendedTimeLabel`, recap `acknowledgedTimeLabel`,
and the TEST_SUSPENDED `headerSubline`) are relative strings derived per read from stored
UTC timestamps and the response `serverTime` (`just now` / `a minute ago` / `N minutes ago`).
The stored `render_payload` keeps the UTC absolute label as the audit record.
This endpoint is a pure read of recorded platform state; it does not run notification
selection or mint presentations. A reminder for an acknowledged still-open incident is
scheduled by the platform at cooldown expiry via internal evaluation, not on this request.
# Get session insights
Source: https://docs.timeback.com/beta/api-reference/platform/insights/get-session-insights
/openapi/learn-with-ai/platform-api.yaml get /insights/1.0/sessions/{sessionId}
Retrieve insights for a specific session with pagination.
When no `insights_session` row exists for the given `sessionId`
(caliperSessionId) — for example, immediately after a session
starts but before the asynchronous Caliper ingestion pipeline has
materialized the row — the endpoint returns `200` with an empty
result (`insights: []`, `total: 0`, and a default session summary
with zero duration). This mirrors the behavior of `getUserInsights`
when no sessions match. Findings stamped `rolloutState=SHADOW` are omitted;
unstamped (`null`) findings remain visible.
# Get user insights
Source: https://docs.timeback.com/beta/api-reference/platform/insights/get-user-insights
/openapi/learn-with-ai/platform-api.yaml get /insights/1.0/users/{userId}
Retrieve insights for a specific user with optional filtering and pagination. Findings stamped `rolloutState=SHADOW` are omitted; unstamped (`null`) findings remain visible.
# Get user insights overview
Source: https://docs.timeback.com/beta/api-reference/platform/insights/get-user-insights-overview
/openapi/learn-with-ai/platform-api.yaml get /insights/1.0/users/{userId}/overview
Retrieve aggregated overview (trend and breakdown) for a user within a date range
# Get user sessions
Source: https://docs.timeback.com/beta/api-reference/platform/insights/get-user-sessions
/openapi/learn-with-ai/platform-api.yaml get /insights/1.0/users/{userId}/sessions
Retrieve sessions for a specific user with optional application filtering and pagination. `insightGroups` excludes sessions that have no caller-visible insight of a member type and scopes the returned metrics to that filter. `category` on this endpoint scopes metrics only and never removes a session.
# List Insight Groups
Source: https://docs.timeback.com/beta/api-reference/platform/insights/list-insight-groups
/openapi/learn-with-ai/platform-api.yaml get /insights/1.0/insight-groups
Retrieve the published Insight Groups with family, membership rule, customer-facing flag, and a count of insight types visible to the authenticated caller in each group. Results do not vary by organization. External callers count only `visibility=external` types; internal callers count active internal and external types. Catalog reads are cached in-process for up to 10 minutes; registry changes become visible across warm Lambda containers within that bound.
# List insight types
Source: https://docs.timeback.com/beta/api-reference/platform/insights/list-insight-types
/openapi/learn-with-ai/platform-api.yaml get /insights/1.0/types
Retrieve all active registered insight types visible to the authenticated caller, optionally filtered by category. Membership is global and does not vary by organization. External callers receive only `visibility=external` types; internal callers receive active internal and external types. Locale message bundles and organization policy are not part of this endpoint. Waste Meter consumers select entries with `isWaste=true` from this response. Catalog reads are cached in-process for up to 10 minutes; registry changes become visible across warm Lambda containers within that bound.
# List sessions with waste and insight enrichment
Source: https://docs.timeback.com/beta/api-reference/platform/insights/list-sessions-with-waste-and-insight-enrichment
/openapi/learn-with-ai/platform-api.yaml get /insights/1.0/sessions
Retrieve paginated list of sessions with organization or user scoping, enriched with waste metrics and insights summary. Supports filtering by waste percentage, waste duration, and insight types. Enrichment for insightsSummary can be selected independently of the session filter via enrichmentInsightTypes and enrichmentInsightCategories.
# Mint a session share URL
Source: https://docs.timeback.com/beta/api-reference/platform/insights/mint-a-session-share-url
/openapi/learn-with-ai/platform-api.yaml post /insights/1.0/sessions/{sessionId}/shares
Records one SESSION share for the given caliper session and returns the share id,
the Vault share URL, and the instant the parent-facing link stops resolving
(always exactly 604800 seconds after the row was written). The caller must hold
`events.readonly` and an organization grant that covers the student. There is
no request body. Each call mints a new share; nothing is deduplicated and no
existing share is extended. Store the returned `url`; do not assemble a Vault
session path from the session id.
# Create a webhook subscription
Source: https://docs.timeback.com/beta/api-reference/platform/webhooks/create-a-webhook-subscription
/openapi/learn-with-ai/platform-api.yaml post /webhooks/1.0/{sourcedApplicationId}/subscriptions
Registers a webhook subscription tied to the App's auth client. The
platform POSTs a signed, thin event notification to `url` whenever an event
whose type is in `eventTypes` is attributed to an org within the auth client's
org scope. Events with empty org attribution (for example `application.*`)
are delivered only to internal auth clients; an org-scoped external
subscription can list those types but will not receive them.
The response includes `signingSecret` — the HMAC-SHA256 secret used to verify
deliveries. It is returned exactly once at creation time and cannot be
retrieved again; rotate it via the rotate-secret endpoint if lost.
The caller must hold the `app:manage_webhooks` grant on `urn:app:`.
# Delete a webhook subscription
Source: https://docs.timeback.com/beta/api-reference/platform/webhooks/delete-a-webhook-subscription
/openapi/learn-with-ai/platform-api.yaml delete /webhooks/1.0/{sourcedApplicationId}/subscriptions/{subscriptionId}
Soft-deletes the subscription. No further deliveries are attempted.
The caller must hold the `app:manage_webhooks` grant on `urn:app:`.
# Get a webhook subscription
Source: https://docs.timeback.com/beta/api-reference/platform/webhooks/get-a-webhook-subscription
/openapi/learn-with-ai/platform-api.yaml get /webhooks/1.0/{sourcedApplicationId}/subscriptions/{subscriptionId}
Returns a single webhook subscription tied to the App. The signing secret
is not included.
The caller must hold the `app:manage_webhooks` grant on `urn:app:`.
# List the App's webhook subscriptions
Source: https://docs.timeback.com/beta/api-reference/platform/webhooks/list-the-apps-webhook-subscriptions
/openapi/learn-with-ai/platform-api.yaml get /webhooks/1.0/{sourcedApplicationId}/subscriptions
Returns every webhook subscription tied to the App. The signing secret is
never included in list/get responses — it is only revealed at create and
rotate time.
The caller must hold the `app:manage_webhooks` grant on `urn:app:`.
# Rotate a webhook subscription's signing secret
Source: https://docs.timeback.com/beta/api-reference/platform/webhooks/rotate-a-webhook-subscriptions-signing-secret
/openapi/learn-with-ai/platform-api.yaml post /webhooks/1.0/{sourcedApplicationId}/subscriptions/{subscriptionId}/rotate-secret
Issues a new signing secret and returns it once. The previous secret keeps
verifying for a rotation window (so in-flight deliveries signed with the old
secret still validate), then expires.
The caller must hold the `app:manage_webhooks` grant on `urn:app:`.
# Update a webhook subscription
Source: https://docs.timeback.com/beta/api-reference/platform/webhooks/update-a-webhook-subscription
/openapi/learn-with-ai/platform-api.yaml put /webhooks/1.0/{sourcedApplicationId}/subscriptions/{subscriptionId}
Updates the subscription's `url`, `eventTypes`, and/or `status`. `status`
accepts only `active`/`paused` (resume/pause); `disabled` is reserved for
platform auto-disable after repeated terminal delivery failures.
The caller must hold the `app:manage_webhooks` grant on `urn:app:`.
# Get user XP ledger entries
Source: https://docs.timeback.com/beta/api-reference/platform/xp/get-user-xp-ledger-entries
/openapi/learn-with-ai/platform-api.yaml get /xp/1.0/users/{userId}/entries
Retrieves XP ledger entries for a user from the XP Ledger table.
Supports optional filtering by applicationId, curriculumId, and date range.
Supports pagination for efficient data retrieval.