Skip to main content

Get Course Structure

Learn how to retrieve course structure, sections, lessons, and completion criteria using the Thought Industries REST API.

Overview

The Course Structure endpoints allow you to retrieve the complete hierarchical structure of a course, including:

  • Course ID, title, and status
  • Sections (with ID, title, releaseAt)
  • Lessons within each section (with ID, title, accessLevel)
  • Topics within each lesson (with ID, title, type)
  • Completion criteria

Authentication

All requests require Bearer token authentication:

-H "Authorization: Bearer ${API_TOKEN}"
-H "Content-Type: application/json"

Get Complete Course Structure

Retrieve the full hierarchical course structure in a single request.

Endpoint

GET ${BASE_URL}/v2/courses/{courseId}/structure

Request Example

curl -X GET "${BASE_URL}/v2/courses/c76d5b5d-b7b1-4258-a116-07d9228884ca/structure" \
-H "Authorization: Bearer ${API_TOKEN}" \
-H "Content-Type: application/json"

Response Example

{
"id": "c76d5b5d-b7b1-4258-a116-07d9228884ca",
"title": "Multi SCORM page course",
"status": "draft",
"sections": [
{
"id": "61ee198a-ccd8-4046-9fca-5717d88f2e93",
"title": "Section 1",
"releaseAt": null,
"lessons": [
{
"id": "93e6076b-ba84-4012-84ff-73c0367006e0",
"title": "To Begin",
"accessLevel": null,
"topics": [
{
"id": "1366501c-3740-4a30-9464-1a2f111499ee",
"title": "Getting Started",
"type": "text",
"editableByChildren": false
}
]
},
{
"id": "619004f9-213a-45e0-843d-c4c93db941de",
"title": "Lesson 1.2",
"accessLevel": null,
"topics": [
{
"id": "fdbec9af-e134-4c42-81c4-05a056dd40ca",
"title": "SCORM 1.2",
"type": "shareableContentObject",
"editableByChildren": false
},
{
"id": "ef8967dc-dace-4215-baf0-0258fcd2fff5",
"title": "Quiz 1.2",
"type": "quiz",
"editableByChildren": false
}
]
}
]
},
{
"id": "5c7a5ae8-afeb-4ddd-acf9-8aebd6328048",
"title": "Section 2",
"releaseAt": null,
"lessons": [
{
"id": "7d1a7a0f-574e-447b-9755-476ff007c880",
"title": "Exam and Conclusion",
"accessLevel": null,
"topics": [
{
"id": "fc36dc1d-d683-4760-ae8d-97809d3c30a8",
"title": "Final Exam",
"type": "test",
"editableByChildren": false
},
{
"id": "61237fd7-89d2-4bc3-9431-374dc5263edb",
"title": "Last page",
"type": "text",
"editableByChildren": false
}
]
}
]
}
],
"completionCriteria": [
{
"id": "5c13255a-e1ee-42ef-a79d-d4a7d839e578",
"type": "coursePercentViewed",
"coursePercentViewed": 90,
"articlePercentViewed": null,
"articleTimeViewedInSeconds": null,
"videoPercentViewed": null,
"courseAssignmentComplete": null,
"courseAssessmentPassed": null,
"courseTopicViewed": null,
"courseMeetingAttended": null,
"scormComplete": null,
"surveyGizmoComplete": null,
"xApiComplete": null,
"bongoAssignmentCompleted": null
},
{
"id": "4a33f62e-323f-4f9b-a502-40aadf39ef87",
"type": "courseAssessmentPassed",
"coursePercentViewed": null,
"articlePercentViewed": null,
"articleTimeViewedInSeconds": null,
"videoPercentViewed": null,
"courseAssignmentComplete": null,
"courseAssessmentPassed": "fc36dc1d-d683-4760-ae8d-97809d3c30a8",
"courseTopicViewed": null,
"courseMeetingAttended": null,
"scormComplete": null,
"surveyGizmoComplete": null,
"xApiComplete": null,
"bongoAssignmentCompleted": null
}
]
}

Get Course Sections

Retrieve only the sections of a course without nested lessons.

Endpoint

GET ${BASE_URL}/v2/courses/{courseId}/sections

Request Example

curl -X GET "${BASE_URL}/v2/courses/c76d5b5d-b7b1-4258-a116-07d9228884ca/sections" \
-H "Authorization: Bearer ${API_TOKEN}" \
-H "Content-Type: application/json"

Response Example

{
"sections": [
{
"id": "61ee198a-ccd8-4046-9fca-5717d88f2e93",
"title": "Section 1",
"slug": "section-1",
"status": "published",
"releaseAt": null,
"parentSectionId": null,
"lessonCount": 3
},
{
"id": "5c7a5ae8-afeb-4ddd-acf9-8aebd6328048",
"title": "Section 2",
"slug": "section-2",
"status": "published",
"releaseAt": null,
"parentSectionId": null,
"lessonCount": 1
}
],
"pageInfo": {
"total": 2,
"currentPage": 1,
"perPage": 50,
"hasMore": false
}
}

