Browse 50 demo/seeded portfolios on Showcase →
Field NotesxAPI18 min read

xAPI hosting, explained without jargon.

What an LRS actually does, why your Captivate course "doesn't work" when you self-host, and the cheapest path to a working xAPI pipeline for a freelance ID.

A nurse-onboarding client emailed me last spring with a screenshot and three words: "it doesn't work."The Captivate course played fine on her laptop. She uploaded it to the hospital's intranet, learners clicked through it, and the completion report came back empty. Every nurse showed zero progress. The course wasn't broken — it was talking to a Learning Record Store that, as far as the hospital's network was concerned, didn't exist.

If you're moving from SCORM to xAPI, this is the wall you hit. SCORM hid the plumbing inside the LMS, so you never had to think about where the data went. xAPI hands you the plumbing — and an empty completion report is what it looks like when one pipe isn't connected. So let's connect it, with no jargon and no LMS-vendor sales deck.

xAPI doesn't store data in the course. It mails it to an address. If the address is wrong, the mail just vanishes.— what every empty completion report is really saying

What an LRS actually does.

Strip away the acronyms and xAPI is one idea: your course sends short sentences about what a learner did to a database that knows how to store them. Each sentence is a statement, and it's shaped like actor + verb + object— "Devon completed Module 3," "Priya scored 88 on the triage quiz," "Sam launched the fire-safety sim." The database that receives and stores those statements is the LRS (Learning Record Store).

That's the whole model. The course is the sender. The LRS is the mailbox. xAPI (often still called "Tin Can") is just the agreed format of the envelope so any sender can talk to any mailbox. Where SCORM trapped completion and score inside a single course session and handed it to the LMS at the end, xAPI fires statements the moment things happen and ships them somewhere they can outlive the session entirely.

Why anyone bothers: an LRS can record things SCORM physically cannot. Activity that happens outsidea formal course — a simulation, a coaching session, a job aid someone opened on the floor. Offline learning that syncs later. And it lets you query the data back out with real filters instead of staring at an LMS's fixed completion column.

The one-sentence version

An LRS is a database that only speaks xAPI. Your course POSTs "actor did verb to object" statements to it over HTTPS, and it stores them so you can query them later. Everything else is detail.

Why self-hosting bites you.

Here's the trap that produced my nurse client's empty report. An xAPI package is just static files — HTML, JavaScript, media. So it's tempting to think "I'll drop it on my own web server and I'm done." You can absolutely host the filesanywhere. But the files aren't the hard part. The mailbox is.

When you publish from Storyline, Rise, or Captivate, the authoring tool bakes three things into the package: the LRS endpoint (a URL), a username and password(Basic auth credentials), and the activity ID. Move the files to a new server and those baked-in values don't change — they still point at whatever LRS you typed into the publish dialog. If that was a test endpoint, a trial that expired, or a placeholder, every statement gets mailed into the void.

And even when the endpoint is right, the browser will often refuse to deliver the mail. The page serving your course lives on one domain; the LRS lives on another. That's a cross-origin request, and unless the LRS explicitly whitelists the course's domain via CORS, the browser blocks the POST before it leaves the building. The course runs. Nothing records. No error a learner would ever see.

If you skip this

Open your browser's network tab and launch the course before you ship it. Watch for the POST to the xAPI endpoint. A green 200 means the mail arrived; a red 401 is bad credentials; a 0/CORS error means the LRS hasn't whitelisted your domain. This 30-second check saves a week of "it doesn't work" emails.

One xAPI statement · what your course actually mailsJSON
// POSTed to <LRS endpoint>/statements with Basic auth.
// This is what "Devon completed Module 3" looks like on the wire.
{
  "actor": { "mbox": "mailto:devon@example.org" },
  "verb": { "id": "http://adlnet.gov/expapi/verbs/completed" },
  "object": { "id": "https://courses.example.org/onboarding/module-3" }
}

Where to put your LRS, ranked.

You don't build an LRS — you point your course at one. Here are the four real places to get one, ranked by how fast a freelance ID gets to a working pipeline that survives a client's network.

1

A hosting platform with the LRS built in (Training OS, SCORM Cloud).

Best for: freelance + in-house IDs who want a working pipeline today

Recommended

This is the path I steer almost everyone to. Because the course and the LRS sit behind the same host, the cross-origin failure that kills self-hosting never happens. You upload the package, the platform gives you a launch URL plus a real endpoint and credentials, and your statements land in a store you can actually query. It collapses the "files" problem and the "mailbox" problem into one upload. For a freelance ID who needs to show a client it works this week, nothing else is close.

What's good

  • Files and LRS live on one domain — the CORS problem disappears
  • Endpoint and credentials handed to you, not guessed in a publish dialog
  • Query and export statements without standing up infrastructure
  • Free tier is enough to prove a pipeline end to end

What's not

  • Less raw control than running your own LRS
  • You inherit the platform’s statement-query model
2

A standalone managed LRS (Watershed, Veracity, Yet Analytics).

Best for: data-heavy programs that have outgrown completion columns

Solid

When the point of going xAPI is the analytics— cohort comparisons, learning-path reporting, slicing interactions across dozens of activities — a dedicated LRS earns its keep. The catch for a freelancer: the LRS is the mailbox, but you're still hosting the course files elsewhere, which means you own the CORS whitelist between the two. Worth it for an ongoing program; heavy for a one-off portfolio piece.

