{
  "generated_from": "TUTORIAL-MAP.md",
  "warning": "Generated by tools/build-map.mjs. Do not edit by hand — edit the matrix and rebuild.",
  "courses": [
    {
      "code": "FR-101",
      "title": "FlowRunner Foundations",
      "lane": "foundations",
      "released": true,
      "promise": "Your first week automating Flowland",
      "lesson_count": 15,
      "modules": [
        {
          "id": "FR-101.1",
          "course": "FR-101",
          "module": 1,
          "title": "Your first flow, end to end",
          "lessons": [
            "FR-101.1.1",
            "FR-101.1.2",
            "FR-101.1.3"
          ],
          "minutes": 27
        },
        {
          "id": "FR-101.2",
          "course": "FR-101",
          "module": 2,
          "title": "How a flow is put together",
          "lessons": [
            "FR-101.2.1",
            "FR-101.2.2",
            "FR-101.2.3",
            "FR-101.2.4"
          ],
          "minutes": 21
        },
        {
          "id": "FR-101.3",
          "course": "FR-101",
          "module": 3,
          "title": "Triggers, variables, and expressions",
          "lessons": [
            "FR-101.3.1",
            "FR-101.3.2",
            "FR-101.3.3",
            "FR-101.3.4",
            "FR-101.3.5"
          ],
          "minutes": 46
        },
        {
          "id": "FR-101.4",
          "course": "FR-101",
          "module": 4,
          "title": "Test it, take it live, and watch it run",
          "lessons": [
            "FR-101.4.1",
            "FR-101.4.2",
            "FR-101.4.3"
          ],
          "minutes": 29
        }
      ]
    },
    {
      "code": "FR-102",
      "title": "Controlling the Flow",
      "lane": "control",
      "released": false,
      "promise": "When a flow has to decide",
      "lesson_count": 10,
      "modules": [
        {
          "id": "FR-102.1",
          "course": "FR-102",
          "module": 1,
          "title": "Making decisions",
          "lessons": [
            "FR-102.1.1",
            "FR-102.1.2"
          ],
          "minutes": 17
        },
        {
          "id": "FR-102.2",
          "course": "FR-102",
          "module": 2,
          "title": "Repeating work",
          "lessons": [
            "FR-102.2.1",
            "FR-102.2.2",
            "FR-102.2.3"
          ],
          "minutes": 20
        },
        {
          "id": "FR-102.3",
          "course": "FR-102",
          "module": 3,
          "title": "Doing things at the same time, and waiting",
          "lessons": [
            "FR-102.3.1",
            "FR-102.3.2"
          ],
          "minutes": 12
        },
        {
          "id": "FR-102.4",
          "course": "FR-102",
          "module": 4,
          "title": "When something does not work",
          "lessons": [
            "FR-102.4.1",
            "FR-102.4.2",
            "FR-102.4.3"
          ],
          "minutes": 24
        }
      ]
    },
    {
      "code": "FR-103",
      "title": "Working with Data",
      "lane": "data",
      "released": false,
      "promise": "The shop's data, tamed",
      "lesson_count": 9,
      "modules": [
        {
          "id": "FR-103.1",
          "course": "FR-103",
          "module": 1,
          "title": "Where data comes from and where it goes",
          "lessons": [
            "FR-103.1.1",
            "FR-103.1.2",
            "FR-103.1.3"
          ],
          "minutes": 21
        },
        {
          "id": "FR-103.2",
          "course": "FR-103",
          "module": 2,
          "title": "Storing and shaping values",
          "lessons": [
            "FR-103.2.1",
            "FR-103.2.2",
            "FR-103.2.3"
          ],
          "minutes": 20
        },
        {
          "id": "FR-103.3",
          "course": "FR-103",
          "module": 3,
          "title": "Remembering things between runs",
          "lessons": [
            "FR-103.3.1",
            "FR-103.3.2",
            "FR-103.3.3"
          ],
          "minutes": 22
        }
      ]
    },
    {
      "code": "FR-104",
      "title": "Connecting to the Outside World",
      "lane": "integration",
      "released": false,
      "promise": "Flowland meets its customers, its bank, and its carrier",
      "lesson_count": 13,
      "modules": [
        {
          "id": "FR-104.1",
          "course": "FR-104",
          "module": 1,
          "title": "Calling other systems",
          "lessons": [
            "FR-104.1.1",
            "FR-104.1.2",
            "FR-104.1.3"
          ],
          "minutes": 21
        },
        {
          "id": "FR-104.2",
          "course": "FR-104",
          "module": 2,
          "title": "Being called by other systems",
          "lessons": [
            "FR-104.2.1",
            "FR-104.2.2"
          ],
          "minutes": 14
        },
        {
          "id": "FR-104.3",
          "course": "FR-104",
          "module": 3,
          "title": "Pausing for people and events",
          "lessons": [
            "FR-104.3.1",
            "FR-104.3.2",
            "FR-104.3.3"
          ],
          "minutes": 20
        },
        {
          "id": "FR-104.4",
          "course": "FR-104",
          "module": 4,
          "title": "Reusing flows and adding blocks",
          "lessons": [
            "FR-104.4.1",
            "FR-104.4.2",
            "FR-104.4.3",
            "FR-104.4.4",
            "FR-104.4.5"
          ],
          "minutes": 37
        }
      ]
    },
    {
      "code": "FR-105",
      "title": "AI-Powered Automation",
      "lane": "ai",
      "released": false,
      "promise": "The shop hires an AI",
      "lesson_count": 10,
      "modules": [
        {
          "id": "FR-105.1",
          "course": "FR-105",
          "module": 1,
          "title": "AI as a step",
          "lessons": [
            "FR-105.1.1",
            "FR-105.1.2",
            "FR-105.1.3"
          ],
          "minutes": 18
        },
        {
          "id": "FR-105.2",
          "course": "FR-105",
          "module": 2,
          "title": "Build an AI agent",
          "lessons": [
            "FR-105.2.1",
            "FR-105.2.2",
            "FR-105.2.3"
          ],
          "minutes": 21
        },
        {
          "id": "FR-105.3",
          "course": "FR-105",
          "module": 3,
          "title": "Give an agent documents and memory",
          "lessons": [
            "FR-105.3.1",
            "FR-105.3.2",
            "FR-105.3.3",
            "FR-105.3.4"
          ],
          "minutes": 31
        }
      ]
    },
    {
      "code": "FR-106",
      "title": "Running and Operating Flows",
      "lane": "operations",
      "released": false,
      "promise": "Running the shop for real",
      "lesson_count": 12,
      "modules": [
        {
          "id": "FR-106.1",
          "course": "FR-106",
          "module": 1,
          "title": "Changing a flow that is already live",
          "lessons": [
            "FR-106.1.1",
            "FR-106.1.2"
          ],
          "minutes": 13
        },
        {
          "id": "FR-106.2",
          "course": "FR-106",
          "module": 2,
          "title": "Find it, fix it, prove it",
          "lessons": [
            "FR-106.2.1",
            "FR-106.2.2",
            "FR-106.2.3",
            "FR-106.2.4"
          ],
          "minutes": 27
        },
        {
          "id": "FR-106.3",
          "course": "FR-106",
          "module": 3,
          "title": "Checking two systems against each other",
          "lessons": [
            "FR-106.3.1",
            "FR-106.3.2"
          ],
          "minutes": 15
        },
        {
          "id": "FR-106.4",
          "course": "FR-106",
          "module": 4,
          "title": "Running a workspace: people, limits and audit",
          "lessons": [
            "FR-106.4.1",
            "FR-106.4.2",
            "FR-106.4.3",
            "FR-106.4.4"
          ],
          "minutes": 26
        }
      ]
    },
    {
      "code": "FR-210",
      "title": "FlowRunner for Developers",
      "lane": "developer",
      "released": false,
      "promise": "Solve it fast: code where code is faster, platform where the platform is faster",
      "lesson_count": 13,
      "modules": [
        {
          "id": "FR-210.1",
          "course": "FR-210",
          "module": 1,
          "title": "How FlowRunner is put together",
          "lessons": [
            "FR-210.1.1",
            "FR-210.1.2"
          ],
          "minutes": 12
        },
        {
          "id": "FR-210.2",
          "course": "FR-210",
          "module": 2,
          "title": "Code as the fastest path through a flow",
          "lessons": [
            "FR-210.2.1",
            "FR-210.2.2",
            "FR-210.2.3",
            "FR-210.2.4"
          ],
          "minutes": 26
        },
        {
          "id": "FR-210.3",
          "course": "FR-210",
          "module": 3,
          "title": "Your own building blocks (ships with the custom actions/triggers release)",
          "lessons": [
            "FR-210.3.1",
            "FR-210.3.2",
            "FR-210.3.3"
          ],
          "minutes": 21
        },
        {
          "id": "FR-210.4",
          "course": "FR-210",
          "module": 4,
          "title": "Using FlowRunner from your own code",
          "lessons": [
            "FR-210.4.1",
            "FR-210.4.2",
            "FR-210.4.3",
            "FR-210.4.4"
          ],
          "minutes": 29
        }
      ]
    }
  ],
  "modules": [
    {
      "id": "FR-101.1",
      "course": "FR-101",
      "module": 1,
      "title": "Your first flow, end to end",
      "lessons": [
        "FR-101.1.1",
        "FR-101.1.2",
        "FR-101.1.3"
      ],
      "minutes": 27
    },
    {
      "id": "FR-101.2",
      "course": "FR-101",
      "module": 2,
      "title": "How a flow is put together",
      "lessons": [
        "FR-101.2.1",
        "FR-101.2.2",
        "FR-101.2.3",
        "FR-101.2.4"
      ],
      "minutes": 21
    },
    {
      "id": "FR-101.3",
      "course": "FR-101",
      "module": 3,
      "title": "Triggers, variables, and expressions",
      "lessons": [
        "FR-101.3.1",
        "FR-101.3.2",
        "FR-101.3.3",
        "FR-101.3.4",
        "FR-101.3.5"
      ],
      "minutes": 46
    },
    {
      "id": "FR-101.4",
      "course": "FR-101",
      "module": 4,
      "title": "Test it, take it live, and watch it run",
      "lessons": [
        "FR-101.4.1",
        "FR-101.4.2",
        "FR-101.4.3"
      ],
      "minutes": 29
    },
    {
      "id": "FR-102.1",
      "course": "FR-102",
      "module": 1,
      "title": "Making decisions",
      "lessons": [
        "FR-102.1.1",
        "FR-102.1.2"
      ],
      "minutes": 17
    },
    {
      "id": "FR-102.2",
      "course": "FR-102",
      "module": 2,
      "title": "Repeating work",
      "lessons": [
        "FR-102.2.1",
        "FR-102.2.2",
        "FR-102.2.3"
      ],
      "minutes": 20
    },
    {
      "id": "FR-102.3",
      "course": "FR-102",
      "module": 3,
      "title": "Doing things at the same time, and waiting",
      "lessons": [
        "FR-102.3.1",
        "FR-102.3.2"
      ],
      "minutes": 12
    },
    {
      "id": "FR-102.4",
      "course": "FR-102",
      "module": 4,
      "title": "When something does not work",
      "lessons": [
        "FR-102.4.1",
        "FR-102.4.2",
        "FR-102.4.3"
      ],
      "minutes": 24
    },
    {
      "id": "FR-103.1",
      "course": "FR-103",
      "module": 1,
      "title": "Where data comes from and where it goes",
      "lessons": [
        "FR-103.1.1",
        "FR-103.1.2",
        "FR-103.1.3"
      ],
      "minutes": 21
    },
    {
      "id": "FR-103.2",
      "course": "FR-103",
      "module": 2,
      "title": "Storing and shaping values",
      "lessons": [
        "FR-103.2.1",
        "FR-103.2.2",
        "FR-103.2.3"
      ],
      "minutes": 20
    },
    {
      "id": "FR-103.3",
      "course": "FR-103",
      "module": 3,
      "title": "Remembering things between runs",
      "lessons": [
        "FR-103.3.1",
        "FR-103.3.2",
        "FR-103.3.3"
      ],
      "minutes": 22
    },
    {
      "id": "FR-104.1",
      "course": "FR-104",
      "module": 1,
      "title": "Calling other systems",
      "lessons": [
        "FR-104.1.1",
        "FR-104.1.2",
        "FR-104.1.3"
      ],
      "minutes": 21
    },
    {
      "id": "FR-104.2",
      "course": "FR-104",
      "module": 2,
      "title": "Being called by other systems",
      "lessons": [
        "FR-104.2.1",
        "FR-104.2.2"
      ],
      "minutes": 14
    },
    {
      "id": "FR-104.3",
      "course": "FR-104",
      "module": 3,
      "title": "Pausing for people and events",
      "lessons": [
        "FR-104.3.1",
        "FR-104.3.2",
        "FR-104.3.3"
      ],
      "minutes": 20
    },
    {
      "id": "FR-104.4",
      "course": "FR-104",
      "module": 4,
      "title": "Reusing flows and adding blocks",
      "lessons": [
        "FR-104.4.1",
        "FR-104.4.2",
        "FR-104.4.3",
        "FR-104.4.4",
        "FR-104.4.5"
      ],
      "minutes": 37
    },
    {
      "id": "FR-105.1",
      "course": "FR-105",
      "module": 1,
      "title": "AI as a step",
      "lessons": [
        "FR-105.1.1",
        "FR-105.1.2",
        "FR-105.1.3"
      ],
      "minutes": 18
    },
    {
      "id": "FR-105.2",
      "course": "FR-105",
      "module": 2,
      "title": "Build an AI agent",
      "lessons": [
        "FR-105.2.1",
        "FR-105.2.2",
        "FR-105.2.3"
      ],
      "minutes": 21
    },
    {
      "id": "FR-105.3",
      "course": "FR-105",
      "module": 3,
      "title": "Give an agent documents and memory",
      "lessons": [
        "FR-105.3.1",
        "FR-105.3.2",
        "FR-105.3.3",
        "FR-105.3.4"
      ],
      "minutes": 31
    },
    {
      "id": "FR-106.1",
      "course": "FR-106",
      "module": 1,
      "title": "Changing a flow that is already live",
      "lessons": [
        "FR-106.1.1",
        "FR-106.1.2"
      ],
      "minutes": 13
    },
    {
      "id": "FR-106.2",
      "course": "FR-106",
      "module": 2,
      "title": "Find it, fix it, prove it",
      "lessons": [
        "FR-106.2.1",
        "FR-106.2.2",
        "FR-106.2.3",
        "FR-106.2.4"
      ],
      "minutes": 27
    },
    {
      "id": "FR-106.3",
      "course": "FR-106",
      "module": 3,
      "title": "Checking two systems against each other",
      "lessons": [
        "FR-106.3.1",
        "FR-106.3.2"
      ],
      "minutes": 15
    },
    {
      "id": "FR-106.4",
      "course": "FR-106",
      "module": 4,
      "title": "Running a workspace: people, limits and audit",
      "lessons": [
        "FR-106.4.1",
        "FR-106.4.2",
        "FR-106.4.3",
        "FR-106.4.4"
      ],
      "minutes": 26
    },
    {
      "id": "FR-210.1",
      "course": "FR-210",
      "module": 1,
      "title": "How FlowRunner is put together",
      "lessons": [
        "FR-210.1.1",
        "FR-210.1.2"
      ],
      "minutes": 12
    },
    {
      "id": "FR-210.2",
      "course": "FR-210",
      "module": 2,
      "title": "Code as the fastest path through a flow",
      "lessons": [
        "FR-210.2.1",
        "FR-210.2.2",
        "FR-210.2.3",
        "FR-210.2.4"
      ],
      "minutes": 26
    },
    {
      "id": "FR-210.3",
      "course": "FR-210",
      "module": 3,
      "title": "Your own building blocks (ships with the custom actions/triggers release)",
      "lessons": [
        "FR-210.3.1",
        "FR-210.3.2",
        "FR-210.3.3"
      ],
      "minutes": 21
    },
    {
      "id": "FR-210.4",
      "course": "FR-210",
      "module": 4,
      "title": "Using FlowRunner from your own code",
      "lessons": [
        "FR-210.4.1",
        "FR-210.4.2",
        "FR-210.4.3",
        "FR-210.4.4"
      ],
      "minutes": 29
    }
  ],
  "overview": {
    "width": 1344,
    "height": 712,
    "nodes": [
      {
        "code": "FR-101",
        "kind": "course",
        "title": "FlowRunner Foundations",
        "objective": "Your first week automating Flowland",
        "course": "FR-101",
        "lane": "foundations",
        "released": true,
        "requires": [],
        "lesson_count": 15,
        "minutes": 123,
        "modules": 4,
        "docs": [],
        "page": null,
        "status": "published",
        "x": 12,
        "y": 346,
        "w": 190,
        "h": 122
      },
      {
        "code": "FR-102",
        "kind": "course",
        "title": "Controlling the Flow",
        "objective": "When a flow has to decide",
        "course": "FR-102",
        "lane": "control",
        "released": false,
        "requires": [
          "FR-101"
        ],
        "lesson_count": 10,
        "minutes": 73,
        "modules": 4,
        "docs": [],
        "page": null,
        "status": "planned",
        "x": 352,
        "y": 326,
        "w": 190,
        "h": 122
      },
      {
        "code": "FR-103",
        "kind": "course",
        "title": "Working with Data",
        "objective": "The shop's data, tamed",
        "course": "FR-103",
        "lane": "data",
        "released": false,
        "requires": [
          "FR-101"
        ],
        "lesson_count": 9,
        "minutes": 63,
        "modules": 3,
        "docs": [],
        "page": null,
        "status": "planned",
        "x": 352,
        "y": 518,
        "w": 190,
        "h": 122
      },
      {
        "code": "FR-104",
        "kind": "course",
        "title": "Connecting to the Outside World",
        "objective": "Flowland meets its customers, its bank, and its carrier",
        "course": "FR-104",
        "lane": "integration",
        "released": false,
        "requires": [
          "FR-102",
          "FR-103"
        ],
        "lesson_count": 13,
        "minutes": 92,
        "modules": 4,
        "docs": [],
        "page": null,
        "status": "planned",
        "x": 692,
        "y": 346,
        "w": 190,
        "h": 122
      },
      {
        "code": "FR-105",
        "kind": "course",
        "title": "AI-Powered Automation",
        "objective": "The shop hires an AI",
        "course": "FR-105",
        "lane": "ai",
        "released": false,
        "requires": [
          "FR-102"
        ],
        "lesson_count": 10,
        "minutes": 70,
        "modules": 3,
        "docs": [],
        "page": null,
        "status": "planned",
        "x": 692,
        "y": 154,
        "w": 190,
        "h": 122
      },
      {
        "code": "FR-106",
        "kind": "course",
        "title": "Running and Operating Flows",
        "objective": "Running the shop for real",
        "course": "FR-106",
        "lane": "operations",
        "released": false,
        "requires": [
          "FR-103",
          "FR-102"
        ],
        "lesson_count": 12,
        "minutes": 81,
        "modules": 4,
        "docs": [],
        "page": null,
        "status": "planned",
        "x": 692,
        "y": 538,
        "w": 190,
        "h": 122
      },
      {
        "code": "FR-210",
        "kind": "course",
        "title": "FlowRunner for Developers",
        "objective": "Solve it fast: code where code is faster, platform where the platform is faster",
        "course": "FR-210",
        "lane": "developer",
        "released": false,
        "requires": [],
        "lesson_count": 13,
        "minutes": 88,
        "modules": 4,
        "docs": [],
        "page": null,
        "status": "planned",
        "x": 12,
        "y": 12,
        "w": 190,
        "h": 122
      },
      {
        "code": "RTS-01",
        "kind": "finale",
        "title": "Run the Shop",
        "objective": "Wire Flowland end to end and operate it: replay the defect orders, find and fix the failures, and reconcile orders against payments using the seeded mismatches",
        "requires": [
          "FR-106",
          "FR-104",
          "FR-105"
        ],
        "docs": [],
        "page": null,
        "lane": "finale",
        "status": "planned",
        "video": null,
        "available": false,
        "x": 1032,
        "y": 332,
        "w": 260,
        "h": 150
      }
    ],
    "edges": [
      {
        "from": "FR-101",
        "to": "FR-102",
        "uncertain": false,
        "points": [
          [
            202,
            387
          ],
          [
            202,
            387
          ],
          [
            277,
            387
          ],
          [
            352,
            387
          ]
        ]
      },
      {
        "from": "FR-101",
        "to": "FR-103",
        "uncertain": false,
        "points": [
          [
            202,
            427
          ],
          [
            202,
            427
          ],
          [
            277,
            564
          ],
          [
            352,
            579
          ]
        ]
      },
      {
        "from": "FR-102",
        "to": "FR-104",
        "uncertain": false,
        "points": [
          [
            542,
            387
          ],
          [
            542,
            387
          ],
          [
            617,
            387
          ],
          [
            692,
            387
          ]
        ]
      },
      {
        "from": "FR-103",
        "to": "FR-104",
        "uncertain": false,
        "points": [
          [
            542,
            559
          ],
          [
            542,
            559
          ],
          [
            617,
            493
          ],
          [
            692,
            427
          ]
        ]
      },
      {
        "from": "FR-102",
        "to": "FR-105",
        "uncertain": false,
        "points": [
          [
            542,
            357
          ],
          [
            542,
            357
          ],
          [
            617,
            229
          ],
          [
            692,
            215
          ]
        ]
      },
      {
        "from": "FR-103",
        "to": "FR-106",
        "uncertain": false,
        "points": [
          [
            542,
            599
          ],
          [
            542,
            599
          ],
          [
            617,
            609
          ],
          [
            692,
            619
          ]
        ]
      },
      {
        "from": "FR-102",
        "to": "FR-106",
        "uncertain": false,
        "points": [
          [
            542,
            418
          ],
          [
            542,
            418
          ],
          [
            617,
            563
          ],
          [
            692,
            579
          ]
        ]
      },
      {
        "from": "FR-106",
        "to": "RTS-01",
        "uncertain": false,
        "points": [
          [
            882,
            599
          ],
          [
            882,
            599
          ],
          [
            957,
            584
          ],
          [
            1032,
            445
          ]
        ]
      },
      {
        "from": "FR-104",
        "to": "RTS-01",
        "uncertain": false,
        "points": [
          [
            882,
            407
          ],
          [
            882,
            407
          ],
          [
            957,
            407
          ],
          [
            1032,
            407
          ]
        ]
      },
      {
        "from": "FR-105",
        "to": "RTS-01",
        "uncertain": false,
        "points": [
          [
            882,
            215
          ],
          [
            882,
            215
          ],
          [
            957,
            231
          ],
          [
            1032,
            370
          ]
        ]
      }
    ]
  },
  "views": {
    "FR-101.1": {
      "width": 722,
      "height": 356,
      "nodes": [
        {
          "code": "FR-101.1.1",
          "kind": "lesson",
          "title": "Run your first working flow",
          "objective": "Go from an empty workspace to a working automation - the quickstart contact flow, compressed - and fire it from the real Flowland Contact page before any theory",
          "status": "planned",
          "video": "https://youtu.be/36CFme-XMvM",
          "available": true,
          "course": "FR-101",
          "lane": "foundations",
          "module": 1,
          "moduleTitle": "Your first flow, end to end",
          "requires": [],
          "docs": [
            {
              "label": "Import the finished flow",
              "href": "/flows/contact-us-form.json"
            },
            {
              "label": "Quick Start: A Contact Us Form (no-code)",
              "href": "https://docs.flowrunner.ai/learn/quickstart"
            }
          ],
          "page": "/contact",
          "minutes": 3,
          "x": 12,
          "y": 29,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-101.1.2",
          "kind": "lesson",
          "title": "Step-by-step build of your first flow (No-Code)",
          "objective": "Follow me for a step by step implementation of your first flow - and fire it from the real Flowland Contact page. The email template you paste into the variable is under Start from on this card, and in the Tutorial Assets panel on the contact form.",
          "status": "planned",
          "video": "https://youtu.be/YzRoQE_m5Tk",
          "available": true,
          "course": "FR-101",
          "lane": "foundations",
          "module": 1,
          "moduleTitle": "Your first flow, end to end",
          "requires": [
            "FR-101.1.1"
          ],
          "docs": [
            {
              "label": "Email template (copy this)",
              "href": "/templates/contact-us-email.txt"
            },
            {
              "label": "Quick Start: A Contact Us Form (no-code)",
              "href": "https://docs.flowrunner.ai/learn/quickstart"
            }
          ],
          "page": "/contact",
          "minutes": 12,
          "x": 306,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-101.1.3",
          "kind": "lesson",
          "title": "Step-by-step build of your first flow (w/Code)",
          "objective": "Follow me for a step by step implementation of your first flow - in code, with the email template built inside a code block rather than stored in a variable - and fire it from the real Flowland Contact page. The finished code is under Start from on this card, and in the Tutorial Assets panel on the contact form.",
          "status": "planned",
          "video": "https://youtu.be/6TGuLTZySzo",
          "available": true,
          "course": "FR-101",
          "lane": "foundations",
          "module": 1,
          "moduleTitle": "Your first flow, end to end",
          "requires": [
            "FR-101.1.1"
          ],
          "docs": [
            {
              "label": "The finished code (copy this)",
              "href": "/templates/contact-us-email-code.txt"
            },
            {
              "label": "Quick Start: A Contact Us Form (no-code)",
              "href": "https://docs.flowrunner.ai/learn/quickstart"
            }
          ],
          "page": "/contact",
          "minutes": 12,
          "x": 306,
          "y": 180,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-101.2",
          "kind": "module",
          "side": "after",
          "title": "How a flow is put together",
          "objective": "Module 2 of FR-101",
          "course": "FR-101",
          "module": 2,
          "lane": "foundations",
          "touches": [
            "FR-101.1.1"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 522,
          "y": 134,
          "w": 160,
          "h": 68
        }
      ],
      "edges": [
        {
          "from": "FR-101.1.1",
          "to": "FR-101.1.2",
          "uncertain": false,
          "points": [
            [
              202,
              64
            ],
            [
              202,
              64
            ],
            [
              254,
              64
            ],
            [
              306,
              64
            ]
          ]
        },
        {
          "from": "FR-101.1.1",
          "to": "FR-101.1.3",
          "uncertain": false,
          "points": [
            [
              202,
              98
            ],
            [
              202,
              98
            ],
            [
              254,
              219
            ],
            [
              306,
              232
            ]
          ]
        },
        {
          "from": "FR-101.1.2",
          "to": "FR-101.1#out",
          "module": true,
          "points": [
            [
              496,
              64
            ],
            [
              516,
              64
            ],
            [
              489,
              148
            ],
            [
              509,
              148
            ]
          ]
        },
        {
          "from": "FR-101.1.3",
          "to": "FR-101.1#out",
          "module": true,
          "points": [
            [
              496,
              232
            ],
            [
              516,
              232
            ],
            [
              489,
              148
            ],
            [
              509,
              148
            ]
          ]
        },
        {
          "from": "FR-101.1#out",
          "to": "FR-101.2",
          "module": true,
          "points": [
            [
              509,
              148
            ],
            [
              529,
              148
            ],
            [
              502,
              168
            ],
            [
              522,
              168
            ]
          ]
        }
      ],
      "course": "FR-101",
      "module": 1,
      "title": "Your first flow, end to end",
      "lesson_count": 3,
      "minutes": 27
    },
    "FR-101.2": {
      "width": 1190,
      "height": 356,
      "nodes": [
        {
          "code": "FR-101.1",
          "kind": "module",
          "side": "before",
          "title": "Your first flow, end to end",
          "objective": "Module 1 of FR-101",
          "course": "FR-101",
          "module": 1,
          "lane": "foundations",
          "touches": [
            "FR-101.1.1"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 134,
          "w": 160,
          "h": 68
        },
        {
          "code": "FR-101.2.1",
          "kind": "lesson",
          "title": "A tour of the workspace and the flow editor",
          "objective": "Navigate the workspace and identify the editor's working areas",
          "status": "planned",
          "video": "https://youtu.be/hYLhzZB5zkc",
          "available": true,
          "course": "FR-101",
          "lane": "foundations",
          "module": 2,
          "moduleTitle": "How a flow is put together",
          "requires": [
            "FR-101.1.1"
          ],
          "docs": [
            {
              "label": "Workspace",
              "href": "https://docs.flowrunner.ai/platform/workspace"
            },
            {
              "label": "The Flow Editor",
              "href": "https://docs.flowrunner.ai/build/flow-editor"
            }
          ],
          "page": null,
          "minutes": 6,
          "x": 186,
          "y": 29,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-101.2.2",
          "kind": "lesson",
          "title": "What a flow is",
          "objective": "Describe what a flow is and what its parts do: the connected blocks, the data that moves between them, a schedule, flow instances",
          "status": "planned",
          "video": "https://youtu.be/H41VQmRLZmA",
          "available": true,
          "course": "FR-101",
          "lane": "foundations",
          "module": 2,
          "moduleTitle": "How a flow is put together",
          "requires": [
            "FR-101.2.1"
          ],
          "docs": [
            {
              "label": "What is FlowRunner?",
              "href": "https://docs.flowrunner.ai/index"
            }
          ],
          "page": null,
          "minutes": 5,
          "x": 480,
          "y": 29,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-101.2.3",
          "kind": "lesson",
          "title": "One flow, many runs at the same time",
          "objective": "Explain that a flow is the design and each run is an independent instance",
          "status": "planned",
          "video": "https://youtu.be/mOCIllhRH5Y",
          "available": true,
          "course": "FR-101",
          "lane": "foundations",
          "module": 2,
          "moduleTitle": "How a flow is put together",
          "requires": [
            "FR-101.2.2"
          ],
          "docs": [
            {
              "label": "Flows and Instances",
              "href": "https://docs.flowrunner.ai/learn/concepts/flows-and-instances"
            },
            {
              "label": "Blocks",
              "href": "https://docs.flowrunner.ai/learn/concepts/blocks"
            }
          ],
          "page": null,
          "minutes": 6,
          "x": 774,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-101.2.4",
          "kind": "lesson",
          "title": "What can start a flow",
          "objective": "List the events that can start or resume a flow",
          "status": "planned",
          "video": "https://youtu.be/50fiJ9fvCbM",
          "available": true,
          "course": "FR-101",
          "lane": "foundations",
          "module": 2,
          "moduleTitle": "How a flow is put together",
          "requires": [
            "FR-101.2.2"
          ],
          "docs": [
            {
              "label": "Triggers",
              "href": "https://docs.flowrunner.ai/learn/concepts/triggers"
            }
          ],
          "page": null,
          "minutes": 4,
          "x": 774,
          "y": 180,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-101.3",
          "kind": "module",
          "side": "after",
          "title": "Triggers, variables, and expressions",
          "objective": "Module 3 of FR-101",
          "course": "FR-101",
          "module": 3,
          "lane": "foundations",
          "touches": [
            "FR-101.2.1",
            "FR-101.2.4"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 990,
          "y": 127,
          "w": 160,
          "h": 83
        }
      ],
      "edges": [
        {
          "from": "FR-101.2.1",
          "to": "FR-101.2.2",
          "uncertain": false,
          "points": [
            [
              376,
              81
            ],
            [
              376,
              81
            ],
            [
              428,
              81
            ],
            [
              480,
              81
            ]
          ]
        },
        {
          "from": "FR-101.2.2",
          "to": "FR-101.2.3",
          "uncertain": false,
          "points": [
            [
              670,
              64
            ],
            [
              670,
              64
            ],
            [
              722,
              64
            ],
            [
              774,
              64
            ]
          ]
        },
        {
          "from": "FR-101.2.2",
          "to": "FR-101.2.4",
          "uncertain": false,
          "points": [
            [
              670,
              98
            ],
            [
              670,
              98
            ],
            [
              722,
              219
            ],
            [
              774,
              232
            ]
          ]
        },
        {
          "from": "FR-101.1",
          "to": "FR-101.2.1",
          "module": true,
          "points": [
            [
              160,
              168
            ],
            [
              180,
              168
            ],
            [
              166,
              81
            ],
            [
              186,
              81
            ]
          ]
        },
        {
          "from": "FR-101.2.3",
          "to": "FR-101.2#out",
          "module": true,
          "points": [
            [
              964,
              64
            ],
            [
              984,
              64
            ],
            [
              957,
              148
            ],
            [
              977,
              148
            ]
          ]
        },
        {
          "from": "FR-101.2.4",
          "to": "FR-101.2#out",
          "module": true,
          "points": [
            [
              964,
              232
            ],
            [
              984,
              232
            ],
            [
              957,
              148
            ],
            [
              977,
              148
            ]
          ]
        },
        {
          "from": "FR-101.2#out",
          "to": "FR-101.3",
          "module": true,
          "points": [
            [
              977,
              148
            ],
            [
              997,
              148
            ],
            [
              970,
              169
            ],
            [
              990,
              169
            ]
          ]
        }
      ],
      "course": "FR-101",
      "module": 2,
      "title": "How a flow is put together",
      "lesson_count": 4,
      "minutes": 21
    },
    "FR-101.3": {
      "width": 1484,
      "height": 356,
      "nodes": [
        {
          "code": "FR-101.2",
          "kind": "module",
          "side": "before",
          "title": "How a flow is put together",
          "objective": "Module 2 of FR-101",
          "course": "FR-101",
          "module": 2,
          "lane": "foundations",
          "touches": [
            "FR-101.2.1",
            "FR-101.2.4"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 134,
          "w": 160,
          "h": 68
        },
        {
          "code": "FR-101.3.1",
          "kind": "lesson",
          "title": "Wake a flow with a trigger and see what data arrives",
          "objective": "Know that a flow waits at a trigger until its event arrives; use the External Callback URL and the triggers extensions add, telling real-time from polling; capture what arrives with Learning Mode; hand the callback URL to another system from the Expression Editor",
          "status": "planned",
          "video": "https://youtu.be/IKqkdqUjTmI",
          "available": true,
          "course": "FR-101",
          "lane": "foundations",
          "module": 3,
          "moduleTitle": "Triggers, variables, and expressions",
          "requires": [
            "FR-101.2.1",
            "FR-101.2.4"
          ],
          "docs": [
            {
              "label": "Quick Start: A Contact Us Form (no-code)",
              "href": "https://docs.flowrunner.ai/learn/quickstart"
            },
            {
              "label": "Triggers",
              "href": "https://docs.flowrunner.ai/learn/concepts/triggers"
            },
            {
              "label": "External Callback",
              "href": "https://docs.flowrunner.ai/reference/external-callback"
            }
          ],
          "page": "/contact",
          "minutes": 10,
          "x": 186,
          "y": 29,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-101.3.2",
          "kind": "lesson",
          "title": "Send Data into Your Flow",
          "objective": "Pass values into a flow that has no trigger - as a query string on a GET, as JSON on a POST - and read them inside it from the Initial Data pill under Flow Context, one property or the whole object; check what arrived in the instance summary",
          "status": "planned",
          "video": "https://youtu.be/W8N71bgt8yE",
          "available": true,
          "course": "FR-101",
          "lane": "foundations",
          "module": 3,
          "moduleTitle": "Triggers, variables, and expressions",
          "requires": [
            "FR-101.3.1"
          ],
          "docs": [
            {
              "label": "Quick Start: A Contact Us Form (no-code)",
              "href": "https://docs.flowrunner.ai/learn/quickstart"
            },
            {
              "label": "Call Flow (Blocking)",
              "href": "https://docs.flowrunner.ai/api/call-flow-blocking"
            }
          ],
          "page": "/purchase-order",
          "minutes": 8,
          "x": 480,
          "y": 180,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-101.3.3",
          "kind": "lesson",
          "title": "About Variables",
          "objective": "Create variables with Set Variables, read them from the Expression Editor, assign a block's result back to a variable to change it, carry a value out of a loop, and know what a Data Bucket is",
          "status": "planned",
          "video": "https://youtu.be/KRFHVWNZRl8",
          "available": true,
          "course": "FR-101",
          "lane": "foundations",
          "module": 3,
          "moduleTitle": "Triggers, variables, and expressions",
          "requires": [
            "FR-101.3.1"
          ],
          "docs": [
            {
              "label": "Quick Start: A Contact Us Form (no-code)",
              "href": "https://docs.flowrunner.ai/learn/quickstart"
            },
            {
              "label": "Variables & Data Buckets",
              "href": "https://docs.flowrunner.ai/learn/concepts/variables"
            }
          ],
          "page": null,
          "minutes": 8,
          "x": 480,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-101.3.4",
          "kind": "lesson",
          "title": "Use one block's result inside another's value",
          "objective": "Know that every block produces a result and any later block on the same path can read it; name a trigger's data and a block's result so they are easy to spot in the Expression Editor; pull one property out of a result into another block; choose between a block result, which keeps the value the block produced, and a variable, which can be reassigned; know that a result is only there for blocks downstream of it on that path",
          "status": "planned",
          "video": "https://youtu.be/ZOS4xxUfkss",
          "available": true,
          "course": "FR-101",
          "lane": "foundations",
          "module": 3,
          "moduleTitle": "Triggers, variables, and expressions",
          "requires": [
            "FR-101.3.2",
            "FR-101.3.3"
          ],
          "docs": [
            {
              "label": "Quick Start: A Contact Us Form (no-code)",
              "href": "https://docs.flowrunner.ai/learn/quickstart"
            },
            {
              "label": "Expression Editor",
              "href": "https://docs.flowrunner.ai/learn/concepts/expressions"
            },
            {
              "label": "Transform Data",
              "href": "https://docs.flowrunner.ai/reference/transform-data"
            }
          ],
          "page": "/purchase-order",
          "minutes": 13,
          "x": 774,
          "y": 119,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-101.3.5",
          "kind": "lesson",
          "title": "Send an answer back to whoever called your flow (Return Result)",
          "objective": "Compose what the caller gets back and know that Return Result ends that path; answer the purchase order form with more than \"it worked\" and watch it appear on the page",
          "status": "planned",
          "video": "https://youtu.be/-uM7iFr5MMs",
          "available": true,
          "course": "FR-101",
          "lane": "foundations",
          "module": 3,
          "moduleTitle": "Triggers, variables, and expressions",
          "requires": [
            "FR-101.3.2",
            "FR-101.3.4"
          ],
          "docs": [
            {
              "label": "Returning a Result",
              "href": "https://docs.flowrunner.ai/build/integrations/returning-a-result"
            },
            {
              "label": "Return Result",
              "href": "https://docs.flowrunner.ai/reference/return-result"
            }
          ],
          "page": "/purchase-order",
          "minutes": 7,
          "x": 1068,
          "y": 180,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-101.4",
          "kind": "module",
          "side": "after",
          "title": "Test it, take it live, and watch it run",
          "objective": "Module 4 of FR-101",
          "course": "FR-101",
          "module": 4,
          "lane": "foundations",
          "touches": [
            "FR-101.3.5"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 1284,
          "y": 127,
          "w": 160,
          "h": 83
        }
      ],
      "edges": [
        {
          "from": "FR-101.3.1",
          "to": "FR-101.3.2",
          "uncertain": false,
          "points": [
            [
              376,
              98
            ],
            [
              376,
              98
            ],
            [
              428,
              219
            ],
            [
              480,
              232
            ]
          ]
        },
        {
          "from": "FR-101.3.1",
          "to": "FR-101.3.3",
          "uncertain": false,
          "points": [
            [
              376,
              64
            ],
            [
              376,
              64
            ],
            [
              428,
              64
            ],
            [
              480,
              64
            ]
          ]
        },
        {
          "from": "FR-101.3.2",
          "to": "FR-101.3.4",
          "uncertain": false,
          "points": [
            [
              670,
              215
            ],
            [
              670,
              215
            ],
            [
              722,
              202
            ],
            [
              774,
              188
            ]
          ]
        },
        {
          "from": "FR-101.3.3",
          "to": "FR-101.3.4",
          "uncertain": false,
          "points": [
            [
              670,
              64
            ],
            [
              670,
              64
            ],
            [
              722,
              73
            ],
            [
              774,
              154
            ]
          ]
        },
        {
          "from": "FR-101.3.2",
          "to": "FR-101.3.5",
          "uncertain": false,
          "points": [
            [
              670,
              249
            ],
            [
              722,
              249
            ],
            [
              796,
              249
            ],
            [
              869,
              249
            ],
            [
              943,
              249
            ],
            [
              1016,
              249
            ],
            [
              1068,
              249
            ]
          ]
        },
        {
          "from": "FR-101.3.4",
          "to": "FR-101.3.5",
          "uncertain": false,
          "points": [
            [
              964,
              171
            ],
            [
              964,
              171
            ],
            [
              1016,
              175
            ],
            [
              1068,
              215
            ]
          ]
        },
        {
          "from": "FR-101.2",
          "to": "FR-101.3.1",
          "module": true,
          "points": [
            [
              160,
              168
            ],
            [
              180,
              168
            ],
            [
              166,
              81
            ],
            [
              186,
              81
            ]
          ]
        },
        {
          "from": "FR-101.3.5",
          "to": "FR-101.4",
          "module": true,
          "points": [
            [
              1258,
              232
            ],
            [
              1278,
              232
            ],
            [
              1264,
              169
            ],
            [
              1284,
              169
            ]
          ]
        }
      ],
      "course": "FR-101",
      "module": 3,
      "title": "Triggers, variables, and expressions",
      "lesson_count": 5,
      "minutes": 46
    },
    "FR-101.4": {
      "width": 1190,
      "height": 188,
      "nodes": [
        {
          "code": "FR-101.3",
          "kind": "module",
          "side": "before",
          "title": "Triggers, variables, and expressions",
          "objective": "Module 3 of FR-101",
          "course": "FR-101",
          "module": 3,
          "lane": "foundations",
          "touches": [
            "FR-101.3.5"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 43,
          "w": 160,
          "h": 83
        },
        {
          "code": "FR-101.4.1",
          "kind": "lesson",
          "title": "Run your flow one step at a time (Test Monitor)",
          "objective": "Run one block and read what went in and what came out; supply the Initial Data a test has no trigger to bring, on the Flow Data tab; then replay a run that already happened, whole or from any block in the middle",
          "status": "planned",
          "video": "https://youtu.be/zMsh_FoQBl4",
          "available": true,
          "course": "FR-101",
          "lane": "foundations",
          "module": 4,
          "moduleTitle": "Test it, take it live, and watch it run",
          "requires": [
            "FR-101.3.5"
          ],
          "docs": [
            {
              "label": "Testing",
              "href": "https://docs.flowrunner.ai/run/testing"
            }
          ],
          "page": "/purchase-order",
          "minutes": 13,
          "x": 186,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-101.4.2",
          "kind": "lesson",
          "title": "Turn your flow on, and know which version is running (Versions)",
          "objective": "A flow has versions and only one of them is LIVE; read the Ready, Live or Not Ready status in the toolbar and find what a flagged block is objecting to; clone a live version to change it, then switch over - the old version suspends, stops taking new requests, and finishes the ones it is holding",
          "status": "planned",
          "video": "https://youtu.be/u2u7KlBxnzs",
          "available": true,
          "course": "FR-101",
          "lane": "foundations",
          "module": 4,
          "moduleTitle": "Test it, take it live, and watch it run",
          "requires": [
            "FR-101.4.1"
          ],
          "docs": [
            {
              "label": "Running Flows",
              "href": "https://docs.flowrunner.ai/run/running-flows"
            },
            {
              "label": "The Flow Editor",
              "href": "https://docs.flowrunner.ai/build/flow-editor"
            },
            {
              "label": "Flows",
              "href": "https://docs.flowrunner.ai/manage/flows"
            }
          ],
          "page": "/purchase-order",
          "minutes": 8,
          "x": 480,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-101.4.3",
          "kind": "lesson",
          "title": "See what happened on every run (Instances and Analytics)",
          "objective": "Find a run in the Instances list and read what every block received and produced; tell completed, completed-with-a-handled-error and terminated apart; filter by status, read the share that went down each path on the dashboard, and find where the time went on the performance tab",
          "status": "planned",
          "video": "https://youtu.be/Z-ilicvvYlc",
          "available": true,
          "course": "FR-101",
          "lane": "foundations",
          "module": 4,
          "moduleTitle": "Test it, take it live, and watch it run",
          "requires": [
            "FR-101.4.2",
            "FR-101.2.3"
          ],
          "docs": [
            {
              "label": "Running Flows",
              "href": "https://docs.flowrunner.ai/run/running-flows"
            },
            {
              "label": "Inspecting a Single Run",
              "href": "https://docs.flowrunner.ai/run/inspecting-a-run"
            },
            {
              "label": "Monitoring & Analytics",
              "href": "https://docs.flowrunner.ai/run/monitoring"
            }
          ],
          "page": "/contact",
          "minutes": 8,
          "x": 774,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-102.1",
          "kind": "module",
          "side": "after",
          "title": "Making decisions",
          "objective": "Module 1 of FR-102",
          "course": "FR-102",
          "module": 1,
          "lane": "control",
          "touches": [],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 990,
          "y": 58,
          "w": 160,
          "h": 53
        }
      ],
      "edges": [
        {
          "from": "FR-101.4.1",
          "to": "FR-101.4.2",
          "uncertain": false,
          "points": [
            [
              376,
              64
            ],
            [
              376,
              64
            ],
            [
              428,
              64
            ],
            [
              480,
              64
            ]
          ]
        },
        {
          "from": "FR-101.4.2",
          "to": "FR-101.4.3",
          "uncertain": false,
          "points": [
            [
              670,
              64
            ],
            [
              670,
              64
            ],
            [
              722,
              64
            ],
            [
              774,
              64
            ]
          ]
        },
        {
          "from": "FR-101.3",
          "to": "FR-101.4.1",
          "module": true,
          "points": [
            [
              160,
              85
            ],
            [
              180,
              85
            ],
            [
              166,
              64
            ],
            [
              186,
              64
            ]
          ]
        },
        {
          "from": "FR-101.4.3",
          "to": "FR-102.1",
          "module": true,
          "points": [
            [
              964,
              64
            ],
            [
              984,
              64
            ],
            [
              970,
              85
            ],
            [
              990,
              85
            ]
          ]
        }
      ],
      "course": "FR-101",
      "module": 4,
      "title": "Test it, take it live, and watch it run",
      "lesson_count": 3,
      "minutes": 29
    },
    "FR-102.1": {
      "width": 896,
      "height": 188,
      "nodes": [
        {
          "code": "FR-101.4",
          "kind": "module",
          "side": "before",
          "title": "Test it, take it live, and watch it run",
          "objective": "Module 4 of FR-101",
          "course": "FR-101",
          "module": 4,
          "lane": "foundations",
          "touches": [],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 43,
          "w": 160,
          "h": 83
        },
        {
          "code": "FR-102.1.1",
          "kind": "lesson",
          "title": "Choose what happens next, based on the data (Condition)",
          "objective": "Fork a flow on a typed comparison and wire both paths, from a single check to several combined with AND, OR and parentheses",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-102",
          "lane": "control",
          "module": 1,
          "moduleTitle": "Making decisions",
          "requires": [
            "FR-101.3.4"
          ],
          "docs": [
            {
              "label": "Yes/No Branching",
              "href": "https://docs.flowrunner.ai/build/flow-control/branching"
            },
            {
              "label": "Condition",
              "href": "https://docs.flowrunner.ai/reference/condition"
            }
          ],
          "page": null,
          "minutes": 10,
          "x": 186,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-102.1.2",
          "kind": "lesson",
          "title": "Handle more than two possible outcomes (Value Router)",
          "objective": "Use Value Router with exact, collection, and range matches; know when to prefer Condition or AI Router",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-102",
          "lane": "control",
          "module": 1,
          "moduleTitle": "Making decisions",
          "requires": [
            "FR-102.1.1"
          ],
          "docs": [
            {
              "label": "Routing on a Value",
              "href": "https://docs.flowrunner.ai/build/flow-control/routing"
            },
            {
              "label": "Value Router",
              "href": "https://docs.flowrunner.ai/reference/value-router"
            }
          ],
          "page": null,
          "minutes": 7,
          "x": 480,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-102.2",
          "kind": "module",
          "side": "after",
          "title": "Repeating work",
          "objective": "Module 2 of FR-102",
          "course": "FR-102",
          "module": 2,
          "lane": "control",
          "touches": [
            "FR-102.1.1"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 696,
          "y": 58,
          "w": 160,
          "h": 53
        }
      ],
      "edges": [
        {
          "from": "FR-102.1.1",
          "to": "FR-102.1.2",
          "uncertain": false,
          "points": [
            [
              376,
              64
            ],
            [
              376,
              64
            ],
            [
              428,
              64
            ],
            [
              480,
              64
            ]
          ]
        },
        {
          "from": "FR-101.4",
          "to": "FR-102.1.1",
          "module": true,
          "points": [
            [
              160,
              85
            ],
            [
              180,
              85
            ],
            [
              166,
              64
            ],
            [
              186,
              64
            ]
          ]
        },
        {
          "from": "FR-102.1.2",
          "to": "FR-102.2",
          "module": true,
          "points": [
            [
              670,
              64
            ],
            [
              690,
              64
            ],
            [
              676,
              85
            ],
            [
              696,
              85
            ]
          ]
        }
      ],
      "course": "FR-102",
      "module": 1,
      "title": "Making decisions",
      "lesson_count": 2,
      "minutes": 17
    },
    "FR-102.2": {
      "width": 1190,
      "height": 188,
      "nodes": [
        {
          "code": "FR-102.1",
          "kind": "module",
          "side": "before",
          "title": "Making decisions",
          "objective": "Module 1 of FR-102",
          "course": "FR-102",
          "module": 1,
          "lane": "control",
          "touches": [
            "FR-102.1.1"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 58,
          "w": 160,
          "h": 53
        },
        {
          "code": "FR-102.2.1",
          "kind": "lesson",
          "title": "Do the same steps for every item on a list (List Iterator)",
          "objective": "Run steps per element and carry a result out of the loop",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-102",
          "lane": "control",
          "module": 2,
          "moduleTitle": "Repeating work",
          "requires": [
            "FR-102.1.1",
            "FR-101.3.2"
          ],
          "docs": [
            {
              "label": "Working Through a List",
              "href": "https://docs.flowrunner.ai/build/flow-control/collections"
            },
            {
              "label": "List Iterator",
              "href": "https://docs.flowrunner.ai/reference/list-iterator"
            }
          ],
          "page": null,
          "minutes": 8,
          "x": 186,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-102.2.2",
          "kind": "lesson",
          "title": "Keep checking until something is ready (Repeat)",
          "objective": "Build condition-driven loops capped against running away",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-102",
          "lane": "control",
          "module": 2,
          "moduleTitle": "Repeating work",
          "requires": [
            "FR-102.2.1"
          ],
          "docs": [
            {
              "label": "Repeating Steps",
              "href": "https://docs.flowrunner.ai/build/flow-control/repeating"
            },
            {
              "label": "Repeat",
              "href": "https://docs.flowrunner.ai/reference/repeat"
            }
          ],
          "page": null,
          "minutes": 7,
          "x": 480,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-102.2.3",
          "kind": "lesson",
          "title": "Stop going through a list once you have what you need (Break)",
          "objective": "Exit a loop on a condition and use the current pass in logic",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-102",
          "lane": "control",
          "module": 2,
          "moduleTitle": "Repeating work",
          "requires": [
            "FR-102.2.2"
          ],
          "docs": [
            {
              "label": "Repeating Steps",
              "href": "https://docs.flowrunner.ai/build/flow-control/repeating"
            },
            {
              "label": "Break",
              "href": "https://docs.flowrunner.ai/reference/break"
            }
          ],
          "page": null,
          "minutes": 5,
          "x": 774,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-102.3",
          "kind": "module",
          "side": "after",
          "title": "Doing things at the same time, and waiting",
          "objective": "Module 3 of FR-102",
          "course": "FR-102",
          "module": 3,
          "lane": "control",
          "touches": [],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 990,
          "y": 43,
          "w": 160,
          "h": 83
        }
      ],
      "edges": [
        {
          "from": "FR-102.2.1",
          "to": "FR-102.2.2",
          "uncertain": false,
          "points": [
            [
              376,
              64
            ],
            [
              376,
              64
            ],
            [
              428,
              64
            ],
            [
              480,
              64
            ]
          ]
        },
        {
          "from": "FR-102.2.2",
          "to": "FR-102.2.3",
          "uncertain": false,
          "points": [
            [
              670,
              64
            ],
            [
              670,
              64
            ],
            [
              722,
              64
            ],
            [
              774,
              64
            ]
          ]
        },
        {
          "from": "FR-102.1",
          "to": "FR-102.2.1",
          "module": true,
          "points": [
            [
              160,
              85
            ],
            [
              180,
              85
            ],
            [
              166,
              64
            ],
            [
              186,
              64
            ]
          ]
        },
        {
          "from": "FR-102.2.3",
          "to": "FR-102.3",
          "module": true,
          "points": [
            [
              964,
              64
            ],
            [
              984,
              64
            ],
            [
              970,
              85
            ],
            [
              990,
              85
            ]
          ]
        }
      ],
      "course": "FR-102",
      "module": 2,
      "title": "Repeating work",
      "lesson_count": 3,
      "minutes": 20
    },
    "FR-102.3": {
      "width": 602,
      "height": 312,
      "nodes": [
        {
          "code": "FR-102.2",
          "kind": "module",
          "side": "before",
          "title": "Repeating work",
          "objective": "Module 2 of FR-102",
          "course": "FR-102",
          "module": 2,
          "lane": "control",
          "touches": [],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 120,
          "w": 160,
          "h": 53
        },
        {
          "code": "FR-102.3.1",
          "kind": "lesson",
          "title": "Do several things at once instead of one after another (Actions Group, Synchronize)",
          "objective": "Fan out independent steps with an Actions Group, choose whether the flow moves on when they start or when they finish, and join branches with Synchronize",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-102",
          "lane": "control",
          "module": 3,
          "moduleTitle": "Doing things at the same time, and waiting",
          "requires": [
            "FR-102.1.1"
          ],
          "docs": [
            {
              "label": "Running Steps in Parallel",
              "href": "https://docs.flowrunner.ai/build/flow-control/parallel"
            },
            {
              "label": "Synchronize",
              "href": "https://docs.flowrunner.ai/reference/synchronize"
            },
            {
              "label": "Actions Group",
              "href": "https://docs.flowrunner.ai/reference/actions-group"
            }
          ],
          "page": null,
          "minutes": 8,
          "x": 186,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-102.3.2",
          "kind": "lesson",
          "title": "Pause before the next step (Wait)",
          "objective": "Pause a branch for a fixed or data-driven time",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-102",
          "lane": "control",
          "module": 3,
          "moduleTitle": "Doing things at the same time, and waiting",
          "requires": [
            "FR-102.1.1"
          ],
          "docs": [
            {
              "label": "Adding a Delay",
              "href": "https://docs.flowrunner.ai/build/flow-control/waiting"
            },
            {
              "label": "Wait",
              "href": "https://docs.flowrunner.ai/reference/wait"
            }
          ],
          "page": null,
          "minutes": 4,
          "x": 186,
          "y": 136,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-102.4",
          "kind": "module",
          "side": "after",
          "title": "When something does not work",
          "objective": "Module 4 of FR-102",
          "course": "FR-102",
          "module": 4,
          "lane": "control",
          "touches": [
            "FR-102.3.1"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 402,
          "y": 112,
          "w": 160,
          "h": 68
        }
      ],
      "edges": [
        {
          "from": "FR-102.2",
          "to": "FR-102.3#in",
          "module": true,
          "points": [
            [
              160,
              147
            ],
            [
              180,
              147
            ],
            [
              153,
              126
            ],
            [
              173,
              126
            ]
          ]
        },
        {
          "from": "FR-102.3#in",
          "to": "FR-102.3.1",
          "module": true,
          "points": [
            [
              173,
              126
            ],
            [
              193,
              126
            ],
            [
              166,
              64
            ],
            [
              186,
              64
            ]
          ]
        },
        {
          "from": "FR-102.3#in",
          "to": "FR-102.3.2",
          "module": true,
          "points": [
            [
              173,
              126
            ],
            [
              193,
              126
            ],
            [
              166,
              188
            ],
            [
              186,
              188
            ]
          ]
        },
        {
          "from": "FR-102.3.1",
          "to": "FR-102.3#out",
          "module": true,
          "points": [
            [
              376,
              64
            ],
            [
              396,
              64
            ],
            [
              369,
              126
            ],
            [
              389,
              126
            ]
          ]
        },
        {
          "from": "FR-102.3.2",
          "to": "FR-102.3#out",
          "module": true,
          "points": [
            [
              376,
              188
            ],
            [
              396,
              188
            ],
            [
              369,
              126
            ],
            [
              389,
              126
            ]
          ]
        },
        {
          "from": "FR-102.3#out",
          "to": "FR-102.4",
          "module": true,
          "points": [
            [
              389,
              126
            ],
            [
              409,
              126
            ],
            [
              382,
              146
            ],
            [
              402,
              146
            ]
          ]
        }
      ],
      "course": "FR-102",
      "module": 3,
      "title": "Doing things at the same time, and waiting",
      "lesson_count": 2,
      "minutes": 12
    },
    "FR-102.4": {
      "width": 1190,
      "height": 188,
      "nodes": [
        {
          "code": "FR-102.3",
          "kind": "module",
          "side": "before",
          "title": "Doing things at the same time, and waiting",
          "objective": "Module 3 of FR-102",
          "course": "FR-102",
          "module": 3,
          "lane": "control",
          "touches": [
            "FR-102.3.1"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 43,
          "w": 160,
          "h": 83
        },
        {
          "code": "FR-102.4.1",
          "kind": "lesson",
          "title": "Carry on when one step does not work (Handle Error)",
          "objective": "Attach Handle Error and keep the run going, against an inventory service having a bad morning",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-102",
          "lane": "control",
          "module": 4,
          "moduleTitle": "When something does not work",
          "requires": [
            "FR-102.1.1"
          ],
          "docs": [
            {
              "label": "Handling Errors",
              "href": "https://docs.flowrunner.ai/build/flow-control/error-handling"
            },
            {
              "label": "Handle Error",
              "href": "https://docs.flowrunner.ai/reference/handle-error"
            }
          ],
          "page": "/back-office",
          "minutes": 7,
          "x": 186,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-102.4.2",
          "kind": "lesson",
          "title": "Try again a few times before giving up (Retries)",
          "objective": "Branch on error details and configure retries against deterministic failures",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-102",
          "lane": "control",
          "module": 4,
          "moduleTitle": "When something does not work",
          "requires": [
            "FR-102.4.1"
          ],
          "docs": [
            {
              "label": "Handling Errors",
              "href": "https://docs.flowrunner.ai/build/flow-control/error-handling"
            },
            {
              "label": "Error Handling",
              "href": "https://docs.flowrunner.ai/reference/error-handling-concept"
            }
          ],
          "page": "/back-office",
          "minutes": 7,
          "x": 480,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-102.4.3",
          "kind": "lesson",
          "title": "Capstone: sort and answer incoming requests",
          "objective": "Assemble branching, loops, parallel work, and error handling in one flow",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-102",
          "lane": "control",
          "module": 4,
          "moduleTitle": "When something does not work",
          "requires": [
            "FR-102.1.2",
            "FR-102.2.3",
            "FR-102.3.1",
            "FR-102.4.2"
          ],
          "docs": [],
          "page": "/back-office",
          "minutes": 10,
          "x": 774,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-103.1",
          "kind": "module",
          "side": "after",
          "title": "Where data comes from and where it goes",
          "objective": "Module 1 of FR-103",
          "course": "FR-103",
          "module": 1,
          "lane": "data",
          "touches": [],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 990,
          "y": 43,
          "w": 160,
          "h": 83
        }
      ],
      "edges": [
        {
          "from": "FR-102.4.1",
          "to": "FR-102.4.2",
          "uncertain": false,
          "points": [
            [
              376,
              64
            ],
            [
              376,
              64
            ],
            [
              428,
              64
            ],
            [
              480,
              64
            ]
          ]
        },
        {
          "from": "FR-102.4.2",
          "to": "FR-102.4.3",
          "uncertain": false,
          "points": [
            [
              670,
              64
            ],
            [
              670,
              64
            ],
            [
              722,
              64
            ],
            [
              774,
              64
            ]
          ]
        },
        {
          "from": "FR-102.3",
          "to": "FR-102.4.1",
          "module": true,
          "points": [
            [
              160,
              85
            ],
            [
              180,
              85
            ],
            [
              166,
              64
            ],
            [
              186,
              64
            ]
          ]
        },
        {
          "from": "FR-102.4.3",
          "to": "FR-103.1",
          "module": true,
          "points": [
            [
              964,
              64
            ],
            [
              984,
              64
            ],
            [
              970,
              85
            ],
            [
              990,
              85
            ]
          ]
        }
      ],
      "course": "FR-102",
      "module": 4,
      "title": "When something does not work",
      "lesson_count": 3,
      "minutes": 24
    },
    "FR-103.1": {
      "width": 1190,
      "height": 188,
      "nodes": [
        {
          "code": "FR-102.4",
          "kind": "module",
          "side": "before",
          "title": "When something does not work",
          "objective": "Module 4 of FR-102",
          "course": "FR-102",
          "module": 4,
          "lane": "control",
          "touches": [],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 50,
          "w": 160,
          "h": 68
        },
        {
          "code": "FR-103.1.1",
          "kind": "lesson",
          "title": "Where each step's output goes",
          "objective": "Explain Initial Data, block results, and what a later step can and cannot see",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-103",
          "lane": "data",
          "module": 1,
          "moduleTitle": "Where data comes from and where it goes",
          "requires": [
            "FR-101.4.3"
          ],
          "docs": [
            {
              "label": "Passing Data Between Blocks",
              "href": "https://docs.flowrunner.ai/build/data-and-variables/passing-data"
            },
            {
              "label": "Blocks",
              "href": "https://docs.flowrunner.ai/learn/concepts/blocks"
            }
          ],
          "page": null,
          "minutes": 6,
          "x": 186,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-103.1.2",
          "kind": "lesson",
          "title": "Read JSON without getting lost",
          "objective": "Read an object, an array, and a nested value using real Flowland order payloads",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-103",
          "lane": "data",
          "module": 1,
          "moduleTitle": "Where data comes from and where it goes",
          "requires": [
            "FR-103.1.1"
          ],
          "docs": [],
          "page": "/api#api-shop-orders-GET",
          "minutes": 7,
          "x": 480,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-103.1.3",
          "kind": "lesson",
          "title": "Build a value out of other values",
          "objective": "Build a value from earlier results, literals, and JSON",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-103",
          "lane": "data",
          "module": 1,
          "moduleTitle": "Where data comes from and where it goes",
          "requires": [
            "FR-103.1.2",
            "FR-101.3.4"
          ],
          "docs": [
            {
              "label": "Expression Editor",
              "href": "https://docs.flowrunner.ai/learn/concepts/expressions"
            }
          ],
          "page": null,
          "minutes": 8,
          "x": 774,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-103.2",
          "kind": "module",
          "side": "after",
          "title": "Storing and shaping values",
          "objective": "Module 2 of FR-103",
          "course": "FR-103",
          "module": 2,
          "lane": "data",
          "touches": [
            "FR-103.1.3",
            "FR-103.1.2"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 990,
          "y": 50,
          "w": 160,
          "h": 68
        }
      ],
      "edges": [
        {
          "from": "FR-103.1.1",
          "to": "FR-103.1.2",
          "uncertain": false,
          "points": [
            [
              376,
              64
            ],
            [
              376,
              64
            ],
            [
              428,
              64
            ],
            [
              480,
              64
            ]
          ]
        },
        {
          "from": "FR-103.1.2",
          "to": "FR-103.1.3",
          "uncertain": false,
          "points": [
            [
              670,
              64
            ],
            [
              670,
              64
            ],
            [
              722,
              64
            ],
            [
              774,
              64
            ]
          ]
        },
        {
          "from": "FR-102.4",
          "to": "FR-103.1.1",
          "module": true,
          "points": [
            [
              160,
              84
            ],
            [
              180,
              84
            ],
            [
              166,
              64
            ],
            [
              186,
              64
            ]
          ]
        },
        {
          "from": "FR-103.1.3",
          "to": "FR-103.2",
          "module": true,
          "points": [
            [
              964,
              64
            ],
            [
              984,
              64
            ],
            [
              970,
              84
            ],
            [
              990,
              84
            ]
          ]
        }
      ],
      "course": "FR-103",
      "module": 1,
      "title": "Where data comes from and where it goes",
      "lesson_count": 3,
      "minutes": 21
    },
    "FR-103.2": {
      "width": 896,
      "height": 312,
      "nodes": [
        {
          "code": "FR-103.1",
          "kind": "module",
          "side": "before",
          "title": "Where data comes from and where it goes",
          "objective": "Module 1 of FR-103",
          "course": "FR-103",
          "module": 1,
          "lane": "data",
          "touches": [
            "FR-103.1.3",
            "FR-103.1.2"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 105,
          "w": 160,
          "h": 83
        },
        {
          "code": "FR-103.2.1",
          "kind": "lesson",
          "title": "Save a value to use later in the run",
          "objective": "Create, read, update, and clear variables; know their lifetime",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-103",
          "lane": "data",
          "module": 2,
          "moduleTitle": "Storing and shaping values",
          "requires": [
            "FR-103.1.3",
            "FR-101.3.3"
          ],
          "docs": [
            {
              "label": "Variables & Data Buckets",
              "href": "https://docs.flowrunner.ai/learn/concepts/variables"
            },
            {
              "label": "Holding Values in Variables",
              "href": "https://docs.flowrunner.ai/build/data-and-variables/holding-values"
            }
          ],
          "page": null,
          "minutes": 7,
          "x": 186,
          "y": 136,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-103.2.2",
          "kind": "lesson",
          "title": "Add things up as the flow runs",
          "objective": "Accumulate results into a variable across steps",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-103",
          "lane": "data",
          "module": 2,
          "moduleTitle": "Storing and shaping values",
          "requires": [
            "FR-103.2.1"
          ],
          "docs": [
            {
              "label": "Holding Values in Variables",
              "href": "https://docs.flowrunner.ai/build/data-and-variables/holding-values"
            }
          ],
          "page": null,
          "minutes": 5,
          "x": 480,
          "y": 136,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-103.2.3",
          "kind": "lesson",
          "title": "Clean up messy data from an old system",
          "objective": "Pick the right Transform Data operation to extract, convert, sort, or filter, against Flowland's legacy endpoint",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-103",
          "lane": "data",
          "module": 2,
          "moduleTitle": "Storing and shaping values",
          "requires": [
            "FR-103.1.2",
            "FR-103.1.3"
          ],
          "docs": [
            {
              "label": "Reshaping Data",
              "href": "https://docs.flowrunner.ai/build/data-and-variables/reshaping-data"
            },
            {
              "label": "Transform Data",
              "href": "https://docs.flowrunner.ai/reference/transform-data"
            }
          ],
          "page": "/api#api-shop-legacy-orders-GET",
          "minutes": 8,
          "x": 186,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-103.3",
          "kind": "module",
          "side": "after",
          "title": "Remembering things between runs",
          "objective": "Module 3 of FR-103",
          "course": "FR-103",
          "module": 3,
          "lane": "data",
          "touches": [
            "FR-103.2.1",
            "FR-103.2.2",
            "FR-103.2.3"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 696,
          "y": 112,
          "w": 160,
          "h": 68
        }
      ],
      "edges": [
        {
          "from": "FR-103.2.1",
          "to": "FR-103.2.2",
          "uncertain": false,
          "points": [
            [
              376,
              188
            ],
            [
              376,
              188
            ],
            [
              428,
              188
            ],
            [
              480,
              188
            ]
          ]
        },
        {
          "from": "FR-103.1",
          "to": "FR-103.2#in",
          "module": true,
          "points": [
            [
              160,
              147
            ],
            [
              180,
              147
            ],
            [
              153,
              126
            ],
            [
              173,
              126
            ]
          ]
        },
        {
          "from": "FR-103.2#in",
          "to": "FR-103.2.1",
          "module": true,
          "points": [
            [
              173,
              126
            ],
            [
              193,
              126
            ],
            [
              166,
              188
            ],
            [
              186,
              188
            ]
          ]
        },
        {
          "from": "FR-103.2#in",
          "to": "FR-103.2.3",
          "module": true,
          "points": [
            [
              173,
              126
            ],
            [
              193,
              126
            ],
            [
              166,
              64
            ],
            [
              186,
              64
            ]
          ]
        },
        {
          "from": "FR-103.2.2",
          "to": "FR-103.2#out",
          "module": true,
          "points": [
            [
              670,
              188
            ],
            [
              690,
              188
            ],
            [
              663,
              126
            ],
            [
              683,
              126
            ]
          ]
        },
        {
          "from": "FR-103.2.3",
          "to": "FR-103.2#out",
          "module": true,
          "points": [
            [
              376,
              64
            ],
            [
              530,
              64
            ],
            [
              530,
              126
            ],
            [
              683,
              126
            ]
          ]
        },
        {
          "from": "FR-103.2#out",
          "to": "FR-103.3",
          "module": true,
          "points": [
            [
              683,
              126
            ],
            [
              703,
              126
            ],
            [
              676,
              146
            ],
            [
              696,
              146
            ]
          ]
        }
      ],
      "course": "FR-103",
      "module": 2,
      "title": "Storing and shaping values",
      "lesson_count": 3,
      "minutes": 20
    },
    "FR-103.3": {
      "width": 1190,
      "height": 188,
      "nodes": [
        {
          "code": "FR-103.2",
          "kind": "module",
          "side": "before",
          "title": "Storing and shaping values",
          "objective": "Module 2 of FR-103",
          "course": "FR-103",
          "module": 2,
          "lane": "data",
          "touches": [
            "FR-103.2.1",
            "FR-103.2.2",
            "FR-103.2.3"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 50,
          "w": 160,
          "h": 68
        },
        {
          "code": "FR-103.3.1",
          "kind": "lesson",
          "title": "Remember something after the run ends",
          "objective": "Decide when a value belongs in a variable vs Shared Memory",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-103",
          "lane": "data",
          "module": 3,
          "moduleTitle": "Remembering things between runs",
          "requires": [
            "FR-103.2.1"
          ],
          "docs": [
            {
              "label": "Sharing Data Across Runs & Flows",
              "href": "https://docs.flowrunner.ai/build/data-and-variables/across-runs"
            },
            {
              "label": "Shared Memory",
              "href": "https://docs.flowrunner.ai/learn/concepts/shared-memory"
            }
          ],
          "page": null,
          "minutes": 6,
          "x": 186,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-103.3.2",
          "kind": "lesson",
          "title": "Count things across separate runs",
          "objective": "Write, read, and clear shared values; set lifetime and anchoring correctly",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-103",
          "lane": "data",
          "module": 3,
          "moduleTitle": "Remembering things between runs",
          "requires": [
            "FR-103.3.1"
          ],
          "docs": [
            {
              "label": "Shared Memory",
              "href": "https://docs.flowrunner.ai/learn/concepts/shared-memory"
            },
            {
              "label": "Shared Memory: Put",
              "href": "https://docs.flowrunner.ai/reference/shared-memory-put"
            }
          ],
          "page": null,
          "minutes": 8,
          "x": 480,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-103.3.3",
          "kind": "lesson",
          "title": "Capstone: build a daily summary report",
          "objective": "Combine transforms, variables, and Shared Memory in one build over live shop data",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-103",
          "lane": "data",
          "module": 3,
          "moduleTitle": "Remembering things between runs",
          "requires": [
            "FR-103.2.2",
            "FR-103.2.3",
            "FR-103.3.2"
          ],
          "docs": [],
          "page": "/api#api-shop-orders-GET",
          "minutes": 8,
          "x": 774,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-104.1",
          "kind": "module",
          "side": "after",
          "title": "Calling other systems",
          "objective": "Module 1 of FR-104",
          "course": "FR-104",
          "module": 1,
          "lane": "integration",
          "touches": [],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 990,
          "y": 50,
          "w": 160,
          "h": 68
        }
      ],
      "edges": [
        {
          "from": "FR-103.3.1",
          "to": "FR-103.3.2",
          "uncertain": false,
          "points": [
            [
              376,
              64
            ],
            [
              376,
              64
            ],
            [
              428,
              64
            ],
            [
              480,
              64
            ]
          ]
        },
        {
          "from": "FR-103.3.2",
          "to": "FR-103.3.3",
          "uncertain": false,
          "points": [
            [
              670,
              64
            ],
            [
              670,
              64
            ],
            [
              722,
              64
            ],
            [
              774,
              64
            ]
          ]
        },
        {
          "from": "FR-103.2",
          "to": "FR-103.3.1",
          "module": true,
          "points": [
            [
              160,
              84
            ],
            [
              180,
              84
            ],
            [
              166,
              64
            ],
            [
              186,
              64
            ]
          ]
        },
        {
          "from": "FR-103.3.3",
          "to": "FR-104.1",
          "module": true,
          "points": [
            [
              964,
              64
            ],
            [
              984,
              64
            ],
            [
              970,
              84
            ],
            [
              990,
              84
            ]
          ]
        }
      ],
      "course": "FR-103",
      "module": 3,
      "title": "Remembering things between runs",
      "lesson_count": 3,
      "minutes": 22
    },
    "FR-104.1": {
      "width": 896,
      "height": 312,
      "nodes": [
        {
          "code": "FR-103.3",
          "kind": "module",
          "side": "before",
          "title": "Remembering things between runs",
          "objective": "Module 3 of FR-103",
          "course": "FR-103",
          "module": 3,
          "lane": "data",
          "touches": [],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 112,
          "w": 160,
          "h": 68
        },
        {
          "code": "FR-104.1.1",
          "kind": "lesson",
          "title": "Call another system from your flow",
          "objective": "Choose between a connector block and a raw HTTP call, and read the response",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-104",
          "lane": "integration",
          "module": 1,
          "moduleTitle": "Calling other systems",
          "requires": [
            "FR-102.4.1",
            "FR-103.1.2"
          ],
          "docs": [
            {
              "label": "Calling an External Service",
              "href": "https://docs.flowrunner.ai/build/integrations/calling-a-service"
            },
            {
              "label": "HTTP Request",
              "href": "https://docs.flowrunner.ai/reference/http-request"
            }
          ],
          "page": null,
          "minutes": 8,
          "x": 186,
          "y": 136,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-104.1.2",
          "kind": "lesson",
          "title": "Store a password or API key safely",
          "objective": "Store provider keys and account connections once; use them in blocks",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-104",
          "lane": "integration",
          "module": 1,
          "moduleTitle": "Calling other systems",
          "requires": [
            "FR-104.1.1"
          ],
          "docs": [
            {
              "label": "API Keys",
              "href": "https://docs.flowrunner.ai/platform/api-keys"
            },
            {
              "label": "OAuth Connections",
              "href": "https://docs.flowrunner.ai/platform/oauth-connections"
            }
          ],
          "page": null,
          "minutes": 6,
          "x": 480,
          "y": 136,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-104.1.3",
          "kind": "lesson",
          "title": "Build a form people can fill in",
          "objective": "Design a form and connect its pages to flows",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-104",
          "lane": "integration",
          "module": 1,
          "moduleTitle": "Calling other systems",
          "requires": [
            "FR-101.2.4"
          ],
          "docs": [
            {
              "label": "Forms",
              "href": "https://docs.flowrunner.ai/platform/forms"
            }
          ],
          "page": null,
          "minutes": 7,
          "x": 186,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-104.2",
          "kind": "module",
          "side": "after",
          "title": "Being called by other systems",
          "objective": "Module 2 of FR-104",
          "course": "FR-104",
          "module": 2,
          "lane": "integration",
          "touches": [
            "FR-104.1.1"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 696,
          "y": 112,
          "w": 160,
          "h": 68
        }
      ],
      "edges": [
        {
          "from": "FR-104.1.1",
          "to": "FR-104.1.2",
          "uncertain": false,
          "points": [
            [
              376,
              188
            ],
            [
              376,
              188
            ],
            [
              428,
              188
            ],
            [
              480,
              188
            ]
          ]
        },
        {
          "from": "FR-103.3",
          "to": "FR-104.1#in",
          "module": true,
          "points": [
            [
              160,
              146
            ],
            [
              180,
              146
            ],
            [
              153,
              126
            ],
            [
              173,
              126
            ]
          ]
        },
        {
          "from": "FR-104.1#in",
          "to": "FR-104.1.1",
          "module": true,
          "points": [
            [
              173,
              126
            ],
            [
              193,
              126
            ],
            [
              166,
              188
            ],
            [
              186,
              188
            ]
          ]
        },
        {
          "from": "FR-104.1#in",
          "to": "FR-104.1.3",
          "module": true,
          "points": [
            [
              173,
              126
            ],
            [
              193,
              126
            ],
            [
              166,
              64
            ],
            [
              186,
              64
            ]
          ]
        },
        {
          "from": "FR-104.1.2",
          "to": "FR-104.1#out",
          "module": true,
          "points": [
            [
              670,
              188
            ],
            [
              690,
              188
            ],
            [
              663,
              126
            ],
            [
              683,
              126
            ]
          ]
        },
        {
          "from": "FR-104.1.3",
          "to": "FR-104.1#out",
          "module": true,
          "points": [
            [
              376,
              64
            ],
            [
              530,
              64
            ],
            [
              530,
              126
            ],
            [
              683,
              126
            ]
          ]
        },
        {
          "from": "FR-104.1#out",
          "to": "FR-104.2",
          "module": true,
          "points": [
            [
              683,
              126
            ],
            [
              703,
              126
            ],
            [
              676,
              146
            ],
            [
              696,
              146
            ]
          ]
        }
      ],
      "course": "FR-104",
      "module": 1,
      "title": "Calling other systems",
      "lesson_count": 3,
      "minutes": 21
    },
    "FR-104.2": {
      "width": 896,
      "height": 188,
      "nodes": [
        {
          "code": "FR-104.1",
          "kind": "module",
          "side": "before",
          "title": "Calling other systems",
          "objective": "Module 1 of FR-104",
          "course": "FR-104",
          "module": 1,
          "lane": "integration",
          "touches": [
            "FR-104.1.1"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 50,
          "w": 160,
          "h": 68
        },
        {
          "code": "FR-104.2.1",
          "kind": "lesson",
          "title": "Answer a program, not a person",
          "objective": "Shape a reply another system can rely on: what a blocking caller receives against what a non-blocking one gets, several results or none, and releasing the caller before the run has finished",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-104",
          "lane": "integration",
          "module": 2,
          "moduleTitle": "Being called by other systems",
          "requires": [
            "FR-104.1.1",
            "FR-101.3.5"
          ],
          "docs": [
            {
              "label": "Returning a Result",
              "href": "https://docs.flowrunner.ai/build/integrations/returning-a-result"
            },
            {
              "label": "Return Result",
              "href": "https://docs.flowrunner.ai/reference/return-result"
            }
          ],
          "page": "/checkout",
          "minutes": 6,
          "x": 186,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-104.2.2",
          "kind": "lesson",
          "title": "Start a flow from another program",
          "objective": "Launch a run with a blocking or non-blocking call and read the envelope",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-104",
          "lane": "integration",
          "module": 2,
          "moduleTitle": "Being called by other systems",
          "requires": [
            "FR-104.2.1"
          ],
          "docs": [
            {
              "label": "Call Flow (Blocking)",
              "href": "https://docs.flowrunner.ai/api/call-flow-blocking"
            },
            {
              "label": "Call Flow (Non-Blocking)",
              "href": "https://docs.flowrunner.ai/api/call-flow-nonblocking"
            },
            {
              "label": "Retrieving Block Results",
              "href": "https://docs.flowrunner.ai/api/block-results"
            }
          ],
          "page": "/checkout",
          "minutes": 8,
          "x": 480,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-104.3",
          "kind": "module",
          "side": "after",
          "title": "Pausing for people and events",
          "objective": "Module 3 of FR-104",
          "course": "FR-104",
          "module": 3,
          "lane": "integration",
          "touches": [
            "FR-104.2.2"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 696,
          "y": 50,
          "w": 160,
          "h": 68
        }
      ],
      "edges": [
        {
          "from": "FR-104.2.1",
          "to": "FR-104.2.2",
          "uncertain": false,
          "points": [
            [
              376,
              64
            ],
            [
              376,
              64
            ],
            [
              428,
              64
            ],
            [
              480,
              64
            ]
          ]
        },
        {
          "from": "FR-104.1",
          "to": "FR-104.2.1",
          "module": true,
          "points": [
            [
              160,
              84
            ],
            [
              180,
              84
            ],
            [
              166,
              64
            ],
            [
              186,
              64
            ]
          ]
        },
        {
          "from": "FR-104.2.2",
          "to": "FR-104.3",
          "module": true,
          "points": [
            [
              670,
              64
            ],
            [
              690,
              64
            ],
            [
              676,
              84
            ],
            [
              696,
              84
            ]
          ]
        }
      ],
      "course": "FR-104",
      "module": 2,
      "title": "Being called by other systems",
      "lesson_count": 2,
      "minutes": 14
    },
    "FR-104.3": {
      "width": 1190,
      "height": 188,
      "nodes": [
        {
          "code": "FR-104.2",
          "kind": "module",
          "side": "before",
          "title": "Being called by other systems",
          "objective": "Module 2 of FR-104",
          "course": "FR-104",
          "module": 2,
          "lane": "integration",
          "touches": [
            "FR-104.2.2"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 50,
          "w": 160,
          "h": 68
        },
        {
          "code": "FR-104.3.1",
          "kind": "lesson",
          "title": "Pause a flow until a person approves it",
          "objective": "Pause a run and resume it with a callback; understand the execution id",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-104",
          "lane": "integration",
          "module": 3,
          "moduleTitle": "Pausing for people and events",
          "requires": [
            "FR-104.2.2",
            "FR-104.1.1"
          ],
          "docs": [
            {
              "label": "Waiting on an External System",
              "href": "https://docs.flowrunner.ai/build/flow-control/external-callbacks"
            },
            {
              "label": "External Callback",
              "href": "https://docs.flowrunner.ai/reference/external-callback"
            }
          ],
          "page": "/back-office",
          "minutes": 8,
          "x": 186,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-104.3.2",
          "kind": "lesson",
          "title": "Handle only the messages you want",
          "objective": "Capture the payload shape with Learning Mode and filter which calls resume the run",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-104",
          "lane": "integration",
          "module": 3,
          "moduleTitle": "Pausing for people and events",
          "requires": [
            "FR-104.3.1"
          ],
          "docs": [
            {
              "label": "Waiting on an External System",
              "href": "https://docs.flowrunner.ai/build/flow-control/external-callbacks"
            },
            {
              "label": "Activating an External Callback",
              "href": "https://docs.flowrunner.ai/api/activating-a-trigger"
            }
          ],
          "page": "/back-office",
          "minutes": 7,
          "x": 480,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-104.3.3",
          "kind": "lesson",
          "title": "Wait for two things before continuing",
          "objective": "Use rendezvous mode when several triggers must arrive",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-104",
          "lane": "integration",
          "module": 3,
          "moduleTitle": "Pausing for people and events",
          "requires": [
            "FR-104.3.2",
            "FR-102.3.1?"
          ],
          "docs": [
            {
              "label": "Triggers Group",
              "href": "https://docs.flowrunner.ai/reference/triggers-group"
            }
          ],
          "page": "/back-office",
          "minutes": 5,
          "x": 774,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-104.4",
          "kind": "module",
          "side": "after",
          "title": "Reusing flows and adding blocks",
          "objective": "Module 4 of FR-104",
          "course": "FR-104",
          "module": 4,
          "lane": "integration",
          "touches": [
            "FR-104.3.1"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 990,
          "y": 50,
          "w": 160,
          "h": 68
        }
      ],
      "edges": [
        {
          "from": "FR-104.3.1",
          "to": "FR-104.3.2",
          "uncertain": false,
          "points": [
            [
              376,
              64
            ],
            [
              376,
              64
            ],
            [
              428,
              64
            ],
            [
              480,
              64
            ]
          ]
        },
        {
          "from": "FR-104.3.2",
          "to": "FR-104.3.3",
          "uncertain": false,
          "points": [
            [
              670,
              64
            ],
            [
              670,
              64
            ],
            [
              722,
              64
            ],
            [
              774,
              64
            ]
          ]
        },
        {
          "from": "FR-104.2",
          "to": "FR-104.3.1",
          "module": true,
          "points": [
            [
              160,
              84
            ],
            [
              180,
              84
            ],
            [
              166,
              64
            ],
            [
              186,
              64
            ]
          ]
        },
        {
          "from": "FR-104.3.3",
          "to": "FR-104.4",
          "module": true,
          "points": [
            [
              964,
              64
            ],
            [
              984,
              64
            ],
            [
              970,
              84
            ],
            [
              990,
              84
            ]
          ]
        }
      ],
      "course": "FR-104",
      "module": 3,
      "title": "Pausing for people and events",
      "lesson_count": 3,
      "minutes": 20
    },
    "FR-104.4": {
      "width": 1190,
      "height": 312,
      "nodes": [
        {
          "code": "FR-104.3",
          "kind": "module",
          "side": "before",
          "title": "Pausing for people and events",
          "objective": "Module 3 of FR-104",
          "course": "FR-104",
          "module": 3,
          "lane": "integration",
          "touches": [
            "FR-104.3.1"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 112,
          "w": 160,
          "h": 68
        },
        {
          "code": "FR-104.4.1",
          "kind": "lesson",
          "title": "Reuse the same steps in several flows",
          "objective": "Extract shared steps into a subflow with inputs and a return value",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-104",
          "lane": "integration",
          "module": 4,
          "moduleTitle": "Reusing flows and adding blocks",
          "requires": [
            "FR-104.2.1"
          ],
          "docs": [
            {
              "label": "Subflows",
              "href": "https://docs.flowrunner.ai/learn/concepts/subflows"
            },
            {
              "label": "SubFlow",
              "href": "https://docs.flowrunner.ai/reference/subflow"
            }
          ],
          "page": null,
          "minutes": 7,
          "x": 186,
          "y": 136,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-104.4.2",
          "kind": "lesson",
          "title": "Have one flow start another",
          "objective": "Choose blocking vs fire-and-forget between flows",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-104",
          "lane": "integration",
          "module": 4,
          "moduleTitle": "Reusing flows and adding blocks",
          "requires": [
            "FR-104.4.1",
            "FR-104.2.2"
          ],
          "docs": [
            {
              "label": "Running Another Flow",
              "href": "https://docs.flowrunner.ai/build/integrations/running-another-flow"
            },
            {
              "label": "Call Flow",
              "href": "https://docs.flowrunner.ai/reference/call-flow"
            }
          ],
          "page": null,
          "minutes": 6,
          "x": 480,
          "y": 136,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-104.4.3",
          "kind": "lesson",
          "title": "Add ready-made blocks from elsewhere",
          "objective": "Register an MCP server and use its tools as blocks",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-104",
          "lane": "integration",
          "module": 4,
          "moduleTitle": "Reusing flows and adding blocks",
          "requires": [
            "FR-104.1.1"
          ],
          "docs": [
            {
              "label": "MCP Servers",
              "href": "https://docs.flowrunner.ai/platform/mcp-servers"
            },
            {
              "label": "Custom & Marketplace Actions",
              "href": "https://docs.flowrunner.ai/build/integrations/custom-actions"
            }
          ],
          "page": null,
          "minutes": 7,
          "x": 186,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-104.4.4",
          "kind": "lesson",
          "title": "Point the same flow at your own systems",
          "objective": "Replace Flowland endpoints with the learner's real services - connector blocks, API keys, OAuth - keeping the same flow shape; the bridge from the training world to Your One Process",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-104",
          "lane": "integration",
          "module": 4,
          "moduleTitle": "Reusing flows and adding blocks",
          "requires": [
            "FR-104.1.2"
          ],
          "docs": [
            {
              "label": "Calling an External Service",
              "href": "https://docs.flowrunner.ai/build/integrations/calling-a-service"
            },
            {
              "label": "API Keys",
              "href": "https://docs.flowrunner.ai/platform/api-keys"
            },
            {
              "label": "OAuth Connections",
              "href": "https://docs.flowrunner.ai/platform/oauth-connections"
            }
          ],
          "page": null,
          "minutes": 7,
          "x": 396,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-104.4.5",
          "kind": "lesson",
          "title": "Capstone: quote a price, get approval, take payment",
          "objective": "Combine checkout, a service call, a human pause, a payment webhook, and a returned result",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-104",
          "lane": "integration",
          "module": 4,
          "moduleTitle": "Reusing flows and adding blocks",
          "requires": [
            "FR-104.1.1",
            "FR-104.2.1",
            "FR-104.3.1",
            "FR-104.4.2"
          ],
          "docs": [],
          "page": "/checkout",
          "minutes": 10,
          "x": 774,
          "y": 136,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-105.1",
          "kind": "module",
          "side": "after",
          "title": "AI as a step",
          "objective": "Module 1 of FR-105",
          "course": "FR-105",
          "module": 1,
          "lane": "ai",
          "touches": [],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 990,
          "y": 120,
          "w": 160,
          "h": 53
        }
      ],
      "edges": [
        {
          "from": "FR-104.4.1",
          "to": "FR-104.4.2",
          "uncertain": false,
          "points": [
            [
              376,
              188
            ],
            [
              376,
              188
            ],
            [
              428,
              188
            ],
            [
              480,
              188
            ]
          ]
        },
        {
          "from": "FR-104.4.2",
          "to": "FR-104.4.5",
          "uncertain": false,
          "points": [
            [
              670,
              188
            ],
            [
              670,
              188
            ],
            [
              722,
              188
            ],
            [
              774,
              188
            ]
          ]
        },
        {
          "from": "FR-104.3",
          "to": "FR-104.4#in",
          "module": true,
          "points": [
            [
              160,
              146
            ],
            [
              180,
              146
            ],
            [
              153,
              105
            ],
            [
              173,
              105
            ]
          ]
        },
        {
          "from": "FR-104.4#in",
          "to": "FR-104.4.1",
          "module": true,
          "points": [
            [
              173,
              105
            ],
            [
              193,
              105
            ],
            [
              166,
              188
            ],
            [
              186,
              188
            ]
          ]
        },
        {
          "from": "FR-104.4#in",
          "to": "FR-104.4.3",
          "module": true,
          "points": [
            [
              173,
              105
            ],
            [
              193,
              105
            ],
            [
              166,
              64
            ],
            [
              186,
              64
            ]
          ]
        },
        {
          "from": "FR-104.4#in",
          "to": "FR-104.4.4",
          "module": true,
          "points": [
            [
              173,
              105
            ],
            [
              285,
              105
            ],
            [
              285,
              64
            ],
            [
              396,
              64
            ]
          ]
        },
        {
          "from": "FR-104.4.3",
          "to": "FR-104.4#out",
          "module": true,
          "points": [
            [
              376,
              64
            ],
            [
              677,
              64
            ],
            [
              677,
              105
            ],
            [
              977,
              105
            ]
          ]
        },
        {
          "from": "FR-104.4.4",
          "to": "FR-104.4#out",
          "module": true,
          "points": [
            [
              586,
              64
            ],
            [
              782,
              64
            ],
            [
              782,
              105
            ],
            [
              977,
              105
            ]
          ]
        },
        {
          "from": "FR-104.4.5",
          "to": "FR-104.4#out",
          "module": true,
          "points": [
            [
              964,
              188
            ],
            [
              984,
              188
            ],
            [
              957,
              105
            ],
            [
              977,
              105
            ]
          ]
        },
        {
          "from": "FR-104.4#out",
          "to": "FR-105.1",
          "module": true,
          "points": [
            [
              977,
              105
            ],
            [
              997,
              105
            ],
            [
              970,
              147
            ],
            [
              990,
              147
            ]
          ]
        }
      ],
      "course": "FR-104",
      "module": 4,
      "title": "Reusing flows and adding blocks",
      "lesson_count": 5,
      "minutes": 37
    },
    "FR-105.1": {
      "width": 896,
      "height": 356,
      "nodes": [
        {
          "code": "FR-104.4",
          "kind": "module",
          "side": "before",
          "title": "Reusing flows and adding blocks",
          "objective": "Module 4 of FR-104",
          "course": "FR-104",
          "module": 4,
          "lane": "integration",
          "touches": [],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 134,
          "w": 160,
          "h": 68
        },
        {
          "code": "FR-105.1.1",
          "kind": "lesson",
          "title": "Connect an AI model to your workspace",
          "objective": "Configure a model and provider key once, correctly",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-105",
          "lane": "ai",
          "module": 1,
          "moduleTitle": "AI as a step",
          "requires": [
            "FR-102.1.2"
          ],
          "docs": [
            {
              "label": "AI in Flows",
              "href": "https://docs.flowrunner.ai/build/ai-in-flows"
            },
            {
              "label": "API Keys",
              "href": "https://docs.flowrunner.ai/platform/api-keys"
            }
          ],
          "page": null,
          "minutes": 5,
          "x": 186,
          "y": 29,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-105.1.2",
          "kind": "lesson",
          "title": "Rewrite text with AI instead of rules",
          "objective": "Use an AI transform where a rule would be brittle",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-105",
          "lane": "ai",
          "module": 1,
          "moduleTitle": "AI as a step",
          "requires": [
            "FR-105.1.1"
          ],
          "docs": [
            {
              "label": "AI in Flows",
              "href": "https://docs.flowrunner.ai/build/ai-in-flows"
            }
          ],
          "page": null,
          "minutes": 6,
          "x": 480,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-105.1.3",
          "kind": "lesson",
          "title": "Ask AI a question and branch on the answer",
          "objective": "Branch on a yes/no no rule can answer; route an inquiry on intent in plain language",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-105",
          "lane": "ai",
          "module": 1,
          "moduleTitle": "AI as a step",
          "requires": [
            "FR-105.1.1",
            "FR-102.1.2"
          ],
          "docs": [
            {
              "label": "AI in Flows",
              "href": "https://docs.flowrunner.ai/build/ai-in-flows"
            },
            {
              "label": "AI Router",
              "href": "https://docs.flowrunner.ai/reference/ai-router"
            }
          ],
          "page": null,
          "minutes": 7,
          "x": 480,
          "y": 180,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-105.2",
          "kind": "module",
          "side": "after",
          "title": "Build an AI agent",
          "objective": "Module 2 of FR-105",
          "course": "FR-105",
          "module": 2,
          "lane": "ai",
          "touches": [
            "FR-105.1.1"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 696,
          "y": 142,
          "w": 160,
          "h": 53
        }
      ],
      "edges": [
        {
          "from": "FR-105.1.1",
          "to": "FR-105.1.2",
          "uncertain": false,
          "points": [
            [
              376,
              64
            ],
            [
              376,
              64
            ],
            [
              428,
              64
            ],
            [
              480,
              64
            ]
          ]
        },
        {
          "from": "FR-105.1.1",
          "to": "FR-105.1.3",
          "uncertain": false,
          "points": [
            [
              376,
              98
            ],
            [
              376,
              98
            ],
            [
              428,
              219
            ],
            [
              480,
              232
            ]
          ]
        },
        {
          "from": "FR-104.4",
          "to": "FR-105.1.1",
          "module": true,
          "points": [
            [
              160,
              168
            ],
            [
              180,
              168
            ],
            [
              166,
              81
            ],
            [
              186,
              81
            ]
          ]
        },
        {
          "from": "FR-105.1.2",
          "to": "FR-105.1#out",
          "module": true,
          "points": [
            [
              670,
              64
            ],
            [
              690,
              64
            ],
            [
              663,
              148
            ],
            [
              683,
              148
            ]
          ]
        },
        {
          "from": "FR-105.1.3",
          "to": "FR-105.1#out",
          "module": true,
          "points": [
            [
              670,
              232
            ],
            [
              690,
              232
            ],
            [
              663,
              148
            ],
            [
              683,
              148
            ]
          ]
        },
        {
          "from": "FR-105.1#out",
          "to": "FR-105.2",
          "module": true,
          "points": [
            [
              683,
              148
            ],
            [
              703,
              148
            ],
            [
              676,
              169
            ],
            [
              696,
              169
            ]
          ]
        }
      ],
      "course": "FR-105",
      "module": 1,
      "title": "AI as a step",
      "lesson_count": 3,
      "minutes": 18
    },
    "FR-105.2": {
      "width": 1190,
      "height": 188,
      "nodes": [
        {
          "code": "FR-105.1",
          "kind": "module",
          "side": "before",
          "title": "AI as a step",
          "objective": "Module 1 of FR-105",
          "course": "FR-105",
          "module": 1,
          "lane": "ai",
          "touches": [
            "FR-105.1.1"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 58,
          "w": 160,
          "h": 53
        },
        {
          "code": "FR-105.2.1",
          "kind": "lesson",
          "title": "Add an AI agent and tell it its job",
          "objective": "Place an AI Agent, choose a model, and write its instructions",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-105",
          "lane": "ai",
          "module": 2,
          "moduleTitle": "Build an AI agent",
          "requires": [
            "FR-105.1.1"
          ],
          "docs": [
            {
              "label": "Quick Start: An AI Agent",
              "href": "https://docs.flowrunner.ai/learn/quickstart-agent"
            },
            {
              "label": "AI Agent",
              "href": "https://docs.flowrunner.ai/reference/ai-agent"
            }
          ],
          "page": null,
          "minutes": 8,
          "x": 186,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-105.2.2",
          "kind": "lesson",
          "title": "Give the agent tools it can use",
          "objective": "Attach capabilities and flows as tools; pin exact inputs; describe tools well; add the order-lookup tool",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-105",
          "lane": "ai",
          "module": 2,
          "moduleTitle": "Build an AI agent",
          "requires": [
            "FR-105.2.1",
            "FR-104.4.1?"
          ],
          "docs": [
            {
              "label": "Quick Start: An AI Agent",
              "href": "https://docs.flowrunner.ai/learn/quickstart-agent"
            },
            {
              "label": "Flows as Agent Tools",
              "href": "https://docs.flowrunner.ai/reference/flows-as-agent-tools-concept"
            }
          ],
          "page": null,
          "minutes": 8,
          "x": 480,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-105.2.3",
          "kind": "lesson",
          "title": "Check the agent's answer before acting on it",
          "objective": "Treat the agent as an accountable step whose output the flow controls",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-105",
          "lane": "ai",
          "module": 2,
          "moduleTitle": "Build an AI agent",
          "requires": [
            "FR-105.2.2"
          ],
          "docs": [
            {
              "label": "Quick Start: An AI Agent",
              "href": "https://docs.flowrunner.ai/learn/quickstart-agent"
            },
            {
              "label": "AI in Flows",
              "href": "https://docs.flowrunner.ai/build/ai-in-flows"
            }
          ],
          "page": null,
          "minutes": 5,
          "x": 774,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-105.3",
          "kind": "module",
          "side": "after",
          "title": "Give an agent documents and memory",
          "objective": "Module 3 of FR-105",
          "course": "FR-105",
          "module": 3,
          "lane": "ai",
          "touches": [
            "FR-105.2.1",
            "FR-105.2.3",
            "FR-105.2.2"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 990,
          "y": 50,
          "w": 160,
          "h": 68
        }
      ],
      "edges": [
        {
          "from": "FR-105.2.1",
          "to": "FR-105.2.2",
          "uncertain": false,
          "points": [
            [
              376,
              64
            ],
            [
              376,
              64
            ],
            [
              428,
              64
            ],
            [
              480,
              64
            ]
          ]
        },
        {
          "from": "FR-105.2.2",
          "to": "FR-105.2.3",
          "uncertain": false,
          "points": [
            [
              670,
              64
            ],
            [
              670,
              64
            ],
            [
              722,
              64
            ],
            [
              774,
              64
            ]
          ]
        },
        {
          "from": "FR-105.1",
          "to": "FR-105.2.1",
          "module": true,
          "points": [
            [
              160,
              85
            ],
            [
              180,
              85
            ],
            [
              166,
              64
            ],
            [
              186,
              64
            ]
          ]
        },
        {
          "from": "FR-105.2.3",
          "to": "FR-105.3",
          "module": true,
          "points": [
            [
              964,
              64
            ],
            [
              984,
              64
            ],
            [
              970,
              84
            ],
            [
              990,
              84
            ]
          ]
        }
      ],
      "course": "FR-105",
      "module": 2,
      "title": "Build an AI agent",
      "lesson_count": 3,
      "minutes": 21
    },
    "FR-105.3": {
      "width": 896,
      "height": 373,
      "nodes": [
        {
          "code": "FR-105.2",
          "kind": "module",
          "side": "before",
          "title": "Build an AI agent",
          "objective": "Module 2 of FR-105",
          "course": "FR-105",
          "module": 2,
          "lane": "ai",
          "touches": [
            "FR-105.2.1",
            "FR-105.2.3",
            "FR-105.2.2"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 150,
          "w": 160,
          "h": 53
        },
        {
          "code": "FR-105.3.1",
          "kind": "lesson",
          "title": "Answer from your own documents",
          "objective": "Create a Knowledge Base from the Flowland knowledge pack, attach it to the agent, verify grounded answers",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-105",
          "lane": "ai",
          "module": 3,
          "moduleTitle": "Give an agent documents and memory",
          "requires": [
            "FR-105.2.1"
          ],
          "docs": [
            {
              "label": "Knowledge Bases (RAG stores)",
              "href": "https://docs.flowrunner.ai/reference/knowledge-bases-concept"
            },
            {
              "label": "Knowledge Base: Add Document",
              "href": "https://docs.flowrunner.ai/reference/knowledge-base-add-document"
            }
          ],
          "page": null,
          "minutes": 8,
          "x": 186,
          "y": 29,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-105.3.2",
          "kind": "lesson",
          "title": "Keep those documents up to date",
          "objective": "List, add, and delete documents with the KB blocks",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-105",
          "lane": "ai",
          "module": 3,
          "moduleTitle": "Give an agent documents and memory",
          "requires": [
            "FR-105.3.1"
          ],
          "docs": [
            {
              "label": "Knowledge Base: List Documents",
              "href": "https://docs.flowrunner.ai/reference/knowledge-base-list-documents"
            },
            {
              "label": "Knowledge Base: Delete Document",
              "href": "https://docs.flowrunner.ai/reference/knowledge-base-delete-document"
            },
            {
              "label": "Knowledge Base: Delete by Filter",
              "href": "https://docs.flowrunner.ai/reference/knowledge-base-delete-by-filter"
            }
          ],
          "page": null,
          "minutes": 6,
          "x": 480,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-105.3.3",
          "kind": "lesson",
          "title": "Remember each person separately",
          "objective": "Carry conversation context across runs, scoped per caller with an anchor, proven with the site's persona switcher",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-105",
          "lane": "ai",
          "module": 3,
          "moduleTitle": "Give an agent documents and memory",
          "requires": [
            "FR-105.2.3"
          ],
          "docs": [
            {
              "label": "Agent Memory",
              "href": "https://docs.flowrunner.ai/reference/flow-memory-concept"
            },
            {
              "label": "Per-User Memory",
              "href": "https://docs.flowrunner.ai/reference/per-user-memory-concept"
            }
          ],
          "page": null,
          "minutes": 7,
          "x": 186,
          "y": 197,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-105.3.4",
          "kind": "lesson",
          "title": "Capstone: a support agent answering real questions",
          "objective": "Agent + Knowledge Base + order tool + human approval, live in the site's chat widget",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-105",
          "lane": "ai",
          "module": 3,
          "moduleTitle": "Give an agent documents and memory",
          "requires": [
            "FR-105.2.2",
            "FR-105.3.1",
            "FR-105.3.3",
            "FR-104.3.1?"
          ],
          "docs": [],
          "page": null,
          "minutes": 10,
          "x": 480,
          "y": 180,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-106.1",
          "kind": "module",
          "side": "after",
          "title": "Changing a flow that is already live",
          "objective": "Module 1 of FR-106",
          "course": "FR-106",
          "module": 1,
          "lane": "operations",
          "touches": [],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 696,
          "y": 135,
          "w": 160,
          "h": 83
        }
      ],
      "edges": [
        {
          "from": "FR-105.3.1",
          "to": "FR-105.3.2",
          "uncertain": false,
          "points": [
            [
              376,
              64
            ],
            [
              376,
              64
            ],
            [
              428,
              64
            ],
            [
              480,
              64
            ]
          ]
        },
        {
          "from": "FR-105.3.1",
          "to": "FR-105.3.4",
          "uncertain": false,
          "points": [
            [
              376,
              98
            ],
            [
              376,
              98
            ],
            [
              428,
              157
            ],
            [
              480,
              215
            ]
          ]
        },
        {
          "from": "FR-105.3.3",
          "to": "FR-105.3.4",
          "uncertain": false,
          "points": [
            [
              376,
              249
            ],
            [
              376,
              249
            ],
            [
              428,
              249
            ],
            [
              480,
              249
            ]
          ]
        },
        {
          "from": "FR-105.2",
          "to": "FR-105.3#in",
          "module": true,
          "points": [
            [
              160,
              177
            ],
            [
              180,
              177
            ],
            [
              153,
              165
            ],
            [
              173,
              165
            ]
          ]
        },
        {
          "from": "FR-105.3#in",
          "to": "FR-105.3.1",
          "module": true,
          "points": [
            [
              173,
              165
            ],
            [
              193,
              165
            ],
            [
              166,
              81
            ],
            [
              186,
              81
            ]
          ]
        },
        {
          "from": "FR-105.3#in",
          "to": "FR-105.3.3",
          "module": true,
          "points": [
            [
              173,
              165
            ],
            [
              193,
              165
            ],
            [
              166,
              249
            ],
            [
              186,
              249
            ]
          ]
        },
        {
          "from": "FR-105.3.2",
          "to": "FR-105.3#out",
          "module": true,
          "points": [
            [
              670,
              64
            ],
            [
              690,
              64
            ],
            [
              663,
              148
            ],
            [
              683,
              148
            ]
          ]
        },
        {
          "from": "FR-105.3.4",
          "to": "FR-105.3#out",
          "module": true,
          "points": [
            [
              670,
              232
            ],
            [
              690,
              232
            ],
            [
              663,
              148
            ],
            [
              683,
              148
            ]
          ]
        },
        {
          "from": "FR-105.3#out",
          "to": "FR-106.1",
          "module": true,
          "points": [
            [
              683,
              148
            ],
            [
              703,
              148
            ],
            [
              676,
              177
            ],
            [
              696,
              177
            ]
          ]
        }
      ],
      "course": "FR-105",
      "module": 3,
      "title": "Give an agent documents and memory",
      "lesson_count": 4,
      "minutes": 31
    },
    "FR-106.1": {
      "width": 896,
      "height": 188,
      "nodes": [
        {
          "code": "FR-105.3",
          "kind": "module",
          "side": "before",
          "title": "Give an agent documents and memory",
          "objective": "Module 3 of FR-105",
          "course": "FR-105",
          "module": 3,
          "lane": "ai",
          "touches": [],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 50,
          "w": 160,
          "h": 68
        },
        {
          "code": "FR-106.1.1",
          "kind": "lesson",
          "title": "Change a running flow without disrupting it",
          "objective": "Carry the switch-over out on a flow that is genuinely busy: decide what may change inside a version and what needs a new one, watch requests in flight land on the version that started them, and know how to go back if the new one is worse",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-106",
          "lane": "operations",
          "module": 1,
          "moduleTitle": "Changing a flow that is already live",
          "requires": [
            "FR-101.4.2"
          ],
          "docs": [
            {
              "label": "Flows",
              "href": "https://docs.flowrunner.ai/manage/flows"
            },
            {
              "label": "The Flow Editor",
              "href": "https://docs.flowrunner.ai/build/flow-editor"
            },
            {
              "label": "Terminology",
              "href": "https://docs.flowrunner.ai/definitions"
            }
          ],
          "page": null,
          "minutes": 6,
          "x": 186,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-106.1.2",
          "kind": "lesson",
          "title": "Run a flow on a schedule",
          "objective": "Put a flow on a schedule; start and stop schedules from a flow",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-106",
          "lane": "operations",
          "module": 1,
          "moduleTitle": "Changing a flow that is already live",
          "requires": [
            "FR-106.1.1"
          ],
          "docs": [
            {
              "label": "Flow Scheduling",
              "href": "https://docs.flowrunner.ai/reference/flow-scheduling-concept"
            },
            {
              "label": "Start Scheduled Runs",
              "href": "https://docs.flowrunner.ai/reference/start-scheduled-runs"
            },
            {
              "label": "Stop Scheduled Runs",
              "href": "https://docs.flowrunner.ai/reference/stop-scheduled-runs"
            }
          ],
          "page": null,
          "minutes": 7,
          "x": 480,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-106.2",
          "kind": "module",
          "side": "after",
          "title": "Find it, fix it, prove it",
          "objective": "Module 2 of FR-106",
          "course": "FR-106",
          "module": 2,
          "lane": "operations",
          "touches": [
            "FR-106.1.1"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 696,
          "y": 50,
          "w": 160,
          "h": 68
        }
      ],
      "edges": [
        {
          "from": "FR-106.1.1",
          "to": "FR-106.1.2",
          "uncertain": false,
          "points": [
            [
              376,
              64
            ],
            [
              376,
              64
            ],
            [
              428,
              64
            ],
            [
              480,
              64
            ]
          ]
        },
        {
          "from": "FR-105.3",
          "to": "FR-106.1.1",
          "module": true,
          "points": [
            [
              160,
              84
            ],
            [
              180,
              84
            ],
            [
              166,
              64
            ],
            [
              186,
              64
            ]
          ]
        },
        {
          "from": "FR-106.1.2",
          "to": "FR-106.2",
          "module": true,
          "points": [
            [
              670,
              64
            ],
            [
              690,
              64
            ],
            [
              676,
              84
            ],
            [
              696,
              84
            ]
          ]
        }
      ],
      "course": "FR-106",
      "module": 1,
      "title": "Changing a flow that is already live",
      "lesson_count": 2,
      "minutes": 13
    },
    "FR-106.2": {
      "width": 1484,
      "height": 188,
      "nodes": [
        {
          "code": "FR-106.1",
          "kind": "module",
          "side": "before",
          "title": "Changing a flow that is already live",
          "objective": "Module 1 of FR-106",
          "course": "FR-106",
          "module": 1,
          "lane": "operations",
          "touches": [
            "FR-106.1.1"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 43,
          "w": 160,
          "h": 83
        },
        {
          "code": "FR-106.2.1",
          "kind": "lesson",
          "title": "Find the run that failed",
          "objective": "Send a documented defect order from the Replayer and locate the failed instance in Monitoring",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-106",
          "lane": "operations",
          "module": 2,
          "moduleTitle": "Find it, fix it, prove it",
          "requires": [
            "FR-106.1.1",
            "FR-101.4.3"
          ],
          "docs": [
            {
              "label": "Monitoring & Analytics",
              "href": "https://docs.flowrunner.ai/run/monitoring"
            }
          ],
          "page": "/replay",
          "minutes": 7,
          "x": 186,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-106.2.2",
          "kind": "lesson",
          "title": "Work out why a run failed",
          "objective": "Step through loop passes and retry attempts; find the failing step and read its error",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-106",
          "lane": "operations",
          "module": 2,
          "moduleTitle": "Find it, fix it, prove it",
          "requires": [
            "FR-106.2.1",
            "FR-102.4.1?"
          ],
          "docs": [
            {
              "label": "Inspecting a Single Run",
              "href": "https://docs.flowrunner.ai/run/inspecting-a-run"
            }
          ],
          "page": null,
          "minutes": 7,
          "x": 480,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-106.2.3",
          "kind": "lesson",
          "title": "Fix it and prove the fix works",
          "objective": "Correct the flow in a new version, take it live, and demonstrate the fix against the identical input",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-106",
          "lane": "operations",
          "module": 2,
          "moduleTitle": "Find it, fix it, prove it",
          "requires": [
            "FR-106.2.2"
          ],
          "docs": [
            {
              "label": "Flows",
              "href": "https://docs.flowrunner.ai/manage/flows"
            },
            {
              "label": "Testing",
              "href": "https://docs.flowrunner.ai/run/testing"
            }
          ],
          "page": "/replay",
          "minutes": 7,
          "x": 774,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-106.2.4",
          "kind": "lesson",
          "title": "See how all your flows are doing",
          "objective": "Read the dashboard, Performance view, and logs with the data a real learner has; narrow logs to answer a question",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-106",
          "lane": "operations",
          "module": 2,
          "moduleTitle": "Find it, fix it, prove it",
          "requires": [
            "FR-106.2.3"
          ],
          "docs": [
            {
              "label": "Monitoring & Analytics",
              "href": "https://docs.flowrunner.ai/run/monitoring"
            }
          ],
          "page": null,
          "minutes": 6,
          "x": 1068,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-106.3",
          "kind": "module",
          "side": "after",
          "title": "Checking two systems against each other",
          "objective": "Module 3 of FR-106",
          "course": "FR-106",
          "module": 3,
          "lane": "operations",
          "touches": [],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 1284,
          "y": 43,
          "w": 160,
          "h": 83
        }
      ],
      "edges": [
        {
          "from": "FR-106.2.1",
          "to": "FR-106.2.2",
          "uncertain": false,
          "points": [
            [
              376,
              64
            ],
            [
              376,
              64
            ],
            [
              428,
              64
            ],
            [
              480,
              64
            ]
          ]
        },
        {
          "from": "FR-106.2.2",
          "to": "FR-106.2.3",
          "uncertain": false,
          "points": [
            [
              670,
              64
            ],
            [
              670,
              64
            ],
            [
              722,
              64
            ],
            [
              774,
              64
            ]
          ]
        },
        {
          "from": "FR-106.2.3",
          "to": "FR-106.2.4",
          "uncertain": false,
          "points": [
            [
              964,
              64
            ],
            [
              964,
              64
            ],
            [
              1016,
              64
            ],
            [
              1068,
              64
            ]
          ]
        },
        {
          "from": "FR-106.1",
          "to": "FR-106.2.1",
          "module": true,
          "points": [
            [
              160,
              85
            ],
            [
              180,
              85
            ],
            [
              166,
              64
            ],
            [
              186,
              64
            ]
          ]
        },
        {
          "from": "FR-106.2.4",
          "to": "FR-106.3",
          "module": true,
          "points": [
            [
              1258,
              64
            ],
            [
              1278,
              64
            ],
            [
              1264,
              85
            ],
            [
              1284,
              85
            ]
          ]
        }
      ],
      "course": "FR-106",
      "module": 2,
      "title": "Find it, fix it, prove it",
      "lesson_count": 4,
      "minutes": 27
    },
    "FR-106.3": {
      "width": 896,
      "height": 188,
      "nodes": [
        {
          "code": "FR-106.2",
          "kind": "module",
          "side": "before",
          "title": "Find it, fix it, prove it",
          "objective": "Module 2 of FR-106",
          "course": "FR-106",
          "module": 2,
          "lane": "operations",
          "touches": [],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 50,
          "w": 160,
          "h": 68
        },
        {
          "code": "FR-106.3.1",
          "kind": "lesson",
          "title": "Check two systems against each other",
          "objective": "Compare two datasets and surface mismatches, against Flowland's seeded defects",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-106",
          "lane": "operations",
          "module": 3,
          "moduleTitle": "Checking two systems against each other",
          "requires": [
            "FR-103.2.3",
            "FR-102.2.1",
            "FR-104.1.1?"
          ],
          "docs": [
            {
              "label": "Monitoring & Analytics",
              "href": "https://docs.flowrunner.ai/run/monitoring"
            },
            {
              "label": "Answer key: known mismatches",
              "href": "/answers"
            }
          ],
          "page": null,
          "minutes": 8,
          "x": 186,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-106.3.2",
          "kind": "lesson",
          "title": "Find records that fell through the cracks",
          "objective": "Find cross-system inconsistencies (paid-unfulfilled, shipped-unpaid) and report them",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-106",
          "lane": "operations",
          "module": 3,
          "moduleTitle": "Checking two systems against each other",
          "requires": [
            "FR-106.3.1"
          ],
          "docs": [],
          "page": null,
          "minutes": 7,
          "x": 480,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-106.4",
          "kind": "module",
          "side": "after",
          "title": "Running a workspace: people, limits and audit",
          "objective": "Module 4 of FR-106",
          "course": "FR-106",
          "module": 4,
          "lane": "operations",
          "touches": [
            "FR-106.3.2"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 696,
          "y": 43,
          "w": 160,
          "h": 83
        }
      ],
      "edges": [
        {
          "from": "FR-106.3.1",
          "to": "FR-106.3.2",
          "uncertain": false,
          "points": [
            [
              376,
              64
            ],
            [
              376,
              64
            ],
            [
              428,
              64
            ],
            [
              480,
              64
            ]
          ]
        },
        {
          "from": "FR-106.2",
          "to": "FR-106.3.1",
          "module": true,
          "points": [
            [
              160,
              84
            ],
            [
              180,
              84
            ],
            [
              166,
              64
            ],
            [
              186,
              64
            ]
          ]
        },
        {
          "from": "FR-106.3.2",
          "to": "FR-106.4",
          "module": true,
          "points": [
            [
              670,
              64
            ],
            [
              690,
              64
            ],
            [
              676,
              85
            ],
            [
              696,
              85
            ]
          ]
        }
      ],
      "course": "FR-106",
      "module": 3,
      "title": "Checking two systems against each other",
      "lesson_count": 2,
      "minutes": 15
    },
    "FR-106.4": {
      "width": 812,
      "height": 312,
      "nodes": [
        {
          "code": "FR-106.3",
          "kind": "module",
          "side": "before",
          "title": "Checking two systems against each other",
          "objective": "Module 3 of FR-106",
          "course": "FR-106",
          "module": 3,
          "lane": "operations",
          "touches": [
            "FR-106.3.2"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 105,
          "w": 160,
          "h": 83
        },
        {
          "code": "FR-106.4.1",
          "kind": "lesson",
          "title": "Escalate anything that takes too long",
          "objective": "Set a target, measure in business hours, escalate late runs",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-106",
          "lane": "operations",
          "module": 4,
          "moduleTitle": "Running a workspace: people, limits and audit",
          "requires": [
            "FR-106.1.2"
          ],
          "docs": [
            {
              "label": "SLA Goals",
              "href": "https://docs.flowrunner.ai/platform/sla-goals"
            },
            {
              "label": "Compliance & Security",
              "href": "https://docs.flowrunner.ai/platform/compliance-and-security"
            }
          ],
          "page": null,
          "minutes": 6,
          "x": 186,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-106.4.2",
          "kind": "lesson",
          "title": "See who changed what, and stop everything",
          "objective": "Review the audit trail; know Panic Mode for compromised credentials; know where a regulated workspace turns on HIPAA compliance and what the BAA step is",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-106",
          "lane": "operations",
          "module": 4,
          "moduleTitle": "Running a workspace: people, limits and audit",
          "requires": [
            "FR-106.1.1"
          ],
          "docs": [
            {
              "label": "Compliance & Security",
              "href": "https://docs.flowrunner.ai/platform/compliance-and-security"
            }
          ],
          "page": null,
          "minutes": 5,
          "x": 396,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-106.4.3",
          "kind": "lesson",
          "title": "Add your team and understand the bill",
          "objective": "Manage members and ownership; understand plans and execution counting",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-106",
          "lane": "operations",
          "module": 4,
          "moduleTitle": "Running a workspace: people, limits and audit",
          "requires": [
            "FR-106.1.1"
          ],
          "docs": [
            {
              "label": "Team",
              "href": "https://docs.flowrunner.ai/manage/team"
            },
            {
              "label": "Workspace Settings",
              "href": "https://docs.flowrunner.ai/manage/workspace-settings"
            },
            {
              "label": "Billing",
              "href": "https://docs.flowrunner.ai/manage/billing"
            }
          ],
          "page": null,
          "minutes": 6,
          "x": 186,
          "y": 136,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-106.4.4",
          "kind": "lesson",
          "title": "Capstone: run the shop for a day",
          "objective": "Replay, find, diagnose, fix, re-version, prove, reconcile - end to end",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-106",
          "lane": "operations",
          "module": 4,
          "moduleTitle": "Running a workspace: people, limits and audit",
          "requires": [
            "FR-106.2.3",
            "FR-106.2.4",
            "FR-106.3.2"
          ],
          "docs": [],
          "page": "/replay",
          "minutes": 9,
          "x": 396,
          "y": 136,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-210.1",
          "kind": "module",
          "side": "after",
          "title": "How FlowRunner is put together",
          "objective": "Module 1 of FR-210",
          "course": "FR-210",
          "module": 1,
          "lane": "developer",
          "touches": [],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 612,
          "y": 112,
          "w": 160,
          "h": 68
        }
      ],
      "edges": [
        {
          "from": "FR-106.3",
          "to": "FR-106.4#in",
          "module": true,
          "points": [
            [
              160,
              147
            ],
            [
              180,
              147
            ],
            [
              153,
              126
            ],
            [
              173,
              126
            ]
          ]
        },
        {
          "from": "FR-106.4#in",
          "to": "FR-106.4.1",
          "module": true,
          "points": [
            [
              173,
              126
            ],
            [
              193,
              126
            ],
            [
              166,
              64
            ],
            [
              186,
              64
            ]
          ]
        },
        {
          "from": "FR-106.4#in",
          "to": "FR-106.4.2",
          "module": true,
          "points": [
            [
              173,
              126
            ],
            [
              285,
              126
            ],
            [
              285,
              64
            ],
            [
              396,
              64
            ]
          ]
        },
        {
          "from": "FR-106.4#in",
          "to": "FR-106.4.3",
          "module": true,
          "points": [
            [
              173,
              126
            ],
            [
              193,
              126
            ],
            [
              166,
              188
            ],
            [
              186,
              188
            ]
          ]
        },
        {
          "from": "FR-106.4#in",
          "to": "FR-106.4.4",
          "module": true,
          "points": [
            [
              173,
              126
            ],
            [
              285,
              126
            ],
            [
              285,
              188
            ],
            [
              396,
              188
            ]
          ]
        },
        {
          "from": "FR-106.4.1",
          "to": "FR-106.4#out",
          "module": true,
          "points": [
            [
              376,
              64
            ],
            [
              488,
              64
            ],
            [
              488,
              126
            ],
            [
              599,
              126
            ]
          ]
        },
        {
          "from": "FR-106.4.2",
          "to": "FR-106.4#out",
          "module": true,
          "points": [
            [
              586,
              64
            ],
            [
              606,
              64
            ],
            [
              579,
              126
            ],
            [
              599,
              126
            ]
          ]
        },
        {
          "from": "FR-106.4.3",
          "to": "FR-106.4#out",
          "module": true,
          "points": [
            [
              376,
              188
            ],
            [
              488,
              188
            ],
            [
              488,
              126
            ],
            [
              599,
              126
            ]
          ]
        },
        {
          "from": "FR-106.4.4",
          "to": "FR-106.4#out",
          "module": true,
          "points": [
            [
              586,
              188
            ],
            [
              606,
              188
            ],
            [
              579,
              126
            ],
            [
              599,
              126
            ]
          ]
        },
        {
          "from": "FR-106.4#out",
          "to": "FR-210.1",
          "module": true,
          "points": [
            [
              599,
              126
            ],
            [
              619,
              126
            ],
            [
              592,
              146
            ],
            [
              612,
              146
            ]
          ]
        }
      ],
      "course": "FR-106",
      "module": 4,
      "title": "Running a workspace: people, limits and audit",
      "lesson_count": 4,
      "minutes": 26
    },
    "FR-210.1": {
      "width": 896,
      "height": 188,
      "nodes": [
        {
          "code": "FR-106.4",
          "kind": "module",
          "side": "before",
          "title": "Running a workspace: people, limits and audit",
          "objective": "Module 4 of FR-106",
          "course": "FR-106",
          "module": 4,
          "lane": "operations",
          "touches": [],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 43,
          "w": 160,
          "h": 83
        },
        {
          "code": "FR-210.1.1",
          "kind": "lesson",
          "title": "The whole model, without the hand-holding",
          "objective": "Map FlowRunner's model to familiar concepts: the results pool as a context object, triggers as event sources, versions as deployments",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-210",
          "lane": "developer",
          "module": 1,
          "moduleTitle": "How FlowRunner is put together",
          "requires": [],
          "docs": [
            {
              "label": "What is FlowRunner?",
              "href": "https://docs.flowrunner.ai/index"
            },
            {
              "label": "Flows and Instances",
              "href": "https://docs.flowrunner.ai/learn/concepts/flows-and-instances"
            },
            {
              "label": "Blocks",
              "href": "https://docs.flowrunner.ai/learn/concepts/blocks"
            }
          ],
          "page": null,
          "minutes": 6,
          "x": 186,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-210.1.2",
          "kind": "lesson",
          "title": "The editor, at your own pace",
          "objective": "Place, wire, configure, and test blocks without the beginner narration",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-210",
          "lane": "developer",
          "module": 1,
          "moduleTitle": "How FlowRunner is put together",
          "requires": [
            "FR-210.1.1"
          ],
          "docs": [
            {
              "label": "The Flow Editor",
              "href": "https://docs.flowrunner.ai/build/flow-editor"
            },
            {
              "label": "Testing",
              "href": "https://docs.flowrunner.ai/run/testing"
            }
          ],
          "page": null,
          "minutes": 6,
          "x": 480,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-210.2",
          "kind": "module",
          "side": "after",
          "title": "Code as the fastest path through a flow",
          "objective": "Module 2 of FR-210",
          "course": "FR-210",
          "module": 2,
          "lane": "developer",
          "touches": [
            "FR-210.1.2"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 696,
          "y": 43,
          "w": 160,
          "h": 83
        }
      ],
      "edges": [
        {
          "from": "FR-210.1.1",
          "to": "FR-210.1.2",
          "uncertain": false,
          "points": [
            [
              376,
              64
            ],
            [
              376,
              64
            ],
            [
              428,
              64
            ],
            [
              480,
              64
            ]
          ]
        },
        {
          "from": "FR-106.4",
          "to": "FR-210.1.1",
          "module": true,
          "points": [
            [
              160,
              85
            ],
            [
              180,
              85
            ],
            [
              166,
              64
            ],
            [
              186,
              64
            ]
          ]
        },
        {
          "from": "FR-210.1.2",
          "to": "FR-210.2",
          "module": true,
          "points": [
            [
              670,
              64
            ],
            [
              690,
              64
            ],
            [
              676,
              85
            ],
            [
              696,
              85
            ]
          ]
        }
      ],
      "course": "FR-210",
      "module": 1,
      "title": "How FlowRunner is put together",
      "lesson_count": 2,
      "minutes": 12
    },
    "FR-210.2": {
      "width": 1190,
      "height": 356,
      "nodes": [
        {
          "code": "FR-210.1",
          "kind": "module",
          "side": "before",
          "title": "How FlowRunner is put together",
          "objective": "Module 1 of FR-210",
          "course": "FR-210",
          "module": 1,
          "lane": "developer",
          "touches": [
            "FR-210.1.2"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 134,
          "w": 160,
          "h": 68
        },
        {
          "code": "FR-210.2.1",
          "kind": "lesson",
          "title": "Replace ten blocks with fifteen lines",
          "objective": "Rebuild the contact-form flow with one Custom Cloud Code block replacing the no-code chain",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-210",
          "lane": "developer",
          "module": 2,
          "moduleTitle": "Code as the fastest path through a flow",
          "requires": [
            "FR-210.1.2"
          ],
          "docs": [
            {
              "label": "Quick Start: A Contact Us Form (with code)",
              "href": "https://docs.flowrunner.ai/learn/quickstart-code"
            },
            {
              "label": "Custom Cloud Code",
              "href": "https://docs.flowrunner.ai/reference/custom-cloud-code"
            }
          ],
          "page": null,
          "minutes": 8,
          "x": 186,
          "y": 29,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-210.2.2",
          "kind": "lesson",
          "title": "Read the flow's data inside your code",
          "objective": "Read flow data (Initial Data, earlier results) inside code and hand results to later blocks",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-210",
          "lane": "developer",
          "module": 2,
          "moduleTitle": "Code as the fastest path through a flow",
          "requires": [
            "FR-210.2.1"
          ],
          "docs": [
            {
              "label": "Quick Start: A Contact Us Form (with code)",
              "href": "https://docs.flowrunner.ai/learn/quickstart-code"
            },
            {
              "label": "Custom Cloud Code",
              "href": "https://docs.flowrunner.ai/reference/custom-cloud-code"
            },
            {
              "label": "Expression Editor",
              "href": "https://docs.flowrunner.ai/learn/concepts/expressions"
            }
          ],
          "page": null,
          "minutes": 6,
          "x": 480,
          "y": 29,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-210.2.3",
          "kind": "lesson",
          "title": "Have AI write the block for you",
          "objective": "Prompt an AI to produce the block's code from a plain description of the transformation, then verify it in the Test Monitor",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-210",
          "lane": "developer",
          "module": 2,
          "moduleTitle": "Code as the fastest path through a flow",
          "requires": [
            "FR-210.2.2"
          ],
          "docs": [
            {
              "label": "Custom Cloud Code",
              "href": "https://docs.flowrunner.ai/reference/custom-cloud-code"
            }
          ],
          "page": null,
          "minutes": 6,
          "x": 774,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-210.2.4",
          "kind": "lesson",
          "title": "When a block beats writing code",
          "objective": "Judge deliberately: connectors, waits, retries, approvals, and loops-with-monitoring are platform strengths; data transformation chains are code territory",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-210",
          "lane": "developer",
          "module": 2,
          "moduleTitle": "Code as the fastest path through a flow",
          "requires": [
            "FR-210.2.2"
          ],
          "docs": [
            {
              "label": "AI in Flows",
              "href": "https://docs.flowrunner.ai/build/ai-in-flows"
            },
            {
              "label": "Error Handling",
              "href": "https://docs.flowrunner.ai/reference/error-handling-concept"
            },
            {
              "label": "Adding a Delay",
              "href": "https://docs.flowrunner.ai/build/flow-control/waiting"
            }
          ],
          "page": null,
          "minutes": 6,
          "x": 774,
          "y": 180,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-210.3",
          "kind": "module",
          "side": "after",
          "title": "Your own building blocks (ships with the custom actions/triggers release)",
          "objective": "Module 3 of FR-210",
          "course": "FR-210",
          "module": 3,
          "lane": "developer",
          "touches": [
            "FR-210.2.2"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 990,
          "y": 112,
          "w": 160,
          "h": 113
        }
      ],
      "edges": [
        {
          "from": "FR-210.2.1",
          "to": "FR-210.2.2",
          "uncertain": false,
          "points": [
            [
              376,
              81
            ],
            [
              376,
              81
            ],
            [
              428,
              81
            ],
            [
              480,
              81
            ]
          ]
        },
        {
          "from": "FR-210.2.2",
          "to": "FR-210.2.3",
          "uncertain": false,
          "points": [
            [
              670,
              64
            ],
            [
              670,
              64
            ],
            [
              722,
              64
            ],
            [
              774,
              64
            ]
          ]
        },
        {
          "from": "FR-210.2.2",
          "to": "FR-210.2.4",
          "uncertain": false,
          "points": [
            [
              670,
              98
            ],
            [
              670,
              98
            ],
            [
              722,
              219
            ],
            [
              774,
              232
            ]
          ]
        },
        {
          "from": "FR-210.1",
          "to": "FR-210.2.1",
          "module": true,
          "points": [
            [
              160,
              168
            ],
            [
              180,
              168
            ],
            [
              166,
              81
            ],
            [
              186,
              81
            ]
          ]
        },
        {
          "from": "FR-210.2.3",
          "to": "FR-210.2#out",
          "module": true,
          "points": [
            [
              964,
              64
            ],
            [
              984,
              64
            ],
            [
              957,
              148
            ],
            [
              977,
              148
            ]
          ]
        },
        {
          "from": "FR-210.2.4",
          "to": "FR-210.2#out",
          "module": true,
          "points": [
            [
              964,
              232
            ],
            [
              984,
              232
            ],
            [
              957,
              148
            ],
            [
              977,
              148
            ]
          ]
        },
        {
          "from": "FR-210.2#out",
          "to": "FR-210.3",
          "module": true,
          "points": [
            [
              977,
              148
            ],
            [
              997,
              148
            ],
            [
              970,
              169
            ],
            [
              990,
              169
            ]
          ]
        }
      ],
      "course": "FR-210",
      "module": 2,
      "title": "Code as the fastest path through a flow",
      "lesson_count": 4,
      "minutes": 26
    },
    "FR-210.3": {
      "width": 1190,
      "height": 188,
      "nodes": [
        {
          "code": "FR-210.2",
          "kind": "module",
          "side": "before",
          "title": "Code as the fastest path through a flow",
          "objective": "Module 2 of FR-210",
          "course": "FR-210",
          "module": 2,
          "lane": "developer",
          "touches": [
            "FR-210.2.2"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 43,
          "w": 160,
          "h": 83
        },
        {
          "code": "FR-210.3.1",
          "kind": "lesson",
          "title": "Turn your code into a reusable block",
          "objective": "Author an action in code and use it like any built-in block",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-210",
          "lane": "developer",
          "module": 3,
          "moduleTitle": "Your own building blocks (ships with the custom actions/triggers release)",
          "requires": [
            "FR-210.2.2"
          ],
          "docs": [
            {
              "label": "About Custom Actions",
              "href": "https://docs.flowrunner.ai/extend/custom-actions"
            },
            {
              "label": "Custom & Marketplace Actions",
              "href": "https://docs.flowrunner.ai/build/integrations/custom-actions"
            }
          ],
          "page": null,
          "minutes": 8,
          "x": 186,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-210.3.2",
          "kind": "lesson",
          "title": "Make a trigger for your own events",
          "objective": "Author a trigger in code so flows can react to your own event source",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-210",
          "lane": "developer",
          "module": 3,
          "moduleTitle": "Your own building blocks (ships with the custom actions/triggers release)",
          "requires": [
            "FR-210.3.1"
          ],
          "docs": [],
          "page": null,
          "minutes": 7,
          "x": 480,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-210.3.3",
          "kind": "lesson",
          "title": "Share your blocks with your team",
          "objective": "Package authored actions and triggers so the whole workspace builds with them",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-210",
          "lane": "developer",
          "module": 3,
          "moduleTitle": "Your own building blocks (ships with the custom actions/triggers release)",
          "requires": [
            "FR-210.3.2"
          ],
          "docs": [],
          "page": null,
          "minutes": 6,
          "x": 774,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-210.4",
          "kind": "module",
          "side": "after",
          "title": "Using FlowRunner from your own code",
          "objective": "Module 4 of FR-210",
          "course": "FR-210",
          "module": 4,
          "lane": "developer",
          "touches": [
            "FR-210.3.1"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 990,
          "y": 43,
          "w": 160,
          "h": 83
        }
      ],
      "edges": [
        {
          "from": "FR-210.3.1",
          "to": "FR-210.3.2",
          "uncertain": false,
          "points": [
            [
              376,
              64
            ],
            [
              376,
              64
            ],
            [
              428,
              64
            ],
            [
              480,
              64
            ]
          ]
        },
        {
          "from": "FR-210.3.2",
          "to": "FR-210.3.3",
          "uncertain": false,
          "points": [
            [
              670,
              64
            ],
            [
              670,
              64
            ],
            [
              722,
              64
            ],
            [
              774,
              64
            ]
          ]
        },
        {
          "from": "FR-210.2",
          "to": "FR-210.3.1",
          "module": true,
          "points": [
            [
              160,
              85
            ],
            [
              180,
              85
            ],
            [
              166,
              64
            ],
            [
              186,
              64
            ]
          ]
        },
        {
          "from": "FR-210.3.3",
          "to": "FR-210.4",
          "module": true,
          "points": [
            [
              964,
              64
            ],
            [
              984,
              64
            ],
            [
              970,
              85
            ],
            [
              990,
              85
            ]
          ]
        }
      ],
      "course": "FR-210",
      "module": 3,
      "title": "Your own building blocks (ships with the custom actions/triggers release)",
      "lesson_count": 3,
      "minutes": 21
    },
    "FR-210.4": {
      "width": 1016,
      "height": 356,
      "nodes": [
        {
          "code": "FR-210.3",
          "kind": "module",
          "side": "before",
          "title": "Your own building blocks (ships with the custom actions/triggers release)",
          "objective": "Module 3 of FR-210",
          "course": "FR-210",
          "module": 3,
          "lane": "developer",
          "touches": [
            "FR-210.3.1"
          ],
          "requires": [],
          "docs": [],
          "page": null,
          "minutes": null,
          "status": "planned",
          "video": null,
          "x": 0,
          "y": 112,
          "w": 160,
          "h": 113
        },
        {
          "code": "FR-210.4.1",
          "kind": "lesson",
          "title": "The entire API, in one lesson",
          "objective": "Use the deliberately thin API surface - start a flow, read block results, activate a trigger - and understand why that is all there is",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-210",
          "lane": "developer",
          "module": 4,
          "moduleTitle": "Using FlowRunner from your own code",
          "requires": [
            "FR-210.2.2"
          ],
          "docs": [
            {
              "label": "Call Flow (Blocking)",
              "href": "https://docs.flowrunner.ai/api/call-flow-blocking"
            },
            {
              "label": "Call Flow (Non-Blocking)",
              "href": "https://docs.flowrunner.ai/api/call-flow-nonblocking"
            },
            {
              "label": "Retrieving Block Results",
              "href": "https://docs.flowrunner.ai/api/block-results"
            },
            {
              "label": "Activating an External Callback",
              "href": "https://docs.flowrunner.ai/api/activating-a-trigger"
            }
          ],
          "page": null,
          "minutes": 7,
          "x": 186,
          "y": 29,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-210.4.2",
          "kind": "lesson",
          "title": "Long waits without running a queue",
          "objective": "Let the platform hold long waits, external callbacks, schedules, and retries your code would otherwise need queues and cron for",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-210",
          "lane": "developer",
          "module": 4,
          "moduleTitle": "Using FlowRunner from your own code",
          "requires": [
            "FR-210.4.1"
          ],
          "docs": [
            {
              "label": "Waiting on an External System",
              "href": "https://docs.flowrunner.ai/build/flow-control/external-callbacks"
            },
            {
              "label": "Flow Scheduling",
              "href": "https://docs.flowrunner.ai/reference/flow-scheduling-concept"
            },
            {
              "label": "Error Handling",
              "href": "https://docs.flowrunner.ai/reference/error-handling-concept"
            }
          ],
          "page": "/back-office",
          "minutes": 7,
          "x": 480,
          "y": 180,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-210.4.3",
          "kind": "lesson",
          "title": "Give an agent your code as tools",
          "objective": "Register an MCP server; attach flows and your custom actions to an AI Agent as tools",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-210",
          "lane": "developer",
          "module": 4,
          "moduleTitle": "Using FlowRunner from your own code",
          "requires": [
            "FR-210.3.1",
            "FR-210.4.1",
            "FR-105.2.2?"
          ],
          "docs": [
            {
              "label": "MCP Servers",
              "href": "https://docs.flowrunner.ai/platform/mcp-servers"
            },
            {
              "label": "Flows as Agent Tools",
              "href": "https://docs.flowrunner.ai/reference/flows-as-agent-tools-concept"
            }
          ],
          "page": null,
          "minutes": 6,
          "x": 480,
          "y": 12,
          "w": 190,
          "h": 104
        },
        {
          "code": "FR-210.4.4",
          "kind": "lesson",
          "title": "Capstone: build it your way",
          "objective": "One code block, one custom action, one flow: validation in code, packaged for reuse, with the platform carrying the waits and retries",
          "status": "planned",
          "video": null,
          "available": false,
          "course": "FR-210",
          "lane": "developer",
          "module": 4,
          "moduleTitle": "Using FlowRunner from your own code",
          "requires": [
            "FR-210.2.4",
            "FR-210.3.1",
            "FR-210.4.2"
          ],
          "docs": [],
          "page": "/back-office",
          "minutes": 9,
          "x": 774,
          "y": 180,
          "w": 190,
          "h": 104
        }
      ],
      "edges": [
        {
          "from": "FR-210.4.1",
          "to": "FR-210.4.2",
          "uncertain": false,
          "points": [
            [
              376,
              98
            ],
            [
              376,
              98
            ],
            [
              428,
              219
            ],
            [
              480,
              232
            ]
          ]
        },
        {
          "from": "FR-210.4.1",
          "to": "FR-210.4.3",
          "uncertain": false,
          "points": [
            [
              376,
              64
            ],
            [
              376,
              64
            ],
            [
              428,
              64
            ],
            [
              480,
              64
            ]
          ]
        },
        {
          "from": "FR-210.4.2",
          "to": "FR-210.4.4",
          "uncertain": false,
          "points": [
            [
              670,
              232
            ],
            [
              670,
              232
            ],
            [
              722,
              232
            ],
            [
              774,
              232
            ]
          ]
        },
        {
          "from": "FR-210.3",
          "to": "FR-210.4.1",
          "module": true,
          "points": [
            [
              160,
              169
            ],
            [
              180,
              169
            ],
            [
              166,
              81
            ],
            [
              186,
              81
            ]
          ]
        }
      ],
      "course": "FR-210",
      "module": 4,
      "title": "Using FlowRunner from your own code",
      "lesson_count": 4,
      "minutes": 29
    }
  },
  "recipes": [
    {
      "code": "QW-01",
      "kind": "quickwin",
      "title": "Turn a form submission into an automatic reply",
      "objective": "First flow, form trigger, template email",
      "explains": null,
      "surfaces": "Contact page + starter flow import",
      "page": "/contact",
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "QW-02",
      "kind": "quickwin",
      "title": "Have AI draft your support replies",
      "objective": "First AI Agent from the agent quickstart",
      "explains": null,
      "surfaces": "Agent quickstart (product); chat widget once site Phase C lands",
      "page": null,
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "QW-03",
      "kind": "quickwin",
      "title": "Get a notification when something happens",
      "objective": "React to a real event with a flow",
      "explains": null,
      "surfaces": "Checkout (site Phase B) + starter flow import",
      "page": null,
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-01",
      "kind": "recipe",
      "title": "Build one view of a customer from several systems",
      "objective": "Multi-API aggregation",
      "explains": "FR-103",
      "surfaces": "Static orders, payments, customers APIs",
      "page": "/api#api-shop-customers-GET",
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-02",
      "kind": "recipe",
      "title": "Find your highest-value customers",
      "objective": "Aggregation, scoring",
      "explains": "FR-103",
      "surfaces": "Static customers/orders",
      "page": "/api#api-shop-customers-GET",
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-03",
      "kind": "recipe",
      "title": "Import data from an old system that exports badly",
      "objective": "Transformation / ETL",
      "explains": "FR-103",
      "surfaces": "Legacy endpoint",
      "page": "/api#api-shop-legacy-orders-GET",
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-04",
      "kind": "recipe",
      "title": "Process only the records added since last time",
      "objective": "State via Shared Memory",
      "explains": "FR-103",
      "surfaces": "Static orders (timestamps)",
      "page": "/api#api-shop-orders-GET",
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-05",
      "kind": "recipe",
      "title": "Send each inquiry to the right team",
      "objective": "Conditions, routing",
      "explains": "FR-102",
      "surfaces": "Contact page",
      "page": "/contact",
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-06",
      "kind": "recipe",
      "title": "Score a lead before anyone follows it up",
      "objective": "Rules and scoring",
      "explains": "FR-102",
      "surfaces": "Contact page (wholesale examples)",
      "page": "/contact",
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-07",
      "kind": "recipe",
      "title": "Check incoming data before you use it",
      "objective": "Validation, branching",
      "explains": "FR-102",
      "surfaces": "Checkout, replay orders",
      "page": null,
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-08",
      "kind": "recipe",
      "title": "Keep working when an API keeps failing",
      "objective": "Retries, backoff",
      "explains": "FR-102",
      "surfaces": "Operations, with the inventory service struggling",
      "page": null,
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-09",
      "kind": "recipe",
      "title": "Make five API calls at the same time",
      "objective": "Parallel + synchronize",
      "explains": "FR-102",
      "surfaces": "Inventory API",
      "page": null,
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-10",
      "kind": "recipe",
      "title": "Handle a request you can only partly fill",
      "objective": "Complex branching",
      "explains": "FR-102",
      "surfaces": "Inventory API (seeded zero/low SKUs)",
      "page": null,
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-11",
      "kind": "recipe",
      "title": "Choose which location fulfills the request",
      "objective": "Multi-variable decisions",
      "explains": "FR-102",
      "surfaces": "Inventory warehouses",
      "page": null,
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-12",
      "kind": "recipe",
      "title": "Keep your pricing rules in one place",
      "objective": "Conditional business rules",
      "explains": "FR-102",
      "surfaces": "Checkout quote flow",
      "page": null,
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-13",
      "kind": "recipe",
      "title": "Require approval above a certain amount",
      "objective": "Human-in-the-loop",
      "explains": "FR-104",
      "surfaces": "Checkout, then Operations — an order over the approval threshold",
      "page": null,
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-14",
      "kind": "recipe",
      "title": "Retry a failed payment without losing the order",
      "objective": "Retry, state, branching",
      "explains": "FR-104",
      "surfaces": "Operations — an order whose card declined",
      "page": null,
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-15",
      "kind": "recipe",
      "title": "Stop the same webhook being processed twice",
      "objective": "Idempotency",
      "explains": "FR-104",
      "surfaces": "Operations, with the carrier reporting the same update twice",
      "page": null,
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-16",
      "kind": "recipe",
      "title": "Update your records when another system does",
      "objective": "Async events, state machines",
      "explains": "FR-104",
      "surfaces": "Operations — a parcel moving through its lifecycle",
      "page": null,
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-17",
      "kind": "recipe",
      "title": "Detect an update that never arrived",
      "objective": "Watchdogs, timeouts",
      "explains": "FR-104",
      "surfaces": "Operations, with the carrier going quiet after dispatch",
      "page": null,
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-18",
      "kind": "recipe",
      "title": "Handle events that arrive in the wrong order",
      "objective": "Event consistency",
      "explains": "FR-104",
      "surfaces": "Operations, with the carrier reporting delivery before dispatch",
      "page": null,
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-19",
      "kind": "recipe",
      "title": "Wait for two approvals before continuing",
      "objective": "Rendezvous / synchronization",
      "explains": "FR-104",
      "surfaces": "Operations — payment and approval both needed before fulfilment",
      "page": null,
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-20",
      "kind": "recipe",
      "title": "Follow up when someone abandons a form",
      "objective": "Delayed automation",
      "explains": "FR-104",
      "surfaces": "Checkout abandon control",
      "page": null,
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-21",
      "kind": "recipe",
      "title": "Work out what a message is asking for",
      "objective": "AI classification",
      "explains": "FR-105",
      "surfaces": "Contact page canned examples",
      "page": "/contact",
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-22",
      "kind": "recipe",
      "title": "Decide a refund with policy, AI and a human",
      "objective": "Full orchestration with AI and humans",
      "explains": "FR-105",
      "surfaces": "Return Request page, then Operations, payments",
      "page": null,
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-23",
      "kind": "recipe",
      "title": "Moderate user content automatically",
      "objective": "AI classification, escalation",
      "explains": "FR-105",
      "surfaces": "Review page + static reviews",
      "page": null,
      "status": "planned",
      "starter_flow": null
    },
    {
      "code": "PB-24",
      "kind": "recipe",
      "title": "Make an agent hand off to a human",
      "objective": "Agent guardrails, handoff",
      "explains": "FR-105",
      "surfaces": "Chat widget, then Operations",
      "page": null,
      "status": "planned",
      "starter_flow": null
    }
  ],
  "finale": "RTS-01",
  "stats": {
    "courses": 7,
    "lessons": 82,
    "modules": 26,
    "recipes": 27,
    "uncertain_edges": 6
  }
}