Get Course Lessons

Retrieve all lessons within a course.

Endpoint

GET ${BASE_URL}/v2/courses/{courseId}/lessons

Request Example

curl -X GET "${BASE_URL}/v2/courses/c76d5b5d-b7b1-4258-a116-07d9228884ca/lessons" \
-H "Authorization: Bearer ${API_TOKEN}" \
-H "Content-Type: application/json"

Response Example

{
"lessons": [
{
"id": "93e6076b-ba84-4012-84ff-73c0367006e0",
"title": "To Begin",
"slug": "to-begin",
"accessLevel": null,
"sectionId": "61ee198a-ccd8-4046-9fca-5717d88f2e93",
"sectionTitle": "Section 1",
"topicCount": 1
},
{
"id": "619004f9-213a-45e0-843d-c4c93db941de",
"title": "Lesson 1.2",
"slug": "lesson-12",
"accessLevel": null,
"sectionId": "61ee198a-ccd8-4046-9fca-5717d88f2e93",
"sectionTitle": "Section 1",
"topicCount": 2
},
{
"id": "cb52cdaf-19b2-4bb5-9f17-865de65474ee",
"title": "Lesson 1.3",
"slug": "lesson-13",
"accessLevel": null,
"sectionId": "61ee198a-ccd8-4046-9fca-5717d88f2e93",
"sectionTitle": "Section 1",
"topicCount": 2
},
{
"id": "7d1a7a0f-574e-447b-9755-476ff007c880",
"title": "Exam and Conclusion",
"slug": "exam-and-conclusion",
"accessLevel": null,
"sectionId": "5c7a5ae8-afeb-4ddd-acf9-8aebd6328048",
"sectionTitle": "Section 2",
"topicCount": 2
}
],
"pageInfo": {
"total": 4,
"currentPage": 1,
"perPage": 50,
"hasMore": false
}
}

Get Course Completion Criteria

Retrieve the completion criteria for a course.

Endpoint

GET ${BASE_URL}/v2/courses/{courseId}/completionCriteria

Request Example

curl -X GET "${BASE_URL}/v2/courses/c76d5b5d-b7b1-4258-a116-07d9228884ca/completionCriteria" \
-H "Authorization: Bearer ${API_TOKEN}" \
-H "Content-Type: application/json"

Response Example

[
{
"id": "5c13255a-e1ee-42ef-a79d-d4a7d839e578",
"type": "coursePercentViewed",
"coursePercentViewed": 90,
"articlePercentViewed": null,
"articleTimeViewedInSeconds": null,
"videoPercentViewed": null,
"courseAssignmentComplete": null,
"courseAssessmentPassed": null,
"courseTopicViewed": null,
"courseMeetingAttended": null,
"scormComplete": null,
"surveyGizmoComplete": null,
"xApiComplete": null,
"bongoAssignmentCompleted": null
},
{
"id": "4a33f62e-323f-4f9b-a502-40aadf39ef87",
"type": "courseAssessmentPassed",
"coursePercentViewed": null,
"articlePercentViewed": null,
"articleTimeViewedInSeconds": null,
"videoPercentViewed": null,
"courseAssignmentComplete": null,
"courseAssessmentPassed": "fc36dc1d-d683-4760-ae8d-97809d3c30a8",
"courseTopicViewed": null,
"courseMeetingAttended": null,
"scormComplete": null,
"surveyGizmoComplete": null,
"xApiComplete": null,
"bongoAssignmentCompleted": null
}
]

Complete Endpoint Reference

Course Groups

MethodEndpointDescription
GET/v2/courseGroupsList all course groups
GET/v2/courseGroups/:idGet specific course group by ID
GET/v2/courseGroups/slug/:slugGet course group by slug
GET/v2/courseGroups/:id/coursesList courses in a course group
GET/v2/courseGroups/:id/displayCourseGet display course for group

Courses

MethodEndpointDescription
GET/v2/courses/:courseId/structureGet complete course structure
GET/v2/courses/:courseId/sectionsGet course sections
GET/v2/courses/:courseId/lessonsGet course lessons
GET/v2/courses/:courseId/completionCriteriaGet completion criteria

Sections

MethodEndpointDescription
GET/v2/sections/:idGet specific section
GET/v2/sections/:sectionId/lessonsGet lessons in a section

Lessons

MethodEndpointDescription
GET/v2/lessons/:idGet specific lesson

Topics

MethodEndpointDescription
GET/v2/topics/:idGet specific topic
GET/v2/topics/course/:courseIdGet all topics in a course
GET/v2/topics/lesson/:lessonIdGet all topics in a lesson

Content (Generic)

MethodEndpointDescription
GET/v2/contentList all content
GET/v2/content/:type/:idGet specific content by type and ID
GET/v2/fullContent/:type/:idGet full content details

Topic Types Reference

Topics can have various types:

  • text - Text content
  • video - Video content
  • quiz - Quiz/assessment
  • test - Test/exam
  • shareableContentObject - SCORM content
  • assignment - Assignment
  • meeting - Live meeting/webinar
  • survey - Survey
  • xApi - xAPI/Tin Can content

