Skip to main content
RapidDev - Software Development Agency
bolt-ai-integrationsBolt Chat + API Route

How to Integrate Bolt.new with LearnWorlds

LearnWorlds has a REST API for courses, users, enrollments, and progress tracking. Get API credentials from your LearnWorlds school admin panel (Settings → API), then build Next.js API routes in Bolt that proxy LearnWorlds API calls with your API key header. Create a custom learning portal frontend displaying courses, enrollment status, and progress — or use the API to sync enrollments with your own payment and authentication system.

What you'll learn

  • How to get LearnWorlds API credentials and authenticate with the Lw-Client header
  • How to fetch courses, users, enrollment status, and progress data from the LearnWorlds REST API
  • How to build a custom learning portal frontend in React backed by LearnWorlds course content
  • How to enroll users in courses via the API and track their progress and completions
  • How to handle LearnWorlds webhook events for enrollment and completion notifications after deployment
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate19 min read35 minutesOtherApril 2026RapidDev Engineering Team
TL;DR

LearnWorlds has a REST API for courses, users, enrollments, and progress tracking. Get API credentials from your LearnWorlds school admin panel (Settings → API), then build Next.js API routes in Bolt that proxy LearnWorlds API calls with your API key header. Create a custom learning portal frontend displaying courses, enrollment status, and progress — or use the API to sync enrollments with your own payment and authentication system.

Building a Custom Learning Portal with LearnWorlds and Bolt.new

LearnWorlds positions itself as the most feature-rich white-label course platform, with interactive video (pause-and-answer, hotspots), SCORM/xAPI content support, built-in certificates, and a comprehensive REST API. The platform is used by corporate trainers, online educators, and academic institutions who need more control than Teachable or Thinkific offer.

The LearnWorlds REST API covers the key operations for building a custom learning experience: listing courses and their structure, querying user enrollments, reading progress and quiz results, creating or updating user accounts, enrolling users in courses programmatically, and retrieving certificates. The API is authenticated with a simple API key (the Lw-Client header) — no OAuth flow required. This makes it one of the simpler integrations in the e-learning space.

The primary use case for a Bolt integration is building a custom learning portal on top of a LearnWorlds school. By default, students access courses through LearnWorlds's hosted school interface (your-school.learnworlds.com). With the API, you can build a branded React frontend at your own domain that shows course listings, student progress dashboards, and completion certificates — with LearnWorlds handling all the video hosting, SCORM execution, and assessment logic behind the scenes. When a student clicks to take a course, you redirect them to the LearnWorlds player URL with single sign-on so they are automatically logged in.

A secondary use case is automation: enroll users when they purchase through your own payment system (Stripe), sync user data from your CRM, send custom email notifications on completion, or build custom reporting dashboards for organizations tracking employee training completion. The LearnWorlds API's enrollment and progress endpoints make these automation scenarios straightforward.

Integration method

Bolt Chat + API Route

LearnWorlds API uses API key authentication (sent as the Lw-Client header). Bolt generates the React frontend and Next.js API routes that proxy all LearnWorlds API calls — keeping the API key server-side. Course browsing, enrollment queries, and progress tracking work in the Bolt WebContainer preview via outbound HTTPS calls. LearnWorlds webhook events (enrollment completions, course completions, certificate issuance) require a deployed URL and cannot be received in the WebContainer.

