Experience API

Advanced Distributed Learning (ADL) Co-Laboratories

This document was authored by members of the Experience API Working Group (see list on pp. 7–8) in support of the Office of the Deputy Assistant Secretary of Defense (Readiness) Advanced Distributed Learning (ADL) Initiative. Please send all feedback and inquiries to helpdesk@adlnet.gov

Table of Contents

1.0 Revision History

0.8 (Project Tin Can API Deliverable) to 0.9 (March 31, 2012):

Rustici Software, who delivered the Project Tin Can API, made modifications to the API prior to the April 2012 Kickoff Meeting. It was voted in this meeting to move those changes into the current specification and revision to 0.9.

0.90 to 0.95 (August 31, 2012):

“Core” verbs and activity types were removed from the specification. References to these verbs in results, context, interactions, and activity definitions were also removed. It was recommended that implementers prefer community defined verbs to creating their own verbs. - Verbs, activity types, and extension keys are now URIs - Restructured and added language around some of the other implementation details and scope. - Changed from using a person-centric view of agents to a persona-centric view. - Friend of a Friend (FOAF) agent merging requirement was removed. - Agent objects must now have exactly 1 uniquely identifying property, instead of at least one.

0.95 to 1.0.0 (April 26, 2013):

Various refinements and clarifications including: - Adding attachments - Activity metadata is now stored as JSON rather than XML - Changes to voiding statements - Clarification and naming of the Document APIs - Changes to querying the Statement API - Signed statements

2.0 Role of the Experience API

The Experience API is a service that allows for statements of experience to be delivered to and stored securely in a Learning Record Store (LRS). These statements of experience are typically learning experiences, but the API can address statements of any kind of experience. The Experience API is dependent on Learning Activity Providers to create and track these learning experiences; this specification provides a data model and associated components on how to accomplish these tasks.

Specifically, the Experience API provides:

The Experience API is the first of many envisioned technologies that will enable a richer architecture of online learning and training. Authentication services, querying services, visualization services, and personal data services are some examples of additional technologies for which the Experience API is designed to support. While the implementation details of these services are not specified here, the Experience API is designed with this larger architectural vision in mind.

2.1 ADL’s Role in the Experience API

ADL has taken a role of steward and facilitator in the development of the Experience API. The Experience API is seen as one piece of the ADL Training and Learning Architecture, which facilitates learning anytime and anywhere. ADL views the Experience API as an evolved version of SCORM that can support similar use cases, but can also support many of the use cases gathered by ADL and submitted by those involved in distributed learning which SCORM could not enable.

2.2 Contributors

“My thanks to everyone who contributed to the Experience API project. Many of you have called into the weekly meetings and helped to shape the specification into something that is useful for the entire distributed learning community. Many of you assisted in releasing code samples, products, and documentation to aid those who are creating and adopting the specification. I’d also like to thank all of those who were involved in supplying useful, honest information about your organization’s use of SCORM and other learning best practices. Through the use-cases, shared experiences, and knowledge you have shared, ADL and the community clearly identified the first step in creating the Training and Learning Architecture–the Experience API. You are truly the community leaders on which we depend to make our training and education the very best.”

Kristy S. Murray, Ed.D.
Director, ADL Initiative
OSD, Training Readiness & Strategy (TRS)

2.2.1 Working Group Participants

Aaron SilversADL
Al BejcekNetDimensions
Ali ShahrazadSaLTBOX
Andrew DownesEpic
Andy JohnsonADL
Andy WhitakerRustici Software
Anthony AltieriAmerican Red Cross
Anto ValanOmnivera Learning Solutions
Avron BarrAldo Ventures, Inc.
Ben ClarkRustici Software
Bill McDonaldBoeing
Brian J. MillerRustici Software
Chad UdellFloat Mobile Learning
Chris SawwaMeridian Knowledge Solutions
Dan AllenLitmos
Dan KuemmelSentry Insurance
Dave MozealousArticulate
David EllsRustici Software
David N. JohnsonClear Learning Systems
Doug HagyTwin Lakes Consulting Corporation
Eric JohnsonPlanning and Learning Technologies, Inc.
Fiona LeteneyFeenix e-learning
Greg TatkaMenco Social Learning
Ingo DahnUniversity Koblenz-Landau
Jason HaagADL
Jeff PlaceQuestionmark
Jennifer CameronSencia Corporate Web Solutions
Jeremy Brockman
Jhorlin De ArmasRiptide Software
Joe GorupCourseAvenue
John KleemanQuestionmark
Jonathan ArchibaldBrightwave
Jonathan PoltrackADL
Kris Milleredcetra Training
Kris RockwellHybrid Learning Systems
Lang Holloman
Lou WolfordADL
Luke HickeydominKnow
Marcus BirtwhistleADL
Mark DavisExambuilder
Matteo Scaramuccia
Megan BoweRustici Software
Melanie VanHornADL
Michael FloresHere Everything's Better
Michael RobertsvTrainingRoom
Mike PalmerOnPoint Digital
Mike RusticiRustici Software
Nick WashburnRiptide Software
Nikolaus HruskaADL
Patrick KedzioraKedzoh
Paul EschNine Set
Paul RobertsQuestionmark
Rich ChetwyndLitmos
Richard FouchauxOntario Human Rights Commission
Richard LenzOrganizational Strategies, Inc.
Rick Raymer
Rob ChadwickADL
Robert LoweNetDimensions
Russell DuhonSaLTBOX
Stephen TrevorrowProblem Solutions, LLC.
Steve Baumgartner
Steve FlowersXPConcept
Thomas Ho
Tim MartinRustici Software
Tom CreightonADL
Walt GrataADL

2.2.2 Requirements Gathering Participants

In collection of requirements for the Experience API, many people and organizations provided invaluable feedback to the Sharable Content Object Reference Model (SCORM®), distributed learning efforts, and learning technology efforts in general. While not an exhaustive listing, the white papers gathered in 2008 by the Learning Education and Training Standards Interoperability (LETSI) group, the Rustici Software UserVoice website, one-on-one interviews and various blogs were important sources from which requirements were gathered for the Experience API specification.

2.3 Reading guidelines for the non-technically inclined.

This is a definitive document which describes how the Experience API is to be implemented across a variety of systems. It is a technical document authored specifically for individuals and organizations implementing this technology with the intent of such individuals developing interoperable tools, systems and services that are independent of each other and interoperable with each other.

Whenever possible, the language and formatting used in this document is intended to be considerate of non-technical readers because various tools, systems and services are based on the specification set described below. For this reason, sections that provide a high-level overview of a given facet of the Experience API are labeled description or rationale. Items in this document labeled as details or examples are more technical.

3.0 Definitions

Activity: A thing with which to be interacted. An Activity can be a unit of instruction, experience, or performance that is to be tracked in meaningful combination with a Verb. Interpretation of ‘Activity’ is broad, meaning that Activities can even be tangible objects. In the statement “Anna tried a cake recipe,” the recipe constitutes the Activity in terms of the xAPI statement. Other examples include a book, an e-learning course, a hike or a meeting.

Activity Provider (AP): The software object that is communicating with the LRS to record information about a learning experience. May be similar to a SCORM package in that it is possible to bundle learning assets with the software object that performs this communication, but an Activity Provider may also be separate from the experience it is reporting about.

Actor: An identity or persona of an individual or group tracked using Statements as doing an
action (Verb) within an Activity.

Authentication: The concept of verifying the identity of a user or system. Authentication allows interactions between the two “trusted” parties.

Authorization: The affordance of permissions based on a user or system’s role; the process of making one user or system “trusted” by another.

Community of Practice: A group, usually connected by a common cause, role or purpose, which operates in a common modality.

Experience API (xAPI): The API defined in this document, the product of “Project Tin Can”. A simple, lightweight way for any permitted actor to store and retrieve extensible learning records, learner and learning experience profiles, regardless of platform.

Immutable: Adjective used to describe things which cannot be changed. With some exceptions, statements in the xAPI are immutable. This ensures that when statements are shared between LRSs, multiple copies of the statement remain the same.

Internationalized Resource Idenfiers (IRI): A unique identifier which may be a URL. In the xAPI, all IRIs should be a full absolute IRI including a scheme. Relative IRIs should not be used. URLs should be defined within a domain controlled by the person creating the URL.

Inverse Functional Identifier: An identifier which is unique to a particular persona or group. Used to identify Agents and Groups.

Learning Management System (LMS): “A software package
used to administer one or more courses to one or more learners. An LMS is typically a web-based system that allows learners to authenticate themselves, register for courses, complete courses and take
assessments” (LSAL, 2004 in Gallagher, 2007). In this document the term will be used in the context of existing systems implementing learning standards.

Learning Record Store (LRS): A system that stores learning information. Prior to the xAPI most LRSs were Learning Management Systems (LMSs), however this document uses the term LRS to be clear that a full LMS is not necessary to implement the xAPI. The xAPI is dependent on an LRS to function.

MUST / SHOULD / MAY: Three levels of obligation with regards to conformance to the xAPI specification. A system that fails to implement a MUST (or a MUST NOT) requirement is non-conformant. Failing to meet a SHOULD requirement is not a violation of conformity, but goes against best practices. MAY indicates an option, to be decided by the developer with no consequences for conformity.

Profile: A construct where information about the learner or activity is kept, typically in name/document pairs that have meaning to an instructional system component.

Registration: An instance of a learner experiencing a particular Activity.

Representational State Transfer (REST): An architecture for designing networked web Services. It relies on HTTP methods and uses current web best practices.

Service: A software component responsible for one or more aspects of the distributed learning process. An LMS typically combines many services to design a complete learning experience.

Statement: A simple construct consisting of <Actor (learner)> <verb> <object>, with <result>, in <context> to track an aspect of a learning experience. A set of several statements may be used to track complete details about a learning experience.

Tin Can API (TCAPI): The previous name of the API defined in this document, often used in informal references to the Experience API.

