How to Send an xAPI Statement from a Storyline Course

By
Devlin Peck
. Updated on 
May 5, 2023
.
Send an xAPI Statement from Articulate Storyline tutorial cover photo

Welcome back to the Getting Started with xAPI Tutorial Series

Are you ready to collect detailed data about how people interact with your eLearning experiences? If so, then you're in the right place. By following along with this final tutorial of the series, you'll learn how to generate custom xAPI statements to collect exactly the data you need from your Storyline course.

You do not need any coding knowledge coming into this tutorial. However, you should have completed Part 1 and Part 2 of this tutorial series before beginning this part, as you will be building on the work that you've already completed.

Feel free to ask for help in the eLearning Development space in the ID community (free for mailing list subscribers).

Setting up the Learning Record Store (LRS)

The first thing we'll do in this tutorial is set up the LRS, as this is where you'll be sending all of your xAPI statements. Feel free to use a different LRS, but we'll use Veracity Learning LRS for the purposes of this tutorial.

To get your Veracity Learning LRS up and running, follow these steps:

  1. Visit the Veracity LRS sign-up page and select "Create Account".
  2. Enter your user name, email address, and password, then select "Create Account" again.
  3. Go to your emails and select the link that they sent you to verify your email address.
  4. Once you're signed in, select the "Create an LRS" button.
  5. Enter a name for your LRS, then select "Create."
  6. Select your LRS from the "Your LRSs" dashboard.
  7. Select the red "No Access Keys!" button.
  8. Select the green "Create New Key" button.
  9. Enter "My Key" as the key name, then select "Save."

Good work! Now you have a running LRS instance with an access key that you can use to read or write xAPI statements. This is where all of your xAPI statements will be stored.

We will need your key's "Username" and "Password" in a later step, so leave this page open.

Configuring the xapi-statement.js File

In this section, you'll finish coding the xapi-statement.js file so that it's ready to send statements to the LRS.

Setting Up the Function and its Parameters

Let's create the JavaScript function that will send the xAPI statements to the LRS. Start by opening the xapi-statement.js file that you've worked on during the last two parts of the tutorial series. The JavaScript will get slightly more complex at this point, but fear not! Follow the syntax closely and you will be sending statements with ease.

First, we need to wrap all of the code that we've written so far in a JavaScript function. A function is a block of code that performs a specific task; in this case, our task is to send an xAPI statement.

We can also set "parameters" for a function, which are essentially placeholders that you can use throughout the function. This is useful because you can pass "arguments" to these paremeters, which then populate the function with whatever data you pass. If this sounds confusing - don't worry. You'll see what I mean shortly.

Above everything else in your xapi-statement.js file, add the following line of code:

