{
  "updatedAt": "2026-02-13T20:12:28.086Z",
  "createdAt": "2026-02-13T19:47:57.154Z",
  "id": "mxDwUMJ4LvfvTcKK",
  "name": "Interactive Lesson Workflow",
  "description": null,
  "active": true,
  "isArchived": false,
  "nodes": [
    {
      "parameters": {
        "path": "lesson",
        "options": {}
      },
      "id": "f949a577-7d7e-4d4c-9ad8-947788305b8f",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1.1,
      "position": [
        -608,
        128
      ]
    },
    {
      "parameters": {
        "jsCode": "// Parse request and validate\nconst query = $input.item.json.query || {};\nconst action = query.action || 'get_lesson';\nconst studentId = parseInt(query.studentId) || 999;\nconst lessonId = parseInt(query.lessonId);\nconst topicId = parseInt(query.topicId);\nconst completionPercentage = parseInt(query.completionPercentage) || 100;\nconst confidenceRating = parseInt(query.confidenceRating);\nconst timeSpent = parseInt(query.timeSpent) || 0;\n\n// Validate required fields based on action\nif (action === 'get_lesson' && !lessonId && !topicId) {\n  return {\n    json: {\n      error: true,\n      message: 'Either lessonId or topicId is required',\n      action\n    }\n  };\n}\n\nif (action === 'mark_complete' && !lessonId) {\n  return {\n    json: {\n      error: true,\n      message: 'lessonId is required for mark_complete action',\n      action\n    }\n  };\n}\n\n// Sanitize inputs\nconst sanitize = (str, maxLength = 500) => {\n  if (typeof str !== 'string') return '';\n  return str.slice(0, maxLength).trim();\n};\n\nconst notes = sanitize(query.notes || '', 1000);\n\nreturn {\n  json: {\n    action,\n    studentId,\n    lessonId: lessonId || null,\n    topicId: topicId || null,\n    completionPercentage,\n    confidenceRating: confidenceRating || null,\n    timeSpent,\n    notes,\n    timestamp: new Date().toISOString()\n  }\n};"
      },
      "id": "7a807867-b172-4c39-8d71-599b74913b5f",
      "name": "Parse Request",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -400,
        128
      ]
    },
    {
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.error }}",
              "value2": "true"
            }
          ]
        }
      },
      "id": "42f2e262-714f-4ac8-a26d-60534ec1f826",
      "name": "If Error",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        -208,
        128
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ $json }}",
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Access-Control-Allow-Origin",
                "value": "*"
              },
              {
                "name": "Access-Control-Allow-Methods",
                "value": "GET, POST, OPTIONS"
              },
              {
                "name": "Access-Control-Allow-Headers",
                "value": "Content-Type"
              }
            ]
          }
        }
      },
      "id": "40e29a7c-6089-4aec-a93c-0dea3497559d",
      "name": "Respond Error",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.action }}",
              "value2": "get_lesson"
            }
          ]
        }
      },
      "id": "8d6fb72f-bb74-498c-8f9d-8db5a4824979",
      "name": "If Get Lesson",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        0,
        240
      ]
    },
    {
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.action }}",
              "value2": "get_lessons_by_topic"
            }
          ]
        }
      },
      "id": "2e6ecc99-4491-48e4-8cc1-25f93cd4bed3",
      "name": "If Get Lessons By Topic",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        208,
        400
      ]
    },
    {
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.action }}",
              "value2": "mark_complete"
            }
          ]
        }
      },
      "id": "cfc0e340-5c9c-4e0b-98a5-f4e03b912c11",
      "name": "If Mark Complete",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        400,
        560
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT \n  l.lesson_id,\n  l.lesson_title,\n  l.lesson_type,\n  l.lesson_number,\n  l.content_blocks,\n  l.learning_objectives,\n  l.success_criteria,\n  l.estimated_minutes,\n  l.difficulty_rating,\n  l.mark_scheme_notes,\n  l.exam_tips,\n  l.common_mistakes,\n  t.topic_name,\n  t.topic_id,\n  s.subject_name,\n  s.subject_id,\n  slp.status as progress_status,\n  slp.completion_percentage,\n  slp.confidence_rating,\n  slp.time_spent_minutes,\n  slp.notes as student_notes,\n  slp.bookmarked,\n  slp.last_accessed_at\nFROM lessons l\nJOIN topics t ON l.topic_id = t.topic_id\nJOIN subjects s ON t.subject_id = s.subject_id\nLEFT JOIN student_lesson_progress slp ON l.lesson_id = slp.lesson_id AND slp.student_id = $1\nWHERE l.lesson_id = $2 AND l.is_published = true",
        "options": {}
      },
      "id": "39318f59-0ed6-4f58-9b6b-95aec31036c9",
      "name": "Fetch Lesson",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        208,
        128
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT \n  l.lesson_id,\n  l.lesson_title,\n  l.lesson_type,\n  l.lesson_number,\n  l.estimated_minutes,\n  l.difficulty_rating,\n  slp.status as progress_status,\n  slp.completion_percentage,\n  slp.confidence_rating\nFROM lessons l\nLEFT JOIN student_lesson_progress slp ON l.lesson_id = slp.lesson_id AND slp.student_id = $1\nWHERE l.topic_id = $2 AND l.is_published = true\nORDER BY l.lesson_number ASC",
        "options": {}
      },
      "id": "2efe68fd-26c6-400b-9ff4-490442d34b06",
      "name": "Fetch Lessons By Topic",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        400,
        288
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "-- Update or insert lesson progress\nINSERT INTO student_lesson_progress (\n  student_id, \n  lesson_id, \n  status, \n  completion_percentage, \n  time_spent_minutes,\n  confidence_rating,\n  notes,\n  started_at,\n  completed_at,\n  last_accessed_at\n)\nVALUES (\n  $1, $2, $3, $4, $5, $6, $7,\n  COALESCE((SELECT started_at FROM student_lesson_progress WHERE student_id = $1 AND lesson_id = $2), NOW()),\n  CASE WHEN $4 >= 100 THEN NOW() ELSE NULL END,\n  NOW()\n)\nON CONFLICT (student_id, lesson_id)\nDO UPDATE SET\n  status = CASE \n    WHEN $4 >= 100 THEN 'completed'\n    WHEN $4 > 0 THEN 'in_progress'\n    ELSE student_lesson_progress.status\n  END,\n  completion_percentage = $4,\n  time_spent_minutes = student_lesson_progress.time_spent_minutes + $5,\n  confidence_rating = COALESCE($6, student_lesson_progress.confidence_rating),\n  notes = COALESCE($7, student_lesson_progress.notes),\n  completed_at = CASE \n    WHEN $4 >= 100 AND student_lesson_progress.completed_at IS NULL THEN NOW()\n    ELSE student_lesson_progress.completed_at\n  END,\n  last_accessed_at = NOW();\n\n-- Update student stats\nUPDATE students SET\n  total_lessons_completed = (\n    SELECT COUNT(*) FROM student_lesson_progress \n    WHERE student_id = $1 AND status = 'completed'\n  ),\n  xp_points = xp_points + CASE WHEN $4 >= 100 THEN 20 ELSE 5 END,\n  last_activity_date = CURRENT_DATE,\n  updated_at = NOW()\nWHERE student_id = $1;\n\n-- Return updated progress\nSELECT \n  slp.*,\n  s.xp_points,\n  s.current_level,\n  s.total_lessons_completed\nFROM student_lesson_progress slp\nJOIN students s ON slp.student_id = s.student_id\nWHERE slp.student_id = $1 AND slp.lesson_id = $2;",
        "options": {}
      },
      "id": "8dcbb09c-82c0-4c90-a671-50f7807e8b5b",
      "name": "Update Progress",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        608,
        448
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "-- Log confidence rating\nINSERT INTO confidence_logs (student_id, topic_id, confidence_rating, context, notes, logged_at)\nSELECT $1, l.topic_id, $2, 'lesson_complete', $3, NOW()\nFROM lessons l\nWHERE l.lesson_id = $4;\n\n-- Log learning event\nINSERT INTO learning_events (student_id, event_type, resource_type, resource_id, metadata, timestamp)\nVALUES ($1, 'lesson_completed', 'lesson', $4, \n  json_build_object(\n    'completion_percentage', $5,\n    'time_spent_minutes', $6,\n    'confidence_rating', $2\n  )::jsonb,\n  NOW()\n);",
        "options": {}
      },
      "id": "980401eb-4c59-4c62-90ac-fed113704781",
      "name": "Log Confidence & Event",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        800,
        448
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Build lesson response\nconst lesson = $input.first().json;\n\nif (!lesson || !lesson.lesson_id) {\n  return {\n    json: {\n      success: false,\n      error: 'Lesson not found',\n      lessonId: $('Parse Request').item.json.lessonId\n    }\n  };\n}\n\n// Parse content blocks if string\nlet contentBlocks = lesson.content_blocks;\nif (typeof contentBlocks === 'string') {\n  try {\n    contentBlocks = JSON.parse(contentBlocks);\n  } catch (e) {\n    contentBlocks = [];\n  }\n}\n\nreturn {\n  json: {\n    success: true,\n    lesson: {\n      id: lesson.lesson_id,\n      title: lesson.lesson_title,\n      type: lesson.lesson_type,\n      number: lesson.lesson_number,\n      contentBlocks: contentBlocks || [],\n      learningObjectives: lesson.learning_objectives || [],\n      successCriteria: lesson.success_criteria || [],\n      estimatedMinutes: lesson.estimated_minutes,\n      difficultyRating: lesson.difficulty_rating,\n      markSchemeNotes: lesson.mark_scheme_notes,\n      examTips: lesson.exam_tips || [],\n      commonMistakes: lesson.common_mistakes || [],\n      topic: {\n        id: lesson.topic_id,\n        name: lesson.topic_name\n      },\n      subject: {\n        id: lesson.subject_id,\n        name: lesson.subject_name\n      }\n    },\n    progress: {\n      status: lesson.progress_status || 'not_started',\n      completionPercentage: lesson.completion_percentage || 0,\n      confidenceRating: lesson.confidence_rating || null,\n      timeSpentMinutes: lesson.time_spent_minutes || 0,\n      notes: lesson.student_notes || '',\n      bookmarked: lesson.bookmarked || false,\n      lastAccessedAt: lesson.last_accessed_at\n    },\n    meta: {\n      timestamp: new Date().toISOString()\n    }\n  }\n};"
      },
      "id": "c6fe3dc7-146a-4859-ad2f-9eb6f5c79e8b",
      "name": "Build Lesson Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        400,
        128
      ]
    },
    {
      "parameters": {
        "jsCode": "// Build lessons list response\nconst lessons = $input.all().map(item => item.json);\nconst request = $('Parse Request').item.json;\n\nreturn {\n  json: {\n    success: true,\n    topicId: request.topicId,\n    lessons: lessons.map(l => ({\n      id: l.lesson_id,\n      title: l.lesson_title,\n      type: l.lesson_type,\n      number: l.lesson_number,\n      estimatedMinutes: l.estimated_minutes,\n      difficultyRating: l.difficulty_rating,\n      progress: {\n        status: l.progress_status || 'not_started',\n        completionPercentage: l.completion_percentage || 0,\n        confidenceRating: l.confidence_rating || null\n      }\n    })),\n    meta: {\n      totalLessons: lessons.length,\n      timestamp: new Date().toISOString()\n    }\n  }\n};"
      },
      "id": "749df3b6-9bbc-486f-ab03-a5626c1948da",
      "name": "Build Lessons List Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        608,
        288
      ]
    },
    {
      "parameters": {
        "jsCode": "// Build completion response\nconst progress = $input.first().json;\nconst request = $('Parse Request').item.json;\n\nreturn {\n  json: {\n    success: true,\n    message: request.completionPercentage >= 100 ? 'Lesson completed!' : 'Progress saved',\n    progress: {\n      lessonId: progress.lesson_id,\n      status: progress.status,\n      completionPercentage: progress.completion_percentage,\n      confidenceRating: progress.confidence_rating,\n      timeSpentMinutes: progress.time_spent_minutes\n    },\n    student: {\n      xpPoints: progress.xp_points,\n      currentLevel: progress.current_level,\n      totalLessonsCompleted: progress.total_lessons_completed,\n      xpGained: request.completionPercentage >= 100 ? 20 : 5\n    },\n    meta: {\n      timestamp: new Date().toISOString()\n    }\n  }\n};"
      },
      "id": "ef03467f-0e5b-4d48-815c-c16e9e0acc18",
      "name": "Build Completion Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1008,
        448
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ $json }}",
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Access-Control-Allow-Origin",
                "value": "*"
              },
              {
                "name": "Access-Control-Allow-Methods",
                "value": "GET, POST, OPTIONS"
              },
              {
                "name": "Access-Control-Allow-Headers",
                "value": "Content-Type"
              }
            ]
          }
        }
      },
      "id": "ba7828d7-428f-47fa-9f3a-43dc6b5e10f4",
      "name": "Respond Success",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        1200,
        288
      ]
    },
    {
      "parameters": {
        "jsCode": "// Handle unknown action\nconst request = $input.item.json;\n\nreturn {\n  json: {\n    success: false,\n    error: 'Unknown action',\n    action: request.action,\n    validActions: ['get_lesson', 'get_lessons_by_topic', 'mark_complete']\n  }\n};"
      },
      "id": "030ede0f-5269-4997-87c4-356fb3b751bd",
      "name": "Unknown Action",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        608,
        720
      ]
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Parse Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Request": {
      "main": [
        [
          {
            "node": "If Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Error": {
      "main": [
        [
          {
            "node": "Respond Error",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "If Get Lesson",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Get Lesson": {
      "main": [
        [
          {
            "node": "Fetch Lesson",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "If Get Lessons By Topic",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Get Lessons By Topic": {
      "main": [
        [
          {
            "node": "Fetch Lessons By Topic",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "If Mark Complete",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Mark Complete": {
      "main": [
        [
          {
            "node": "Update Progress",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Unknown Action",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Lesson": {
      "main": [
        [
          {
            "node": "Build Lesson Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Lessons By Topic": {
      "main": [
        [
          {
            "node": "Build Lessons List Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Progress": {
      "main": [
        [
          {
            "node": "Log Confidence & Event",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Confidence & Event": {
      "main": [
        [
          {
            "node": "Build Completion Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Lesson Response": {
      "main": [
        [
          {
            "node": "Respond Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Lessons List Response": {
      "main": [
        [
          {
            "node": "Respond Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Completion Response": {
      "main": [
        [
          {
            "node": "Respond Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Unknown Action": {
      "main": [
        [
          {
            "node": "Respond Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "availableInMCP": false
  },
  "staticData": null,
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "versionId": "40e8e684-0ce5-4cfa-a2f6-cb29690efee6",
  "activeVersionId": "40e8e684-0ce5-4cfa-a2f6-cb29690efee6",
  "versionCounter": 9,
  "triggerCount": 1,
  "shared": [
    {
      "updatedAt": "2026-02-13T19:47:57.154Z",
      "createdAt": "2026-02-13T19:47:57.154Z",
      "role": "workflow:owner",
      "workflowId": "mxDwUMJ4LvfvTcKK",
      "projectId": "xsdyFVsct988qLUy",
      "project": {
        "updatedAt": "2025-12-04T20:24:17.537Z",
        "createdAt": "2025-12-04T19:47:48.685Z",
        "id": "xsdyFVsct988qLUy",
        "name": "andrew johnson <andrew.ralston.johnson@gmail.com>",
        "type": "personal",
        "icon": null,
        "description": null,
        "creatorId": "e2485274-7097-4eb5-8502-e39b2308096c"
      }
    }
  ],
  "tags": [],
  "activeVersion": {
    "updatedAt": "2026-02-13T20:01:56.378Z",
    "createdAt": "2026-02-13T19:48:04.057Z",
    "versionId": "40e8e684-0ce5-4cfa-a2f6-cb29690efee6",
    "workflowId": "mxDwUMJ4LvfvTcKK",
    "nodes": [
      {
        "parameters": {
          "path": "lesson",
          "options": {}
        },
        "id": "f949a577-7d7e-4d4c-9ad8-947788305b8f",
        "name": "Webhook",
        "type": "n8n-nodes-base.webhook",
        "typeVersion": 1.1,
        "position": [
          -608,
          128
        ],
        "webhookId": "lesson-api"
      },
      {
        "parameters": {
          "jsCode": "// Parse request and validate\nconst query = $input.item.json.query || {};\nconst action = query.action || 'get_lesson';\nconst studentId = parseInt(query.studentId) || 999;\nconst lessonId = parseInt(query.lessonId);\nconst topicId = parseInt(query.topicId);\nconst completionPercentage = parseInt(query.completionPercentage) || 100;\nconst confidenceRating = parseInt(query.confidenceRating);\nconst timeSpent = parseInt(query.timeSpent) || 0;\n\n// Validate required fields based on action\nif (action === 'get_lesson' && !lessonId && !topicId) {\n  return {\n    json: {\n      error: true,\n      message: 'Either lessonId or topicId is required',\n      action\n    }\n  };\n}\n\nif (action === 'mark_complete' && !lessonId) {\n  return {\n    json: {\n      error: true,\n      message: 'lessonId is required for mark_complete action',\n      action\n    }\n  };\n}\n\n// Sanitize inputs\nconst sanitize = (str, maxLength = 500) => {\n  if (typeof str !== 'string') return '';\n  return str.slice(0, maxLength).trim();\n};\n\nconst notes = sanitize(query.notes || '', 1000);\n\nreturn {\n  json: {\n    action,\n    studentId,\n    lessonId: lessonId || null,\n    topicId: topicId || null,\n    completionPercentage,\n    confidenceRating: confidenceRating || null,\n    timeSpent,\n    notes,\n    timestamp: new Date().toISOString()\n  }\n};"
        },
        "id": "7a807867-b172-4c39-8d71-599b74913b5f",
        "name": "Parse Request",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          -400,
          128
        ]
      },
      {
        "parameters": {
          "conditions": {
            "string": [
              {
                "value1": "={{ $json.error }}",
                "value2": "true"
              }
            ]
          }
        },
        "id": "42f2e262-714f-4ac8-a26d-60534ec1f826",
        "name": "If Error",
        "type": "n8n-nodes-base.if",
        "typeVersion": 1,
        "position": [
          -208,
          128
        ]
      },
      {
        "parameters": {
          "respondWith": "json",
          "responseBody": "={{ $json }}",
          "options": {
            "responseHeaders": {
              "entries": [
                {
                  "name": "Access-Control-Allow-Origin",
                  "value": "*"
                },
                {
                  "name": "Access-Control-Allow-Methods",
                  "value": "GET, POST, OPTIONS"
                },
                {
                  "name": "Access-Control-Allow-Headers",
                  "value": "Content-Type"
                }
              ]
            }
          }
        },
        "id": "40e29a7c-6089-4aec-a93c-0dea3497559d",
        "name": "Respond Error",
        "type": "n8n-nodes-base.respondToWebhook",
        "typeVersion": 1,
        "position": [
          0,
          0
        ]
      },
      {
        "parameters": {
          "conditions": {
            "string": [
              {
                "value1": "={{ $json.action }}",
                "value2": "get_lesson"
              }
            ]
          }
        },
        "id": "8d6fb72f-bb74-498c-8f9d-8db5a4824979",
        "name": "If Get Lesson",
        "type": "n8n-nodes-base.if",
        "typeVersion": 1,
        "position": [
          0,
          240
        ]
      },
      {
        "parameters": {
          "conditions": {
            "string": [
              {
                "value1": "={{ $json.action }}",
                "value2": "get_lessons_by_topic"
              }
            ]
          }
        },
        "id": "2e6ecc99-4491-48e4-8cc1-25f93cd4bed3",
        "name": "If Get Lessons By Topic",
        "type": "n8n-nodes-base.if",
        "typeVersion": 1,
        "position": [
          208,
          400
        ]
      },
      {
        "parameters": {
          "conditions": {
            "string": [
              {
                "value1": "={{ $json.action }}",
                "value2": "mark_complete"
              }
            ]
          }
        },
        "id": "cfc0e340-5c9c-4e0b-98a5-f4e03b912c11",
        "name": "If Mark Complete",
        "type": "n8n-nodes-base.if",
        "typeVersion": 1,
        "position": [
          400,
          560
        ]
      },
      {
        "parameters": {
          "operation": "executeQuery",
          "query": "SELECT \n  l.lesson_id,\n  l.lesson_title,\n  l.lesson_type,\n  l.lesson_number,\n  l.content_blocks,\n  l.learning_objectives,\n  l.success_criteria,\n  l.estimated_minutes,\n  l.difficulty_rating,\n  l.mark_scheme_notes,\n  l.exam_tips,\n  l.common_mistakes,\n  t.topic_name,\n  t.topic_id,\n  s.subject_name,\n  s.subject_id,\n  slp.status as progress_status,\n  slp.completion_percentage,\n  slp.confidence_rating,\n  slp.time_spent_minutes,\n  slp.notes as student_notes,\n  slp.bookmarked,\n  slp.last_accessed_at\nFROM lessons l\nJOIN topics t ON l.topic_id = t.topic_id\nJOIN subjects s ON t.subject_id = s.subject_id\nLEFT JOIN student_lesson_progress slp ON l.lesson_id = slp.lesson_id AND slp.student_id = $1\nWHERE l.lesson_id = $2 AND l.is_published = true",
          "options": {}
        },
        "id": "39318f59-0ed6-4f58-9b6b-95aec31036c9",
        "name": "Fetch Lesson",
        "type": "n8n-nodes-base.postgres",
        "typeVersion": 2.4,
        "position": [
          208,
          128
        ],
        "credentials": {
          "postgres": {
            "id": "LadjvFzuiQFYT469",
            "name": "Postgres account 2"
          }
        }
      },
      {
        "parameters": {
          "operation": "executeQuery",
          "query": "SELECT \n  l.lesson_id,\n  l.lesson_title,\n  l.lesson_type,\n  l.lesson_number,\n  l.estimated_minutes,\n  l.difficulty_rating,\n  slp.status as progress_status,\n  slp.completion_percentage,\n  slp.confidence_rating\nFROM lessons l\nLEFT JOIN student_lesson_progress slp ON l.lesson_id = slp.lesson_id AND slp.student_id = $1\nWHERE l.topic_id = $2 AND l.is_published = true\nORDER BY l.lesson_number ASC",
          "options": {}
        },
        "id": "2efe68fd-26c6-400b-9ff4-490442d34b06",
        "name": "Fetch Lessons By Topic",
        "type": "n8n-nodes-base.postgres",
        "typeVersion": 2.4,
        "position": [
          400,
          288
        ],
        "credentials": {
          "postgres": {
            "id": "LadjvFzuiQFYT469",
            "name": "Postgres account 2"
          }
        }
      },
      {
        "parameters": {
          "operation": "executeQuery",
          "query": "-- Update or insert lesson progress\nINSERT INTO student_lesson_progress (\n  student_id, \n  lesson_id, \n  status, \n  completion_percentage, \n  time_spent_minutes,\n  confidence_rating,\n  notes,\n  started_at,\n  completed_at,\n  last_accessed_at\n)\nVALUES (\n  $1, $2, $3, $4, $5, $6, $7,\n  COALESCE((SELECT started_at FROM student_lesson_progress WHERE student_id = $1 AND lesson_id = $2), NOW()),\n  CASE WHEN $4 >= 100 THEN NOW() ELSE NULL END,\n  NOW()\n)\nON CONFLICT (student_id, lesson_id)\nDO UPDATE SET\n  status = CASE \n    WHEN $4 >= 100 THEN 'completed'\n    WHEN $4 > 0 THEN 'in_progress'\n    ELSE student_lesson_progress.status\n  END,\n  completion_percentage = $4,\n  time_spent_minutes = student_lesson_progress.time_spent_minutes + $5,\n  confidence_rating = COALESCE($6, student_lesson_progress.confidence_rating),\n  notes = COALESCE($7, student_lesson_progress.notes),\n  completed_at = CASE \n    WHEN $4 >= 100 AND student_lesson_progress.completed_at IS NULL THEN NOW()\n    ELSE student_lesson_progress.completed_at\n  END,\n  last_accessed_at = NOW();\n\n-- Update student stats\nUPDATE students SET\n  total_lessons_completed = (\n    SELECT COUNT(*) FROM student_lesson_progress \n    WHERE student_id = $1 AND status = 'completed'\n  ),\n  xp_points = xp_points + CASE WHEN $4 >= 100 THEN 20 ELSE 5 END,\n  last_activity_date = CURRENT_DATE,\n  updated_at = NOW()\nWHERE student_id = $1;\n\n-- Return updated progress\nSELECT \n  slp.*,\n  s.xp_points,\n  s.current_level,\n  s.total_lessons_completed\nFROM student_lesson_progress slp\nJOIN students s ON slp.student_id = s.student_id\nWHERE slp.student_id = $1 AND slp.lesson_id = $2;",
          "options": {}
        },
        "id": "8dcbb09c-82c0-4c90-a671-50f7807e8b5b",
        "name": "Update Progress",
        "type": "n8n-nodes-base.postgres",
        "typeVersion": 2.4,
        "position": [
          608,
          448
        ],
        "credentials": {
          "postgres": {
            "id": "LadjvFzuiQFYT469",
            "name": "Postgres account 2"
          }
        }
      },
      {
        "parameters": {
          "operation": "executeQuery",
          "query": "-- Log confidence rating\nINSERT INTO confidence_logs (student_id, topic_id, confidence_rating, context, notes, logged_at)\nSELECT $1, l.topic_id, $2, 'lesson_complete', $3, NOW()\nFROM lessons l\nWHERE l.lesson_id = $4;\n\n-- Log learning event\nINSERT INTO learning_events (student_id, event_type, resource_type, resource_id, metadata, timestamp)\nVALUES ($1, 'lesson_completed', 'lesson', $4, \n  json_build_object(\n    'completion_percentage', $5,\n    'time_spent_minutes', $6,\n    'confidence_rating', $2\n  )::jsonb,\n  NOW()\n);",
          "options": {}
        },
        "id": "980401eb-4c59-4c62-90ac-fed113704781",
        "name": "Log Confidence & Event",
        "type": "n8n-nodes-base.postgres",
        "typeVersion": 2.4,
        "position": [
          800,
          448
        ],
        "credentials": {
          "postgres": {
            "id": "LadjvFzuiQFYT469",
            "name": "Postgres account 2"
          }
        }
      },
      {
        "parameters": {
          "jsCode": "// Build lesson response\nconst lesson = $input.first().json;\n\nif (!lesson || !lesson.lesson_id) {\n  return {\n    json: {\n      success: false,\n      error: 'Lesson not found',\n      lessonId: $('Parse Request').item.json.lessonId\n    }\n  };\n}\n\n// Parse content blocks if string\nlet contentBlocks = lesson.content_blocks;\nif (typeof contentBlocks === 'string') {\n  try {\n    contentBlocks = JSON.parse(contentBlocks);\n  } catch (e) {\n    contentBlocks = [];\n  }\n}\n\nreturn {\n  json: {\n    success: true,\n    lesson: {\n      id: lesson.lesson_id,\n      title: lesson.lesson_title,\n      type: lesson.lesson_type,\n      number: lesson.lesson_number,\n      contentBlocks: contentBlocks || [],\n      learningObjectives: lesson.learning_objectives || [],\n      successCriteria: lesson.success_criteria || [],\n      estimatedMinutes: lesson.estimated_minutes,\n      difficultyRating: lesson.difficulty_rating,\n      markSchemeNotes: lesson.mark_scheme_notes,\n      examTips: lesson.exam_tips || [],\n      commonMistakes: lesson.common_mistakes || [],\n      topic: {\n        id: lesson.topic_id,\n        name: lesson.topic_name\n      },\n      subject: {\n        id: lesson.subject_id,\n        name: lesson.subject_name\n      }\n    },\n    progress: {\n      status: lesson.progress_status || 'not_started',\n      completionPercentage: lesson.completion_percentage || 0,\n      confidenceRating: lesson.confidence_rating || null,\n      timeSpentMinutes: lesson.time_spent_minutes || 0,\n      notes: lesson.student_notes || '',\n      bookmarked: lesson.bookmarked || false,\n      lastAccessedAt: lesson.last_accessed_at\n    },\n    meta: {\n      timestamp: new Date().toISOString()\n    }\n  }\n};"
        },
        "id": "c6fe3dc7-146a-4859-ad2f-9eb6f5c79e8b",
        "name": "Build Lesson Response",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          400,
          128
        ]
      },
      {
        "parameters": {
          "jsCode": "// Build lessons list response\nconst lessons = $input.all().map(item => item.json);\nconst request = $('Parse Request').item.json;\n\nreturn {\n  json: {\n    success: true,\n    topicId: request.topicId,\n    lessons: lessons.map(l => ({\n      id: l.lesson_id,\n      title: l.lesson_title,\n      type: l.lesson_type,\n      number: l.lesson_number,\n      estimatedMinutes: l.estimated_minutes,\n      difficultyRating: l.difficulty_rating,\n      progress: {\n        status: l.progress_status || 'not_started',\n        completionPercentage: l.completion_percentage || 0,\n        confidenceRating: l.confidence_rating || null\n      }\n    })),\n    meta: {\n      totalLessons: lessons.length,\n      timestamp: new Date().toISOString()\n    }\n  }\n};"
        },
        "id": "749df3b6-9bbc-486f-ab03-a5626c1948da",
        "name": "Build Lessons List Response",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          608,
          288
        ]
      },
      {
        "parameters": {
          "jsCode": "// Build completion response\nconst progress = $input.first().json;\nconst request = $('Parse Request').item.json;\n\nreturn {\n  json: {\n    success: true,\n    message: request.completionPercentage >= 100 ? 'Lesson completed!' : 'Progress saved',\n    progress: {\n      lessonId: progress.lesson_id,\n      status: progress.status,\n      completionPercentage: progress.completion_percentage,\n      confidenceRating: progress.confidence_rating,\n      timeSpentMinutes: progress.time_spent_minutes\n    },\n    student: {\n      xpPoints: progress.xp_points,\n      currentLevel: progress.current_level,\n      totalLessonsCompleted: progress.total_lessons_completed,\n      xpGained: request.completionPercentage >= 100 ? 20 : 5\n    },\n    meta: {\n      timestamp: new Date().toISOString()\n    }\n  }\n};"
        },
        "id": "ef03467f-0e5b-4d48-815c-c16e9e0acc18",
        "name": "Build Completion Response",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          1008,
          448
        ]
      },
      {
        "parameters": {
          "respondWith": "json",
          "responseBody": "={{ $json }}",
          "options": {
            "responseHeaders": {
              "entries": [
                {
                  "name": "Access-Control-Allow-Origin",
                  "value": "*"
                },
                {
                  "name": "Access-Control-Allow-Methods",
                  "value": "GET, POST, OPTIONS"
                },
                {
                  "name": "Access-Control-Allow-Headers",
                  "value": "Content-Type"
                }
              ]
            }
          }
        },
        "id": "ba7828d7-428f-47fa-9f3a-43dc6b5e10f4",
        "name": "Respond Success",
        "type": "n8n-nodes-base.respondToWebhook",
        "typeVersion": 1,
        "position": [
          1200,
          288
        ]
      },
      {
        "parameters": {
          "jsCode": "// Handle unknown action\nconst request = $input.item.json;\n\nreturn {\n  json: {\n    success: false,\n    error: 'Unknown action',\n    action: request.action,\n    validActions: ['get_lesson', 'get_lessons_by_topic', 'mark_complete']\n  }\n};"
        },
        "id": "030ede0f-5269-4997-87c4-356fb3b751bd",
        "name": "Unknown Action",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          608,
          720
        ]
      }
    ],
    "connections": {
      "Webhook": {
        "main": [
          [
            {
              "node": "Parse Request",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Parse Request": {
        "main": [
          [
            {
              "node": "If Error",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "If Error": {
        "main": [
          [
            {
              "node": "Respond Error",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "If Get Lesson",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "If Get Lesson": {
        "main": [
          [
            {
              "node": "Fetch Lesson",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "If Get Lessons By Topic",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "If Get Lessons By Topic": {
        "main": [
          [
            {
              "node": "Fetch Lessons By Topic",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "If Mark Complete",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "If Mark Complete": {
        "main": [
          [
            {
              "node": "Update Progress",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Unknown Action",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Fetch Lesson": {
        "main": [
          [
            {
              "node": "Build Lesson Response",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Fetch Lessons By Topic": {
        "main": [
          [
            {
              "node": "Build Lessons List Response",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Update Progress": {
        "main": [
          [
            {
              "node": "Log Confidence & Event",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Log Confidence & Event": {
        "main": [
          [
            {
              "node": "Build Completion Response",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Build Lesson Response": {
        "main": [
          [
            {
              "node": "Respond Success",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Build Lessons List Response": {
        "main": [
          [
            {
              "node": "Respond Success",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Build Completion Response": {
        "main": [
          [
            {
              "node": "Respond Success",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Unknown Action": {
        "main": [
          [
            {
              "node": "Respond Success",
              "type": "main",
              "index": 0
            }
          ]
        ]
      }
    },
    "authors": "andrew johnson",
    "name": "Version 40e8e684",
    "description": "",
    "autosaved": true,
    "workflowPublishHistory": [
      {
        "createdAt": "2026-02-13T20:01:56.376Z",
        "id": 146,
        "workflowId": "mxDwUMJ4LvfvTcKK",
        "versionId": "40e8e684-0ce5-4cfa-a2f6-cb29690efee6",
        "event": "activated",
        "userId": "e2485274-7097-4eb5-8502-e39b2308096c"
      }
    ]
  }
}