Completion Criteria Types

Completion criteria can be based on:

  • coursePercentViewed - Percentage of course viewed
  • articlePercentViewed - Percentage of article viewed
  • articleTimeViewedInSeconds - Time spent viewing article
  • videoPercentViewed - Percentage of video viewed
  • courseAssignmentComplete - Assignment completion
  • courseAssessmentPassed - Assessment passed
  • courseTopicViewed - Specific topic viewed
  • courseMeetingAttended - Meeting attended
  • scormComplete - SCORM completion
  • surveyGizmoComplete - Survey completion
  • xApiComplete - xAPI completion
  • bongoAssignmentCompleted - Bongo assignment completion

Use Cases

Displaying Course Outline

Use the structure endpoint to build a course outline for learners:

async function getCourseOutline(courseId) {
const response = await fetch(
`${BASE_URL}/v2/courses/${courseId}/structure`,
{
headers: {
'Authorization': `Bearer ${API_TOKEN}`,
'Content-Type': 'application/json'
}
}
);

const structure = await response.json();

// Build navigation
const outline = structure.sections.map(section => ({
title: section.title,
lessons: section.lessons.map(lesson => ({
title: lesson.title,
topics: lesson.topics.map(topic => ({
title: topic.title,
type: topic.type
}))
}))
}));

return outline;
}

Checking Course Progress

Combine structure with completion criteria to track progress:

async function checkCourseProgress(courseId, userId) {
// Get structure
const structure = await fetch(
`${BASE_URL}/v2/courses/${courseId}/structure`,
{
headers: {
'Authorization': `Bearer ${API_TOKEN}`,
'Content-Type': 'application/json'
}
}
).then(r => r.json());

// Get completion criteria
const criteria = await fetch(
`${BASE_URL}/v2/courses/${courseId}/completionCriteria`,
{
headers: {
'Authorization': `Bearer ${API_TOKEN}`,
'Content-Type': 'application/json'
}
}
).then(r => r.json());

// Calculate what's required for completion
const requirements = criteria.map(c => ({
type: c.type,
value: c[c.type]
}));

return {
structure,
requirements
};
}

Building Custom Navigation

Create a custom course navigation from sections and lessons:

async function buildCourseNavigation(courseId) {
const response = await fetch(
`${BASE_URL}/v2/courses/${courseId}/lessons`,
{
headers: {
'Authorization': `Bearer ${API_TOKEN}`,
'Content-Type': 'application/json'
}
}
);

const { lessons } = await response.json();

// Group by section
const navigation = lessons.reduce((acc, lesson) => {
const section = lesson.sectionTitle;
if (!acc[section]) {
acc[section] = [];
}
acc[section].push({
id: lesson.id,
title: lesson.title,
slug: lesson.slug,
topicCount: lesson.topicCount
});
return acc;
}, {});

return navigation;
}

Pagination

Endpoints that return lists support pagination:

Query Parameters

  • page - Page number (default: 1)
  • perPage - Items per page (default: 50, max: 100)

Example

curl -X GET "${BASE_URL}/v2/courses/${courseId}/lessons?page=2&perPage=25" \
-H "Authorization: Bearer ${API_TOKEN}" \
-H "Content-Type: application/json"

Response Structure

{
"lessons": [...],
"pageInfo": {
"total": 100,
"currentPage": 2,
"perPage": 25,
"hasMore": true
}
}

Error Handling

Common Errors

404 Not Found

{
"error": "NotFound",
"message": "Course not found",
"courseId": "invalid-id"
}

401 Unauthorized

{
"error": "Unauthorized",
"message": "Invalid or missing API token"
}

403 Forbidden

{
"error": "Forbidden",
"message": "Insufficient permissions to access this course"
}

Best Practices

1. Use Structure Endpoint for Complete Data

When you need full course hierarchy, use /structure:

  • ✅ Single API call
  • ✅ Complete nested data
  • ✅ Better performance

2. Use Specific Endpoints for Partial Data

When you only need sections or lessons:

  • ✅ Smaller response payload
  • ✅ Faster response time
  • ✅ Better for pagination

3. Cache Structure Data

Course structure changes infrequently:

const cache = new Map();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes

async function getCachedStructure(courseId) {
const cached = cache.get(courseId);

if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return cached.data;
}

const data = await fetchCourseStructure(courseId);
cache.set(courseId, {
data,
timestamp: Date.now()
});

return data;
}

4. Handle Pagination Properly

For courses with many lessons:

async function getAllLessons(courseId) {
let page = 1;
let hasMore = true;
const allLessons = [];

while (hasMore) {
const response = await fetch(
`${BASE_URL}/v2/courses/${courseId}/lessons?page=${page}&perPage=50`,
{
headers: {
'Authorization': `Bearer ${API_TOKEN}`,
'Content-Type': 'application/json'
}
}
);

const { lessons, pageInfo } = await response.json();
allLessons.push(...lessons);

hasMore = pageInfo.hasMore;
page++;
}

return allLessons;
}

Next Steps