## 📦 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://chatwoot.fr.nocodespacelab.com/hc/celestorydoc/articles/fr-exports-creer-son-export-scorm-pour-lms-ee1a71): reuse the SCORM example graph (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
```javascript
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
```javascript
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
```javascript
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
```javascript
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
```javascript
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
<?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 (Tin Can/xAPI compatible) or check the statement stream received directly in your LRS's dashboard.