Verb: Defines the action being done by the Actor within the Activity within a Statement.

4.0 Statement

The statement is the core of the xAPI. All learning events are stored as statements. A statement is akin to a sentence of the form “I did this”.

4.1 Statement Properties:

Actor, verb, and object are required, all other properties are optional. Properties can occur in any order, but are limited to one use each. Each property is discussed below.

idUUID UUID assigned by LRS if not set by the Learning Activity Provider.
actorObject Who the statement is about, as an Agent or Group object. Represents the "I" in "I Did This".
verbObject Action of the Learner or Team object. Represents the "Did" in "I Did This".
objectObject Activity, agent, or another statement that is the object of the statement. Represents the "This" in "I Did This". Note that objects which are provided as a value for this field should include an "objectType" field. If not specified, the object is assumed to be an activity.
resultObject Result object, further details representing a measured outcome relevant to the specified verb.
contextObject Context that gives the statement more meaning. Examples: a team the actor is working with, altitude at which a scenario was attempted in a flight simulator.
timestampDate/Time Timestamp (Formatted according to ISO 8601) of when the events described within this statement occurred. If not provided, LRS should set this to the value of "stored" time.
storedDate/Time Timestamp (Formatted according to ISO 8601) of when this statement was recorded. Set by LRS.
authorityObject Agent who is asserting this statement is true. Verified by the LRS based on authentication, and set by LRS if left blank.
versionVersion The statement’s associated xAPI version, formatted according to Semantic Versioning 1.0.0.
attachments Array of attachment objects Headers for attachments to the statement

Aside from (potential or required) assignments of properties during LRS processing (“id”, “authority”, “stored”, “timestamp”, “version”) statements are immutable. Note that the content of activities that are referenced in statements is not considered part of the statement itself. So while the statement is immutable, the activities referenced by that statement are not. This means a deep serialization of a statement into JSON will change if the referenced activities change (see the Statement API’s “format” parameter for details).

An example of the simplest possible statement using all properties that MUST or SHOULD be used:
{ "id": "12345678-1234-5678-1234-567812345678", "actor":{ "mbox":"mailto:xapi@adlnet.gov" }, "verb":{ "id":"http://adlnet.gov/expapi/verbs/created", "display":{ "en-US":"created" } }, "object":{ "id":"http://example.adlnet.gov/xapi/example/activity" } }
See Appendix D: Example Statements for more examples.

4.1.1 ID:


A UUID (see RFC 4122 for requirements, and the UUID must be in standard string form).


4.1.2 Actor:


A mandatory Agent or Group object. When the Actor ObjectType is Agent

An Agent (an individual) is a persona or system.


The table below lists the properties of Agent objects.

objectTypestring"Agent". This property is optional except when the Agent is used as a statement's Object.no
namestringFull name of the Agent.no
see Inverse Functional Identifier An inverse functional identifier unique to the Agent.yes When the Actor ObjectType is Group

A Group represents a collection of Agents and can be used in most of the same situations an Agent can be used. There are two types of Groups, anonymous and identified.


An anonymous Group is used describe a cluster of people where there is no ready identifier for this cluster, e.g. an ad hoc team;

The table below lists all properties of an anonymous Group.

objectTypeString"Group". yes
nameStringName of the group.no
memberArray of Agent objectsThe members of this Group.yes

An identified Group is used to uniquely identify a cluster of Agents.

The table below lists all properties of an identified Group.

objectTypeString"Group". yes
nameStringName of the group.no
memberArray of Agent objectsThe members of this Group.yes
see Inverse Functional Identifier An inverse functional identifier unique to the Group.yes Inverse Functional Identifier

An “inverse functional identifier” is a value of an Agent or identified Group that is guaranteed to only ever refer to that Agent or identified Group.


Learning experiences become meaningless if they cannot be attributed to identifiable individuals and/or groups. In an xAPI statement this is accomplished with a set of inverse functional identifiers loosely inspired on the widely accepted FOAF principle (see: Friend Of A Friend).


The table below lists all possible inverse functional identifier properties.

mboxmailto URIThe required format is "mailto:email address".
The local part of the email address must be URI encoded.
Only email addresses that have only ever been and will ever be assigned to this Agent, but no others, should be used for this property and mbox_sha1sum.
mbox_sha1sumstringThe SHA1 hash of a mailto URI (i.e. the value of an mbox property). An LRS MAY include Agents with a matching hash when a request is based on an mbox.
openIDURIAn openID that uniquely identifies the Agent.
accountaccount objectA user account on an existing system e.g. an LMS or intranet.

Account Object

A user account on an existing system, such as a private system (LMS or intranet) or a public system (social networking site).


The table below lists all properties of Account objects.

homepageURLThe canonical home page for the system the account is on. This is based on FOAF's accountServiceHomePage.
namestringThe unique ID or name used to log in to this account. This is based on FOAF's accountName.

This example shows an agent identified by an opaque account:

{ "objectType": "Agent", "account": { "homePage": "http://www.example.com", "name": "1625378" } }

4.1.3 Verb:


The verb defines the action between Actor and Activity.


The verb in an xAPI statement describes the action performed during the learning experience. The xAPI does not specify any particular verbs. (With one exception, namely the reserved verb ‘http://adlnet.gov/expapi/verbs/voided’). Instead, it defines how to create verbs so that communities of practice can establish verbs meaningful to their members and make them available for use by anyone. A predefined list of verbs would be limited by definition and might not be able to effectively capture all possible future learning experiences.


Verbs appear in statements as objects consisting of a URI and a set of display names corresponding to multiple languages or dialects which provide human-readable meanings of the verb.

The table below lists all properties of the Verb object.

id URI Corresponds to a verb definition. Each verb definition corresponds to the meaning of a verb, not the word. The URI should be human-readable and contain the verb meaning.
display Language Map The human readable representation of the verb in one or more languages. This does not have any impact on the meaning of the statement, but serves to give a human-readable display of the meaning already determined by the chosen verb.

{ "verb" : { "id":"http://www.adlnet.gov/XAPIprofile/ran(travelled_a_distance)", "display":{ "en-US":"ran", "es" : "corrió" } } }

The verb in the example above is included for illustrative purposes only. This is not intended to imply that a verb with this meaning has been defined with this id. This applies to all example verbs given in this specification document, with the exception of the reserved verb ‘http://adlnet.gov/expapi/verbs/voided’. Use in Language and Semantics of Verbs

The URI represented by the Verb id identifies the particular semantics of a word, not the word itself.

For example, the English word “fired” could mean different things depending on context, such as “fired a weapon”, “fired a kiln”, or “fired an employee”. In this case, a URI MUST identify one of these specific meanings, not the word “fired”.

The display property has some flexibility in tense. While the verb URIs are expected to remain in the past tense, if conjugating verbs to another tense (using the same verb) within the Activity makes sense, it is allowed.


A verb in the Experience API is a URI, and denotes a specific meaning not tied to any particular language.

For example, a particular verb URI such as http://example.org/firearms#fire might denote the action of firing a gun, or the verb URI http://example.com/فعل/خواندن might denote the action of reading a book. Use in Communities of Practice
Establishing New Verbs

Communities of practice will, at some point in time, need to establish new Verbs to meet the needs of their constituency.

Verb Lists and Repositories

It is expected that xAPI generates profiles, lists, and repositories that become centered on Verb vocabularies. ADL is one such organization that is creating a companion document containing Verbs for xAPI. In fulfillment of the requirements above, a collection of IRIs of recommended verbs exists. There are times when Activity Providers may wish to use a different verb for the same meaning.

4.1.4 Object


The Object of a Statement can be an Activity, Agent/Group, Sub-Statement, or Statement Reference. It is the “this” part of the statement, i.e. “I did this”.

Some examples:


Objects which are provided as a value for this field SHOULD include an “objectType” field. If not specified, the objectType is assumed to be “Activity”. Other valid values are: Agent, Group, Sub-Statement or StatementRef. The properties of an Object change according to the objectType. When the ObjectType is Activity

A statement may represent an activity as the object of the statement. The following table lists the Object properties in this case.

objectType String MUST be "Activity" when present. Optional in all cases.
idURI An identifier for a single unique activity. Required.
definition Object Optional Metadata, See below

Activity Definition

The table below lists the properties of the Activity Definition Object:

name Language Map Recommended The human readable/visual name of the activity
description Language Map Recommended A description of the activity
type URI Recommended The type of activity.
moreInfo URL Optional SHOULD resolve to a document human-readable information about the activity, which MAY include a way to 'launch' the activity.
Interaction properties, See: Interaction Activities
extensions Object Optional A map of other properties as needed (see: Extensions)

URI fragments (sometimes called relative URLs) are not valid URIs. As with verbs, it is recommended that Activity Providers look for and use established, widely adopted, activity types.

An LRS should update its internal representation of an activity’s definition upon receiving a statement with the same activity ID, but with a different definition of the Activity from the one stored, but only if it considers the Activity Provider to have the authority to do so.

Activity ID

If it were possible to use the same ID for two different activities, the validity of statements about these Activities could be questioned. This means an LRS may never treat (references to) the same Activity id as belonging to two different Activities, even if it thinks this was intended. Namely, when a conflict with another systems occurs, it’s not possible to determine the intentions.

Activity Metadata

Interaction Activities

Traditional e-learning has included structures for interactions or assessments. As a way to allow these practices and structures to extend Experience API’s utility, this specification includes built-in definitions for interactions, which borrows from the SCORM 2004 4th Edition Data Model. These definitions are intended to provide a simple and familiar utility for recording interaction data. These definitions are simple to use, and consequently limited. It is expected that communities of practice requiring richer interactions definitions will do so through the use of extensions to an activity’s type and definition.


The table below lists the properties for Interaction activities.

interactionType String As in "cmi.interactions.n.type" as defined in the SCORM 2004 4th Edition Run-Time Environment.
correctResponsesPattern An array of strings Corresponds to "cmi.interactions.n.correct_responses.n.pattern" as defined in the SCORM 2004 4th Edition Run-Time Environment, where the final n is the index of the array.
choices | scale | source | target | steps Array of interaction components Specific to the given interactionType (see below).
Interaction Components

Interaction components are defined as follows:

id String As in "cmi.interactions.n.id" as defined in the SCORM 2004 4th Edition Run-Time Environment
description Language Map A description of the interaction component (for example, the text for a given choice in a multiple-choice interaction)

The following table shows the supported lists of CMI interaction components for an interaction activity with the given interactionType.

interactionTypesupported component list(s)
choice, sequencingchoices
matchingsource, target
true-false, fill-in, numeric, other[No component lists defined]

See Appendix C for examples of activity definitions for each of the cmi.interaction types. When the “Object” is an Agent or a Group

See Section 4.1.2 Actor for details regarding Agents. When the “Object” is a Statement

There are two possibilities for using a Statement as an Object. First, an Object can take on the form of a statement that already exists by using a Statement Reference. A common use case for Statement References is grading or commenting on an experience that could be tracked as an independent event. The special case of voiding a statement would also use a Statement Reference. Second, an Object can be brand new statement by using a Sub-Statement. A common use case for Sub-Statements would be any experience that would be misleading as its own statement. Each type is defined below.

Statement References

A Statement Reference is a pointer to another pre-existing Statement.


The table below lists all properties of a Statement Reference object:

objectTypestringIn this case, MUST be "StatementRef".
idUUIDThe UUID of a Statement which is present on the system.
Statement References - Example

Assuming that some statement has already been stored with the ID 8f87ccde-bb56–4c2e-ab83–44982ef22df0, the following example shows how a comment could be issued on the original statement, using a new statement:

{ "actor" : { "objectType": "Agent", "mbox":"mailto:test@example.com" }, "verb" : { "id":"http://example.com/commented", "display": { "en-US":"commented" } }, "object" : { "objectType":"StatementRef", "id":"8f87ccde-bb56-4c2e-ab83-44982ef22df0" }, "result" : { "response" : "Wow, nice work!" } }


A Sub-Statement is a new statement included as part of a parent statement.

Sub-Statements - Example

One interesting use of Sub-Statements is in creating Statements of intention. For example, using Sub-Statements we can create Statements of the form "<I> <planned> (<I> <did> <this>)" to indicate that we’ve planned to take some action. The concrete example that follows logically states that “I planned to visit ‘Some Awesome Website’”.

{ "actor": { "objectType": "Agent", "mbox":"mailto:test@example.com" }, "verb" : { "id":"http://example.com/planned", "display":{ "en-US":"planned" } }, "object": { "objectType": "SubStatement", "actor" : { "objectType": "Agent", "mbox":"mailto:test@example.com" }, "verb" : { "id":"http://example.com/visited", "display":{ "en-US":"will visit" } }, "object": { "id":"http://example.com/website", "definition": { "name" : { "en-US":"Some Awesome Website" } } } } }

4.1.5 Result:


An optional field that represents a measured outcome related to the statement in which it is included.


The following table contains the properties of the Results Object.

score Object The score of the Agent in relation to the success or quality of the experience. See: Score
successBooleanIndicates whether or not the attempt on the activity was successful.
completionBooleanIndicates whether or not the activity was completed.
responseStringA response appropriately formatted for the given activity.
durationFormatted according to ISO 8601 with a precision of 0.01 secondsPeriod of time over which the Statement occurred.
ExtensionsObjectA map of other properties as needed. See: Extensions Score

An optional numeric field that represents the outcome of a graded activity achieved by an agent.


The table below defines the Score Object.

scaledDecimal number between -1 and 1, inclusiveCf. 'cmi.score.scaled' in SCORM 2004 4th Edition
rawDecimal number between min and max (if present, otherwise unrestricted), inclusiveCf. 'cmi.score.raw'
minDecimal number less than max (if present)Cf. 'cmi.score.min'
maxDecimal number greater than min (if present)Cf. 'cmi.score.max'

4.1.6 Context


An optional field that provides a place to add contextual information to a statement. All properties are optional.


The “context” field provides a place to add some contextual information to a statement. It can store information such as the instructor for an experience, if this experience happened as part of a team activity, or how an experience fits into some broader activity.


The following table contains the properties of the Context Object.

registration UUID The registration that the statement is associated with.
instructor Agent (may be a group) Instructor that the statement relates to, if not included as the Actor of the statement.
team Group Team that this statement relates to, if not included as the Actor of the statement.
contextActivities contextActivities object A map of the types of learning activity context that this statement is related to. Valid context types are: "parent", "grouping", "category" and "other".
revision String Revision of the learning activity associated with this statement. Format is free.
platform String Platform used in the experience of this learning activity.
language String (as defined in RFC 5646) Code representing the language in which the experience being recorded in this statement (mainly) occurred in, if applicable and known.
statement Statement by reference or by object Another statement (either existing or new), which should be considered as context for this statement. See: When the "Object" is a Statement for details about including statements within other statements.
extensions Object A map of any other domain-specific context relevant to this statement. For example, in a flight simulator altitude, airspeed, wind, attitude, GPS coordinates might all be relevant (See Extensions)

Note: Revision has no behavioral implications within the scope of xAPI. It is simply stored, so that it is available for reporting tools. Registration property

An instance of a learner undertaking a particular learning activity.


When an LRS is an integral part of an LMS, the LMS likely supports the concept of registration. The Experience API applies the concept of registration more broadly. A registration could be considered to be an attempt, a session, or could span multiple Activities. There is no expectation that completing an activity ends a registration. Nor is a registration necessarily confined to a single Agent. contextActivities property

A map of the types of learning activity context that this statement is related to.


Many statements do not just involve one object activity that is the focus, but relate to other contextually relevant activities. “Context activities” allow for these related activities to be represented in a structured manner.


There are four valid context types. All, any or none of these MAY be used in a given statement:

  1. Parent : an activity with a direct relation to the activity which is the object of the statement. In almost all cases there is only one sensible parent or none, not multiple. For example: a statement about a quiz question would have the quiz as its parent activity.

  2. Grouping : an activity with an indirect relation to the activity which is the object of the statement. For example: a course that is part of a qualification. The course has several classes. The course relates to a class as the parent, the qualification relates to the class as the grouping.

  3. Category : an activity used to categorize the statement. “Tags” would be a synonym. Category SHOULD be used to indicate a “profile” of xAPI behaviors, as well as other categorizations. For example: Anna attempts a biology exam, and the statement is tracked using the CMI–5 profile. The statement’s activity refers to the exam, and the category is the CMI–5 profile.

  4. Other : a context activity that doesn’t fit one of the other fields. For example: Anna studies a textbook for a biology exam. The statement’s activity refers to the textbook, and the exam is a context activity of type “other”.

Single Activity objects are allowed as values so that 0.95 statements will be compatible with 1.0.0.


The values in this section are not for expressing all the relationships the statement object has. Instead, they are for expressing relationships appropriate for the specific statement (though the nature of the object will often be important in determining that). For instance, it is appropriate in a statement about a test to include the course the test is part of as parent, but not to include every possible degree program the course could be part of in the grouping value.


Consider the following hierarchical structure: “Questions 1 to 6” are part of “Test 1” which in turn belongs to the course “Algebra 1”. The six questions are registered as part of the test by declaring “Test 1” as their parent. Also they are grouped with other statements about “Algebra 1” to fully mirror the hierarchy. This is particularly useful when the object of the statement is an agent, not an activity. “Andrew mentored Ben with context Algebra I”.

{ "parent" : [{ "id" : "http://example.adlnet.gov/xapi/example/test 1" }], "grouping" : [{ "id" : "http://example.adlnet.gov/xapi/example/Algebra1" }] }

4.1.7 Timestamp:


The time at which a statement about an experience took place.


A timestamp:

A reporting tool:


A timestamp in a statement related to learning that occurs outside of the system can differ from 4.1.8. Stored (the system time of the event). Namely, there can be delays between the occurrence of the experience and the reception of the corresponding statement by the LRS. Another cause is when statements are propagated to other systems.

4.1.8 Stored:


The time at which a statement is stored by the LRS.


Stored time:

A reporting tool:

4.1.9 Authority:

The authority property provides information about who or what has asserted that this statement is true. Consequently, the asserting authority may be an Agent (representing the authenticating user or some system or application), or in the case of 3-legged OAuth workflows, a Group of two Agents representing an application and user together. Unless used in the aforementioned 3-legged OAuth workflow, a Group MUST NOT be used to assert authority.

LRS Requirements:

If a statement is stored using a validated OAuth connection, and the LRS creates or modifies the authority property of the statement, the authority MUST contain an agent object that represents the OAuth consumer, either by itself, or as part of a group in the case of 3-legged OAuth. This agent MUST be identified by account, and MUST use the consumer key as the account name field. If the OAuth consumer is a registered application, then the token request endpoint MUST be used as the account homePage, otherwise the temporary credentials endpoint must be used as the account homePage.

Except as specified in the paragraph above, agent objects MUST NOT use any OAuth endpoint as an account homePage.

The behavior of an LRS SHOULD NOT rely on Agents with an account using the temporary credentials endpoint as the homePage and with matching account names coming from the same source, as there is no way to verify that, since multiple unregistered applications could choose the same consumer key. Each unregistered consumer SHOULD pick a unique consumer key.

If a user connects directly (using HTTP Basic Authentication) or is included as part of a 3-legged OAuth workflow, the LRS MUST include the user as an Agent in the authority, and MAY identify the user with any of the legal identifying properties.

OAuth Authorities

In a 3-legged OAuth workflow, authentication involves both an OAuth consumer and a user of the OAuth service provider. For instance, requests made by an authorized Twitter plugin on your Facebook account will include credentials that are specific not only to Twitter as a client application, or you as a user, but the unique combination of both.

To support this concept, an LRS preparing the authority of a statement received via 3-legged OAuth MUST use a pairing of an application and a user. Below is a concrete example which represents a pairing of an OAuth consumer and a user.

"authority": { "objectType" : "Group", "member": [ { "account": { "homePage":"http://example.com/xAPI/OAuth/Token", "name":"oauth_consumer_x75db" } }, { "mbox":"mailto:bob@example.com" } ] }

4.1.10 Version

Version information in statements helps systems that process data from an LRS get their bearings. Since the statement data model is guaranteed consistent through all 1.0.x versions, in order to support data flow among such LRSs the LRS is given some flexibility on statement versions that are accepted.

LRS Requirements
Client Requirements

4.1.11 Attachments


A digital artefact providing evidence of a learning experience.


In some cases an attachment may logically be an important part of a learning record. Think of a simulated communication with ATC, an essay, a video, etc. Another example of such an attachment is (the image of) a certificate that was granted as a result of an experience. It is useful to have a way to store these attachments in and retrieve them from an LRS.


The table below lists all properties of the Attachment object.

    <td>Identifies the usage of this attachment. For example: one expected use case
    for attachments is to include a "completion certificate". A type URI corresponding
    to this usage should be coined, and used with completion certificate attachments.</td>
    <td><a href="#misclangmap">Language Map</a></td>
    <td>Display name (title) of this attachment.</td>
    <td><a href="#misclangmap">Language Map</a></td>
    <td>A description of the attachment</td>
    <td><a href="https://www.ietf.org/rfc/rfc2046.txt?number=2046">Internet Media Type</a></td>
    <td>The content type of the attachment.</td>
    <td>The length of the attachment data in octets</td>
    <td>The SHA-2 hash of the attachment data. A minimum key size of 256 bits is recommended.</td>
    <td>A URL at which the attachment data may be retrieved, or from which it used to be retrievable. </td>


Procedure for the exchange of attachments

Since these attachments may lead to very large statements, it should be possible for a client to filter out attachments when retrieving statements, by following this procedure:

  1. A statement including an attachment is construed according to the Transmission Format described below.

  2. The statement is sent to the receiving system using a content-Type of “multipart/mixed”. The attachments are placed at the end of such transmissions.

  3. The receiving system decides whether to accept or reject the statement based on the \ information in the first part.

  4. If it accepts the attachment, it can match the raw data of an attachment with the attachment header in a statement by comparing the SHA–2 of the raw data to the SHA–2 declared in the header.

Requirements for attachment statement batches

A statement batch that includes attachments:

Requirements for the LRS:

Note: There is no requirement that statement batches using the mime/multipart format contain attachments.

Requirements for the client:

Below is an example of a very simple statement with an attachment. Please note the following:

Don’t forget the <CRLF> when building or parsing these messages.


Content-Type: multipart/mixed; boundary=abcABC0123'()+_,-./:=? X-Experience-API-Version:1.0.0 Content: ```

–abcABC0123’()+_,-./:=? Content-Type:application/json

{ “actor”: { “mbox”: “mailto:sample.agent@example.com”, “name”: “Sample Agent”, “objectType”: “Agent” }, “verb”: { “id”: “http://adlnet.gov/expapi/verbs/answered”, “display”: { “en-US”: “answered” } }, “object”: { “id”: “http://www.example.com/tincan/activities/multipart”, “objectType”: “Activity”, “definition”: { “name”: { “en-US”: “Multi Part Activity” }, “description”: { “en-US”: “Multi Part Activity Description” } } }, “attachments”: [ { “usageType”: “http://example.com/attachment-usage/test”, “display”: { “en-US”: “A test attachment” }, “description”: { “en-US”: “A test attachment (description)” }, “contentType”: “text/plain; charset=ascii”, “length”: 27, “sha2”: “495395e777cd98da653df9615d09c0fd6bb2f8d4788394cd53c56a3bfdcd848a” } ] } –abcABC0123’()+_,-./:=? Content-Type:text/plain Content-Transfer-Encoding:binary X-Experience-API-Hash:495395e777cd98da653df9615d09c0fd6bb2f8d4788394cd53c56a3bfdcd848a

here is a simple attachment –abcABC0123’()+_,-./:=?– ```

4.1.12 Data Constraints

All the properties used in statements are restricted to certain types, and those types constrain the behavior of systems processing statements. For clarity, certain key requirements are documented here, emphasizing where compliant systems have a responsibility to act in certain ways.

Client Requirements:

The following requirements reiterate especially important requirements already included elsewhere, to emphasize, clarify, and provide implementation guidance.

LRS Requirements:

4.2 Retrieval of Statements:

A collection of statements can be retrieved by performing a query on the “statements” endpoint, see Section 7.2 “Statement API” for details.
statementsArray of Statements List of statements. If the list returned has been limited (due to pagination), and there are more results, they will be located at the “statements” property within the container located at the URL provided by the “more” element of this statement result object.
moreURL Relative URL that may be used to fetch more results, including the full path and optionally a query string but excluding scheme, host, and port. Empty string if there are no more results to fetch.

        This URL must be usable for at least 24 hours after it is returned by the LRS. 
        In order to avoid the need to store these URLs and associated query data, an 
        LRS may include all necessary information within the URL to continue the 
        query, but should avoid generating extremely long URLs. The consumer should 
        not attempt to interpret any meaning from the URL returned.

4.3 Voided:


The certainty that an LRS has an accurate and complete collection of data is guaranteed by the fact that statements cannot be logically changed or deleted. This immutability of statements is a key factor in enabling the distributed nature of Experience API. However, not all statements are perpetually valid once they have been issued. Mistakes or other factors could require that a previously made statement is marked as invalid. This is called “voiding a statement” and the reserved verb “http://adlnet.gov/expapi/verbs/voided" is used for this purpose.


When issuing a statement that voids another, the object of that voiding statement…

Upon receiving a statement that voids another, the LRS…


{ "actor" : { "objectType": "Agent", "name" : "Example Admin", "mbox" : "mailto:admin@example.adlnet.gov" }, "verb" : { "id":"http://adlnet.gov/expapi/verbs/voided", "display":{ "en-US":"voided" } }, "object" : { "objectType":"StatementRef", "id" : "e05aa883-acaf-40ad-bf54-02c8ce485fb0" } }

This example statement voids a previous statement which it identifies with the statement ID “e05aa883-acaf–40ad-bf54–02c8ce485fb0”.


Any statement that voids another cannot itself be voided. An activity provider that wants to “unvoid” a previously voided statement…

A reporting system…

See “Statement References” in Section When the “Object” is a Statement for details about making references to other statements.

4.4 Signed Statements


A statement may include a digital signature to provide strong and durable evidence of the authenticity and integrity of the statement.


Some statements will have regulatory or legal significance, or otherwise require strong and durable evidence of their authenticity and integrity. It may be necessary to verify these statements without trusting the system they were first recorded in, or perhaps without access to that system. Digital signatures will enable a third-party system to validate such statements.


Signed statements include a JSON web signature (JWS) as an attachment. This allows the original serialization of the statement to be included along with the signature. For interoperability, the “RSA + SHA” series of JWS algorithms have been selected, and for discoverability of the signer X.509 certificates SHOULD be used.

Requirements for a signed statement:

LRS requirements when receiving a signed statement:

Note: The step of validating against the included X.509 certificate is intended as a way to catch mistakes in the signature, not as a security measure. Clients MUST NOT assume a signature is valid simply because an LRS has accepted it. The steps to authenticate a signed statement will vary based on the degree of certainty required and are outside the scope of this specification.

See Appendix F: Example Signed Statement for an example.

5.0 Miscellaneous Types

5.1 Document:

The Experience API provides a facility for Activity Providers to save arbitrary data in the form of documents, which may be related to an Activity, Agent, or combination of both.
idStringSet by AP, unique within state scope (learner, activity).
updatedTimestampWhen the document was most recently modified.
contentsArbitrary binary dataThe contents of the document
Note that in the REST binding, State is a document not an object. ID is stored in the URL, updated is HTTP header information, and contents is the HTTP document itself.

5.2 Language Map

A language map is a dictionary where the key is a  RFC 5646 Language Tag, and the value is a string in the language specified in the tag. This map should be populated as fully as possible based on the knowledge of the string in question in different languages.

5.3 Extensions

Extensions are defined by a map. The keys of that map MUST be URLs, and the values MAY be any JSON value or data structure. The meaning and structure of extension values under a URL key are defined by the person who coined the URL, who SHOULD be the owner of the URL, or have permission from the owner. The owner of the URL SHOULD make a human-readable description of the intended meaning of the extension supported by the URL accessible at the URL. A learning record store MUST NOT reject an Experience API statement based on the values of the extensions map.

Extensions are available as part of activity definitions, as part of statement context, or as part of some statement result. In each case, they’re intended to provide a natural way to extend those elements for some specialized use. The contents of these extensions might be something valuable to just one application, or it might be a convention used by an entire community of practice.

Extensions should logically relate to the part of the statement where they are present. Extensions in statement context should provide context to the core experience, while those in the result should provide elements related to some outcome. For activities, they should provide additional information that helps define an activity within some custom application or community.


A statement should not be totally defined by its extensions, and be meaningless otherwise. Experience API statements should be capturing experiences among actors and objects, and SHOULD always strive to map as much information as possible into the built in elements, in order to leverage interoperability among Experience API conformant tools.

5.4 Identifier Metadata

There are several types of URI identifiers used in this specification: * verb * activity id * activity type * extension key * attachment usage type

For activity ids, see activity definition.

For all other identifiers, metadata MAY be provided in the following JSON format:

name Language Map The human readable/visual name
description Language Map description

If metadata is provided, both name and description SHOULD be included.

As with verbs, we recommend that Learning Activity Providers look for and use established, widely adopted identifiers for all types of URI identifier other than activity id. Where an identifier already exists, the Learning Activity Provider:

6.0 Runtime Communication

Sections 6 and 7 detail the more technical side of the Experience API, dealing with how statements are transferred between Activity Provider and LRS. A number of libraries are under development for a range of technologies (including JavaScript) which handle this part of the specification. It therefore may not be necessary for content developers to fully understand every detail of this part of the specification.

6.1 Encoding:

All strings must be encoded and interpreted as UTF–8.

6.2 API Versioning:


Every request from a client and every response from the LRS must include an HTTP header with the name “X-Experience-API-Version" and the version as the value. Starting with 1.0.0, xAPI will be versioned according to Semantic Versioning 1.0.0

Example: X-Experience-API-Version : 1.0.0


Future revisions of the specification may introduce changes such as properties added to statements.

Systems retrieving statements may then receive responses that include statements of different versions. The version header allows for these version differences to be handled correctly, and to ascertain that no partial or mixed LRS version implementations exist.

Using Semantic Versioning will allow clients and LRSs to reliably know whether they’re compatible or not as the specification changes.


Requirements for the LRS:

Requirements for the client:

Converting statements to other versions:

6.3 Concurrency


Concurrency control makes certain that an API consumer does not PUT changes based on old data into an LRS.


xAPI will use HTTP 1.1 entity tags (ETags) to implement optimistic concurrency control in the portions of the API where PUT may overwrite existing data, being:

The State API will permit PUT statements without concurrency headers, since state conflicts are unlikely. The requirements below only apply to Agent Profile API and Activity Profile API.

Client requirements

An xAPI client using either Agent Profile API or Activity Profile API…

LRS requirements

The LRS that responds to a GET request…

The reason for specifying the LRS ETag format is to allow API consumers that can’t read the ETag header to calculate it themselves.

The LRS that responds to a PUT request…

If the header precondition in either of the above cases fails, the LRS…

If a PUT request is received without either header for a resource that already exists, the LRS…

6.4 Security:


The LRS MUST support authentication using at least one of the following methods: - OAuth 1.0 (rfc5849), with signature methods of “HMAC-SHA1”, “RSA-SHA1”, and “PLAINTEXT” - HTTP Basic Authentication - Common Access Cards (implementation details to follow in a later version)


The LRS is always responsible for making, or delegating, decisions on the validity of statements, and determining what operations may be performed based on the credentials used.

Authentication scenarios

The below matrix describes the possible authentication scenarios.

A registered application is an application that will authenticate to the LRS as an OAuth consumer that has been registered with the LRS. A known user is a user account on the LRS, or on a system which the LRS trusts to define users.

Known userUser unknown
Application is registered Standard workflow for OAuth. LRS trusts application to access xAPI without additional user credentials. OAuth token steps are not invoked
Application is not registered The application Agent is not identified as a registered Agent and the LRS cannot make assumptions on its identity.
No application HTTP Basic Authentication is used instead of OAuth, since no application is involved.
No authentication MAY be supported by the LRS, possibly for testing purposes.

6.4.1 How To Handle Each Scenario

Application registered + known user
Application registered + user unknown
Application not registered + known user
No application + known user
No authorization

Requirements for the LRS:

6.4.2 OAuth Authorization Scope


These are recommendations for scopes which should enable an LRS and an application communicating using the xAPI to negotiate a level of access which accomplishes what the application needs while minimizing the potential for misuse. The limitations of each scope are in addition to any security limitations placed on the user account associated with the request.


The LRS…

An xAPI client…


The list of scopes determines the set of permissions that is being requested. For example, an instructor might grant “statements/read” to a reporting tool, but the LRS would still limit that tool to statements that the instructor could read if querying the LRS with their credentials directly (such as statements relating to their students).


The following table lists xAPI scope values:
statements/writewrite any statement
statements/read/mine read statements written by “me”, that is with an authority matching what the LRS would assign if writing a statement with the current token.
statements/readread any statement
state read/write state data, limited to activities and actors associated with the current token to the extent it is possible to determine this relationship.
define (re)Define activities and actors. If storing a statement when this is not granted, IDs will be saved and the LRS may save the original statement for audit purposes, but should not update its internal representation of any actors or activities.
profile read/write profile data, limited to activities and actors associated with the current token to the extent it is possible to determine this relationship.
all/readunrestricted read access
allunrestricted access

OAuth Extended Parameters

Note that the parameters “consumer_name” and “scope” are not part of OAuth 1.0, and therefore if used should be passed as query string or form parameters, not in the OAuth header.

OAuth Endpoints
Name Endpoint Example
Temporary Credential Request OAuth/initiate http://example.com/xAPI/OAuth/initiate
Resource Owner Authorization OAuth/authorize http://example.com/xAPI/OAuth/authorize
Token Request OAuth/token http://example.com/xAPI/OAuth/token

7.0 Data Transfer (REST)

This section describes The xAPI consists of 4 sub-APIs: Statement, State, Learner, and Activity Profile. These four sub-APIs of the Experience API are handled via RESTful HTTP methods. The Statement API can be used by itself to track learning records.

Note: In all of the example endpoints given in the specification, “http://example.com/xAPI/” is the example URL of the LRS and everything after this represents the endpoint which MUST be used.

LRS Requirements

The LRS MUST reject with HTTP 400 Bad Request status (see below) any request to any of these APIs using any parameters…

7.1 Error Codes

The list below offers some general guidance on HTTP error codes that could be returned from various methods in the API. An LRS MUST return the error code most appropriate to the error condition based on the list below, and SHOULD return a message in the response explaining the cause of the error.

7.2 Statement API:

The basic communication mechanism of the Experience API.

PUT statements

Example endpoint: http://example.com/xAPI/statements

Stores statement with the given ID.

Returns: 204 No Content

statementIdString ID of statement to record
POST statements

Example endpoint: http://example.com/xAPI/statements

Stores a statement, or a set of statements. Since the PUT method targets a specific statement ID, POST must be used rather than PUT to save multiple statements, or to save one statement without first generating a statement ID. An alternative for systems that generate a large amount of statements is to provide the LRS side of the API on the AP, and have the LRS query that API for the list of updated (or new) statements periodically. This will likely only be a realistic option for systems that provide a lot of data to the LRS.

Returns: 200 OK, statement ID(s) (UUID).

Common requirements for PUT and POST

An LRS MUST NOT make any modifications to its state based on a receiving a statement with a statementID that it already has a statement for. Whether it responds with 409 Conflict or 204 No Content, it MUST NOT modify the statement or any other object.

If the LRS receives a statement with an ID it already has a statement for, it SHOULD verify the received statement matches the existing one and return 409 Conflict if they do not match.

The LRS MAY respond before statements that have been stored are available for retrieval.

GET statements

Example endpoint: http://example.com/xAPI/statements

This method may be called to fetch a single statement or multiple statements. If the statementId or voidedStatementId parameter is specified a single statement is returned.

Otherwise returns: A StatementResult object, a list of statements in reverse chronological order based on “stored” time, subject to permissions and maximum list length. If additional results are available, a URL to retrieve them will be included in the StatementResult object.

Returns: 200 OK, statement or Statement Result (See Section 4.2 for details)

statementIdString ID of statement to fetch
voidedStatementIdString ID of voided statement to fetch. see Voided Statements
agentAgent or identified Group Object (JSON) Filter, only return statements for which the specified agent or group is the actor or object of the statement.
  • Agents or identified groups are equal when the same Inverse Functional Identifier is used in each object compared and those Inverse Functional Identifiers have equal values.
  • For the purposes of this filter, groups that have members which match the specified agent based on their Inverse Functional Identifier as described above are considered a match

See agent/group object definition for details.
verbVerb id (IRI) Filter, only return statements matching the specified verb id.
activityActivity id (URI) Filter, only return statements for which the object of the statement is an activity with the specified id.
registrationUUID Filter, only return statements matching the specified registration ID. Note that although frequently a unique registration ID will be used for one actor assigned to one activity, this should not be assumed. If only statements for a certain actor or activity should be returned, those parameters should also be specified.
related_activitiesBooleanFalse Apply the activity filter broadly. Include statements for which the object, any of the context activities, or any of those properties in a contained SubStatement match the activity parameter, instead of that parameter's normal behavior. Matching is defined in the same way it is for the 'activity' parameter."
related_agentsBooleanFalse Apply the agent filter broadly. Include statements for which the actor, object, authority, instructor, team, or any of these properties in a contained SubStatement match the agent parameter, instead of that parameter's normal behavior. Matching is defined in the same way it is for the 'agent' parameter.
sinceTimestamp Only statements stored since the specified timestamp (exclusive) are returned.
untilTimestamp Only statements stored at or before the specified timestamp are returned.
limitNonnegative Integer0 Maximum number of statements to return. 0 indicates return the maximum the server will allow.
formatString: ("ids", "exact", or "canonical")exact If "ids", only include minimum information necessary in agent, activity, and group objects to identify them. For anonymous groups this means including the minimum information needed to identify each member. If "exact", return agent, activity, and group objects populated exactly as they were when the statement was received.

If "canonical", return activity objects populated with the canonical definition of the activity objects as determined by the LRS, after applying the language filtering process defined below, and return the original agent objects as in "exact" mode. Activity objects contain Language Map objects for name and description. Only one language should be returned in each of these maps.

In order to provide these strings in the most relevant language, the LRS will apply the Accept-Language header as described in  RFC 2616 (HTTP 1.1), except that this logic will be applied to each language map individually to select which language entry to include, rather than to the resource (list of statements) as a whole. An LRS requesting statements for the purpose of importing them SHOULD use a format of "exact".
attachmentsBooleanFalse If true LRS MUST use the multipart response format and include any attachments as described in 4.1.11. Attachments, otherwise the LRS MUST NOT include attachments.
ascendingBooleanFalse If true, return results in ascending order of stored time

The LRS MUST reject with an HTTP 400 error any requests to this resource which:

The LRS MUST include the header “X-Experience-API-Consistent-Through”, in ISO 8601 combined date and time format, on all responses to statements requests, with a value of the timestamp for which all statements that have or will have a “stored” property before that time are known with reasonable certainty to be available for retrieval. This time SHOULD take into account any temporary condition, such as excessive load, which might cause a delay in statements becoming available for retrieval.


Due to query string limits, this method MAY be called using POST and form fields if necessary. The LRS MUST differentiate a POST to add a statement or to list statements based on the parameters passed.

Filter Conditions for StatementRefs

For filter parameters which are not time or sequence based (that is, other than since, until, or limit), statements which target another statement (by using a StatementRef as the object of the statement) will meet the filter condition if the targeted statement meets the condition. The time and sequence based parameters must still be applied to the statement making the StatementRef in this manner. This rule applies recursively, so that “statement a” is a match when a targets b which targets c and the filter conditions described above match for “statement c”.

For example, consider the statement “Ben passed explosives training”, and a follow up statement: “Andrew confirmed <StatementRef to original statement>”. The follow up statement will not mention “Ben” or “explosives training”, but when fetching statements with an actor filter of “Ben” or an activity filter of “explosives training”, both statements match and will be returned so long as they fall into the time or sequence being fetched.

This section does not apply when retrieving statements with statementId or voidedStatementId.

Voided Statements

The LRS MUST not return any statement which has been voided, unless that statement has been requested by voidedStatementId. The LRS MUST still return any statements targetting the voided statement when retrieving statements using explicit or implicit time or sequence based retrieval, unless they themselves have been voided, as described in the section on filter conditions for StatementRefs. This includes the voiding statement, which cannot be voided. Reporting tools can identify the presence and statementId of any voided statements by the target of the voiding statement. Reporting tools wishing to retrieve voided statements SHOULD request these individually by voidedStatementId.

7.3 Document APIs:

The three Document APIs provide document storage for learning activity providers and agents. The details of each API are found in the following sections, and the information in this section applies to all three APIs.

New Agents and Activities

An Activity Provider MAY send documents to any of the document APIs for activities and agents that the LRS does not have prior knowledge of. The LRS MUST NOT reject documents on the basis of not having prior knowledge of the activity and/or agent.

POST to store application/json arrays of variables
API Method Endpoint Example
State API POST activities/state http://example.com/xAPI/activities/state
Activity Profile API POST activities/profile http://example.com/xAPI/activities/profile
Agent Profile API POST agent/profile http://example.com/xAPI/agents/profile

APs MAY use Documents of content type “application/json” to store sets of variables. For example a document contains:

{ "x" : "foo", "y" : "bar" }
When an LRS receives a POST request with content type application/json for an existing document also of content type application/json, it MUST merge the posted document with the existing document. In this context merge is defined as: * de-serialize the objects represented by each document * for each property directly defined on the object being posted, set the corresponding property on the existing object equal to the value from the posted object.
* store any valid json serialization of the existing object as the document referenced in the request

Note that only top-level properties are merged, even if a top-level property is an object the entire contents of each original property are replaced with the entire contents of each new property.

For example, this document is POSTed with the same id as the existing document above:

{ "x" : "bash", "z" : "faz" }
the resulting document stored in the LRS is: { "x" : "bash", "y" : "bar", "z" : "faz" } If the original document exists, and the original document or the document being posted do not have a Content-Type: of “application/json”, or if either document can not be parsed as JSON objects, the LRS MUST respond with HTTP status code 400 “Bad Request”, and MUST NOT update the target document as a result of the request.

If the original document does not exist, the LRS MUST treat the request the same as it would a PUT request and store the document being posted.

If the merge is successful, the LRS MUST respond with HTTP status code 204 “No Content”.

If an AP needs to delete a property, it SHOULD use a PUT request to replace the whole document as described below.

7.4 State API:

Generally, this is a scratch area for activity providers that do not have their own internal storage, or need to persist state across devices. When using the State API, be aware of how the stateId parameter affects the semantics of the call. If it is included, the GET and DELETE methods will act upon a single defined state document identified by “stateId”. Otherwise, GET will return the available IDs, and DELETE will delete all state in the context given through the other parameters.

PUT | POST | GET | DELETE activities/state

Example endpoint: http://example.com/xAPI/activities/state

Stores, fetches, or deletes the document specified by the given stateId that exists in the context of the specified activity, agent, and registration (if specified).

Returns: (PUT | POST | DELETE) 204 No Content, (GET) 200 OK - State Content
activityIdStringyes The activity ID associated with this state
agent(JSON/XML)yes The agent associated with this state
registrationUUIDno The registration ID associated with this state.
stateIdStringyes The id for this state, within the given context.

GET activities/state

Example endpoint: http://example.com/xAPI/activities/state

Fetches IDs of all state data for this context (activity + agent [ + registration if specified]). If “since” parameter is specified, this is limited to entries that have been stored or updated since the specified timestamp (exclusive).

Returns: 200 OK, Array of IDs
activityIdStringyes The activity ID associated with these states.
agent(JSON/XML)yes The actor associated with these states.
registrationUUIDno The registration ID associated with these states.
sinceTimestampno Only IDs of states stored since the specified timestamp (exclusive) are returned.

DELETE activities/state

Example endpoint: http://example.com/xAPI/activities/state

Deletes all state data for this context (activity + agent [+ registration if specified]).

Returns: 204 No Content
activityIdStringyes The activity ID associated with this state
agent(JSON/XML)yes The actor associated with this state
registrationUUIDno The registration ID associated with this state.

7.5 Activity Profile API:

The Activity Profile API is much like the State API, allowing for arbitrary key / document pairs to be saved which are related to an Activity. When using the Activity Profile API for manipulating documents, be aware of how the profileId parameter affects the semantics of the call. If it is included, the GET and DELETE methods will act upon a single defined document identified by “profileId”. Otherwise, GET will return the available IDs, and DELETE will delete all state in the context given through the other parameters.

The Activity Profile API also includes a method to retrieve a full description of an activity from the LRS.

GET activities

Example endpoint: http://example.com/xAPI/activities

Loads the complete activity object specified.

Returns: 200 OK - Content
activityIdStringyes The ID associated with the activities to load.

PUT | POST | GET | DELETE activities/profile

Example endpoint: http://example.com/xAPI/activities/profile

Saves/retrieves/deletes the specified profile document in the context of the specified activity.

Returns: (PUT | POST | DELETE) 204 No Content, (GET) 200 OK - Profile Content
activityIdStringyes The activity ID associated with this profile.
profileIdStringyes The profile ID associated with this profile.

GET activities/profile

Example endpoint: http://example.com/xAPI/activities/profile

Loads IDs of all profile entries for an activity. If “since” parameter is specified, this is limited to entries that have been stored or updated since the specified timestamp (exclusive).

Returns: 200 OK - List of IDs
activityIdStringyes The activity ID associated with these profiles.
sinceTimestampno Only IDs of profiles stored since the specified timestamp (exclusive) are returned.

7.6 Agent Profile API:

The Agent Profile API is much like the State API, allowing for arbitrary key / document pairs to be saved which are related to an Agent. When using the Agent Profile API for manipulating documents, be aware of how the profileId parameter affects the semantics of the call. If it is included, the GET and DELETE methods will act upon a single defined document identified by “profileId”. Otherwise, GET will return the available IDs, and DELETE will delete all state in the context given through the other parameters.

The Agent Profile API also includes a method to retrieve a special object with combined information about an Agent derived from an outside service, such as a directory service.

GET agents

Example endpoint: http://example.com/xAPI/agents

Return a special, Person object for a specified agent. The Person object is very similar to an Agent object, but instead of each attribute having a single value, each attribute has an array value, and it is legal to include multiple identifying properties. Note that the argument is still a normal Agent object with a single identifier and no arrays. Note that this is different from the FOAF concept of person, person is being used here to indicate a person-centric view of the LRS agent data, but agents just refer to one persona (a person in one context).

An LRS capable of returning multiple identifying properties for an Person SHOULD require the connecting credentials have increased, explicitly given permissions. An LRS SHOULD reject insufficiently privileged requests with 403 “Forbidden”. If an LRS does not have any additional information about an Agent to return, the LRS MUST still return a Person when queried, but that Person object will only include the information associated with the requested Agent.

Person properties

All array properties must be populated with members with the same definition as the similarly named property from Agent objects.

objectTypeString"Person". Required.
nameArray of strings. Optional. List of names of Agents to retrieve.
mbox* Array of URIs in the form "mailto:email address". List of e-mail addresses of Agents to retrieve.
mbox_sha1sum* Array of strings. List of the SHA1 hashes of mailto URIs (such as go in an mbox property)
openid* Array of strings. List of openids that uniquely identify the agents to retrieve.
account* Array of account objects. List of accounts to match. Complete account objects (homePage and name) must be provided.

See also: Section Agent.

Returns: 200 OK - Expanded Agent Object

agentObject (JSON)yes The agent representation to use in fetching expanded agent information.
PUT | POST | GET | DELETE agents/profile

Example endpoint: http://example.com/xAPI/agents/profile

Saves/retrieves/deletes the specified profile document in the context of the specified agent.

Returns: (PUT | POST | DELETE) 204 No Content, (GET) 200 OK - Profile Content

agentObject (JSON)yes The agent associated with this profile.
profileIdStringyes The profile ID associated with this profile.
GET agents/profile

Example endpoint: http://example.com/xAPI/agents/profile

Loads IDs of all profile entries for an agent. If “since” parameter is specified, this is limited to entries that have been stored or updated since the specified timestamp (exclusive).

Returns: 200 OK - List of IDs
agentObject (JSON)yes The agent associated with this profile.
sinceTimestampno Only IDs of profiles stored since the specified timestamp (exclusive) are returned

7.7. About resource:

GET about

Example endpoint: http://example.com/xAPI/about


Returns JSON object containing information about this LRS, including the xAPI version supported.


Primarily this resource exists to allow clients that suport multiple xAPI versions to decide which version to use when communicating with the LRS. Extensions are included to allow other uses to emerge.


Returns: 200 OK - Single ‘about’ JSON document.
versionarray of version stringsxAPI versions this LRS supports
ExtensionsExtensions objectA map of other properties as needed.

LRS Requirements:

7.8 Cross Origin Requests:

One of the goals of the xAPI is to allow cross-domain tracking, and even though xAPI seeks to enable tracking from applications other than browsers, browsers still need to be supported. Internet Explorer 8 and 9 do not implement Cross Origin Resource Sharing, but rather use their own Cross Domain Request API, which cannot use all of the xAPI as described above due to only supporting “GET” and “POST”, and not allowing HTTP headers to be set.

The following describes alternate syntax for consumers to use only when unable to use the usual syntax for specific calls due to the restrictions mentioned above. All LRSs must support this syntax.

Method: All xAPI requests issued must be POST. The intended xAPI method must be included as the only query string parameter on the request. (example: http://example.com/xAPI/statements?method=PUT)

Headers: Any required parameters which are expected to appear in the HTTP header must instead be included as a form parameter with the same name.

Content: If the xAPI call involved sending content, that content must now be encoded and included as a form parameter called “content”. The LRS will interpret this content as a UTF–8 string, storing binary data is not supported with this syntax.

Attachments: Sending attachment data requires sending a multipart/mixed request, therefore sending attachment data is not supported with this syntax. See 4.1.11. Attachments

See Appendix B for an example function written in Javascript which transforms a normal request into one using this alternate syntax.

It should also be noted that versions of Internet Explorer lower than 10 do not support Cross Domain Requests between HTTP and HTTPS. This means that for IE9 and lower, if the LRS is on an HTTPS domain, the client sending the statement must also be on HTTPS. If the LRS is on HTTP, the client must be too.

There may be cases where there is a requirement for the client activity provider to support IE8 and IE9 where the client code is hosted on a different scheme (HTTP or HTTPS) from the LRS. In these cases, proxy is needed to communicate to the LRS. Two simple solutions might be to 1) set up a proxy pass through on the same scheme as the client code to the LRS or 2) to host an intermediary server side LRS on the same scheme as the client code to route statements to the target LRS. An LRS MAY choose to provide both HTTP and HTTPS endpoints to support this use case. HTTP is inherently less secure than HTTPS, and both LRS and client should consider the security risks before making the decision to use this scheme.

7.9 Validation:

The function of the LRS within the xAPI is to store and retrieve statements. As long as it has sufficient information to perform these tasks, it is expected that it does them. Validation of statements in the Experience API is focused solely on syntax, not semantics. It SHOULD enforce rules regarding structure, but SHOULD NOT enforce rules regarding meaning. Enforcing the rules that ensure valid meaning among verb definitions, activity types, and extensions is the responsibility of the Activity Provider sending the statement.



The LRS will respond to HEAD requests by returning the meta information only, using the HTTP headers, and not the actual document.


Clients accessing the LRS may need to check if a particular statement exists, or determine the modification date of documents such as state or activity or agent profile. Particularly for large documents it’s more efficient not to get the entire document just to check its modification date.

LRS Requirements:

Appendix A: Bookmarklet

An xAPI Bookmarklet enables individual user tracking with base authentication. Examples could be an “I think this,” “I learned this,” “I like this,” or “I don’t like this” statement that allows self-reporting. The following is an example of such a bookmarklet, and the statement that this bookmarklet would send if used on the page: http://adlnet.gov/xapi.

The bookmarklet MAY be provided by the LRS to track a specific user for behavior analytics.

Therefore the LRS URL, authentication, and actor information is hard coded into the bookmarklet. Note that since the authorization token must be included in the bookmarklet, the LRS should provide a token with limited privileges, Ideally the token should enable the storage of self-reported learning statements only.

The UUID SHOULD be included as part of the bookmarklet PUT statement. If a statement is POSTed without a UUID, the LRS MUST generate one.

In order to allow cross-domain reporting of statements, a browser that supports the “Access-Control-Allow-Origin” and “Access-Control-Allow-Methods” headers must be used, such as IE 8+, FF 3.5+, Safari 4+, Safari iOS Chrome, or Android browser. Additionally the server must set the required headers.

In the example below, the following values in the first few lines should be replaced with your own values. All other values should be left as they are.

Value in example Explanation
http://localhost:8080/xAPI/ Endpoint of the LRS to send the statements to.
dGVzdDpwYXNzd29yZA== Base 64 encoded username and password, usually in the form "username : password".
learner@example.adlnet.gov Email address of the learner using the bookmarklet.

```javascript var url = “http://localhost:8080/xAPI/statements?statementId=”+_ruuid(); var auth = “Basic dGVzdDpwYXNzd29yZA==”; var statement = { “actor” : { “objectType” : “Agent”, “mbox” : “mailto:learner@example.adlnet.gov” }, “verb” : { “id” : "“, ”display“ : {} }, ”object“ : { ”id“ : ”“, ”definition" : {} } }; var definition = statement.object.definition;

statement.verb.id = ‘http://adlnet.gov/expapi/verbs/experienced’; statement.verb.display = { “en-US” : “experienced” }; statement.object.id = window.location.toString(); definition.type = “http://adlnet.gov/expapi/activities/link”;

var xhr = new XMLHttpRequest(); xhr.open(“PUT”, url, true); xhr.setRequestHeader(“Content-Type”, “application/json”); xhr.setRequestHeader(“Authorization”, auth); xhr.onreadystatechange = function() { if(xhr.readyState == 4) { alert(xhr.status + “ : ” + xhr.responseText); } }; xhr.send(JSON.stringify(statement));

/*! Modified from: Math.uuid.js (v1.4) http://www.broofa.com mailto:robert@broofa.com

Copyright (c) 2010 Robert Kieffer Dual licensed under the MIT and GPL licenses. / function _ruuid() { return ‘xxxxxxxx-xxxx–4xxx-yxxx-xxxxxxxxxxxx’.replace(/[xy]/g, function(c) { var r = Math.random()16|0, v = c == ‘x’ ? r : (r&0x3|0x8); return v.toString(16); }); } ```

Example Statement Using Bookmarklet

{ "content-type": "application/json; charset=UTF-8", "authorization": "d515309a-044d-4af3-9559-c041e78eb446", "referer": "http://adlnet.gov/xapi/", "content-length": "###", "origin": "http://adlnet.gov" }

Method Path:

``` PUT : /xAPI/Statements/?statementId=ed1d064a-eba6–45ea-a3f6–34cdf6e1dfd9

Body: { “actor”: { “objectType”: “Agent”, “mbox”: “mailto:learner@example.adlnet.gov” }, “verb”: “http://adlnet.gov/expapi/verbs/experienced”, “object”: { “id”: “http://adlnet.gov/xapi/ ”, “definition”: { “type”: “http://adlnet.gov/expapi/activities/link” } } } ```

Appendix B: Creating an “IE Mode” Request

```javascript function getIEModeRequest(method, url, headers, data){

var newUrl = url;

// Everything that was on query string goes into form vars
var formData = new Array();
var qsIndex = newUrl.indexOf('?');
if(qsIndex > 0){
    newUrl = newUrl.substr(0, qsIndex);

// Method has to go on querystring, and nothing else
newUrl = newUrl + '?method=' + method;

// Headers
if(headers !== null){
    for(var headerName in headers){
            headerName + "=" +

// The original data is repackaged as "content" form var
if(data !== null){
    formData.push('content=' + encodeURIComponent(data));

return {

} ```

Appendix C: Example definitions for activities of type “cmi.interaction”


"definition": { "description": { "en-US": "Does the xAPI include the concept of statements?" }, "type": "http://adlnet.gov/expapi/activities/cmi.interaction", "interactionType": "true-false", "correctResponsesPattern": [ "true" ] }


"definition": { "description": { "en-US": "Which of these prototypes are available at the beta site?" }, "type": "http://adlnet.gov/expapi/activities/cmi.interaction", "interactionType": "choice", "correctResponsesPattern": [ "golf[,]tetris" ], "choices": [ { "id": "golf", "description": { "en-US": "Golf Example" } }, { "id": "facebook", "description": { "en-US": "Facebook App" } }, { "id": "tetris", "description": { "en-US": "Tetris Example" } }, { "id": "scrabble", "description": { "en-US": "Scrabble Example" } } ] }


"definition": { "description": { "en-US": "Ben is often heard saying: " }, "type": "http://adlnet.gov/expapi/activities/cmi.interaction", "interactionType": "fill-in", "correctResponsesPattern": [ "Bob's your uncle" ] }


"definition": { "description": { "en-US": "How awesome is Experience API?" }, "type": "http://adlnet.gov/expapi/activities/cmi.interaction", "interactionType": "likert", "correctResponsesPattern": [ "likert_3" ], "scale": [ { "id": "likert_0", "description": { "en-US": "It's OK" } }, { "id": "likert_1", "description": { "en-US": "It's Pretty Cool" } }, { "id": "likert_2", "description": { "en-US": "It's Damn Cool" } }, { "id": "likert_3", "description": { "en-US": "It's Gonna Change the World" } } ] }


"definition": { "description": { "en-US": "Match these people to their kickball team:" }, "type": "http://adlnet.gov/expapi/activities/cmi.interaction", "interactionType": "matching", "correctResponsesPattern": [ "ben[.]3[,]chris[.]2[,]troy[.]4[,]freddie[.]1" ], "source": [ { "id": "ben", "description": { "en-US": "Ben" } }, { "id": "chris", "description": { "en-US": "Chris" } }, { "id": "troy", "description": { "en-US": "Troy" } }, { "id": "freddie", "description": { "en-US": "Freddie" } } ], "target": [ { "id": "1", "description": { "en-US": "Swift Kick in the Grass" } }, { "id": "2", "description": { "en-US": "We got Runs" } }, { "id": "3", "description": { "en-US": "Duck" } }, { "id": "4", "description": { "en-US": "Van Delay Industries" } } ] }


"definition": { "description": { "en-US": "This interaction measures performance over a day of RS sports:" }, "type": "http://adlnet.gov/expapi/activities/cmi.interaction", "interactionType": "performance", "correctResponsesPattern": [ "pong[.]1:[,]dg[.]:10[,]lunch[.]" ], "steps": [ { "id": "pong", "description": { "en-US": "Net pong matches won" } }, { "id": "dg", "description": { "en-US": "Strokes over par in disc golf at Liberty" } }, { "id": "lunch", "description": { "en-US": "Lunch having been eaten" } } ] }


"definition": { "description": { "en-US": "Order players by their pong ladder position:" }, "type": "http://adlnet.gov/expapi/activities/cmi.interaction", "interactionType": "sequencing", "correctResponsesPattern": [ "tim[,]mike[,]ells[,]ben" ], "choices": [ { "id": "tim", "description": { "en-US": "Tim" } }, { "id": "ben", "description": { "en-US": "Ben" } }, { "id": "ells", "description": { "en-US": "Ells" } }, { "id": "mike", "description": { "en-US": "Mike" } } ] }


"definition": { "description": { "en-US": "How many jokes is Chris the butt of each day?" }, "type": "http://adlnet.gov/expapi/activities/cmi.interaction", "interactionType": "numeric", "correctResponsesPattern": [ "4:" ] }


"definition": { "description": { "en-US": "On this map, please mark Franklin, TN" }, "type": "http://adlnet.gov/expapi/activities/cmi.interaction", "interactionType": "other", "correctResponsesPattern": [ "(35.937432,-86.868896)" ] }

Appendix D: Example statements

Example of a simple statement (line breaks are for display purposes only):
{ "id":"fd41c918-b88b-4b20-a0a5-a4c32391aaa0", "actor":{ "objectType": "Agent", "name":"Project Tin Can API", "mbox":"mailto:user@example.com" }, "verb":{ "id":"http://adlnet.gov/expapi/verbs/created", "display":{ "en-US":"created" } }, "object":{ "id":"http://example.adlnet.gov/xapi/example/simplestatement", "definition":{ "name":{ "en-US":"simple statement" }, "description":{ "en-US":"A simple Experience API statement. Note that the LRS does not need to have any prior information about the actor (learner), the verb, or the activity/object." } } } }
Typical simple completion with verb “attempted”:
{ "actor":{ "objectType": "Agent", "name":"Example Learner", "mbox":"mailto:example.learner@adlnet.gov" }, "verb":{ "id":"http://adlnet.gov/expapi/verbs/attempted", "display":{ "en-US":"attempted" } }, "object":{ "id":"http://example.adlnet.gov/xapi/example/simpleCBT", "definition":{ "name":{ "en-US":"simple CBT course" }, "description":{ "en-US":"A fictitious example CBT course." } } }, "result":{ "score":{ "scaled":0.95 }, "success":true, "completion":true } }

Appendix E: Converting Statements to 1.0.0


This is a 1.0.0 specification, and as such implementers should not have to consider prior versions of the specification. However, prior versions did see notable adoption. This data conversion is specified in order to preserve the data tracked using earlier versions, and make it available to new implementers in a consistant manner.

Conversion of statements created based on version 0.9

A 1.0.0 system converting a statement created in 0.9 MUST follow the steps below:

Conversion of statements created based on version 0.95

A 1.0.0 system converting a statement created in 0.95 MUST follow the steps below:


A 0.9 statement: { "id": "d1eec41f-1e93-4ed6-acbf-5c4bd0c24269", "actor": { "objectType": "Person", "name": [ "Joe Schmoe", "Joseph Schmoseph" ], "mbox": [ "mailto:joe@example.com" ], "openid": [ "http://openid.com/joe-schmoe" ] }, "verb": "completed", "inProgress": false, "object": { "objectType": "Activity", "id": "http://www.example.com/activities/001", "definition": { "name": { "en-US": "Example Activity" }, "type": "course" } }, "result": { "completion": true }, "context": { "instructor": { "objectType": "Person", "lastName": [ "Dad" ], "firstName": [ "Joe's" ], "mbox": [ "mailto:joesdad@example.com" ] }, "contextActivities": { "parent": { "objectType": "Activity", "id": "non-absolute-activity-id", "definition": { "name": { "en-US": "Another Activity" } } } } }, "timestamp": "2012-06-01T19:09:13.245Z", "stored": "2012-06-29T15:41:39.165Z" }

Converted to 1.0.0: { "version": "1.0.0", "id": "d1eec41f-1e93-4ed6-acbf-5c4bd0c24269", "actor": { "objectType": "Agent", "name": "Joe Schmoe", "mbox": "mailto:joe@example.com" }, "verb": { "id": "http://adlnet.gov/expapi/verbs/completed", "display": { "en-US": "completed" } }, "object": { "objectType": "Activity", "id": "http://www.example.com/activities/001", "definition": { "name": { "en-US": "Example Activity" }, "type": "http://adlnet.gov/expapi/activities/course" } }, "result": { "completion": true }, "context": { "instructor": { "objectType": "Agent", "mbox": "mailto:joesdad@example.com" }, "contextActivities": { "parent": [ { "objectType": "Activity", "id": "tag:adlnet.gov,2013:expapi:0.9:activities:non-absolute-activity-id", "definition": { "name": { "en-US": "Another Activity" } } } ] } }, "timestamp": "2012-06-01T19:09:13.245Z", "stored": "2012-06-29T15:41:39.165Z", "authority": { "objectType": "Agent", "account": { "homePage": "http://www.example.com", "name": "unknown" } } }

Appendix F: Example Signed Statement

An example signed statement, as described in: 4.4 Signed Statements.

The original statement serialization to be signed. Newlines in this example are included via CR+LF (0x0D + 0x0A).

{ "version": "1.0.0", "id": "33cff416-e331-4c9d-969e-5373a1756120", "actor": { "mbox": "mailto:example@example.com", "name": "Example Learner", "objectType": "Agent" }, "verb": { "id": "http://adlnet.gov/expapi/verbs/experienced", "display": { "en-US": "experienced" } }, "object": { "id": "https://www.youtube.com/watch?v=xh4kIiH3Sm8", "objectType": "Activity", "definition": { "name": { "en-US": "Tax Tips & Information : How to File a Tax Return " }, "description": { "en-US": "Filing a tax return will require filling out either a 1040, 1040A or 1040EZ form" } } }, "timestamp": "2013-04-01T12:00:00Z" }

Example private key for X.590 certificate that will be used for signing: -----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQDjxvZXF30WL4oKjZYXgR0ZyaX+u3y6+JqTqiNkFa/VTnet6Ly2 OT6ZmmcJEPnq3UnewpHoOQ+GfhhTkW13j06j5iNn4obcCVWTL9yXNvJH+Ko+xu4Y l/ySPRrIPyTjtHdG0M2XzIlmmLqm+CAS+KCbJeH4tf543kIWC5pC5p3cVQIDAQAB AoGAOejdvGq2XKuddu1kWXl0Aphn4YmdPpPyCNTaxplU6PBYMRjY0aNgLQE6bO2p /HJiU4Y4PkgzkEgCu0xf/mOq5DnSkX32ICoQS6jChABAe20ErPfm5t8h9YKsTfn9 40lAouuwD9ePRteizd4YvHtiMMwmh5PtUoCbqLefawNApAECQQD1mdBW3zL0okUx 2pc4tttn2qArCG4CsEZMLlGRDd3FwPWJz3ZPNEEgZWXGSpA9F1QTZ6JYXIfejjRo UuvRMWeBAkEA7WvzDBNcv4N+xeUKvH8ILti/BM58LraTtqJlzjQSovek0srxtmDg 5of+xrxN6IM4p7yvQa+7YOUOukrVXjG+1QJBAI2mBrjzxgm9xTa5odn97JD7UMFA /WHjlMe/Nx/35V52qaav1sZbluw+TvKMcqApYj5G2SUpSNudHLDGkmd2nQECQFfc lBRK8g7ZncekbGW3aRLVGVOxClnLLTzwOlamBKOUm8V6XxsMHQ6TE2D+fKJoNUY1 2HGpk+FWwy2D1hRGuoUCQAXfaLSxtaWdPtlZTPVueF7ZikQDsVg+vtTFgpuHloR2 6EVc1RbHHZm32yvGDY8IkcoMfJQqLONDdLfS/05yoNU= -----END RSA PRIVATE KEY-----





Signed Statement { "version": "1.0.0", "id": "33cff416-e331-4c9d-969e-5373a1756120", "actor": { "mbox": "mailto:example@example.com", "name": "Example Learner", "objectType": "Agent" }, "verb": { "id": "http://adlnet.gov/expapi/verbs/experienced", "display": { "en-US": "experienced" } }, "object": { "id": "https://www.youtube.com/watch?v=xh4kIiH3Sm8", "objectType": "Activity", "definition": { "name": { "en-US": "Tax Tips & Information : How to File a Tax Return " }, "description": { "en-US": "Filing a tax return will require filling out either a 1040, 1040A or 1040EZ form" } } }, "timestamp": "2013-04-01T12:00:00Z", "attachments": [ { "usageType": "http://adlnet.gov/expapi/attachments/signature", "display": { "en-US": "Signature" }, "description": { "en-US": "A test signature" }, "contentType": "application/octet-stream", "length": 4235, "sha2": "dc9589e454ff375dd5dfd6f556d2583e231e8cafe55ef40102ddd988b79f86f0" } ] }

Note: Attached signature not shown, see attachments for attachment message format.