{
  "prepared": "2026-09-13",
  "video_count": 59,
  "available_transcripts_read": 58,
  "total_video_seconds": 41373.06,
  "method": "Three subagents read assigned full VTT transcripts. Parent assembled/reviewed summaries, checked current primary documentation, and inspected sampled video 30 frames. No narration transcription for video 30; subtitle-based lessons may omit silent screen details.",
  "lesson30_samples_seconds": [
    5,
    25,
    45,
    65,
    85,
    105,
    125,
    145,
    165,
    185,
    205,
    225,
    245,
    265,
    285,
    305,
    325,
    345,
    365,
    385,
    405,
    425,
    445,
    465,
    485,
    505,
    525,
    545
  ],
  "official_sources": [
    {
      "id": "runtime",
      "title": "Cloudflare Workers runtime APIs",
      "url": "https://developers.cloudflare.com/workers/runtime-apis/",
      "use": "Worker execution APIs and partial Node.js compatibility.",
      "lessons": [
        1,
        2,
        3,
        4,
        5
      ]
    },
    {
      "id": "bindings",
      "title": "Cloudflare resource bindings",
      "url": "https://developers.cloudflare.com/workers/runtime-apis/bindings/",
      "use": "Resource access through env and local versus remote bindings.",
      "lessons": [
        6,
        12,
        15
      ]
    },
    {
      "id": "drizzle",
      "title": "Drizzle with Cloudflare D1",
      "url": "https://orm.drizzle.team/docs/sqlite/connect-cloudflare-d1",
      "use": "The D1 driver and database-query integration. Check documentation matching the installed version.",
      "lessons": [
        8,
        9,
        11,
        30
      ]
    },
    {
      "id": "trpc",
      "title": "tRPC introduction",
      "url": "https://trpc.io/docs/",
      "use": "Shared TypeScript API contracts and client/server integration.",
      "lessons": [
        7,
        10
      ]
    },
    {
      "id": "hono",
      "title": "Hono on Cloudflare Workers",
      "url": "https://hono.dev/docs/getting-started/cloudflare-workers",
      "use": "Routing requests with Hono and binding the Worker fetch handler.",
      "lessons": [
        13,
        16,
        18
      ]
    },
    {
      "id": "geo",
      "title": "Cloudflare Request API",
      "url": "https://developers.cloudflare.com/workers/runtime-apis/request/",
      "use": "request.cf metadata, including visitor geolocation and the serving colo.",
      "lessons": [
        14
      ]
    },
    {
      "id": "kv",
      "title": "How Workers KV works",
      "url": "https://developers.cloudflare.com/kv/concepts/how-kv-works/",
      "use": "Read caching and eventual consistency; reason about stale redirects.",
      "lessons": [
        17
      ]
    },
    {
      "id": "delivery",
      "title": "Queues delivery guarantees",
      "url": "https://developers.cloudflare.com/queues/reference/delivery-guarantees/",
      "use": "At-least-once delivery and duplicate-safe processing.",
      "lessons": [
        19,
        21,
        22,
        23,
        24
      ]
    },
    {
      "id": "queuepricing",
      "title": "Queues pricing",
      "url": "https://developers.cloudflare.com/queues/platform/pricing/",
      "use": "Current plan access and per-operation billing, including retries.",
      "lessons": [
        20
      ]
    },
    {
      "id": "workerpricing",
      "title": "Workers pricing",
      "url": "https://developers.cloudflare.com/workers/platform/pricing/",
      "use": "Current subscription, request, and CPU pricing; recording-era estimates are not a quotation.",
      "lessons": []
    },
    {
      "id": "workflow",
      "title": "Workflows Workers API",
      "url": "https://developers.cloudflare.com/workflows/build/workers-api/",
      "use": "Workflow instances, named steps, and their control API.",
      "lessons": [
        25,
        26,
        27,
        28,
        29,
        31,
        32,
        33
      ]
    },
    {
      "id": "limits",
      "title": "Workflows limits",
      "url": "https://developers.cloudflare.com/workflows/reference/limits/",
      "use": "Step-result limits, streamed output, and external artifact storage.",
      "lessons": [
        34
      ]
    },
    {
      "id": "objects",
      "title": "What are Durable Objects?",
      "url": "https://developers.cloudflare.com/durable-objects/concepts/what-are-durable-objects/",
      "use": "Stateful coordination and per-object storage.",
      "lessons": [
        35,
        36,
        37,
        38,
        40
      ]
    },
    {
      "id": "websockets",
      "title": "Durable Objects WebSockets",
      "url": "https://developers.cloudflare.com/durable-objects/best-practices/websockets/",
      "use": "WebSocket coordination and hibernation behavior.",
      "lessons": [
        39,
        41,
        42,
        43
      ]
    },
    {
      "id": "rpc",
      "title": "Workers remote procedure calls",
      "url": "https://developers.cloudflare.com/workers/runtime-apis/rpc/",
      "use": "Calling service entrypoints and Durable Object methods through bindings.",
      "lessons": [
        44
      ]
    },
    {
      "id": "environments",
      "title": "Wrangler environments",
      "url": "https://developers.cloudflare.com/workers/wrangler/environments/",
      "use": "Environment-specific Worker configuration and non-inherited bindings.",
      "lessons": [
        45,
        46,
        47,
        48
      ]
    },
    {
      "id": "jwt",
      "title": "RFC 7519: JSON Web Token",
      "url": "https://www.rfc-editor.org/info/rfc7519/",
      "use": "Distinguish token claims, signing, and encryption.",
      "lessons": [
        49
      ]
    },
    {
      "id": "authstripe",
      "title": "Better Auth Stripe plugin",
      "url": "https://better-auth.com/docs/plugins/stripe",
      "use": "Subscription schema, checkout integration, and webhook configuration.",
      "lessons": [
        53,
        54,
        55
      ]
    },
    {
      "id": "webhook",
      "title": "Stripe webhooks",
      "url": "https://docs.stripe.com/webhooks",
      "use": "Signed events, delivery retries, duplicate events, and endpoint handling.",
      "lessons": [
        56
      ]
    },
    {
      "id": "testing",
      "title": "Cloudflare Workers Vitest integration",
      "url": "https://developers.cloudflare.com/workers/testing/vitest-integration/",
      "use": "Complement mocked business-logic tests with actual Worker runtime tests.",
      "lessons": [
        57,
        58,
        59
      ]
    },
    {
      "id": "auth",
      "title": "Better Auth basic usage",
      "url": "https://better-auth.com/docs/basic-usage",
      "use": "Client session state and server-side session validation.",
      "lessons": [
        50,
        51,
        52
      ]
    }
  ],
  "lessons": [
    {
      "number": 1,
      "title": "Intro & Overview",
      "section": "Getting Started",
      "duration": 1214.8,
      "evidence": "transcript",
      "study_page": "/lessons/0002-cloudflare-01-intro-overview.html",
      "timestamps": [
        {
          "time": "00:07:00",
          "topic": "Course scope and pace"
        },
        {
          "time": "00:09:00",
          "topic": "Smart Links product demonstration"
        },
        {
          "time": "00:13:02",
          "topic": "User application versus data service responsibilities"
        },
        {
          "time": "00:15:02",
          "topic": "Shared package and request flow"
        }
      ],
      "video_file": "01-Getting-Started-Intro-Overview.mp4",
      "transcript_file": "01-Getting-Started-Intro-Overview.en.vtt"
    },
    {
      "number": 2,
      "title": "Setting up the project",
      "section": "Getting Started",
      "duration": 451.2,
      "evidence": "transcript",
      "study_page": "/lessons/0003-cloudflare-02-setting-up-the-project.html",
      "timestamps": [
        {
          "time": "00:00:00",
          "topic": "Fork and clone the starter"
        },
        {
          "time": "00:01:00",
          "topic": "Monorepo structure"
        },
        {
          "time": "00:03:00",
          "topic": "Build the Data Ops package"
        },
        {
          "time": "00:04:01",
          "topic": "Root scripts versus app-local scripts"
        }
      ],
      "video_file": "02-Getting-Started-Setting-up-the-project.mp4",
      "transcript_file": "02-Getting-Started-Setting-up-the-project.en.vtt"
    },
    {
      "number": 3,
      "title": "Deploy the project",
      "section": "Getting Started",
      "duration": 349.6,
      "evidence": "transcript",
      "study_page": "/lessons/0004-cloudflare-03-deploy-the-project.html",
      "timestamps": [
        {
          "time": "00:00:39",
          "topic": "Locate the Wrangler configuration"
        },
        {
          "time": "00:01:59",
          "topic": "Build and Wrangler deploy script"
        },
        {
          "time": "00:03:04",
          "topic": "Authorize Wrangler access"
        },
        {
          "time": "00:04:28",
          "topic": "Inspect the deployed Worker dashboard"
        }
      ],
      "video_file": "03-Getting-Started-Deploy-the-project.mp4",
      "transcript_file": "03-Getting-Started-Deploy-the-project.en.vtt"
    },
    {
      "number": 4,
      "title": "Understand the Worker Runtime",
      "section": "Building on Cloudflare Workers",
      "duration": 704.8,
      "evidence": "transcript",
      "study_page": "/lessons/0005-cloudflare-04-understand-the-worker-runtime.html",
      "timestamps": [
        {
          "time": "00:00:00",
          "topic": "V8 isolates and the traditional server model"
        },
        {
          "time": "00:02:01",
          "topic": "Serverless allocation concept"
        },
        {
          "time": "00:07:00",
          "topic": "Workers request execution flow"
        },
        {
          "time": "00:10:00",
          "topic": "CPU work versus time waiting on APIs"
        }
      ],
      "video_file": "04-Building-on-Cloudflare-Workers-Understand-the-Worker-Runtime.mp4",
      "transcript_file": "04-Building-on-Cloudflare-Workers-Understand-the-Worker-Runtime.en.vtt"
    },
    {
      "number": 5,
      "title": "Framework bundling, Entrypoints, & Env",
      "section": "Building on Cloudflare Workers",
      "duration": 1358.9,
      "evidence": "transcript",
      "study_page": "/lessons/0006-cloudflare-05-framework-bundling-entrypoints-env.html",
      "timestamps": [
        {
          "time": "00:00:00",
          "topic": "The fetch handler as the server contract"
        },
        {
          "time": "00:07:02",
          "topic": "Framework chunks and OpenNext output"
        },
        {
          "time": "00:13:00",
          "topic": "Local development variables and generated types"
        },
        {
          "time": "00:19:00",
          "topic": "Deployed secrets and shared secret storage"
        }
      ],
      "video_file": "05-Building-on-Cloudflare-Workers-Framework-bundling-Entrypoints-Env.mp4",
      "transcript_file": "05-Building-on-Cloudflare-Workers-Framework-bundling-Entrypoints-Env.en.vtt"
    },
    {
      "number": 6,
      "title": "Binding resources to your Worker",
      "section": "Building on Cloudflare Workers",
      "duration": 419,
      "evidence": "transcript",
      "study_page": "/lessons/0007-cloudflare-06-binding-resources-to-your-worker.html",
      "timestamps": [
        {
          "time": "00:01:00",
          "topic": "Resources and bindings across products"
        },
        {
          "time": "00:02:00",
          "topic": "Create and bind a KV namespace"
        },
        {
          "time": "00:03:00",
          "topic": "Local state versus remote bindings"
        },
        {
          "time": "00:04:00",
          "topic": "Generate types and implement KV routes"
        }
      ],
      "video_file": "06-Building-on-Cloudflare-Workers-Binding-resources-to-your-Worker.mp4",
      "transcript_file": "06-Building-on-Cloudflare-Workers-Binding-resources-to-your-Worker.en.vtt"
    },
    {
      "number": 7,
      "title": "React + Tanstack + TRPC on Workers",
      "section": "Building in a Mono Repo",
      "duration": 861.4,
      "evidence": "transcript",
      "study_page": "/lessons/0008-cloudflare-07-react-tanstack-trpc-on-workers.html",
      "timestamps": [
        {
          "time": "00:02:01",
          "topic": "File routes and dashboard loaders"
        },
        {
          "time": "00:06:00",
          "topic": "Problems in manual fetch state management"
        },
        {
          "time": "00:09:00",
          "topic": "tRPC adds typed communication"
        },
        {
          "time": "00:11:01",
          "topic": "Trace the table to the Worker backend"
        }
      ],
      "video_file": "07-Building-in-a-Mono-Repo-React-Tanstack-TRPC-on-Workers.mp4",
      "transcript_file": "07-Building-in-a-Mono-Repo-React-Tanstack-TRPC-on-Workers.en.vtt"
    },
    {
      "number": 8,
      "title": "Setting up D1 Database & Drizzle ORM",
      "section": "Building in a Mono Repo",
      "duration": 1069.3,
      "evidence": "transcript",
      "study_page": "/lessons/0009-cloudflare-08-setting-up-d1-database-drizzle-orm.html",
      "timestamps": [
        {
          "time": "00:03:01",
          "topic": "Why introduce Drizzle and shared queries"
        },
        {
          "time": "00:08:01",
          "topic": "Drizzle configuration and introspection"
        },
        {
          "time": "00:12:00",
          "topic": "Create the D1 tooling token"
        },
        {
          "time": "00:14:02",
          "topic": "Create application tables and indexes"
        },
        {
          "time": "00:17:00",
          "topic": "Pull the resulting schemas"
        }
      ],
      "video_file": "08-Building-in-a-Mono-Repo-Setting-up-D1-Database-Drizzle-ORM.mp4",
      "transcript_file": "08-Building-in-a-Mono-Repo-Setting-up-D1-Database-Drizzle-ORM.en.vtt"
    },
    {
      "number": 9,
      "title": "Creating SQL Queries in our Mono Repo",
      "section": "Building in a Mono Repo",
      "duration": 1343.2,
      "evidence": "transcript",
      "study_page": "/lessons/0010-cloudflare-09-creating-sql-queries-in-our-mono-repo.html",
      "timestamps": [
        {
          "time": "00:03:00",
          "topic": "Shared input schema and query creation"
        },
        {
          "time": "00:06:00",
          "topic": "Database initialization and accessor"
        },
        {
          "time": "00:14:01",
          "topic": "Diagnose database not initialized"
        },
        {
          "time": "00:18:01",
          "topic": "Remote database integration"
        },
        {
          "time": "00:21:00",
          "topic": "Return the actual generated link ID"
        }
      ],
      "video_file": "09-Building-in-a-Mono-Repo-Creating-SQL-Queries-in-our-Mono-Repo.mp4",
      "transcript_file": "09-Building-in-a-Mono-Repo-Creating-SQL-Queries-in-our-Mono-Repo.en.vtt"
    },
    {
      "number": 10,
      "title": "Create all TRPC CRUD operations",
      "section": "Building in a Mono Repo",
      "duration": 1494.3,
      "evidence": "transcript",
      "study_page": "/lessons/0011-cloudflare-10-create-all-trpc-crud-operations.html",
      "timestamps": [
        {
          "time": "00:02:00",
          "topic": "List query and time-based pagination"
        },
        {
          "time": "00:09:01",
          "topic": "Link-detail and update queries"
        },
        {
          "time": "00:11:00",
          "topic": "Destination shape and runtime parsing"
        },
        {
          "time": "00:21:01",
          "topic": "Persist and remove country overrides"
        },
        {
          "time": "00:23:01",
          "topic": "Invalidate queries after mutation success"
        }
      ],
      "video_file": "10-Building-in-a-Mono-Repo-Create-all-TRPC-CRUD-operations.mp4",
      "transcript_file": "10-Building-in-a-Mono-Repo-Create-all-TRPC-CRUD-operations.en.vtt"
    },
    {
      "number": 11,
      "title": "Deploy your changes!",
      "section": "Building in a Mono Repo",
      "duration": 267.7,
      "evidence": "transcript",
      "study_page": "/lessons/0012-cloudflare-11-deploy-your-changes.html",
      "timestamps": [
        {
          "time": "00:00:00",
          "topic": "Why deploy incrementally"
        },
        {
          "time": "00:01:00",
          "topic": "Run deploy and inspect its failure"
        },
        {
          "time": "00:02:00",
          "topic": "Fix the unused placeholder variable"
        },
        {
          "time": "00:03:00",
          "topic": "Verify the deployed database-backed UI"
        }
      ],
      "video_file": "11-Building-in-a-Mono-Repo-Deploy-your-changes.mp4",
      "transcript_file": "11-Building-in-a-Mono-Repo-Deploy-your-changes.en.vtt"
    },
    {
      "number": 12,
      "title": "Setting up the Data Service",
      "section": "Smart Routing Service",
      "duration": 439.9,
      "evidence": "transcript",
      "study_page": "/lessons/0013-cloudflare-12-setting-up-the-data-service.html",
      "timestamps": [
        {
          "time": "00:00:00",
          "topic": "Default and country-specific redirect requirements"
        },
        {
          "time": "00:02:00",
          "topic": "Move into the separate data service"
        },
        {
          "time": "00:04:01",
          "topic": "Worker triggers"
        },
        {
          "time": "00:05:01",
          "topic": "Constructor and local startup"
        },
        {
          "time": "00:06:01",
          "topic": "Deploy the minimal service"
        }
      ],
      "video_file": "12-Smart-Routing-Service-Setting-up-the-Data-Service.mp4",
      "transcript_file": "12-Smart-Routing-Service-Setting-up-the-Data-Service.en.vtt"
    },
    {
      "number": 13,
      "title": "Setting up Hono",
      "section": "Smart Routing Service",
      "duration": 505.8,
      "evidence": "transcript",
      "study_page": "/lessons/0014-cloudflare-13-setting-up-hono.html",
      "timestamps": [
        {
          "time": "00:00:00",
          "topic": "Why manual path conditions become cumbersome"
        },
        {
          "time": "00:03:00",
          "topic": "Create the typed Hono app and dynamic route"
        },
        {
          "time": "00:05:01",
          "topic": "Forward the Worker request and context"
        },
        {
          "time": "00:07:00",
          "topic": "Test matched and unmatched paths"
        }
      ],
      "video_file": "13-Smart-Routing-Service-Setting-up-Hono.mp4",
      "transcript_file": "13-Smart-Routing-Service-Setting-up-Hono.en.vtt"
    },
    {
      "number": 14,
      "title": "Cloudflare Worker location headers",
      "section": "Smart Routing Service",
      "duration": 349.9,
      "evidence": "transcript",
      "study_page": "/lessons/0015-cloudflare-14-cloudflare-worker-location-headers.html",
      "timestamps": [
        {
          "time": "00:01:02",
          "topic": "Access the original raw request"
        },
        {
          "time": "00:02:00",
          "topic": "Inspect the Cloudflare cf object"
        },
        {
          "time": "00:03:00",
          "topic": "Country and other location metadata"
        },
        {
          "time": "00:04:00",
          "topic": "Extract optional routing and coordinate values"
        }
      ],
      "video_file": "14-Smart-Routing-Service-Cloudflare-Worker-location-headers.mp4",
      "transcript_file": "14-Smart-Routing-Service-Cloudflare-Worker-location-headers.en.vtt"
    },
    {
      "number": 15,
      "title": "Connecting our database",
      "section": "Smart Routing Service",
      "duration": 487.8,
      "evidence": "transcript",
      "study_page": "/lessons/0016-cloudflare-15-connecting-our-database.html",
      "timestamps": [
        {
          "time": "00:01:01",
          "topic": "Extract the ID and reuse get-link"
        },
        {
          "time": "00:03:01",
          "topic": "Configure the data service D1 binding"
        },
        {
          "time": "00:05:02",
          "topic": "Missing await reveals the initialization error"
        },
        {
          "time": "00:06:00",
          "topic": "Initialize the database in the constructor"
        }
      ],
      "video_file": "15-Smart-Routing-Service-Connecting-our-database.mp4",
      "transcript_file": "15-Smart-Routing-Service-Connecting-our-database.en.vtt"
    },
    {
      "number": 16,
      "title": "Geo based smart routing",
      "section": "Smart Routing Service",
      "duration": 710.5,
      "evidence": "transcript",
      "study_page": "/lessons/0017-cloudflare-16-geo-based-smart-routing.html",
      "timestamps": [
        {
          "time": "00:00:00",
          "topic": "Missing-link handling"
        },
        {
          "time": "00:02:00",
          "topic": "Shared metadata schema and coordinate conversion"
        },
        {
          "time": "00:04:00",
          "topic": "Metadata parsing failure behavior"
        },
        {
          "time": "00:06:00",
          "topic": "Country override and default selection"
        },
        {
          "time": "00:10:01",
          "topic": "Malaysia VPN redirect experiment"
        }
      ],
      "video_file": "16-Smart-Routing-Service-Geo-based-smart-routing.mp4",
      "transcript_file": "16-Smart-Routing-Service-Geo-based-smart-routing.en.vtt"
    },
    {
      "number": 17,
      "title": "Speed things up with KV",
      "section": "Smart Routing Service",
      "duration": 726.3,
      "evidence": "transcript",
      "study_page": "/lessons/0018-cloudflare-17-speed-things-up-with-kv.html",
      "timestamps": [
        {
          "time": "00:00:00",
          "topic": "Database distance and eventual consistency"
        },
        {
          "time": "00:03:02",
          "topic": "Cache-first lookup order"
        },
        {
          "time": "00:05:00",
          "topic": "Validate cached JSON and coordinate fallback"
        },
        {
          "time": "00:08:01",
          "topic": "Set the 24-hour expiration"
        },
        {
          "time": "00:11:01",
          "topic": "Repeated-request observation and cache logging"
        }
      ],
      "video_file": "17-Smart-Routing-Service-Speed-things-up-with-KV.mp4",
      "transcript_file": "17-Smart-Routing-Service-Speed-things-up-with-KV.en.vtt"
    },
    {
      "number": 18,
      "title": "Extending our data services",
      "section": "Smart Routing Service",
      "duration": 187.7,
      "evidence": "transcript",
      "study_page": "/lessons/0019-cloudflare-18-extending-our-data-services.html",
      "timestamps": [
        {
          "time": "00:00:00",
          "topic": "Remaining dashboard and health-check features"
        },
        {
          "time": "00:01:00",
          "topic": "Separate redirecting from click processing"
        },
        {
          "time": "00:02:01",
          "topic": "Queue events feeding analytics, Durable Objects, and Workflows"
        }
      ],
      "video_file": "18-Smart-Routing-Service-Extending-our-data-services.mp4",
      "transcript_file": "18-Smart-Routing-Service-Extending-our-data-services.en.vtt"
    },
    {
      "number": 19,
      "title": "What are queues?",
      "section": "Working with Queues",
      "duration": 336,
      "evidence": "transcript",
      "study_page": "/lessons/0020-cloudflare-19-what-are-queues.html",
      "timestamps": [
        {
          "time": "00:01:21",
          "topic": "Queue as a background-processing middle layer"
        },
        {
          "time": "00:02:41",
          "topic": "Event-driven systems"
        },
        {
          "time": "00:03:00",
          "topic": "At-least-once delivery motivation"
        },
        {
          "time": "00:04:21",
          "topic": "Dead-letter queue"
        },
        {
          "time": "00:05:04",
          "topic": "Link-click pipeline and downstream features"
        }
      ],
      "video_file": "19-Working-with-Queues-What-are-queues.mp4",
      "transcript_file": "19-Working-with-Queues-What-are-queues.en.vtt"
    },
    {
      "number": 20,
      "title": "Workers Paid Tier + Pricing breakdown",
      "section": "Working with Queues",
      "duration": 284.6,
      "evidence": "transcript",
      "study_page": "/lessons/0021-cloudflare-20-workers-paid-tier-pricing-breakdown.html",
      "timestamps": [
        {
          "time": "00:00:01",
          "topic": "Recorded paid-tier requirement"
        },
        {
          "time": "00:00:41",
          "topic": "Account and team billing model"
        },
        {
          "time": "00:01:53",
          "topic": "Usage-based pricing"
        },
        {
          "time": "00:02:36",
          "topic": "Learning costs from observed usage"
        },
        {
          "time": "00:04:06",
          "topic": "Forecasting a known high-volume workload"
        }
      ],
      "video_file": "20-Working-with-Queues-Workers-Paid-Tier-Pricing-breakdown.mp4",
      "transcript_file": "20-Working-with-Queues-Workers-Paid-Tier-Pricing-breakdown.en.vtt"
    },
    {
      "number": 21,
      "title": "Creating & Using Queues",
      "section": "Working with Queues",
      "duration": 530.1,
      "evidence": "transcript",
      "study_page": "/lessons/0022-cloudflare-21-creating-using-queues.html",
      "timestamps": [
        {
          "time": "00:01:54",
          "topic": "Implementing the Worker queue handler"
        },
        {
          "time": "00:02:50",
          "topic": "Message metadata and acknowledgement"
        },
        {
          "time": "00:05:14",
          "topic": "One consumer Worker per queue"
        },
        {
          "time": "00:06:37",
          "topic": "Observing a batch containing several messages"
        },
        {
          "time": "00:07:04",
          "topic": "Pause and resume delivery"
        }
      ],
      "video_file": "21-Working-with-Queues-Creating-Using-Queues.mp4",
      "transcript_file": "21-Working-with-Queues-Creating-Using-Queues.en.vtt"
    },
    {
      "number": 22,
      "title": "Creating a Producer",
      "section": "Working with Queues",
      "duration": 556,
      "evidence": "transcript",
      "study_page": "/lessons/0023-cloudflare-22-creating-a-producer.html",
      "timestamps": [
        {
          "time": "00:00:38",
          "topic": "Capturing clicks without delaying redirects"
        },
        {
          "time": "00:02:03",
          "topic": "send and sendBatch"
        },
        {
          "time": "00:03:11",
          "topic": "Shared link-click message type"
        },
        {
          "time": "00:05:38",
          "topic": "Moving queue send into waitUntil"
        },
        {
          "time": "00:08:20",
          "topic": "Checking the received JSON event"
        }
      ],
      "video_file": "22-Working-with-Queues-Creating-a-Producer.mp4",
      "transcript_file": "22-Working-with-Queues-Creating-a-Producer.en.vtt"
    },
    {
      "number": 23,
      "title": "Building Type Safe Queue Handlers",
      "section": "Working with Queues",
      "duration": 660.4,
      "evidence": "transcript",
      "study_page": "/lessons/0024-cloudflare-23-building-type-safe-queue-handlers.html",
      "timestamps": [
        {
          "time": "00:00:23",
          "topic": "Runtime validation with Zod"
        },
        {
          "time": "00:01:42",
          "topic": "Discriminated union by event type"
        },
        {
          "time": "00:02:48",
          "topic": "safeParse on message.body"
        },
        {
          "time": "00:05:14",
          "topic": "Shared add-link-click database query"
        },
        {
          "time": "00:09:10",
          "topic": "Confirming a persisted click in D1"
        }
      ],
      "video_file": "23-Working-with-Queues-Building-Type-Safe-Queue-Handlers.mp4",
      "transcript_file": "23-Working-with-Queues-Building-Type-Safe-Queue-Handlers.en.vtt"
    },
    {
      "number": 24,
      "title": "Advanced Configuration: Delay, Retries & Dead Letter",
      "section": "Working with Queues",
      "duration": 784.3,
      "evidence": "transcript",
      "study_page": "/lessons/0025-cloudflare-24-advanced-configuration-delay-retries-dead-letter.html",
      "timestamps": [
        {
          "time": "00:01:23",
          "topic": "Local queue testing and separate-service caveats"
        },
        {
          "time": "00:04:31",
          "topic": "Producer delivery delay"
        },
        {
          "time": "00:06:23",
          "topic": "Retry settings"
        },
        {
          "time": "00:08:05",
          "topic": "Creating and pausing the dead-letter queue"
        },
        {
          "time": "00:11:58",
          "topic": "Recovering failed events after restoring the handler"
        }
      ],
      "video_file": "24-Working-with-Queues-Advanced-Configuration-Delay-Retries-Dead-Letter.mp4",
      "transcript_file": "24-Working-with-Queues-Advanced-Configuration-Delay-Retries-Dead-Letter.en.vtt"
    },
    {
      "number": 25,
      "title": "What are Cloudflare Workflows?",
      "section": "Building AI Workflows",
      "duration": 446.7,
      "evidence": "transcript",
      "study_page": "/lessons/0026-cloudflare-25-what-are-cloudflare-workflows.html",
      "timestamps": [
        {
          "time": "00:00:30",
          "topic": "Render, evaluate, and preserve destination evidence"
        },
        {
          "time": "00:02:08",
          "topic": "Persisted multi-step applications"
        },
        {
          "time": "00:03:09",
          "topic": "Workflow entrypoint and run method"
        },
        {
          "time": "00:04:06",
          "topic": "Per-step retries and backoff"
        },
        {
          "time": "00:05:09",
          "topic": "Recorded workflow concurrency constraints"
        }
      ],
      "video_file": "25-Building-AI-Workflows-What-are-Cloudflare-Workflows.mp4",
      "transcript_file": "25-Building-AI-Workflows-What-are-Cloudflare-Workflows.en.vtt"
    },
    {
      "number": 26,
      "title": "Build & Deploy a Workflow",
      "section": "Building AI Workflows",
      "duration": 554.9,
      "evidence": "transcript",
      "study_page": "/lessons/0027-cloudflare-26-build-deploy-a-workflow.html",
      "timestamps": [
        {
          "time": "00:01:20",
          "topic": "WorkflowEntrypoint subclass"
        },
        {
          "time": "00:02:37",
          "topic": "Defining a named step"
        },
        {
          "time": "00:05:05",
          "topic": "Export and Wrangler configuration"
        },
        {
          "time": "00:06:58",
          "topic": "Manual instance trigger"
        },
        {
          "time": "00:07:58",
          "topic": "Inspecting returned step output"
        }
      ],
      "video_file": "26-Building-AI-Workflows-Build-Deploy-a-Workflow.mp4",
      "transcript_file": "26-Building-AI-Workflows-Build-Deploy-a-Workflow.en.vtt"
    },
    {
      "number": 27,
      "title": "Rendering Webpages with Cloudflare Browser Render",
      "section": "Building AI Workflows",
      "duration": 890.23,
      "evidence": "transcript",
      "study_page": "/lessons/0028-cloudflare-27-rendering-webpages-with-cloudflare-browser-render.html",
      "timestamps": [
        {
          "time": "00:01:26",
          "topic": "Cloudflare Puppeteer integration"
        },
        {
          "time": "00:05:18",
          "topic": "Browser binding setup"
        },
        {
          "time": "00:07:51",
          "topic": "Typed workflow input"
        },
        {
          "time": "00:08:31",
          "topic": "Wait for network idle and extract content"
        },
        {
          "time": "00:13:55",
          "topic": "Closing the browser session"
        }
      ],
      "video_file": "27-Building-AI-Workflows-Rendering-Webpages-with-Cloudflare-Browser-Render.mp4",
      "transcript_file": "27-Building-AI-Workflows-Rendering-Webpages-with-Cloudflare-Browser-Render.en.vtt"
    },
    {
      "number": 28,
      "title": "Workers AI & AI SDK",
      "section": "Building AI Workflows",
      "duration": 406,
      "evidence": "transcript",
      "study_page": "/lessons/0029-cloudflare-28-workers-ai-ai-sdk.html",
      "timestamps": [
        {
          "time": "00:00:38",
          "topic": "Workers AI as the course model service"
        },
        {
          "time": "00:01:22",
          "topic": "Why structured responses help application code"
        },
        {
          "time": "00:02:28",
          "topic": "JSON response schema example"
        },
        {
          "time": "00:04:23",
          "topic": "AI SDK provider abstraction"
        },
        {
          "time": "00:05:44",
          "topic": "Workers AI binding and model invocation"
        }
      ],
      "video_file": "28-Building-AI-Workflows-Workers-AI-AI-SDK.mp4",
      "transcript_file": "28-Building-AI-Workflows-Workers-AI-AI-SDK.en.vtt"
    },
    {
      "number": 29,
      "title": "Create an AI Workflow Step",
      "section": "Building AI Workflows",
      "duration": 1311.7,
      "evidence": "transcript",
      "study_page": "/lessons/0030-cloudflare-29-create-an-ai-workflow-step.html",
      "timestamps": [
        {
          "time": "00:02:59",
          "topic": "Body text rather than full HTML as model input"
        },
        {
          "time": "00:04:27",
          "topic": "Zod output schema"
        },
        {
          "time": "00:10:24",
          "topic": "Workers AI binding"
        },
        {
          "time": "00:13:44",
          "topic": "AI step and retry budget"
        },
        {
          "time": "00:20:21",
          "topic": "Unknown result despite successful HTTP status"
        }
      ],
      "video_file": "29-Building-AI-Workflows-Create-an-AI-Workflow-Step.mp4",
      "transcript_file": "29-Building-AI-Workflows-Create-an-AI-Workflow-Step.en.vtt"
    },
    {
      "number": 30,
      "title": "Saving AI output in our DB",
      "section": "Building AI Workflows",
      "duration": 559.8,
      "evidence": "sampled-video-frames",
      "study_page": "/lessons/0031-cloudflare-30-saving-ai-output-in-our-db.html",
      "timestamps": [
        {
          "time": "00:01:45",
          "topic": "Shared insert helper generates an ID and writes evaluation fields"
        },
        {
          "time": "00:02:25",
          "topic": "Account-filtered history query and the createdBefore comparison"
        },
        {
          "time": "00:03:05",
          "topic": "Workflow database-save step passes model output and identifiers"
        },
        {
          "time": "00:07:05",
          "topic": "Database initialization added inside the workflow run method"
        },
        {
          "time": "00:09:05",
          "topic": "D1 Studio displays the persisted UNKNOWN_STATUS evaluation"
        }
      ],
      "video_file": "30-Building-AI-Workflows-Saving-AI-output-in-our-DB.mp4",
      "transcript_file": null
    },
    {
      "number": 31,
      "title": "Saving Web Page Data in R2",
      "section": "Building AI Workflows",
      "duration": 936,
      "evidence": "transcript",
      "study_page": "/lessons/0032-cloudflare-31-saving-web-page-data-in-r2.html",
      "timestamps": [
        {
          "time": "00:00:33",
          "topic": "Database records versus large objects"
        },
        {
          "time": "00:05:15",
          "topic": "Creating the staging R2 bucket"
        },
        {
          "time": "00:08:12",
          "topic": "Backup step and account-based paths"
        },
        {
          "time": "00:09:25",
          "topic": "Connecting D1 and R2 with an evaluation ID"
        },
        {
          "time": "00:12:24",
          "topic": "Inspecting stored text to debug missing content"
        }
      ],
      "video_file": "31-Building-AI-Workflows-Saving-Web-Page-Data-in-R2.mp4",
      "transcript_file": "31-Building-AI-Workflows-Saving-Web-Page-Data-in-R2.en.vtt"
    },
    {
      "number": 32,
      "title": "Add Evaluation Data in UI",
      "section": "Building AI Workflows",
      "duration": 345,
      "evidence": "transcript",
      "study_page": "/lessons/0033-cloudflare-32-add-evaluation-data-in-ui.html",
      "timestamps": [
        {
          "time": "00:00:29",
          "topic": "Shared evaluation queries"
        },
        {
          "time": "00:01:13",
          "topic": "Replacing tRPC dummy responses"
        },
        {
          "time": "00:02:35",
          "topic": "Cloudflare login caused remote-query trouble"
        },
        {
          "time": "00:03:33",
          "topic": "Account-ID mismatch explains an empty list"
        },
        {
          "time": "00:04:29",
          "topic": "Viewing real evaluation reasons and URLs"
        }
      ],
      "video_file": "32-Building-AI-Workflows-Add-Evaluation-Data-in-UI.mp4",
      "transcript_file": "32-Building-AI-Workflows-Add-Evaluation-Data-in-UI.en.vtt"
    },
    {
      "number": 33,
      "title": "Taking Screenshot in Browser Rendering",
      "section": "Building AI Workflows",
      "duration": 208.7,
      "evidence": "transcript",
      "study_page": "/lessons/0034-cloudflare-33-taking-screenshot-in-browser-rendering.html",
      "timestamps": [
        {
          "time": "00:00:18",
          "topic": "Using screenshots to diagnose missing content"
        },
        {
          "time": "00:00:47",
          "topic": "Capturing and encoding the screenshot"
        },
        {
          "time": "00:01:18",
          "topic": "Adding a screenshot R2 path"
        },
        {
          "time": "00:02:00",
          "topic": "Converting encoded image data for upload"
        },
        {
          "time": "00:02:25",
          "topic": "Inspecting the saved error-page screenshot"
        }
      ],
      "video_file": "33-Building-AI-Workflows-Taking-Screenshot-in-Browser-Rendering.mp4",
      "transcript_file": "33-Building-AI-Workflows-Taking-Screenshot-in-Browser-Rendering.en.vtt"
    },
    {
      "number": 34,
      "title": "Update: Fixing issues with 1MiB Output limit",
      "section": "Building AI Workflows",
      "duration": 171.7,
      "evidence": "transcript",
      "study_page": "/lessons/0035-cloudflare-34-update-fixing-issues-with-1mib-output-limit.html",
      "timestamps": [
        {
          "time": "00:00:04",
          "topic": "Student-reported maximum-output-size error"
        },
        {
          "time": "00:00:36",
          "topic": "Step result persisted as workflow state"
        },
        {
          "time": "00:01:09",
          "topic": "Moving R2 writes into the collection step"
        },
        {
          "time": "00:01:38",
          "topic": "Returning only text and evaluation identity"
        },
        {
          "time": "00:02:25",
          "topic": "Database query now accepts the existing ID"
        }
      ],
      "video_file": "34-Building-AI-Workflows-Update-Fixing-issues-with-1MiB-Output-limit.mp4",
      "transcript_file": "34-Building-AI-Workflows-Update-Fixing-issues-with-1MiB-Output-limit.en.vtt"
    },
    {
      "number": 35,
      "title": "System design & How we can use Durable Objects",
      "section": "Managing Workflows with Durable Objects",
      "duration": 477.53,
      "evidence": "transcript",
      "study_page": "/lessons/0036-cloudflare-35-system-design-how-we-can-use-durable-objects.html",
      "timestamps": [
        {
          "time": "00:01:14",
          "topic": "Reviewing the application and redirect data flow"
        },
        {
          "time": "00:02:30",
          "topic": "Replacing manual workflow triggers"
        },
        {
          "time": "00:03:07",
          "topic": "Avoiding an evaluation on every click"
        },
        {
          "time": "00:05:22",
          "topic": "State plus alarms as the required capabilities"
        },
        {
          "time": "00:06:13",
          "topic": "Independent object instances and scheduling state"
        }
      ],
      "video_file": "35-Managing-Workflows-with-Durable-Objects-System-design-How-we-can-use-Durable-Objects.mp4",
      "transcript_file": "35-Managing-Workflows-with-Durable-Objects-System-design-How-we-can-use-Durable-Objects.en.vtt"
    },
    {
      "number": 36,
      "title": "Durable Object API",
      "section": "Managing Workflows with Durable Objects",
      "duration": 312.4,
      "evidence": "transcript",
      "study_page": "/lessons/0037-cloudflare-36-durable-object-api.html",
      "timestamps": [
        {
          "time": "00:00:35",
          "topic": "Durable Object class and APIs"
        },
        {
          "time": "00:00:59",
          "topic": "Memory versus persistent state"
        },
        {
          "time": "00:02:15",
          "topic": "Key-value and SQLite storage approaches"
        },
        {
          "time": "00:03:06",
          "topic": "Persistent counter example"
        },
        {
          "time": "00:04:10",
          "topic": "Alarm scheduling with timestamps"
        }
      ],
      "video_file": "36-Managing-Workflows-with-Durable-Objects-Durable-Object-API.mp4",
      "transcript_file": "36-Managing-Workflows-with-Durable-Objects-Durable-Object-API.en.vtt"
    },
    {
      "number": 37,
      "title": "Building the Base Class",
      "section": "Managing Workflows with Durable Objects",
      "duration": 824.5,
      "evidence": "transcript",
      "study_page": "/lessons/0038-cloudflare-37-building-the-base-class.html",
      "timestamps": [
        {
          "time": "00:01:49",
          "topic": "Restoring persisted count safely"
        },
        {
          "time": "00:03:02",
          "topic": "Increment and storage write"
        },
        {
          "time": "00:05:20",
          "topic": "Initial Durable Object migration"
        },
        {
          "time": "00:08:26",
          "topic": "Application identity with idFromName"
        },
        {
          "time": "00:11:19",
          "topic": "Testing independent named counters"
        }
      ],
      "video_file": "37-Managing-Workflows-with-Durable-Objects-Building-the-Base-Class.mp4",
      "transcript_file": "37-Managing-Workflows-with-Durable-Objects-Building-the-Base-Class.en.vtt"
    },
    {
      "number": 38,
      "title": "Full Link Click Integration with Alarms",
      "section": "Managing Workflows with Durable Objects",
      "duration": 1405.1,
      "evidence": "transcript",
      "study_page": "/lessons/0039-cloudflare-38-full-link-click-integration-with-alarms.html",
      "timestamps": [
        {
          "time": "00:02:02",
          "topic": "Persistent click context replaces the counter"
        },
        {
          "time": "00:04:52",
          "topic": "Schedule only when no alarm exists"
        },
        {
          "time": "00:08:46",
          "topic": "Refining workflow input types"
        },
        {
          "time": "00:13:40",
          "topic": "Identity combines link and destination"
        },
        {
          "time": "00:19:49",
          "topic": "Changing the delay to twenty-four hours"
        }
      ],
      "video_file": "38-Managing-Workflows-with-Durable-Objects-Full-Link-Click-Integration-with-Alarms.mp4",
      "transcript_file": "38-Managing-Workflows-with-Durable-Objects-Full-Link-Click-Integration-with-Alarms.en.vtt"
    },
    {
      "number": 39,
      "title": "Overview of realtime link tracking",
      "section": "Advanced Durable Objects",
      "duration": 381.2,
      "evidence": "transcript",
      "study_page": "/lessons/0040-cloudflare-39-overview-of-realtime-link-tracking.html",
      "timestamps": [
        {
          "time": "00:00:05",
          "topic": "SQL and WebSocket learning goals"
        },
        {
          "time": "00:02:09",
          "topic": "Account-scoped tracker design"
        },
        {
          "time": "00:03:43",
          "topic": "Proposed startup history delivery"
        },
        {
          "time": "00:05:19",
          "topic": "Offsets and retained state"
        }
      ],
      "video_file": "39-Advanced-Durable-Objects-Overview-of-realtime-link-tracking.mp4",
      "transcript_file": "39-Advanced-Durable-Objects-Overview-of-realtime-link-tracking.en.vtt"
    },
    {
      "number": 40,
      "title": "Build a SQL backed Durable Object",
      "section": "Advanced Durable Objects",
      "duration": 996.2,
      "evidence": "transcript",
      "study_page": "/lessons/0041-cloudflare-40-build-a-sql-backed-durable-object.html",
      "timestamps": [
        {
          "time": "00:02:17",
          "topic": "Create the geographic click table"
        },
        {
          "time": "00:04:29",
          "topic": "Parameterized SQL bindings"
        },
        {
          "time": "00:10:22",
          "topic": "Queue-first background capture"
        },
        {
          "time": "00:13:21",
          "topic": "Correct SQLite class migration"
        }
      ],
      "video_file": "40-Advanced-Durable-Objects-Build-a-SQL-backed-Durable-Object.mp4",
      "transcript_file": "40-Advanced-Durable-Objects-Build-a-SQL-backed-Durable-Object.en.vtt"
    },
    {
      "number": 41,
      "title": "Setting up Websockets",
      "section": "Advanced Durable Objects",
      "duration": 529.6,
      "evidence": "transcript",
      "study_page": "/lessons/0042-cloudflare-41-setting-up-websockets.html",
      "timestamps": [
        {
          "time": "00:00:45",
          "topic": "Durable Object WebSocket handlers"
        },
        {
          "time": "00:03:22",
          "topic": "Enumerating connected sockets"
        },
        {
          "time": "00:05:12",
          "topic": "Creating a WebSocket pair"
        },
        {
          "time": "00:08:07",
          "topic": "Selecting an account through headers"
        }
      ],
      "video_file": "41-Advanced-Durable-Objects-Setting-up-Websockets.mp4",
      "transcript_file": "41-Advanced-Durable-Objects-Setting-up-Websockets.en.vtt"
    },
    {
      "number": 42,
      "title": "Connecting from the Client (Localhost)",
      "section": "Advanced Durable Objects",
      "duration": 547.2,
      "evidence": "transcript",
      "study_page": "/lessons/0043-cloudflare-42-connecting-from-the-client-localhost.html",
      "timestamps": [
        {
          "time": "00:02:04",
          "topic": "Client hook and socket lifecycle"
        },
        {
          "time": "00:04:06",
          "topic": "Missing account header causes failure"
        },
        {
          "time": "00:05:08",
          "topic": "Observing socket close events"
        },
        {
          "time": "00:06:41",
          "topic": "Planned authentication and proxy boundary"
        }
      ],
      "video_file": "42-Advanced-Durable-Objects-Connecting-from-the-Client-Localhost.mp4",
      "transcript_file": "42-Advanced-Durable-Objects-Connecting-from-the-Client-Localhost.en.vtt"
    },
    {
      "number": 43,
      "title": "Realtime link clicks with Websockets",
      "section": "Advanced Durable Objects",
      "duration": 1052.4,
      "evidence": "transcript",
      "study_page": "/lessons/0044-cloudflare-43-realtime-link-clicks-with-websockets.html",
      "timestamps": [
        {
          "time": "00:01:31",
          "topic": "Two-second batching alarm"
        },
        {
          "time": "00:05:03",
          "topic": "Persistent offset initialization"
        },
        {
          "time": "00:06:35",
          "topic": "Recent-click query helper"
        },
        {
          "time": "00:10:39",
          "topic": "Removing older retained clicks"
        },
        {
          "time": "00:14:47",
          "topic": "Malaysia redirect and map verification"
        }
      ],
      "video_file": "43-Advanced-Durable-Objects-Realtime-link-clicks-with-Websockets.mp4",
      "transcript_file": "43-Advanced-Durable-Objects-Realtime-link-clicks-with-Websockets.en.vtt"
    },
    {
      "number": 44,
      "title": "Service to Service Bindings",
      "section": "Advanced Durable Objects",
      "duration": 649.4,
      "evidence": "transcript",
      "study_page": "/lessons/0045-cloudflare-44-service-to-service-bindings.html",
      "timestamps": [
        {
          "time": "00:02:02",
          "topic": "Declaring a service binding"
        },
        {
          "time": "00:05:03",
          "topic": "Hono socket proxy and account header"
        },
        {
          "time": "00:07:05",
          "topic": "Local proxy connection test"
        },
        {
          "time": "00:09:38",
          "topic": "Broadcast verification across two tabs"
        }
      ],
      "video_file": "44-Advanced-Durable-Objects-Service-to-Service-Bindings.mp4",
      "transcript_file": "44-Advanced-Durable-Objects-Service-to-Service-Bindings.en.vtt"
    },
    {
      "number": 45,
      "title": "Create a specific stage deployment",
      "section": "Dev Ops & Environments",
      "duration": 643.9,
      "evidence": "transcript",
      "study_page": "/lessons/0046-cloudflare-45-create-a-specific-stage-deployment.html",
      "timestamps": [
        {
          "time": "00:02:37",
          "topic": "Shared versus environment-specific settings"
        },
        {
          "time": "00:04:09",
          "topic": "Deployment and type-generation environment selection"
        },
        {
          "time": "00:05:42",
          "topic": "Queue consumer conflict"
        },
        {
          "time": "00:08:47",
          "topic": "Vite environment selection"
        }
      ],
      "video_file": "45-Dev-Ops-Environments-Create-a-specific-stage-deployment.mp4",
      "transcript_file": "45-Dev-Ops-Environments-Create-a-specific-stage-deployment.en.vtt"
    },
    {
      "number": 46,
      "title": "Creating a Production Instance",
      "section": "Dev Ops & Environments",
      "duration": 526.7,
      "evidence": "transcript",
      "study_page": "/lessons/0047-cloudflare-46-creating-a-production-instance.html",
      "timestamps": [
        {
          "time": "00:00:32",
          "topic": "Create isolated production storage"
        },
        {
          "time": "00:02:03",
          "topic": "Production dead-letter configuration"
        },
        {
          "time": "00:04:35",
          "topic": "Production-specific Vite build"
        },
        {
          "time": "00:06:06",
          "topic": "Diagnose missing production tables"
        }
      ],
      "video_file": "46-Dev-Ops-Environments-Creating-a-Production-Instance.mp4",
      "transcript_file": "46-Dev-Ops-Environments-Creating-a-Production-Instance.en.vtt"
    },
    {
      "number": 47,
      "title": "Auto Deployments",
      "section": "Dev Ops & Environments",
      "duration": 949.63,
      "evidence": "transcript",
      "study_page": "/lessons/0048-cloudflare-47-auto-deployments.html",
      "timestamps": [
        {
          "time": "00:00:00",
          "topic": "Connect repository to Cloudflare builds"
        },
        {
          "time": "00:05:38",
          "topic": "Missing dependency installation"
        },
        {
          "time": "00:06:38",
          "topic": "Missing TypeScript build dependency"
        },
        {
          "time": "00:11:50",
          "topic": "Build variables versus runtime secrets"
        }
      ],
      "video_file": "47-Dev-Ops-Environments-Auto-Deployments.mp4",
      "transcript_file": "47-Dev-Ops-Environments-Auto-Deployments.en.vtt"
    },
    {
      "number": 48,
      "title": "Custom Domains & Routes",
      "section": "Dev Ops & Environments",
      "duration": 929.4,
      "evidence": "transcript",
      "study_page": "/lessons/0049-cloudflare-48-custom-domains-routes.html",
      "timestamps": [
        {
          "time": "00:02:32",
          "topic": "Custom domains versus routes"
        },
        {
          "time": "00:06:07",
          "topic": "Wrong environment selected during stage build"
        },
        {
          "time": "00:07:07",
          "topic": "Socket host broken after domain change"
        },
        {
          "time": "00:13:13",
          "topic": "Reserve the /r/ redirect prefix"
        }
      ],
      "video_file": "48-Dev-Ops-Environments-Custom-Domains-Routes.mp4",
      "transcript_file": "48-Dev-Ops-Environments-Custom-Domains-Routes.en.vtt"
    },
    {
      "number": 49,
      "title": "Auth Overview",
      "section": "Better Auth",
      "duration": 779.8,
      "evidence": "transcript",
      "study_page": "/lessons/0050-cloudflare-49-auth-overview.html",
      "timestamps": [
        {
          "time": "00:02:03",
          "topic": "Session creation after social login"
        },
        {
          "time": "00:04:06",
          "topic": "Reusable authentication middleware"
        },
        {
          "time": "00:05:36",
          "topic": "Why the course chooses Better Auth"
        },
        {
          "time": "00:11:16",
          "topic": "Schema generation and plugins"
        }
      ],
      "video_file": "49-Better-Auth-Auth-Overview.mp4",
      "transcript_file": "49-Better-Auth-Auth-Overview.en.vtt"
    },
    {
      "number": 50,
      "title": "Configuration & Schema Creation",
      "section": "Better Auth",
      "duration": 943.7,
      "evidence": "transcript",
      "study_page": "/lessons/0051-cloudflare-50-configuration-schema-creation.html",
      "timestamps": [
        {
          "time": "00:01:33",
          "topic": "Configuration-to-schema-to-SQL workflow"
        },
        {
          "time": "00:06:10",
          "topic": "Generation credentials versus runtime credentials"
        },
        {
          "time": "00:09:43",
          "topic": "Generated core authentication schemas"
        },
        {
          "time": "00:13:48",
          "topic": "Fix package output and exports"
        }
      ],
      "video_file": "50-Better-Auth-Configuration-Schema-Creation.mp4",
      "transcript_file": "50-Better-Auth-Configuration-Schema-Creation.en.vtt"
    },
    {
      "number": 51,
      "title": "Integration with web app",
      "section": "Better Auth",
      "duration": 1018,
      "evidence": "transcript",
      "study_page": "/lessons/0052-cloudflare-51-integration-with-web-app.html",
      "timestamps": [
        {
          "time": "00:01:01",
          "topic": "Mount Hono authentication handler"
        },
        {
          "time": "00:03:33",
          "topic": "Google callback URL configuration"
        },
        {
          "time": "00:06:39",
          "topic": "Worker-first routing for auth paths"
        },
        {
          "time": "00:12:47",
          "topic": "Dashboard before-load session guard"
        },
        {
          "time": "00:15:49",
          "topic": "Deployed runtime secrets"
        }
      ],
      "video_file": "51-Better-Auth-Integration-with-web-app.mp4",
      "transcript_file": "51-Better-Auth-Integration-with-web-app.en.vtt"
    },
    {
      "number": 52,
      "title": "Protecting our API Routes",
      "section": "Better Auth",
      "duration": 522,
      "evidence": "transcript",
      "study_page": "/lessons/0053-cloudflare-52-protecting-our-api-routes.html",
      "timestamps": [
        {
          "time": "00:00:00",
          "topic": "Identify unprotected tRPC and socket endpoints"
        },
        {
          "time": "00:03:02",
          "topic": "Apply middleware selectively"
        },
        {
          "time": "00:05:06",
          "topic": "Validate session from request headers"
        },
        {
          "time": "00:06:38",
          "topic": "User identity versus organization identity"
        }
      ],
      "video_file": "52-Better-Auth-Protecting-our-API-Routes.mp4",
      "transcript_file": "52-Better-Auth-Protecting-our-API-Routes.en.vtt"
    },
    {
      "number": 53,
      "title": "Better Auth Stripe Integration",
      "section": "Payments with Stripe",
      "duration": 957.4,
      "evidence": "transcript",
      "study_page": "/lessons/0054-cloudflare-53-better-auth-stripe-integration.html",
      "timestamps": [
        {
          "time": "00:01:31",
          "topic": "Customer mapping and plugin benefits"
        },
        {
          "time": "00:05:08",
          "topic": "Pin matching plugin versions"
        },
        {
          "time": "00:10:14",
          "topic": "Generate the Stripe customer-ID schema change"
        },
        {
          "time": "00:12:17",
          "topic": "Enable and generate subscriptions"
        }
      ],
      "video_file": "53-Payments-with-Stripe-Better-Auth-Stripe-Integration.mp4",
      "transcript_file": "53-Payments-with-Stripe-Better-Auth-Stripe-Integration.en.vtt"
    },
    {
      "number": 54,
      "title": "Creating Stripe Products",
      "section": "Payments with Stripe",
      "duration": 703.9,
      "evidence": "transcript",
      "study_page": "/lessons/0055-cloudflare-54-creating-stripe-products.html",
      "timestamps": [
        {
          "time": "00:02:04",
          "topic": "Node dependency compatibility error"
        },
        {
          "time": "00:03:37",
          "topic": "Simplify the Stripe helper interface"
        },
        {
          "time": "00:05:10",
          "topic": "Verify customer creation for a fresh user"
        },
        {
          "time": "00:07:11",
          "topic": "Create products and obtain price IDs"
        }
      ],
      "video_file": "54-Payments-with-Stripe-Creating-Stripe-Products.mp4",
      "transcript_file": "54-Payments-with-Stripe-Creating-Stripe-Products.en.vtt"
    },
    {
      "number": 55,
      "title": "Adding Subscriptions in the User Application",
      "section": "Payments with Stripe",
      "duration": 1236.1,
      "evidence": "transcript",
      "study_page": "/lessons/0056-cloudflare-55-adding-subscriptions-in-the-user-application.html",
      "timestamps": [
        {
          "time": "00:04:37",
          "topic": "Subscription upgrade API"
        },
        {
          "time": "00:06:09",
          "topic": "Register subscription schema in adapter"
        },
        {
          "time": "00:07:11",
          "topic": "Local webhook forwarding"
        },
        {
          "time": "00:12:20",
          "topic": "Enable customer-portal plan switching"
        },
        {
          "time": "00:17:24",
          "topic": "Cancellation and restoration"
        }
      ],
      "video_file": "55-Payments-with-Stripe-Adding-Subscriptions-in-the-User-Application.mp4",
      "transcript_file": "55-Payments-with-Stripe-Adding-Subscriptions-in-the-User-Application.en.vtt"
    },
    {
      "number": 56,
      "title": "Deploying & Live Webhooks",
      "section": "Payments with Stripe",
      "duration": 985.37,
      "evidence": "transcript",
      "study_page": "/lessons/0057-cloudflare-56-deploying-live-webhooks.html",
      "timestamps": [
        {
          "time": "00:00:00",
          "topic": "Separate public price IDs from secrets"
        },
        {
          "time": "00:02:31",
          "topic": "Configure deployed webhook endpoint"
        },
        {
          "time": "00:08:04",
          "topic": "Diagnose webhook URL typos"
        },
        {
          "time": "00:09:04",
          "topic": "Endpoint ID versus signing secret"
        },
        {
          "time": "00:12:06",
          "topic": "Fix missing Better Auth secret"
        }
      ],
      "video_file": "56-Payments-with-Stripe-Deploying-Live-Webhooks.mp4",
      "transcript_file": "56-Payments-with-Stripe-Deploying-Live-Webhooks.en.vtt"
    },
    {
      "number": 57,
      "title": "Completing the User Application Dashboard",
      "section": "Wrapping Up",
      "duration": 559,
      "evidence": "transcript",
      "study_page": "/lessons/0058-cloudflare-57-completing-the-user-application-dashboard.html",
      "timestamps": [
        {
          "time": "00:01:02",
          "topic": "Active links grouped by recent clicks"
        },
        {
          "time": "00:04:03",
          "topic": "Combined 24-hour and 48-hour aggregation"
        },
        {
          "time": "00:05:34",
          "topic": "Country breakdown query"
        },
        {
          "time": "00:07:05",
          "topic": "Fix the undefined short-link host"
        }
      ],
      "video_file": "57-Wrapping-Up-Completing-the-User-Application-Dashboard.mp4",
      "transcript_file": "57-Wrapping-Up-Completing-the-User-Application-Dashboard.en.vtt"
    },
    {
      "number": 58,
      "title": "Writing Tests",
      "section": "Wrapping Up",
      "duration": 891.8,
      "evidence": "transcript",
      "study_page": "/lessons/0059-cloudflare-58-writing-tests.html",
      "timestamps": [
        {
          "time": "00:02:32",
          "topic": "Mock queue-handler dependencies"
        },
        {
          "time": "00:04:02",
          "topic": "Assert persistence before workflow scheduling"
        },
        {
          "time": "00:05:04",
          "topic": "Test database failure behavior"
        },
        {
          "time": "00:09:40",
          "topic": "Alarm creation and existing-alarm branches"
        },
        {
          "time": "00:12:13",
          "topic": "Broadcast and cleanup assertions"
        }
      ],
      "video_file": "58-Wrapping-Up-Writing-Tests.mp4",
      "transcript_file": "58-Wrapping-Up-Writing-Tests.en.vtt"
    },
    {
      "number": 59,
      "title": "Stretch Goals",
      "section": "Wrapping Up",
      "duration": 626.6,
      "evidence": "transcript",
      "study_page": "/lessons/0060-cloudflare-59-stretch-goals.html",
      "timestamps": [
        {
          "time": "00:01:01",
          "topic": "Paid-plan entitlements remain unimplemented"
        },
        {
          "time": "00:02:34",
          "topic": "Custom slug reservation and uniqueness"
        },
        {
          "time": "00:04:07",
          "topic": "Deletion and existing public links"
        },
        {
          "time": "00:06:40",
          "topic": "Traffic-triggered evaluation scheduling"
        },
        {
          "time": "00:08:10",
          "topic": "Administrative evaluation and AI quality"
        }
      ],
      "video_file": "59-Wrapping-Up-Stretch-Goals.mp4",
      "transcript_file": "59-Wrapping-Up-Stretch-Goals.en.vtt"
    }
  ],
  "hosting_note": "Original video files are not hosted. Use the lesson number, filename, and timestamp with your own course copy."
}
