# How to Integrate Retool with LearnWorlds

- Tool: Retool
- Difficulty: Beginner
- Time required: 20 minutes
- Last updated: April 2026

## TL;DR

Connect Retool to LearnWorlds by creating a REST API Resource using your LearnWorlds API key. Set the base URL to https://api.learnworlds.com and pass your API key in the Lw-Client and Authorization headers. Once configured, build queries to manage courses, track student progress, view sales data, and monitor completion rates from a centralized internal dashboard.

## Build a LearnWorlds Course Management Dashboard in Retool

LearnWorlds provides a capable native admin interface, but operations teams running large online academies often need a unified view that goes beyond what the platform natively exposes. You may want to cross-reference student enrollment data with CRM records, build custom completion rate dashboards with drill-down by cohort, or create automated workflows that trigger actions when a student completes a course — none of which are straightforward in LearnWorlds' native UI.

LearnWorlds offers a REST API that provides programmatic access to courses, users, enrollments, products, and progress data. Authentication uses an API key that you generate per school, combined with a school-identifier header (Lw-Client). The API follows standard REST conventions with JSON request and response bodies, cursor-based pagination, and resource-scoped endpoints.

Retool's REST API Resource handles LearnWorlds' header-based authentication cleanly. Configure the base URL and headers once, then build queries against any endpoint. Build dashboards where operations teams can search students, view course completion rates, track revenue per product, and manage enrollments without navigating LearnWorlds' admin UI — all from a single Retool panel.

## Before you start

- A LearnWorlds account with at least one published course and some enrolled students
- LearnWorlds API credentials (available on Professional plan or higher — go to School Settings → Integrations → API)
- Your LearnWorlds school subdomain (the part before .learnworlds.com in your admin URL)
- A Retool account with permission to create Resources
- Basic familiarity with Retool's query editor and Table component

## Step-by-step guide

### 1. Generate your LearnWorlds API key

Log into your LearnWorlds admin dashboard and navigate to School Settings (gear icon in the left sidebar) → Integrations → API. If the API section is not visible, your plan may not include API access — LearnWorlds API is available on the Professional plan and above. Contact LearnWorlds support to confirm.

In the API section, click Generate API Key. LearnWorlds will display the key once — copy it immediately and store it securely. If you lose the key, you will need to regenerate it, which invalidates the previous key.

Also note your school subdomain — it appears in your admin URL as https://YOUR_SUBDOMAIN.learnworlds.com. This subdomain is required in the Lw-Client header with every API request. For example, if your admin URL is https://myacademy.learnworlds.com, your school subdomain is myacademy.

The LearnWorlds API base URL is https://api.learnworlds.com — all endpoints are hosted here, not on your subdomain. The Lw-Client header tells the API which school account to authenticate against.

**Expected result:** You have your LearnWorlds API key and your school subdomain identifier ready to configure the Retool resource.

### 2. Create the LearnWorlds REST API Resource in Retool

In Retool, navigate to the Resources tab and click Add Resource. Select REST API from the resource type list.

Configure the following fields:

Name: LearnWorlds API

Base URL: https://api.learnworlds.com

Headers (add both of the following):
- Key: Lw-Client | Value: your-school-subdomain (e.g., myacademy — no https://, no .learnworlds.com)
- Key: Authorization | Value: Bearer YOUR_API_KEY
- Key: Content-Type | Value: application/json

No additional authentication configuration is needed beyond these headers — LearnWorlds uses header-based auth rather than the Basic Auth or OAuth options in Retool's resource settings.

If you prefer to store the API key in Retool Configuration Variables for added security, go to Settings → Configuration Variables, create LEARNWORLDS_API_KEY with your API key value marked as secret, then set the Authorization header value to: Bearer {{ retoolContext.configVars.LEARNWORLDS_API_KEY }}.

Click Create Resource. Test the connection by creating a new query with method GET and path /v2/users — if it returns a JSON object with a data array of users, the authentication is working correctly. If you receive a 401 Unauthorized, double-check that both the Lw-Client and Authorization headers are present and correctly formatted.

**Expected result:** The LearnWorlds REST API Resource is created and a test GET /v2/users returns a list of your school's users in JSON format.

### 3. Fetch courses and build the course list

Create a query named getCourses. Set the method to GET and the path to /v2/courses. LearnWorlds supports optional query parameters to filter and paginate the course list:

- page: {{ table_courses.pageIndex || 1 }} (1-indexed)
- items_per_page: 25
- sort: -created (newest first; use 'created' for oldest first)

The response includes a data array where each course object contains id, title, slug, description, status (draft, published), price, and aggregate stats like enrolled_students and completion_rate if your plan includes them.

Create a transformer to flatten the course data for Retool's Table component. The raw API response uses nested structures for pricing — extract the price values into flat fields for easy display and sorting.

Bind the transformed getCourses data to a Table component named table_courses. Configure column display names: use 'Course Title' for title, 'Status' for status, 'Price' for price. Add a status column tag to visually distinguish published from draft courses.

This course list serves as the primary navigation for the dashboard — selecting a course in the table should trigger the student enrollment and progress queries filtered to that course.

```
// Transformer for LearnWorlds courses
const courses = data.data || [];
return courses.map(course => ({
  id: course.id,
  title: course.title || '(Untitled)',
  slug: course.slug || '',
  status: course.status || 'draft',
  price: course.price != null ? `$${parseFloat(course.price).toFixed(2)}` : 'Free',
  enrolled: course.enrolled_students || 0,
  completion_rate: course.completion_rate != null
    ? `${(parseFloat(course.completion_rate) * 100).toFixed(1)}%`
    : 'N/A',
  created: course.created_at ? new Date(course.created_at * 1000).toLocaleDateString() : ''
}));
```

**Expected result:** The getCourses query populates a Table with all your LearnWorlds courses, showing title, status, price, and enrolled student count.

### 4. Fetch student enrollments and progress data

Create a query named getEnrollments. Set the method to GET and the path to /v2/users/{{ table_courses.selectedRow?.data?.id }}/enrollments — this fetches enrollments for the selected course. Alternatively, use /v2/courses/{{ table_courses.selectedRow?.data?.id }}/users to get users enrolled in the selected course.

For a user-centric view (searching by student), create a separate query getUser using GET /v2/users with a query parameter search: {{ textInput_search.value }}. This returns matching users. Then fetch a specific user's enrollments with GET /v2/users/{{ selected_user_id }}/enrollments.

Each enrollment object in the response includes user_id, course_id, status (enrolled, completed), progress (0-100 integer), enrolled_at, completed_at (null if not completed), and last_activity.

Create a transformer to reshape enrollment data, converting timestamps, computing human-readable progress labels, and flagging at-risk students (enrolled more than 14 days ago with less than 10% progress).

Bind the enrollment data to a second Table below the courses table. Add a color-coded progress column (green for >75%, yellow for 25-75%, red for <25%). Include a 'Days Since Activity' computed column using: Math.floor((Date.now() - (member.last_activity * 1000)) / (1000 * 60 * 60 * 24)).

```
// Transformer for student enrollments
const enrollments = data.data || [];
const now = Date.now();
return enrollments.map(e => ({
  user_id: e.user_id,
  email: e.email || '',
  username: e.username || '',
  status: e.status || 'enrolled',
  progress: e.progress || 0,
  progress_label: `${e.progress || 0}%`,
  enrolled_at: e.enrolled_at ? new Date(e.enrolled_at * 1000).toLocaleDateString() : '',
  completed_at: e.completed_at ? new Date(e.completed_at * 1000).toLocaleDateString() : '—',
  last_activity: e.last_activity ? new Date(e.last_activity * 1000).toLocaleDateString() : '—',
  days_since_activity: e.last_activity
    ? Math.floor((now - e.last_activity * 1000) / 86400000)
    : null,
  at_risk: e.progress < 10 && (now - e.enrolled_at * 1000) > 14 * 86400000
}));
```

**Expected result:** Selecting a course in the top table populates the enrollments table below with student progress data, days since last activity, and at-risk indicators.

### 5. Build enrollment actions and student management

With students displayed in the Table, add management actions using LearnWorlds' write endpoints. The key operations are:

Enroll a student: POST /v2/users/{user_id}/enrollments with a JSON body containing course_id. First, look up the user by email using GET /v2/users?search={email} to get the user_id, then enroll them.

Revoke enrollment: DELETE /v2/users/{user_id}/enrollments/{course_id} — this removes the student's access. Add a confirmation modal before executing this operation using Retool's built-in modal component.

Mark as complete (if your plan supports it): PATCH /v2/users/{user_id}/enrollments/{course_id} with body { "status": "completed", "progress": 100 }.

For bulk enrollment, create a JavaScript query that reads from a Table component where you've pasted student emails, then loops through calling the enroll endpoint for each email. Use Promise.all() for parallel enrollment of small batches (under 20), or sequential await calls for larger batches to respect rate limits.

Set all write queries to run on Manual trigger only — wire them to Button components in the Table's action column. Add an On Success event handler that triggers the getEnrollments query to refresh the table. Show a notification on success: 'Student enrolled successfully'.

For complex implementations combining LearnWorlds data with CRM or Stripe payment records, RapidDev's team can help architect the multi-resource query structure and data transformation pipeline.

```
// Enroll student request body
{
  "course_id": "{{ table_courses.selectedRow.data.id }}"
}
```

**Expected result:** The enrollment management panel supports adding and revoking student access directly from Retool, with each action refreshing the enrollments table and showing a success notification.

### 6. Add revenue and completion analytics charts

Build analytics visuals using Retool's Chart component (Plotly.js-powered). For revenue analytics, use GET /v2/orders with date range filters to fetch order history. Each order includes course_id, amount, status (completed, refunded), and created_at.

Create a getOrders query: GET /v2/orders with parameters date_from and date_to bound to a Retool DateRange component. Add status=completed to exclude refunded orders.

For the revenue chart, group orders by day using a transformer, then bind to a Bar chart component with X-axis = date and Y-axis = daily revenue.

For completion tracking, use the getCourses response which includes aggregate completion rates per course. Create a horizontal Bar chart showing completion rate for each course — courses with low completion rates (under 30%) are flagged as needing content review.

Add Stat components at the top of the dashboard:
- Total Revenue (sum of all completed orders in the period)
- Total Enrolled Students (sum of enrolled_students across all courses)
- Average Completion Rate (mean of completion_rate across all published courses)
- New Enrollments This Month (count of enrollments where enrolled_at > start of current month)

Bind each Stat's value field to a JavaScript expression operating on your query data using Retool's expression engine.

```
// Transformer for revenue chart data grouped by day
const orders = data.data || [];
const byDay = {};
orders.forEach(order => {
  const day = new Date(order.created_at * 1000).toISOString().slice(0, 10);
  if (!byDay[day]) byDay[day] = 0;
  byDay[day] += parseFloat(order.amount || 0);
});
const sortedDays = Object.keys(byDay).sort();
return {
  dates: sortedDays,
  revenue: sortedDays.map(d => byDay[d])
};
```

**Expected result:** The dashboard shows a revenue bar chart, a course completion rate comparison chart, and summary stat components giving an at-a-glance view of academy performance.

## Best practices

- Store your LearnWorlds API key in Retool Configuration Variables (Settings → Configuration Variables) marked as secret rather than inline in the resource header — this prevents the key from being visible to other Retool app builders.
- Always use the internal course id field (not the slug) in API endpoint paths — the slug is human-readable but may change if a course is renamed, while the id is stable.
- Add pagination to all list queries using the page and items_per_page parameters — LearnWorlds limits responses to 25 items by default, and schools with hundreds of courses or thousands of students will see incomplete data without pagination.
- Convert all LearnWorlds timestamps (Unix epoch seconds) in your transformer functions rather than in the Table column display — this keeps your transformer as the single source of truth for data formatting.
- Set write queries (enroll, revoke, delete) to Manual trigger and add confirmation modals for destructive operations — enrollment revocation immediately removes student access and cannot be undone from the API.
- Cache the getCourses query for at least 60 seconds since course lists change infrequently — this reduces API load and improves dashboard performance for teams refreshing frequently.
- Use the search query parameter on /v2/users for student lookups rather than fetching all users and filtering client-side — large schools may have thousands of users, and server-side filtering is significantly faster.

## Use cases

### Build a student progress and completion dashboard

Create a Retool panel showing student enrollment data across all courses, with completion percentages, last activity dates, and certificate issuance status. Operations teams can search by student email, filter by course, and identify at-risk students who have enrolled but not progressed.

Prompt example:

```
Build a Retool student progress dashboard with a search input for student email. Show a Table of enrolled courses with course name, enrollment date, progress percentage, last activity, and completion status. Add a stat component for overall completion rate. Include a filter for courses and a date range for enrollment date.
```

### Build a course revenue and sales tracking panel

Create a Retool dashboard that pulls LearnWorlds product and order data to show revenue per course, total enrollments, average order value, and sales trends. Finance and marketing teams can monitor course monetization performance without needing LearnWorlds admin access.

Prompt example:

```
Build a Retool course revenue dashboard showing all LearnWorlds products in a Table with total enrollments, revenue generated, and average price. Add a Chart showing enrollment count and revenue trend over the last 30 days. Include stat components for total revenue, total students, and best-selling course.
```

### Build a bulk enrollment management tool

Create a Retool panel for managing course enrollments at scale: enroll multiple students into a course from a CSV upload, revoke access for inactive students, and send completion certificates. This is significantly faster than performing these operations one-by-one in LearnWorlds' native UI.

Prompt example:

```
Build a Retool enrollment management tool with a course selector dropdown. Show currently enrolled students in a Table with enrollment date and progress. Add a form to enroll a new student by email. Add a Revoke Access button for selected rows with a confirmation modal. Include a counter showing available seats if the course has a limit.
```

## Troubleshooting

### 401 Unauthorized on every API request despite correct API key

Cause: LearnWorlds requires both the Lw-Client header (school subdomain) and the Authorization header (Bearer token). Missing either one, or formatting the Authorization value without the 'Bearer ' prefix, causes authentication to fail.

Solution: Verify in your Retool resource headers that Lw-Client is set to just your school subdomain (e.g., 'myacademy' — not the full URL), and Authorization is set to 'Bearer YOUR_API_KEY' with a space between 'Bearer' and the key. Open the resource settings, double-click each header value to confirm there are no leading or trailing spaces.

### API returns 403 Forbidden on specific endpoints like /v2/orders

Cause: LearnWorlds API access levels vary by plan. Some endpoints (orders, advanced analytics) are only available on higher-tier plans (Learning Center or higher). Your API key is valid but your plan doesn't include access to that endpoint.

Solution: Check your LearnWorlds plan tier in School Settings → Billing. Review the LearnWorlds API documentation for endpoint availability by plan. If the endpoint you need is on a higher plan, contact LearnWorlds sales. As a workaround, some revenue data is available through the /v2/products endpoint which may be accessible on lower plans.

### Enrollment query returns empty data array even though students are enrolled

Cause: The /v2/courses/{id}/users endpoint uses the internal LearnWorlds course ID, not the slug. If you're using the course slug in the URL path instead of the numeric ID, the endpoint returns empty results.

Solution: Confirm you're using the course's id field (a numeric or UUID string) rather than its slug in the URL path. In your getCourses query, ensure the transformer preserves the id field unchanged. Check the URL path in your enrollment query: it should be /v2/courses/{{ table_courses.selectedRow.data.id }}/users where .id maps to the internal course identifier.

### Date fields display as numbers (Unix timestamps) in the Table instead of readable dates

Cause: LearnWorlds returns timestamps as Unix epoch integers (seconds since 1970). Without conversion in the transformer, the Table displays raw numbers like '1710345600'.

Solution: Add timestamp-to-date conversion in your transformer function for all date fields. Multiply by 1000 (to convert seconds to milliseconds) before passing to JavaScript's Date constructor: new Date(timestamp * 1000).toLocaleDateString(). Apply this pattern to created_at, enrolled_at, completed_at, and last_activity fields.

```
// Convert LearnWorlds Unix timestamp to readable date
const toDate = ts => ts ? new Date(ts * 1000).toLocaleDateString() : '—';
// Usage in transformer:
enrolled_at: toDate(e.enrolled_at),
completed_at: toDate(e.completed_at)
```

## Frequently asked questions

### Does Retool have a native LearnWorlds connector?

No, Retool does not have a native LearnWorlds connector. You connect via a REST API Resource using LearnWorlds' API key in the Authorization header alongside the required Lw-Client school identifier header. Setup takes about 20 minutes and gives full access to the LearnWorlds REST API for courses, users, enrollments, and orders.

### What LearnWorlds plan is required to access the API?

LearnWorlds API access is available on the Professional plan and above. The Starter plan does not include API access. Some advanced endpoints (like orders and detailed analytics) may require the Learning Center or higher plan. Check your current plan in School Settings → Billing and refer to LearnWorlds' API documentation for endpoint availability by tier.

### Can I enroll students into courses through the Retool dashboard?

Yes. The LearnWorlds API supports enrollment creation via POST /v2/users/{user_id}/enrollments. In Retool, build a form that accepts a student email, first looks up the user ID with a search query, then enrolls them into the selected course. For bulk enrollment, use a JavaScript query that processes a list of emails sequentially or in parallel batches, respecting LearnWorlds' rate limits.

### How do I track which students have completed a course?

Enrollment objects in the LearnWorlds API include a status field ('enrolled' or 'completed') and a progress field (0-100 integer). Query GET /v2/courses/{course_id}/users and filter by status=completed to see completed students, or display progress percentages in a Table for all enrolled students. The completed_at timestamp is populated when status becomes 'completed'.

### Can Retool send automatic notifications when a student completes a course?

Yes, using Retool Workflows. Create a scheduled Workflow that runs every hour querying LearnWorlds for newly completed enrollments (status=completed, completed_at greater than the last run timestamp). When new completions are found, trigger follow-up actions like sending a Slack message, updating a CRM record, or triggering a SendGrid email. Store the last-checked timestamp in Retool Database or a configuration variable.

---

Source: https://www.rapidevelopers.com/retool-integrations/learnworlds
© RapidDev — https://www.rapidevelopers.com/retool-integrations/learnworlds