{% c-block language="js" %}
function sendStatement(verb, verbId, object, objectId) {
{% c-block-end %}

Also add a closing curly bracket after the last line of code in your file. This tells the function where to end!

Now our code within this function will only execute if we call the function, and, when we call it, we can include whatever values we'd like for the verb, verbId, object, and objectId parameters. However, this won't do much if we don't include the parameters in the xAPI object itself.

The next step is to replace the strings that we used in the previous version of our xAPI JSON object with the parameter names. For example, the verb's "id" value should be verbId and the verb's "display" "en-US" value should be verb. View the code below to ensure that you've placed the parameter names in the correct locations.

{% c-block language="js" %}
function sendStatement(verb, verbId, object, objectId) {
const player = GetPlayer();
const uNamejs = player.GetVar("uName");
const uEmailjs = player.GetVar("uEmail");
{
  "actor": {
    "name": uNamejs,
    "mbox": "mailto:" + uEmailjs
  },
  "verb": {
    "id": verbId,
    "display": { "en-US": verb }
  },
  "object": {
    "id": objectId,
    "definition": {
      "name": { "en-US": object }
    }
  }
}
}
{% c-block-end %}

Another way to think of it is that the parameters serve as variables for the future data that we will pass into the function. For example, once we have everything set up, we can execute the following line of code in Storyline using a JavaScript trigger:

{% c-block language="js" %}
sendStatement("initialized", "http://adlnet.gov/expapi/verbs/initialized", "Write xAPI Statement Tutorial", "https://www.devlinpeck.com/tutorials/write-xapi-statement");
{% c-block-end %}

When this code is executed, it will replace the parameters in the xAPI object with the arguments that we passed, thereby giving us the exact statement that we had before modifying it. If you can't see why this is so powerful and efficient yet, then read on and prepare to be amazed at how easily we will be able to send statements from Storyline.

Using the ADL xAPI Wrapper

Next, we need to assign our xAPI object to a variable. This is a necessary step to use the ADL xAPI Wrapper, which is a wrapper that eases the xAPI communication between the Storyline course and the LRS.

We'll assign our xAPI object to a variable by typing "const statement = " before our xAPI statement object's opening curly bracket and adding a semicolon after the object's closing curly bracket. That part of the code should now look like this:

{% c-block language="js" %}
const statement = {
  /* xAPI object (actor, verb, object) goes here */
};
{% c-block-end %}

After assigning the xAPI object to the "statement" variable, we need to use the xAPI Wrapper's functionality to tell the wrapper which statement to send. We do this by using the following line of code, which should be the last line of code in your JavaScript function (right before the final closing curly bracket):

{% c-block language="js" %}
const result = ADL.XAPIWrapper.sendStatement(statement);
{% c-block-end %}

This sets the result variable equal to the statement itself, which gets sent to the LRS. The "ADL.XAPIWrapper.sendStatement() function is provided by the wrapper, so that part needs to be typed exactly as shown above. If you've applied everything in this section correctly, then your code should now look like this:

{% c-block language="js" %}
function sendStatement(verb, verbId, object, objectId) {
const player = GetPlayer();
const uNamejs = player.GetVar("uName");
const uEmailjs = player.GetVar("uEmail");
const statement = {
  "actor": {
    "name": uNamejs,
    "mbox": "mailto:" + uEmailjs
  },
  "verb": {
    "id": verbId,
    "display": { "en-US": verb }
  },
  "object": {
    "id": objectId,
    "definition": {
      "name": { "en-US": object }
    }
  }
  };
const result = ADL.XAPIWrapper.sendStatement(statement);
}
{% c-block-end %}

Creating the "conf" Object

The last thing we need to do with this JavaScript file is configure it to work with your LRS. This is managed using the "conf" JSON object, which we can create in the same way that we created the other JSON objects. The "conf" object contains "endpoint" and "auth" properties. Therefore, add the following line of code right above where we define the "statement" variable:

{% c-block language="js" %}
const conf = {
"endpoint": ,
"auth":
};
{% c-block-end %}

If you're using the Veracity Learning LRS, then you can select the small downward facing arrow beneath your LRS's name to view the LRS endpoint.

Select dropdown arrow beneath LRS title

After selecting the dropdown arrow, you should see your endpoint URL.

Copy and paste LRS endpoint from beneath the LRS title

Enter your endpoint within quotation marks as the value for the "endpoint" property. Make sure that there is a final forward slash after your URL.

You also need to set up the "auth" value so that it's ready to take the API Key username and password. Update your "conf" object so that it looks like this:

{% c-block language="js" %}
const conf = {
"endpoint": "https://my-test-lrs.lrs.io/xapi/",
"auth": "Basic " + toBase64("Username:Password")
};
{% c-block-end %}

Important note: Ensure that there is a space between Basic and the quotation mark: "Basic ", not "Basic".

Now look at your API Key information in the Veracity LRS window that we left open earlier. Copy and paste the string in the "Username" field to the Username placeholder in our code, and do the same with the password and the Password placeholder.

The quotation mark before the username and the quotation mark after the password should remain.

Finally, we need to tell the LRS that we've updated the configuration. We can do this by using the following line of code immediately after defining the "conf" object:

{% c-block language="js" %}
ADL.XAPIWrapper.changeConfig(conf);
{% c-block-end %}

All together, your code should look like this:

{% c-block language="js" %}
function sendStatement(verb, verbId, object, objectId) {
const player = GetPlayer();
const uNamejs = player.GetVar("uName");
const uEmailjs = player.GetVar("uEmail");
const conf = {
  "endpoint": "https://my-test-lrs.lrs.io/xapi/",
  "auth": "Basic " + toBase64("oiejfow:neionhwm")
};
ADL.XAPIWrapper.changeConfig(conf);
const statement = {
  "actor": {
    "name": uNamejs,
    "mbox": "mailto:" + uEmailjs
  },
  "verb": {
    "id": verbId,
    "display": { "en-US": verb }
  },
  "object": {
    "id": objectId,
    "definition": {
      "name": { "en-US": object }
    }
  }
};
const result = ADL.XAPIWrapper.sendStatement(statement);
}
{% c-block-end %}

Great work! If you've made it to this point, then rest assured that the most technical parts are behind you. There are a few more steps, but you're almost there.

Adding the JavaScript Trigger(s) in Storyline

In this section, you'll see why the function that we've set up is so powerful.

Open the Storyline project that you started in Part 2 to collect the user's name and email address. We want to send an xAPI statement whenever the user clicks the "Submit" button, so we'll need create a JavaScript trigger to do so.

First, select the "Create a new trigger" icon.

Select "Create a New Trigger" icon on right toolbar

Now configure the trigger to "Execute JavaScript" whenever the user clicks the custom "Submit" button.

Add execute JavaScript trigger

Select the ellipsis next to "Script", then you will see a textbox to enter the JavaScript code that you would like to run. This is where we type the JavaScript function.

Remember, our function takes four parameters: verb, verbId, object, and objectId. Using the information that you learned during Part 1 of this tutorial series, pass the arguments that we would like to send when the user clicks the submit button.

The code in your Execute JavaScript trigger should look like this:

{% c-block language="js" %}
sendStatement("initialized", "http://adlnet.gov/expapi/verbs/initialized", "Sample Course 1","https://www.devlinpeck.com/samplecourse1");
{% c-block-end %}

Important notes: Wrap each argument in quotation marks, and ensure that there is a comma between each argument that you pass. Also, avoid any special characters (such as ?, !, #, etc.) when passing the "object" parameter.

After adding the code to the JavaScript trigger, select "Ok" twice so that the trigger gets added to the course.

The best thing about this workflow is that you can send xAPI statements from anywhere that you can add a Storyline trigger, and all you need to do is switch out what information you'd like to send via the arguments.

For example, if you want to send a "completion" statement when the user arrives at the course completion slide, you can add the following JavaScript trigger when the timeline starts on that slide:

{% c-block language="js" %}
sendStatement("completed", "http://adlnet.gov/expapi/verbs/completed", "Sample Course 1","https://www.devlinpeck.com/samplecourse1");
{% c-block-end %}

The possibilities are vast, but you probably get the idea. However, we're not done yet. The final thing you need to do is make sure that your Storyline course can speak to your JavaScript files. We'll do this in the next section!

Modifying the Storyline Project's Output Folder

We need to publish the Storyline course before we can link it to the external JavaScript files (such as xapi-statement.js).

The custom statements will send from any type of content package, so it does not matter if we publish the course as an HTML5 package, SCORM package, or xAPI package. We're going to keep it simple and publish as HTML5.

From Storyline's "HOME" tab, select "Publish". After that, select "Web", then press "PUBLISH".

When the file is done publishing, select OPEN to open up the folder in your file explorer. Next, download the xAPI Wrapper's JavaScript file to this folder, as this is what let's us take advantage of the wrapper. You can download the .js file here, which is a direct link to the official download.

Save the xapiwrapper.min.js file to your Storyline Output folder or move it there from your downloads folder once it's finished downloading.

Important note: Ensure that the xapiwrapper.min.js file is in your published Storyline Output folder before continuing.

Add xapiwrapper.min.js file to the Storyline output folder

With the xapiwrapper.js file taken care of, you need to add your xapi-statement.js file (the one that you've been working on throughout these tutorials) to the published output folder. I recommend keeping the original saved somewhere safe on your hard drive, as you will be able to use this template for many different Storyline projects.

Once both of these files are in the output folder, we need to tell the Storyline course how to access them. We can do this by right-clicking the "story.html" file (or "story_html5.html" file if you have an older version of Storyline) and opening it with your text editor of choice.

Open the story.html file with a code editor screenshot

You'll see a lot of code, but we'll only be in this file for a minute. Copy the following two lines of code. They tell the .story file where to access the additional JavaScript scripts.

{% c-block language="markdown" %}
<script type="text/javascript" src="xapiwrapper.min.js"></script>
<script type="text/javascript" src="xapi-statement.js"></script>
{% c-block-end %}

Important note: If you've changed the name of your xapi-statement.js file, ensure that you update it in the script tag that you copied above.

With both of the script tags copied, look back to the story.html (or story_html5.html) file. You can either scroll down or use Crtl + F to find the closing body tag (). Add a few line breaks and paste the script tags right above the closing body tag.

Add scripts above closing body tag screenshot

With that done, save the file and feel free to close it. Our xAPI statements are ready to fire.

Testing the xAPI Statement

To make sure that your xAPI statements generate as desired, go ahead and open up the project's story.html (or story_html5.html) file in your browser of choice (you can usually do this just by double clicking the file from your file explorer). Fill out your name and email address, then click "Submit".

Enter a name and email address, then select Submit.

Now hop back over to your LRS. If you're using the Veracity Learning LRS, then select "xAPI Data" from the menu on the left. After that, select "Statements."

If you see the statement there, then congratulations! You've successfully generated an xAPI statement from Storyline. If you've run into an error, then you should refer to the common xAPI and Storyline troubleshooting steps.

See the Full Guide to xAPI and Storyline to continue learning with the intermediate-level tutorials.

Devlin Peck
About
Devlin Peck
Devlin Peck is the founder of DevlinPeck.com, where he helps people build instructional design skills and break into the industry. He previously worked as a freelance instructional designer and graduated from Florida State University.
Learn More about
Devlin Peck
.

Explore more content

Explore by tag