Prerequisites

  • A LearnWorlds school account with at least the Starter plan (API access is available on all paid LearnWorlds plans)
  • LearnWorlds API key from your school admin panel: Settings → API → Generate API Key
  • Your LearnWorlds school URL (e.g., https://your-school.learnworlds.com)
  • At least one published course in your LearnWorlds school to test the integration
  • A Bolt.new project using Next.js for server-side API routes

Step-by-step guide

1

Get LearnWorlds API Credentials

LearnWorlds API authentication is straightforward: every API request includes your API key as the Lw-Client header. There is no OAuth flow, token expiry, or refresh logic to manage. The API key is a static credential tied to your LearnWorlds school account. To get your API key: log into your LearnWorlds school admin panel at your-school.learnworlds.com/admin. Navigate to Settings (gear icon) → API section. Click 'Generate API Key' or copy the existing key. The API key is shown once — if you lose it, generate a new one. Store it as LEARNWORLDS_API_KEY in your Bolt .env file. Also note your school URL: this is the base domain for your API calls. LearnWorlds API endpoints follow the pattern: https://your-school.learnworlds.com/api/v2/courses. Store your school domain as LEARNWORLDS_SCHOOL_URL=https://your-school.learnworlds.com in .env. API rate limits: LearnWorlds limits API calls to prevent abuse. The exact limits depend on your plan, but typical limits are in the hundreds to thousands of requests per hour. For a public-facing portal with many students, implement caching in your API routes to reduce raw API calls. During development in Bolt's WebContainer, all outbound HTTPS calls to your LearnWorlds school domain work correctly. The LearnWorlds API is a standard HTTPS REST API — no TCP sockets, no native libraries, no special network requirements. You can test the full integration including course listing, enrollment creation, and progress retrieval in the Bolt preview without deploying.

Bolt.new Prompt

Create a .env file with LEARNWORLDS_SCHOOL_URL=https://your-school.learnworlds.com and LEARNWORLDS_API_KEY=your_api_key_here as placeholders. Create lib/learnworlds.ts with a learnworldsRequest helper function that adds Lw-Client: {LEARNWORLDS_API_KEY} and Content-Type: application/json headers to all requests, targeting LEARNWORLDS_SCHOOL_URL/api/v2/. Export TypeScript interfaces for Course, User, Enrollment, and Progress objects, and helper functions: getCourses(), getCourse(id), getUser(email), createUser(name, email, password), enrollUser(userId, courseId), getUserEnrollments(userId), getCourseProgress(userId, courseId).

Paste this in Bolt.new chat

lib/learnworlds.ts
1// lib/learnworlds.ts
2const BASE = `${process.env.LEARNWORLDS_SCHOOL_URL}/api/v2`;
3const API_KEY = process.env.LEARNWORLDS_API_KEY!;
4
5export interface LWCourse {
6 id: string;
7 title: string;
8 description: string;
9 image: string;
10 price: number;
11 duration: string;
12 category: string;
13 instructor: { name: string; avatar: string };
14 enrolled_count: number;
15 published: boolean;
16}
17
18export interface LWUser {
19 id: string;
20 email: string;
21 username: string;
22 created_at: string;
23}
24
25export interface LWEnrollment {
26 course_id: string;
27 course_title: string;
28 enrolled_at: string;
29 completed_at: string | null;
30 progress_percent: number;
31 certificate_url: string | null;
32}
33
34async function learnworldsRequest<T>(
35 endpoint: string,
36 options: RequestInit = {}
37): Promise<T> {
38 const res = await fetch(`${BASE}${endpoint}`, {
39 ...options,
40 headers: {
41 'Lw-Client': API_KEY,
42 'Content-Type': 'application/json',
43 Accept: 'application/json',
44 ...options.headers,
45 },
46 });
47 if (!res.ok) {
48 const err = await res.text();
49 throw new Error(`LearnWorlds API error (${res.status}): ${err}`);
50 }
51 return res.json();
52}
53
54export const lw = {
55 getCourses: (params: Record<string, string> = {}) => {
56 const qs = new URLSearchParams(params).toString();
57 return learnworldsRequest<{ data: LWCourse[]; meta: { total: number } }>(
58 `/courses${qs ? '?' + qs : ''}`
59 );
60 },
61 getCourse: (id: string) =>
62 learnworldsRequest<LWCourse>(`/courses/${id}`),
63 getUser: (email: string) =>
64 learnworldsRequest<{ data: LWUser[] }>(`/users?email=${encodeURIComponent(email)}`),
65 createUser: (username: string, email: string, password: string) =>
66 learnworldsRequest<LWUser>('/users', {
67 method: 'POST',
68 body: JSON.stringify({ username, email, password }),
69 }),
70 enrollUser: (userId: string, courseId: string) =>
71 learnworldsRequest(`/users/${userId}/enrollments`, {
72 method: 'POST',
73 body: JSON.stringify({ course_id: courseId }),
74 }),
75 getUserEnrollments: (userId: string) =>
76 learnworldsRequest<{ data: LWEnrollment[] }>(`/users/${userId}/enrollments`),
77};

Pro tip: The LearnWorlds API key (Lw-Client header) gives full admin access to your school — it can read and write all user data, enrollment records, and course content. Never add NEXT_PUBLIC_ prefix to LEARNWORLDS_API_KEY. It must be server-side only.

Expected result: The LearnWorlds API client is configured with your school URL and API key. Calling lw.getCourses() returns your school's published courses. The Bolt preview can display live LearnWorlds data through your API routes.

2

Build Course and Enrollment API Routes

Create Next.js API routes that expose LearnWorlds data to your React frontend. The course routes handle listing published courses (with optional filtering by category or search term), fetching individual course details with curriculum overview, and checking whether a specific user is enrolled. The enrollment routes handle creating enrollments and retrieving progress data. The course listing route should return only the fields your frontend needs: id, title, description, image, price, duration, category, and enrolled_count. LearnWorlds's full course object contains much more data (full curriculum structure, quiz configurations, etc.) that is not needed for a catalog page. Filter the response to reduce bandwidth. For checking enrollment status: your frontend needs to know whether the logged-in user is already enrolled in each course. This determines whether to show a 'Enroll' button, a 'Continue Learning' button with a progress indicator, or a 'Completed' badge. Query the user's enrollments list and create a Set of enrolled course IDs for O(1) lookup when rendering the course grid. For the progress data: LearnWorlds tracks progress as a percentage (0-100) and records the most recently accessed unit/lesson. The progress endpoint returns this data along with quiz scores and completion timestamps for completed courses. Display progress as a progress bar on the course card for enrolled students. The user lookup flow: when a student logs in to your custom portal, look them up in LearnWorlds by email to get their LearnWorlds user ID. Store the LearnWorlds user ID in your session after login so subsequent API calls can use it directly without repeated email lookups.

Bolt.new Prompt

Create Next.js API routes for LearnWorlds. Build: GET /api/courses (list all published courses with optional category and search params — return lean course objects), GET /api/courses/[id] (course detail with curriculum preview), GET /api/courses/[id]/sso-url (generate SSO URL for a user to access this course), GET /api/users/[email]/enrollments (get all enrollments and progress for a user by email — first look up user ID then fetch enrollments), POST /api/enrollments (enroll a user in a course by userId and courseId). Each route imports from lib/learnworlds.ts. Handle 404 cases when course or user does not exist.

Paste this in Bolt.new chat

app/api/courses/route.ts
1// app/api/courses/route.ts
2import { NextResponse } from 'next/server';
3import { lw } from '@/lib/learnworlds';
4
5export async function GET(request: Request) {
6 try {
7 const { searchParams } = new URL(request.url);
8 const params: Record<string, string> = {};
9
10 const category = searchParams.get('category');
11 const search = searchParams.get('search');
12 const page = searchParams.get('page') ?? '1';
13 const perPage = searchParams.get('per_page') ?? '12';
14
15 if (category) params.category = category;
16 if (search) params.search = search;
17 params.page = page;
18 params.per_page = perPage;
19
20 const result = await lw.getCourses(params);
21
22 // Return lean course objects for catalog display
23 const courses = result.data
24 .filter((c) => c.published)
25 .map((c) => ({
26 id: c.id,
27 title: c.title,
28 description: c.description.substring(0, 200),
29 image: c.image,
30 price: c.price,
31 duration: c.duration,
32 category: c.category,
33 instructor: c.instructor?.name ?? 'Unknown',
34 enrolledCount: c.enrolled_count,
35 }));
36
37 return NextResponse.json({
38 courses,
39 total: result.meta?.total ?? courses.length,
40 });
41 } catch (error: unknown) {
42 const e = error as { message: string };
43 return NextResponse.json({ error: e.message }, { status: 500 });
44 }
45}

Pro tip: Cache the course list response for 5 minutes using Next.js fetch caching (add cache: 'force-cache', next: { revalidate: 300 } to the fetch options in lib/learnworlds.ts). Course catalogs change infrequently, and caching eliminates repeated LearnWorlds API calls for every page view.

Expected result: GET /api/courses returns your school's published courses as lean JSON objects. The course detail endpoint returns full curriculum information. User enrollment and progress data is accessible by email address.

3

Build the Custom Learning Portal UI

With the API routes returning LearnWorlds data, prompt Bolt to build the full learning portal interface. The portal has two distinct experiences: the course catalog for browsing and enrolling, and the student dashboard for tracking progress on enrolled courses. The course catalog page should show courses as cards with the course image, title, instructor name, duration, price, and a category badge. For enrolled students, replace the price with a progress indicator and 'Continue Learning' button. A search bar and category filter panel help students find relevant courses. The student dashboard is the heart of the learning portal: a personalized view showing all enrolled courses with progress percentages, recently accessed courses at the top, completed courses with certificate download links, and a learning streak or statistics widget. Fetch all of this from your /api/users/[email]/enrollments endpoint and render it in a clean dashboard layout. For the course detail page: show the full course description, curriculum outline (sections and lessons), instructor bio, and either an enrollment button (unenrolled) or a 'Start Learning' button that redirects to the LearnWorlds course player with SSO. The curriculum outline can come from the course's sections data — show lesson titles and types (video, text, quiz) without the actual content, which lives in the LearnWorlds player. SSO integration: LearnWorlds supports JWT-based single sign-on for seamless handoff from your custom portal to the LearnWorlds course player. Build a GET /api/courses/[id]/sso-url route that generates a signed JWT containing the user's email and name, then returns the LearnWorlds SSO URL. When a student clicks 'Start Learning', they are redirected through SSO and arrive at the course player already authenticated.

Bolt.new Prompt

Build the complete learning portal UI connected to my LearnWorlds API routes. Create: (1) A CourseCatalog page with search bar, category filter sidebar, and a grid of CourseCards. Each CourseCard shows image, title, instructor, duration, price, and progress bar (if enrolled). (2) A StudentDashboard page with a 'My Courses' section showing enrolled courses sorted by last accessed, completion percentage, and 'Continue Learning' buttons. (3) A CourseDetail page with description, curriculum outline (collapsed sections), instructor bio, and enrollment/start button. (4) A CompletedCourses section with certificate download links. Use SWR for data fetching. The student's email is stored in localStorage after login.

Paste this in Bolt.new chat

Pro tip: LearnWorlds course IDs (the `id` field) are used as the course identifier in the API. The `{{course.id}}` pattern from the search query suggests users are building templated emails or automation — make sure your API routes accept this ID format and document what it looks like (a string like 'course-abc123').

Expected result: The course catalog renders live LearnWorlds courses in the Bolt preview with category filters and search. The student dashboard shows enrolled courses with progress. Clicking 'Start Learning' redirects to the LearnWorlds course player.

4

Implement User Enrollment and SSO

Two key integration points make the custom portal functional: automated enrollment (programmatically adding users to courses) and Single Sign-On (automatically logging users into the LearnWorlds course player without re-entering credentials). For enrollment via API: POST to /api/v2/users/{userId}/enrollments with the course_id. The user must exist in LearnWorlds first — if they do not, create them with POST /api/v2/users. A typical flow for a custom payment system: customer pays via Stripe → your server creates a LearnWorlds user account with their email (if not existing) → enrolls them in the purchased course → sends them a welcome email with a link to your portal. All three steps use the LearnWorlds API and can be tested in the Bolt preview. For SSO: LearnWorlds supports JWT-based SSO via a signed token. Generate a JWT containing the user's LearnWorlds user ID or email, sign it with your LEARNWORLDS_SSO_SECRET, and append it to the SSO URL: https://your-school.learnworlds.com/sso/verify?jwt={token}&redirect_url=/course/{courseId}. The student is automatically logged in and taken to the course. SSO token generation: the JWT payload should include at minimum the user's email (sub field), issued at time (iat), and expiry (exp — set to 5 minutes from now for security). Use the jsonwebtoken package to sign the token with your SSO secret. The SSO secret is configured in LearnWorlds Admin → Settings → Integrations → SSO. Add it to your .env as LEARNWORLDS_SSO_SECRET. For the enrollment route, add protection: only allow enrollment API calls from authenticated users in your portal (check a session token or cookie). Otherwise, anyone could call /api/enrollments and give themselves free access to paid courses.

Bolt.new Prompt

Implement enrollment and SSO for the LearnWorlds portal. Create: (1) POST /api/enrollments route that accepts userId and courseId, creates the user in LearnWorlds if they don't exist (by email), then enrolls them — protect with an authentication check. (2) GET /api/courses/[id]/sso-url route that accepts a userId, generates a signed JWT using LEARNWORLDS_SSO_SECRET, and returns the SSO URL for that course. Install jsonwebtoken. (3) A useEnrollment hook in the React frontend that calls the enrollment API and redirects to the SSO URL. (4) Update the CourseDetail page to use the enrollment hook and SSO URL for the 'Start Learning' button.

Paste this in Bolt.new chat

app/api/courses/[id]/sso-url/route.ts
1// app/api/courses/[id]/sso-url/route.ts
2import { NextResponse } from 'next/server';
3import jwt from 'jsonwebtoken';
4import { lw } from '@/lib/learnworlds';
5
6export async function GET(
7 request: Request,
8 { params }: { params: { id: string } }
9) {
10 try {
11 const { searchParams } = new URL(request.url);
12 const email = searchParams.get('email');
13 if (!email) {
14 return NextResponse.json({ error: 'email is required' }, { status: 400 });
15 }
16
17 // Look up the user's LearnWorlds ID
18 const usersResult = await lw.getUser(email);
19 const user = usersResult.data[0];
20 if (!user) {
21 return NextResponse.json({ error: 'User not found in LearnWorlds' }, { status: 404 });
22 }
23
24 // Generate SSO JWT
25 const token = jwt.sign(
26 { sub: user.id, email: user.email },
27 process.env.LEARNWORLDS_SSO_SECRET!,
28 { expiresIn: '5m', algorithm: 'HS256' }
29 );
30
31 const schoolUrl = process.env.LEARNWORLDS_SCHOOL_URL;
32 const courseId = params.id;
33 const ssoUrl = `${schoolUrl}/sso/verify?jwt=${token}&redirect_url=/course/${courseId}`;
34
35 return NextResponse.json({ ssoUrl });
36 } catch (error: unknown) {
37 const e = error as { message: string };
38 return NextResponse.json({ error: e.message }, { status: 500 });
39 }
40}

Pro tip: SSO JWTs expire quickly (5 minutes) for security. Generate the SSO URL on the server only when the user clicks 'Start Learning' — do not pre-generate and cache SSO URLs. An expired or replayed JWT will cause the LearnWorlds SSO to fail with an authentication error.

Expected result: Users can be enrolled in courses via the API. The SSO URL generation creates valid JWTs that log users into the LearnWorlds player automatically. The 'Start Learning' button in the portal redirects to the correct course with automatic authentication.

5

Deploy and Configure LearnWorlds Webhooks

Deploy your Bolt app to Netlify for a stable URL, then configure LearnWorlds webhooks to receive real-time notifications when students complete courses, earn certificates, or update their progress. During Bolt WebContainer development, outbound calls to LearnWorlds work fine, but incoming webhook events from LearnWorlds cannot reach the browser-based runtime since it has no public URL. This is a fundamental constraint of the WebContainer architecture — deploy first, test webhooks on Netlify. Deploy via Bolt Settings → Applications → Netlify → Publish. In Netlify Dashboard → Site Settings → Environment Variables, add LEARNWORLDS_SCHOOL_URL, LEARNWORLDS_API_KEY, LEARNWORLDS_SSO_SECRET, and LEARNWORLDS_WEBHOOK_SECRET. Trigger a redeploy. To configure LearnWorlds webhooks: in your LearnWorlds admin panel, navigate to Settings → Integrations → Webhooks. Add a new webhook endpoint pointing to https://your-app.netlify.app/api/webhooks/learnworlds. Select the events you want to receive: user.enrolled, course.completed, certificate.issued, quiz.completed. LearnWorlds signs webhook payloads with an HMAC signature you can verify. Build the webhook handler at app/api/webhooks/learnworlds/route.ts. The course.completed event is the most valuable — it fires when a student completes all course requirements. Use it to trigger downstream actions: send a congratulations email, update your CRM, grant access to advanced content, or mark training as complete in your HR system. For common automation patterns: course.completed → send certificate reminder email; user.enrolled → send course kickoff email with tips; quiz.completed → track score in your analytics; certificate.issued → update employee training records in your HR system.

Bolt.new Prompt

Prepare the LearnWorlds integration for Netlify deployment. Create netlify.toml with build command 'npm run build', publish '.next', Node 20, @netlify/plugin-nextjs. Create POST /api/webhooks/learnworlds that verifies the HMAC signature from the Lw-Signature header using LEARNWORLDS_WEBHOOK_SECRET, then handles user.enrolled, course.completed, and certificate.issued events by logging relevant data. Create GET /api/health that tests LearnWorlds API connectivity.

Paste this in Bolt.new chat

netlify.toml
1# netlify.toml
2[build]
3 command = "npm run build"
4 publish = ".next"
5
6[build.environment]
7 NODE_VERSION = "20"
8
9[[plugins]]
10 package = "@netlify/plugin-nextjs"

Pro tip: LearnWorlds webhooks retry on failure. Your handler should be idempotent — processing the same course.completed event twice should not double-count the completion. Use the event's unique ID (or combination of user_id + course_id + event_type) to check if the event has already been processed before taking action.

Expected result: The learning portal is deployed to Netlify with all LearnWorlds credentials set. Webhooks fire when students complete courses and are handled by the Netlify function. SSO, enrollment, and progress tracking work in production.

Common use cases

Custom Branded Course Catalog Portal

Build a branded learning portal at your own domain that displays your LearnWorlds courses with custom design. Students browse courses, see their enrollment status and progress, and click through to the LearnWorlds course player with automatic login via SSO. The portal fetches course data from the LearnWorlds API and uses JWT-based SSO to seamlessly hand off authentication to the LearnWorlds player.

Bolt.new Prompt

Build a custom learning portal connected to LearnWorlds. Create a GET /api/courses route that fetches courses from the LearnWorlds API (LEARNWORLDS_SCHOOL_URL and LEARNWORLDS_API_KEY from env, Lw-Client header). Return course title, description, image, duration, price, and enrolled count. Create a GET /api/courses/[id]/progress route that fetches a specific user's progress. Build a course catalog page with search, category filter, and course cards showing progress bars for enrolled courses. Add a 'Start Course' button that redirects to the LearnWorlds SSO URL.

Copy this prompt to try it in Bolt.new

Automated Enrollment After Stripe Purchase

When a customer buys a course through your custom Stripe checkout (bypassing LearnWorlds's native checkout for a customized purchase experience), automatically enroll them in the LearnWorlds course via the API. Use a Stripe webhook to trigger enrollment: payment_intent.succeeded → look up the course → call LearnWorlds enrollment API → send the student a confirmation email with their course access link.

Bolt.new Prompt

Build Stripe-to-LearnWorlds enrollment automation. Create a POST /api/webhooks/stripe route that handles payment_intent.succeeded events. When payment succeeds: (1) Extract the course_id from the payment metadata, (2) Check if the user exists in LearnWorlds by email — create them if not via POST /r/users, (3) Enroll the user in the course via POST /r/users/{userId}/enrollments. Create a helper in lib/learnworlds.ts with getUser(email), createUser(name, email), and enrollUser(userId, courseId) functions. Read LEARNWORLDS_SCHOOL_URL and LEARNWORLDS_API_KEY from env.

Copy this prompt to try it in Bolt.new

Training Completion Dashboard for Organizations

Build an admin dashboard for corporate clients that tracks employee course enrollment and completion rates across a LearnWorlds school. HR managers can see which employees have completed required training, view quiz scores, and download completion certificates. The dashboard pulls data from LearnWorlds's enrollment and progress endpoints and presents it in sortable tables with export functionality.

Bolt.new Prompt

Build a training completion dashboard for corporate HR managers using the LearnWorlds API. Create API routes: GET /api/admin/users (list all LearnWorlds users with pagination), GET /api/admin/users/[id]/enrollments (user's courses with completion status and progress %), GET /api/admin/courses/[id]/completions (list all users who completed a course with completion date and certificate URL). Build a dashboard UI with: user table with search, a completion heatmap showing which employees have completed which required courses, and a 'Download Completions' CSV export button.

Copy this prompt to try it in Bolt.new

Troubleshooting

LearnWorlds API returns 401 Unauthorized with 'Invalid API key'

Cause: The LEARNWORLDS_API_KEY in .env does not match the current API key in your LearnWorlds admin panel, or the API key was regenerated after being copied.

Solution: Log into your LearnWorlds admin panel → Settings → API and verify the API key. If you previously generated a key and lost it, generate a new one — note that regenerating invalidates the old key. Update LEARNWORLDS_API_KEY in .env and restart the Bolt dev server to reload environment variables.

Course detail returns empty sections/curriculum even though the course has content in LearnWorlds

Cause: The course content structure (sections and lessons) is in a nested sections array on the course object. The lean course API response may not include curriculum data unless you request the detailed course endpoint.

Solution: Use the individual course detail endpoint (/api/v2/courses/{id}) rather than the courses list for curriculum data. The list endpoint returns summary information; the detail endpoint includes the full sections array with lesson titles and types.

typescript
1// Use detail endpoint for curriculum:
2const courseDetail = await lw.getCourse(courseId); // /api/v2/courses/{id}
3// courseDetail.sections contains the full curriculum structure
4// Do NOT expect sections in the list endpoint response

SSO redirect fails with 'Invalid or expired token'

Cause: The JWT has expired (SSO tokens are valid for only 5 minutes), the LEARNWORLDS_SSO_SECRET does not match what is configured in LearnWorlds Settings → Integrations → SSO, or the user's LearnWorlds ID in the JWT does not match an existing account.

Solution: Regenerate the SSO URL immediately before redirect (do not cache it). Verify the LEARNWORLDS_SSO_SECRET in .env matches exactly what is shown in LearnWorlds admin → Settings → Integrations → SSO. Confirm the user exists in LearnWorlds by checking the getUser response before generating the JWT.

Webhook events not received after deployment to Netlify

Cause: The webhook URL is not registered in LearnWorlds, the events are not selected in the webhook configuration, or the Netlify function is returning a non-2xx status on the first delivery causing LearnWorlds to mark the endpoint as failed.

Solution: In LearnWorlds admin → Settings → Integrations → Webhooks, verify your Netlify URL is registered and the desired events are checked. Check Netlify function logs for errors in the webhook handler. Send a test event from LearnWorlds webhook settings if available. Return 200 immediately even if downstream processing fails — move processing to a background queue if needed.

Best practices

  • Store LEARNWORLDS_API_KEY and LEARNWORLDS_SSO_SECRET as server-side only environment variables — the API key gives full admin access to your school
  • Cache course listing responses for 5-10 minutes since course catalogs change infrequently — this reduces LearnWorlds API load and improves frontend performance
  • Generate SSO URLs on-demand immediately before redirect, not in advance — SSO tokens expire in 5 minutes and cannot be reused
  • Look up the user's LearnWorlds ID by email once at login and store it in the session to avoid repeated lookups on every API call
  • During Bolt WebContainer development, outbound calls to the LearnWorlds API work fine — only incoming webhooks require a deployed URL
  • Handle the case where a user's email is not in LearnWorlds (no matching account) gracefully — create the account on first enrollment rather than showing an error
  • Make enrollment operations idempotent — calling enroll twice for the same user/course should not create duplicate enrollments or throw errors
  • Use LearnWorlds's course.completed webhook rather than polling progress endpoints — webhooks provide immediate notifications with less API overhead

Alternatives

Frequently asked questions

Does Bolt.new work with the LearnWorlds API during WebContainer development?

Yes, fully for outbound API calls. Bolt's WebContainer can make HTTPS requests to your LearnWorlds school domain, so course listing, user lookup, enrollment creation, and progress retrieval all work in the Bolt preview. The only capability requiring a deployed URL is receiving LearnWorlds webhook events — webhooks need a public HTTPS endpoint to POST to, which the WebContainer cannot provide. Build and test all API calls in Bolt preview; configure webhooks after deploying to Netlify.

What does {{course.id}} mean in LearnWorlds — how do I use it in API calls?

The {{course.id}} pattern is a template variable used in LearnWorlds's email templates and automation triggers. It represents the actual course ID string (something like 'course-abc123' or a numeric ID) for the course associated with an event. In API calls, you use the literal course ID value, not the template syntax. Fetch your course IDs from GET /api/v2/courses and use those values in enrollment, progress, and SSO URL endpoints.

Can I build a fully custom course player with LearnWorlds or do I have to use their player?

LearnWorlds's course player is their core product and is not replaceable via API. The API gives you access to course metadata, enrollment data, and progress information, but the actual video playback, SCORM execution, quiz rendering, and interactive video features are all handled within the LearnWorlds player hosted on their platform. The standard integration pattern is: custom portal (your Bolt app) for browsing, enrollment, and progress dashboards, with SSO redirects to the LearnWorlds player for actual learning.

How many courses can a LearnWorlds school have and what are the API rate limits?

LearnWorlds does not publicly publish a course count limit for most plans. API rate limits are plan-dependent and designed to prevent abuse rather than to restrict normal usage — a typical learning portal calling the API on page loads should stay well within limits. If you are building a high-traffic public catalog that serves thousands of page views per minute, implement response caching in your Next.js routes with a 5-10 minute revalidation window to avoid hitting limits.

Does LearnWorlds support SCORM content, and can I access SCORM results via the API?

Yes, LearnWorlds supports SCORM 1.2 and SCORM 2004 content upload and playback. SCORM completion status and score data is tracked by LearnWorlds and is accessible via the progress and enrollment endpoints in the API. The SCORM execution itself happens within the LearnWorlds player — you cannot embed or control SCORM content outside of their player via API. SCORM results (completion, score) are reflected in the progress percentage and completion status returned by the enrollment API.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.