PlaycademyPlaycademy

Creating Courses

Structure and create courses with OneRoster

Overview

Courses define what content your app offers and control which students can access it.

Course creation workflow showing the steps to create courses, components, and resources

Structure is flexible

OneRoster lets you organize content however fits your app.

Some apps use deep hierarchies (Course, Units, Lessons, Activities), others keep it simpler.

The examples below show one common pattern, but you can adapt the depth and granularity to match your content model.

Choose Environment

Decide on Staging for development or Production for live apps.

Authenticate

Get an access token using your Timeback credentials (see Authentication).

Create the Course

POST to /courses with metadata like title, grades, and subjects.

Add Components

POST to /courses/components to organize content into units, modules, or lessons.

POST to /courses/component-resources to attach learning materials to your structure.

Using Playcademy?

Playcademy handles course setup for you.

Run playcademy timeback setup to create and sync courses from your config.

Environments

Find the OneRoster API base URL for your target environment:

EnvironmentBase URL
Staginghttps://api.staging.alpha-1edtech.ai
Productionhttps://api.alpha-1edtech.ai

Create a Course

Create the top-level course container:

Request
curl -X POST $TIMEBACK_API_URL/ims/oneroster/rostering/v1p2/courses \  -H "Authorization: Bearer $ACCESS_TOKEN" \  -H "Content-Type: application/json" \  -d '{    "course": {      "sourcedId": "math-grade-5",      "status": "active",      "title": "Math Grade 5",      "courseCode": "MATH-G5",      "grades": ["05"],      "subjects": ["Math"],      "org": { "sourcedId": "your-org-id" }    }  }'
const response = await fetch(`${TIMEBACK_API_URL}/ims/oneroster/rostering/v1p2/courses`, {    method: 'POST',    headers: {        Authorization: `Bearer ${accessToken}`,        'Content-Type': 'application/json',    },    body: JSON.stringify({        course: {            sourcedId: 'math-grade-5',            status: 'active',            title: 'Math Grade 5',            courseCode: 'MATH-G5',            grades: ['05'],            subjects: ['Math'],            org: { sourcedId: 'your-org-id' },        },    }),})
FieldDescription
sourcedIdUnique identifier for the course
gradesArray of grade levels (e.g., ["03", "04", "05"])
subjectsArray of subjects (e.g., ["Math", "Science"])

Unique IDs are required

OneRoster requires you to supply your own unique sourcedId for each resource.

This applies to courses, components, and all other entities. The API will not auto-generate IDs for you.

Add Components

Components let you organize content within a course: units, modules, lessons, or any hierarchy that fits your app.

Components can nest arbitrarily deep via the parent field.

Request
curl -X POST $TIMEBACK_API_URL/ims/oneroster/rostering/v1p2/courses/components \  -H "Authorization: Bearer $ACCESS_TOKEN" \  -H "Content-Type: application/json" \  -d '{    "courseComponent": {      "sourcedId": "unit-1-fractions",      "status": "active",      "title": "Unit 1: Fractions",      "sortOrder": 1,      "course": { "sourcedId": "math-grade-5" },      "parent": null    }  }'
const response = await fetch(    `${TIMEBACK_API_URL}/ims/oneroster/rostering/v1p2/courses/components`,    {        method: 'POST',        headers: {            Authorization: `Bearer ${accessToken}`,            'Content-Type': 'application/json',        },        body: JSON.stringify({            courseComponent: {                sourcedId: 'unit-1-fractions',                status: 'active',                title: 'Unit 1: Fractions',                sortOrder: 1,                course: { sourcedId: 'math-grade-5' },                parent: null,            },        }),    },)
FieldDescription
parentSet to null for top-level components, or reference another component to nest
sortOrderControls display order (1, 2, 3...)

Attach Resources

Resources are the actual learning materials: e.g. videos, articles, quizzes.

Create a Resource

Define the learning material with vendor info and metadata.

Request
curl -X POST $TIMEBACK_API_URL/ims/oneroster/resources/v1p2/resources \  -H "Authorization: Bearer $ACCESS_TOKEN" \  -H "Content-Type: application/json" \  -d '{    "resource": {      "sourcedId": "math-app-resource",      "status": "active",      "title": "Math App",      "vendorResourceId": "math-app-v1",      "vendorId": "your-vendor-id",      "applicationId": "your-app-id",      "importance": "primary",      "metadata": {        "type": "interactive",        "subject": "Math",        "grades": ["05"],        "xp": 100      }    }  }'
await fetch(`${TIMEBACK_API_URL}/ims/oneroster/resources/v1p2/resources`, {    method: 'POST',    headers: {        Authorization: `Bearer ${accessToken}`,        'Content-Type': 'application/json',    },    body: JSON.stringify({        resource: {            sourcedId: 'math-app-resource',            status: 'active',            title: 'Math App',            vendorResourceId: 'math-app-v1',            vendorId: 'your-vendor-id',            applicationId: 'your-app-id',            importance: 'primary',            metadata: {                type: 'interactive',                subject: 'Math',                grades: ['05'],                xp: 100,            },        },    }),})
FieldDescription
vendorResourceIdYour internal identifier for this resource
vendorIdYour vendor/organization identifier
applicationIdYour application identifier
importanceprimary or secondary
metadata.typeResource type: interactive, video, text, qti, audio, visual
metadata.xpXP awarded for completing this resource

Connect the resource to a component so it appears in the course structure.

Request
curl -X POST $TIMEBACK_API_URL/ims/oneroster/rostering/v1p2/courses/component-resources \  -H "Authorization: Bearer $ACCESS_TOKEN" \  -H "Content-Type: application/json" \  -d '{    "componentResource": {      "sourcedId": "unit-1-math-app",      "status": "active",      "title": "Math App - Unit 1",      "courseComponent": { "sourcedId": "unit-1-fractions" },      "resource": { "sourcedId": "math-app-resource" },      "sortOrder": 1,      "lessonType": "quiz"    }  }'
await fetch(`${TIMEBACK_API_URL}/ims/oneroster/rostering/v1p2/courses/component-resources`, {    method: 'POST',    headers: {        Authorization: `Bearer ${accessToken}`,        'Content-Type': 'application/json',    },    body: JSON.stringify({        componentResource: {            sourcedId: 'unit-1-math-app',            status: 'active',            title: 'Math App - Unit 1',            courseComponent: { sourcedId: 'unit-1-fractions' },            resource: { sourcedId: 'math-app-resource' },            sortOrder: 1,            lessonType: 'quiz',        },    }),})
FieldDescription
titleDisplay name for this link
courseComponentReference to the component this resource belongs to
resourceReference to the resource being linked
sortOrderControls display order within the component (1, 2, 3...)
lessonTypeContent type: quiz, placement, test-out, unit-test, or null

What's Next?

On this page