Quizzes Are a Lesson Type in Thinkific. They're Not in LearnDash. Here's How We Bridge It.

In Thinkific a quiz is a lesson — `lessonType: QUIZ` sitting alongside videos and PDFs in a chapter. In LearnDash, sfwd-quiz is a separate post type. The mismatch is the single most consequential design decision in any Thinkific-to-LearnDash migration.

Same word, different data model

On Thinkific, a quiz is a curriculum peer. Open the Thinkific course editor and a quiz drags-and-drops into a chapter right between a video lesson and a PDF lesson — the underlying API confirms it: every quiz returns as a lesson node with `lessonType: "QUIZ"` and `content.__typename: "QuizContent"`. On LearnDash, `sfwd-quiz` is its own post type. It can be a global course-level quiz or attached to a single lesson, but it cannot live as a free-floating step between two lessons inside a section. Same word, totally different shape.

What this breaks in a naive migration

If your importer treats every Thinkific lesson uniformly and creates an `sfwd-lessons` post for each, your quizzes become orphaned. They show up as posts with no quiz data attached, because the questions live on a different post type that your loop never reached. We've audited two migrations done by other vendors that shipped with this exact bug — every quiz silently dropped, no warning, courses passed visual QA because the section headings and lesson titles all rendered.

The fix: the quiz-holder lesson pattern

On import, when we encounter a Thinkific `lessonType: QUIZ` node, we do two things instead of one. First, we create an `sfwd-lessons` post titled to match (we keep the original Thinkific title — usually something like "Lesson 3 Quiz"). Second, we create an `sfwd-quiz` post with the imported questions and attach it to that lesson via LearnDash's `course_id` and `lesson_id` post-meta keys. The lesson is the curriculum container; the quiz hangs off it. Reading order is preserved exactly because the placeholder lesson sits in the same position the Thinkific quiz did.

Why we keep the exporter LMS-agnostic

Our exporter doesn't rewrite quizzes to match LearnDash's model — it dumps them as Thinkific saw them. Thinkific is source of truth. The translation happens on import, which means the same export file could feed a LifterLMS importer, a Tutor LMS importer, or a custom Laravel target. Encoding a destination's quirks into the export would lock us in. Decisions about how to bridge a model mismatch belong in the layer that knows the destination, not the layer that talks to the source.

What to look for if you suspect orphaned quizzes

Run a query: `SELECT p.ID, p.post_title FROM wp_posts p WHERE p.post_type = 'sfwd-quiz' AND NOT EXISTS (SELECT 1 FROM wp_postmeta m WHERE m.post_id = p.ID AND m.meta_key = 'lesson_id')`. Any results are quizzes that didn't get connected to a lesson. If you also see lesson posts in your course outline with titles ending in 'Quiz' that contain no actual quiz embed, you have the mirror image of the same problem. Either is recoverable but requires a second pass against the source export.

Last updated: