Back to Work Case Study · n8n Automation

AIEM Auto-Enrollment

A scheduled n8n automation that turns a WooCommerce course purchase into a live LearnWorlds enrollment in under five minutes — looks up the student, checks if they're already enrolled, and only acts when there's something to do. Idempotent by design.

n8n WooCommerce LearnWorlds Google Sheets API Integration LMS Automation Education Tech No-code Idempotent Design
WooCommerce to LearnWorlds Auto-Enrolment n8n workflow — Schedule Trigger fans into Get Orders and Get Course Mapping, merges, extracts enrolment tasks, loops, finds user on LearnWorlds, checks enrolment, and conditionally enrols

About the Workflow

AIEM sells medical training courses through a WordPress / WooCommerce store but delivers them through LearnWorlds — their LMS at student.aiem.co.za. The two systems don't talk natively. This n8n automation is the bridge between them: every completed purchase becomes a LearnWorlds enrollment, automatically, within five minutes.

Client

African Institute of Emergency Medicine (AIEM) — medical training academy running both a WooCommerce storefront and a LearnWorlds LMS.

Platform

n8n (self-hosted), WooCommerce REST API, LearnWorlds Admin API v2, Google Sheets as a config layer. Africa/Johannesburg timezone.

Status

Live in production. Created February 2026, most recently updated May 2026 — AIEM's most active workflow.

Two Systems That Don't Talk

WooCommerce knows who paid for what. LearnWorlds decides who gets access to which course. Until this workflow shipped, the only thing bridging them was a human with two browser tabs open.

Two Identity Systems

A WooCommerce customer is not automatically a LearnWorlds user — emails have to be cross-checked manually before enrolment can even begin.

No Native Integration

WooCommerce Product IDs and LearnWorlds Course IDs use completely different identifiers — there's no off-the-shelf connector that knows the mapping.

Minutes Per Sale

Each manual enrolment took several minutes — fine on a slow week, painful on a busy one, and bad for students stuck waiting on a paid-for course.

Risk of Double-Enrolment

Without a check, a re-processed order or a refund-then-rebuy could enrol a student twice — corrupting course records and confusing reporting.

Purchase → Enrolment in Five Steps

Every five minutes, the workflow pulls the 20 most recent completed orders, cross-references a Google Sheet mapping table, and runs each enrolment through a careful lookup-then-act loop.

  1. 01

    Fetch

    Pulls the 20 most recent completed WooCommerce orders via the REST API.

  2. 02

    Map

    Reads the Order to Course Mapping Google Sheet to translate WooCommerce Product IDs to LearnWorlds Course IDs.

  3. 03

    Build Tasks

    A code node flattens line items and produces a clean list of { email, woo_order_id, lw_course_id } enrolment tasks.

  4. 04

    Look Up & Check

    For each task, finds the student on LearnWorlds by email and fetches their current enrolments.

  5. 05

    Enrol or Skip

    If the student isn't already on the course, POST a new enrolment. If they are, log and skip — no double-enrolments, ever.

Workflow Architecture

One trigger, three HTTP calls per task, and two safe-skip branches — the whole thing is built around "check before you act".

Schedule Trigger Fires every 5 minutes · Africa/Johannesburg
Branch A · Orders
Get 20 Completed Orders WooCommerce REST · limit: 20, order: desc
Branch B · Mapping
Get Course Mapping Google Sheet · woo_product_id → lw_course_id
Merge Orders + Mapping Joins both streams before processing
Extract & Map Enrolment Tasks Code · builds { email, order_id, course_id } list
Has Tasks? Exits early if there's nothing actionable
Loop Each Task Split in Batches · iterates one task at a time
Find User on LearnWorlds GET /admin/api/v2/users/{email}
User Found
Get User Enrolments GET /users/{id}/enrollment
Already Enrolled? Compares target course to enrolled list
Not Enrolled
Enrol User POST /users/{id}/enrollment
Already Enrolled
Skip · No-op Loop continues to the next task
User Not Found
Skip · No-op No LearnWorlds account yet · loop continues

How It Was Designed

Four design choices that shape how this workflow behaves under stress, under repetition, and under non-technical hands.

Google Sheet as Config Layer

Rather than hard-coding product-to-course mappings inside the workflow, the mapping lives in a Google Sheet (Order to Course Mapping, tab: Mapped). AIEM's admins can add new courses without touching n8n — the code node reads the sheet and builds a lookup dictionary at runtime.

Idempotency by Design

The workflow checks existing enrolments before acting. A student who buys the same course twice — or whose order is re-processed — will not be enrolled twice. The Already Enrolled? branch handles repetition gracefully.

Tolerant Error Handling

All three HTTP Request nodes use continueRegularOutput on error. A failed API call for one student doesn't halt processing for the rest of the batch — quiet failures are bounded to a single task.

Unmapped Products Logged

If a WooCommerce product has no entry in the mapping sheet, the code node logs it to the console rather than crashing — visibility into gaps without breaking the workflow for everyone else.

LearnWorlds Admin API

Three endpoints from the LearnWorlds Admin v2 API do all the talking — auth via HTTP header (Lw-Client) plus a Bearer token kept out of the workflow JSON.

Method Endpoint Purpose
GET /admin/api/v2/users/{email} Look up student by email
GET /admin/api/v2/users/{id}/enrollment Fetch student's current enrolments
POST /admin/api/v2/users/{id}/enrollment Enrol student in a course

What's Under the Hood

Fifteen nodes, four services, one purpose: a sale becomes access, without anyone having to know it happened.

Node Type Purpose
Schedule Trigger Trigger Fires every 5 minutes
WooCommerce Action Fetches 20 most recent completed orders
Google Sheets Action Reads product-to-course mapping table
Merge Logic Joins orders + mapping before processing
Code — Extract & Map Transform Flattens line items, builds enrolment task list
Filter — Has Tasks? Logic Exits early if nothing to process
Split in Batches Logic Iterates one task at a time
HTTP Request — Find User Action Looks up student by email on LearnWorlds
Code — Process User Lookup Transform Parses response, flags found/not-found
IF — User Found? Logic Branches: proceed vs. skip
HTTP Request — Get Enrolments Action Fetches student's current course enrolments
Code — Check Enrolment Status Transform Compares target course against enrolled list
IF — Already Enrolled? Logic Branches: enrol vs. skip
HTTP Request — Enrol User Action POSTs enrolment to LearnWorlds API
NoOp ×2 Logic Skip branches (already enrolled / user not found)

Audit Findings & Trade-offs

Honest notes on the workflow as it stands — what's a deliberate trade-off, what to watch as the catalogue grows, and what's already locked in.

Version drift. The activeVersionId doesn't match versionId — the published/active version differs from the latest saved version. The workflow has been edited but not republished. Worth confirming the active version reflects intended behaviour before relying on it.

5-minute polling is aggressive. For an API chain of this length, a batch of 20 orders with 3–5 tasks each can mean runs that drift toward overlapping. A webhook-triggered approach or deduplication mechanism is the next step if volume picks up.

20-order fetch ceiling. The workflow fetches only the 20 most recent completed orders per run. If more than 20 orders complete inside one 5-minute window, the oldest get missed until the next cycle. Acceptable trade-off today — flagged so it's visible if volumes rise.

No success notification. Students and admins receive no confirmation that an enrolment was triggered. A post-enrolment email or Slack ping would improve observability and reassure both sides.

Silent skip on missing user. If a student purchases before registering on LearnWorlds, the workflow drops the task silently and they're never enrolled. A retry queue or notification for unmatched users would close this loop.

Credentials handled correctly. The LearnWorlds Lw-Client ID lives in node parameters (non-sensitive), and the Bearer token comes from genericCredentialType — correctly kept out of the workflow JSON.

Timezone set correctly. Workflow runs on Africa/Johannesburg — enrolment timestamps align with the team's working hours.

Two Systems That Should Be Talking?

Most "we need an integration" problems are actually a small, sturdy n8n workflow waiting to be written. If you've got a sale-to-fulfilment gap, a CRM-to-billing gap, or a paid-but-not-provisioned gap — let's build the bridge.

Bridges like this sit under AI & Automation services — n8n integration design, LearnWorlds + WooCommerce APIs, LangChain agents, and idempotent multi-system workflows.

Related: see the AIEM Order Sync workflow running on the same n8n instance — WooCommerce orders mirrored to Google Sheets every 30 minutes.