Why xAPI exists
Before Actor, Verb, Object means anything to you, it helps to know what problem it's actually solving.
Think about a SCORM package with a video in it. A learner opens the course, the video plays, and eventually SCORM tells your LMS one thing: complete. That's it. Did they watch the whole thing? Did they skip to the end? Did they pause it three times and rewatch a section? SCORM has no idea, and neither do you.
xAPI was built to close that gap. Instead of one blunt "complete" signal locked inside your LMS, xAPI lets any system, anywhere, record what actually happened as a small, structured record called a statement.
SCORM, tracking a video
- Only reports back to the LMS that launched it
- Records completion, maybe a score
- Can't tell you if the video was skipped or replayed
- Learning outside the LMS is invisible
xAPI, tracking a video
- Any system that can send JSON can report
- Records play, pause, seek, and completion separately
- Statements can come from an LMS, an app, a VR headset, anything
- Everything lands in one Learning Record Store, not locked to one platform
That last point is the one worth sitting with. xAPI isn't really a tracking upgrade, it's an interoperability decision. The data belongs to you, not to whichever vendor's LMS happens to be running this year.
Someone Did Something
xapi.com.au sums the whole spec up in three words. Here's what's actually inside them.
Every xAPI statement, no matter how complex it eventually gets, starts as this one sentence. The Actor is who did it, the Verb is what they did, and the Object is what they did it to. Everything else you'll learn about xAPI is just adding detail to this same shape.
- Actor A person or system. Usually identified by an email address (called a
mbox) or an account on another system. - Verb An action, drawn from a shared list so "completed" means the same thing everywhere. Each verb is identified by an IRI, not just a word: an identifier in web-address form (like a URL, but it also allows non-Latin characters). The spec requires this format for verb and activity IDs.
- Object What the action happened to, usually an "Activity" like a course, video, or document, but it can be another statement or even another actor.
A few more real examples
None of these would show up in a traditional LMS report. That's the whole point: xAPI captures the learning that actually happens, on the job, in a simulator, or in conversation, not just what happens inside a course player.
Show the actual JSON
Notice the Verb and Object are both identified by an IRI — the human-readable "played" and "Induction Video" text is just there for display. This is what makes xAPI machine-readable across completely different systems: they're all pointing at the same identifier for "played", not just a matching string.
One rule governs every IRI: if two IRIs match, the systems treat them as the same thing and merge them in reporting. That's why the advice flips depending on the field. For the Verb you deliberately reuse a standard IRI (here, one published by ADL) so your "played" lines up with everyone else's. For the Object you do the opposite: mint your own IRI under a domain you control, so your "Induction Video" never collides with someone else's. Either way the IRI doesn't need to resolve to a real page — it just has to be the right one to share, or the right one to keep unique.
Where statements live
Statements need somewhere to go. That somewhere is called a Learning Record Store, or LRS.
An LRS is a database purpose-built to receive, store, and hand back xAPI statements. Think of it as the inbox for every "Someone Did Something" event happening across your entire learning ecosystem, not just one course player.
Anything capable of sending a properly formed statement is called an Activity Provider. That could be an LMS, a game, a PDF, a video player, a VR headset, Remote Reviewer, or a custom app you've built in-house. The LRS doesn't care where the statement came from, only that it's valid.
This is the decentralised bit that matters most. Your learning data isn't trapped inside one vendor's gradebook. It lives in an LRS you control, and you can point as many Activity Providers at it as you like, or even sync statements between multiple LRSs.
How a statement actually travels
Build your first statement
Pick a scenario below and watch the actual xAPI JSON come together, with every field explained.
Whichever scenario you pick here is carried straight down to Send a real statement to a real LRS, so you can fire this exact event at a live LRS in section 09.
Extensions and context
Actor, Verb, Object gets you a valid statement. These three extra fields turn it into real evidence.
- result What happened as an outcome: a score, a pass/fail, a completion status, or how long it took. This is where assessment data and completion evidence lives.
- context The circumstances around the statement: what platform it happened on, what team or cohort the learner belonged to, or which course this activity was part of.
- extensions Your own custom data that doesn't fit anywhere else, like a location, a device ID, or an internal reference number. You define these yourself using your own IRIs.
Show a fuller statement using result and context
This is exactly the kind of statement that feeds into a proper evidence trail for compliance training, where a completion checkbox in an LMS just isn't enough on its own.
Common patterns
Six situations you'll hit constantly once you start using xAPI in the real world.
A learner finishes a course
Use the completed verb on the top-level course activity, and result.completion: true.
verb: completed
object: "Fire Warden Training"
result: { completion: true }
A learner sits a quiz
Use passed or failed, and always include a scaled score between -1 and 1.
verb: passed
object: "Module 3 Assessment"
result: { success: true, score: { scaled: 0.82 } }
Tracking video engagement
Send separate statements for played, paused, and seeked, each with a timestamp in the extensions.
verb: seeked
object: "Induction Video"
context.extensions: { "time-from": 45, "time-to": 210 }
Learning outside a course
Verbs like read, shared, or mentored capture the learning your LMS never sees.
verb: mentored
object: "New Starter Onboarding"
context: { team: "Warehouse Ops" }
Performance evidence in the field
A supervisor or an app records real-world performance, not a simulated one, using demonstrated.
verb: demonstrated
object: "Correct PPE Use"
context: { instructor: "S. Byrne" }
Building an audit trail
Combine result, context, and a timestamp so a single statement stands alone as evidence.
verb: certified
object: "Working at Heights"
result: { completion: true }, timestamp: "2026-08-14T09:15:00Z"
Querying and reporting
Getting statements into the LRS is half the job. Getting them back out is where the value shows up.
Every LRS exposes a way to query statements back out, usually filtered by actor, verb, activity, or a date range. You don't need to be across the full query syntax to get value from this. Most LRS platforms, including Veracity, give you a reporting dashboard built on top of these queries already.
If you want to go further, you can export raw statement data and feed it into your own analysis. xAPI.com.au has built a couple of tools that make this easier without needing to write query code yourself:
- An xAPI Reporting Assistant GPT, built to read uploaded xAPI data and answer questions about it directly
- A CSV export tool for pulling statements out of an LRS in a format ready for AI tools or a spreadsheet
Common mistakes
The errors almost everyone makes on their first few statements, and how to spot them.
Using a verb that isn't an IRI
Typing "verb": "completed" as plain text isn't enough. The verb needs a proper IRI, like http://adlnet.gov/expapi/verbs/completed, or your statement won't validate.
Fix: pull verbs from the ADL registry rather than inventing your own wording each time. Verb IRIs are meant to be shared — reusing the standard one is how your data lines up with everyone else's.
Mixing up the Actor and the Object
It's easy to accidentally describe a course as the one doing the action. The Actor is always the learner (or system), never the thing being acted on.
Fix: read the statement back as a plain sentence. If it doesn't read naturally as "Someone did something", something's swapped.
Putting outcome data in context instead of result
Scores, pass/fail, and completion status belong in result. Context is for the circumstances, not the outcome.
Fix: ask "is this what happened, or is this the setting it happened in?" The former is result, the latter is context.
Forgetting the Object needs a real, unique IRI
Same rule as verbs, opposite goal: matching IRIs get merged in reporting. For an activity that's a bug — two different activities sharing an ID collapse into one, even if their names look different.
Fix: use an IRI you control as the ID, under your own domain, even if it never resolves to an actual page.
Send a real statement to a real LRS
This is the bit that makes it click: build a statement, send it somewhere real, and see exactly what comes back.
Quick recap, since this is the payoff of everything above: a Learning Record Store (LRS) is a database built to receive xAPI statements over the web. You send it a statement as JSON, and if it's valid, the LRS stores it and hands you back an ID confirming it landed.
To try this for real, you need an LRS endpoint of your own. This can be:
- A free or trial LRS such as Veracity, Learning Locker, or SCORM Cloud
- An LRS your organisation already runs
- A self-hosted open-source LRS you've spun up for testing
Whichever you use, you'll need three things: the statements endpoint URL, and a username and password (or key and secret) the LRS has issued you for Basic Authentication.
Before you hit send, check this
/statements.Step 1: describe what happened
Pick a scenario in Build your first statement and its details drop in here automatically. Edit anything before you send.
Step 2: point it at your LRS
The statement about to be sent
What the LRS sends back
Where to go next
Once the basics feel comfortable, here's where the deeper thinking lives.