What's good

  • Purpose-built dashboards and learning-analytics reporting
  • Scales to millions of statements without you tuning a database
  • Vendor handles uptime, backups, and conformance

What's not

  • You still host the course files somewhere else — CORS is back on your plate
  • Analytics-platform pricing, aimed at programs not single projects
  • Overkill if you just need completions to register
3

Self-host an open-source LRS (Learning Locker, Trax LRS).

Best for: IDs who are also comfortable running a server

If you're technical

Learning Locker (and the leaner Trax LRS) is a real, capable LRS you can stand up yourself. It works. It is also a piece of infrastructure: a database, background workers, HTTPS, and a CORS config you have to get exactly right or the statements never arrive. Choose this if running services is something you enjoy and the data must live on your own box. Otherwise you've signed up to be an LRS operator, which is a different job than designing courses.

What's good

  • Full control — your server, your data, no per-statement pricing
  • Open standards; export and migrate whenever you want
  • You’ll genuinely understand xAPI by the end

What's not

  • You’re now running a database, a queue, and TLS in production
  • CORS, auth, and backups are all yours to get right
  • A weekend project before it stores a single real statement
4

No LRS — just host the xAPI files and hope.

Best for: nothing. This is the empty-report trap itself.

Don't

Because an xAPI package is "just files," people drop it on a generic web host, see it play, and call it shipped. There's no mailbox, so every statement evaporates. This isn't really an option — it's the exact failure mode this whole article exists to prevent. If you take one thing away: hosting the files is not hosting the data.

What's good

  • Feels like the fastest option for about ten minutes.

What's not

  • Statements have nowhere to go — every completion reads zero
  • The course looks perfect, which is exactly why it’s a trap
  • You only find out after a client launches it

The gotchas no one mentions.

Even with a real LRS wired up, three things will quietly eat your statements. Here they are in order of how often I've had to debug them.

1. Stale endpoint and credentials baked in at publish.

Storyline and Captivate write the LRS endpoint and Basic-auth credentials intothe published package. Re-host the files and those values come along unchanged. If you switched LRS providers, or the credentials rotated, the course keeps mailing to the old address. Always re-publish (or re-enter endpoint and credentials) when the destination LRS changes — don't just copy last quarter's files.

2. CORS — the cross-origin block.

If the course is served from courses.example.org and the LRS lives on lrs.vendor.com, the browser treats the statement POSTas cross-origin and blocks it unless the LRS explicitly allows that domain. It's the single most common reason a self-hosted xAPI course records nothing. The fix is on the LRS side: whitelist the domain serving the course. Hosting both behind one platform sidesteps it entirely.

The mistake everyone makes

Testing the course logged into the authoring tool's preview, where it points at a friendly built-in LRS, then shipping the published files to a different host. Preview proves the content works; it proves nothing about whether the real endpoint is reachable. Always verify the published package against the production LRS, from the domain it'll actually be served on.

3. The actor identity mismatch.

xAPI identifies a learner by an actor — usually an email (mbox) or an account on a system. If your launch flow doesn't pass a stable identifier, every learner can come through as the same anonymous actor, or as a brand-new one each session. The statements arrive; they just can't be tied to a person. Decide how learners are identified before launch, not after the report looks scrambled.

TL;DR — the cheapest working path.

If you're a freelance ID who needs it working this week: use a host with the LRS built in. One upload, one domain, no CORS, a real endpoint handed to you. Free tiers cover a full proof-of-concept. This is the cheapest path to a pipeline that actually records.

If the whole point is analytics: a managed LRS like Watershed — just remember you still own the course-host-to-LRS CORS config.

If running servers is your idea of fun: Learning Locker on your own box. Budget a weekend and get the CORS and auth right.

If your client is SCORM-only:don't do any of this. Publish SCORM, skip the LRS, and read where to host a SCORM fileinstead. xAPI earns its complexity only when you need data the SCORM envelope can't carry — and if WordPress is the destination, the embed-in-WordPress walkthrough applies the same host-then-link logic.

Frequently asked questions.

What is an LRS in plain English?

A Learning Record Store is a database that speaks one specific dialect: xAPI statements shaped like actor-verb-object. Your course sends "Devon completed Module 3" to the LRS over HTTPS, and the LRS stores it and lets you query it back. SCORM kept that data trapped inside the course session; xAPI sends it somewhere it can outlive the session.

Can I host an xAPI course without an LRS?

You can host the files anywhere — an xAPI package is just HTML and JavaScript. But without an LRS endpoint to receive statements, nothing is recorded. The course will run and look fine, then report zero completions. The LRS is the part that makes xAPI xAPI.

Why does my Captivate course work locally but not when self-hosted?

Two near-certain causes: the endpoint and credentials baked in at publish time point at a test LRS that no longer exists, and the browser is blocking the cross-origin request because your LRS has not whitelisted the domain serving the course. Open the network tab and look for a failed POST to the xAPI endpoint.

Do I need xAPI if my client only uses SCORM?

No. If the destination is a SCORM-only LMS, publish SCORM and skip the LRS entirely. Reach for xAPI when you need data the SCORM envelope cannot hold — offline activity, events outside one course, or interactions you want to query across modules later.

/06  Try it for free

Drop a SCORM file. See it live in 11 minutes.

Free for 3 modules. No card. Lifetime is $149 once. You read the article, you know how this is supposed to work — see it in your own browser.

Upload your first SCORM →
DR

About the author

Devon Rhee · Healthcare ID · 8 yrs.

/07Keep readingRelated notes