> ## Knowledge Base Index
> Fetch the complete knowledge base index at: https://documentation.celestory.io/sitemap.xml
> Use this file to discover available pages before exploring further.
> Pure-Markdown content can be obtained by appending a '.md' suffix to the content URLs listed in the sitemap (without the trailing slash).

# 📦 Create your xAPI export for an LRS

The xAPI format (Experience API, formerly Tin Can API) allows for much finer tracking than SCORM: instead of just reporting a status and a score to an LMS, your export sends "statements" (activity records) directly to an LRS (Learning Record Store), with or without an LMS around it.
### **Defining the xAPI format**
An xAPI statement reads like an actor - verb - object sentence, sent via HTTP request to the LRS:
* Actor: who performed the action (learner's email or identifier)
* Verb: the action performed (e.g. initialized, completed, passed, progressed)
* Object: the activity involved (your Celestory module)
* Result (optional): score, success, progress, duration...
### **Characteristics of the xAPI format**
* Can work without an LMS: your export can send its statements directly to a standalone LRS (Learning Locker, Watershed, SCORM Cloud LRS...)
* Granular tracking: every interaction can be reported, not just a final score like in SCORM
* Defines no automatic completion rule: you decide when each statement is sent
* ⚠️ Unlike SCORM (which communicates with the parent window), xAPI sends real network requests: the LRS must allow CORS from your export's origin, or you'll get silent failures (check the console)
### **Example resource**
Block placement in the graph follows the exact same logic as the [__SCORM tutorial__](https://documentation.celestory.io/fr/article/creer-son-export-scorm-pour-lms-d3ne2m/): reuse the SCORM example graph ([https://creator.celestory.io/project/phHUew7e6](https://creator.celestory.io/project/phHUew7e6)) and replace the SCORM blocks with the xAPI blocks below, at the same spots (start, intermediate steps, end).
**Important note:** Only the blocks starting with xAPI need to be added wherever you like in your graph.
### **Step 1: Add the Javascript blocks**
#### Block 1: xAPI - Configuration
```
window.XAPI = {
    endpoint: "https://YOUR-LRS.example.com/xapi/",
    auth: "Basic YOUR_KEY_IN_BASE64",
    actor: {
        objectType: "Agent",
        name: "Celestory Player",
        mbox: "mailto:player@example.com"
    },
    activityId: "https://yourdomain.com/activities/module-name"
};
```
Get the endpoint and authentication key from your LRS (or your LMS, if the LRS is built into it). If your export is launched from a Tin Can Launch-compatible LMS, you can replace these fixed values with a read of the parameters passed in the URL at launch instead of hardcoding them.
#### Block 2: xAPI - Send function + Initialize
```
function xAPI_sendStatement(verbId, verbDisplay, resultObj) {
    const statement = {
        actor: window.XAPI.actor,
        verb: {
            id: verbId,
            display: { "en-US": verbDisplay }
        },
        object: {
            id: window.XAPI.activityId,
            objectType: "Activity"
        }
    };
​
    if (resultObj) {
        statement.result = resultObj;
    }
​
    fetch(window.XAPI.endpoint + "statements", {
        method: "POST",
        headers: {
            "Authorization": window.XAPI.auth,
            "Content-Type": "application/json",
            "X-Experience-API-Version": "1.0.3"
        },
        body: JSON.stringify(statement)
    }).then(() => console.log("xAPI: statement sent -", verbDisplay))
      .catch(error => console.log("xAPI send error", error));
}
​
xAPI_sendStatement("http://adlnet.gov/expapi/verbs/initialized", "initialized");
```
The `verbId` (in English, standardized by the ADL) is what matters for interoperability. The `verbDisplay` is just a human-readable label shown in the LRS, feel free to translate it.
#### Block 3: xAPI - Step
```
const step = 70;
​
xAPI_sendStatement(
    "http://adlnet.gov/expapi/verbs/progressed",
    "progressed",
    {
        extensions: {
            "http://id.tincanapi.com/extension/progress": step
        }
    }
);
```
You can add as many steps as you like by duplicating this block with different percentages (e.g. 10%, 50%, 70%), just like the SCORM steps.
#### Block 4: xAPI - Complete
```
const scorePercent = 100;
​
xAPI_sendStatement(
    "http://adlnet.gov/expapi/verbs/completed",
    "completed",
    {
        score: {
            scaled: scorePercent / 100,
            raw: scorePercent,
            min: 0,
            max: 100
        },
        completion: true,
        success: true
    }
);
```
#### Block 5: xAPI - Finish Session
```
xAPI_sendStatement("http://adlnet.gov/expapi/verbs/terminated", "terminated");
console.log("xAPI: session ended");
```
### **Step 2: Export the project**
Export your project as Web or PWA.
### **Step 3: (optional) Add a tincan.xml file**
Some LMS/LRS require a Tin Can package rather than a simple direct link. If so, add a **tincan.xml** file at the root of your export:
```
<?xml version="1.0" encoding="utf-8"?>
<tincan xmlns="http://projecttincan.com/tincan.xsd">
    <activities>
        <activity id="https://yourdomain.com/activities/module-name" type="http://adlnet.gov/expapi/activities/module">
            <name>Module name</name>
            <description lang="en-US">Description of your Celestory module</description>
            <launch lang="en-US">index.html</launch>
        </activity>
    </activities>
</tincan>
```
### **Step 4: Compress and test**
If a package is required, compress your folder into a ZIP (as with SCORM). If your LRS receives statements directly over HTTP, you can deploy the Web content as-is (e.g. Direct Link or HTML5 export).
To test, you can use a site like [https://app.cloud.scorm.com/sc/user/Home](https://app.cloud.scorm.com/sc/user/Home) (Tin Can/xAPI compatible) or check the statement stream received directly in your LRS's dashboard.