TXTRAX LRS
Community

xAPI 2.0

Introduction

xAPI 2.0 was published as IEEE Std 9274.1.1-2023, the first version of xAPI officially recognised by the IEEE. Supporting it was important for TRAX LRS, but xAPI 1.0.3 remains by far the most widely adopted version and could not be dropped. TRAX LRS therefore offers dual support: a single deployment serves both 1.0.3 and 2.0 clients, negotiating the version on each request.

xAPI 2.0 is largely backward-compatible with 1.0.3 — the JSON wire format is unchanged. It relaxes a few rules, tightens a few others, adds two context properties, and updates version negotiation. This page covers what these changes mean in practice when you operate TRAX LRS.

Store configuration

Each store declares the set of xAPI versions it accepts:

  • 1.0.3 — Enabled by default on both existing and new stores, because it is the most widely adopted version.
  • 2.0 — Enable it to accept 2.0 statements.

2.0 is always an explicit opt-in: existing stores remain 1.0.3-only after upgrading, so nothing changes until you turn it on.

A few points to keep in mind:

  • If you enable 2.0 on a store, you will no longer be able to push its statements to an external LRS that is not 2.0 compliant.
  • Enabling 2.0 is required to run the ADL Test Suite against the xAPI 2.0 rules.
  • A store can accept both versions at once. The version of each incoming request is negotiated through the X-Experience-API-Version header and validated against the store's supported set.

LRS connectors

When you create an LRS connector, TRAX LRS sends a request to the About resource of the configured LRS to discover the xAPI versions it supports.

If this request fails, the connector is recorded with a Connection failed warning. This is not caused by the Basic HTTP credentials, because the About resource does not require authentication — so check the endpoint URL and try again. The connector will not be used by TRAX LRS until the connection issue is resolved.

If your external LRS has been upgraded and now supports xAPI 2.0, edit the connector and save it again. TRAX LRS will probe the About resource once more and refresh the cached set of supported versions.

LRS pull and push jobs

When creating an LRS pull or push job, you first select an LRS connector. Only connectors with valid xAPI versions are listed. If a connector is missing, go back to the LRS connectors page and check the versions of that connector.

You then select a store. Only stores with compatible versions are shown:

  • For a push job, the target connector must support every version the store contains.
  • For a pull job, the store must support every version advertised by the source connector.

If a store is missing from the list, go back to the stores page and the LRS connectors page and make sure the xAPI versions are set consistently.

Even with valid configuration, you may still hit a version mismatch at run time. For example, suppose a store first received 2.0 statements and was later reconfigured to accept only 1.0.3. It can now be connected to a 1.0.3-only LRS, but because it still holds 2.0 data, the job will stop as soon as it reaches a 2.0 statement.

Manage your stores' xAPI versions carefully to avoid this situation: a store keeps the data of every version it has ever accepted, even after that version is disabled.

xAPI 2.0 data model

contextAgents

xAPI 2.0 adds context.contextAgents: an array that links one or more Agents to a Statement and, optionally, describes how each Agent is relevant. It complements the existing context.instructor by allowing several typed roles.

{
  "actor": { "mbox": "mailto:learner@example.com" },
  "verb": { "id": "http://adlnet.gov/expapi/verbs/attended" },
  "object": { "id": "http://example.com/activities/team-meeting" },
  "context": {
    "contextAgents": [
      {
        "objectType": "contextAgent",
        "agent": { "mbox": "mailto:facilitator@example.com" },
        "relevantTypes": [
          "https://example.com/types/facilitator"
        ]
      }
    ]
  }
}

contextGroups

context.contextGroups is the Group counterpart of contextAgents. It links one or more Groups (identified or anonymous) to the Statement, with the same optional relevantTypes describing their relevance.

{
  "actor": { "mbox": "mailto:learner@example.com" },
  "verb": { "id": "http://adlnet.gov/expapi/verbs/completed" },
  "object": { "id": "http://example.com/activities/group-project" },
  "context": {
    "contextGroups": [
      {
        "objectType": "contextGroup",
        "group": {
          "objectType": "Group",
          "name": "Cohort A",
          "member": [
            { "mbox": "mailto:alice@example.com" },
            { "mbox": "mailto:bob@example.com" }
          ]
        },
        "relevantTypes": [
          "https://example.com/types/project-team"
        ]
      }
    ]
  }
}

version

The Statement version property now accepts both 1.0.x and 2.0.x strings. When a Statement is stored without an explicit version, TRAX LRS defaults it to the version negotiated for the request: 1.0.0 for a 1.0.x request, 2.0.0 for a 2.0.x request. A valid client-provided version is always preserved as-is.

The negotiated request version and the stored Statement version are independent. A 2.0 request may store a Statement that explicitly declares 1.0.0, and that value is kept.

StatementRef

In 1.0.3, a StatementRef object had to carry objectType: "StatementRef". In 2.0 that objectType is still recommended, but TRAX LRS tolerates its absence wherever a StatementRef is the only thing that can appear — for example context.statement, or the object of a voiding Statement. In those positions a bare object carrying only a UUID id is accepted as a StatementRef:

{
  "context": {
    "statement": { "id": "e0d4b1f0-1234-5678-9abc-def012345678" }
  }
}

mbox_sha1sum

The mbox_sha1sum IFI (a SHA-1 hash of an Agent's mailbox) is officially deprecated in xAPI 2.0. TRAX LRS continues to accept it for Agents, Groups and the Authority, so existing integrations keep working, but new integrations should prefer mbox, openid or account.

Signed Statements (JWS attachment)

For signed Statements, 2.0 broadens the set of allowed JWS signature algorithms. In addition to the RSA algorithms required by 1.0.3 (RS256, RS384, RS512), TRAX LRS now also accepts the elliptic-curve and Edwards-curve algorithms introduced by 2.0: ES256, ES384, ES512 and EdDSA. The LRS verifies whichever algorithm the JWS header declares, provided it is in the allowed set for the request's version.

xAPI 2.0 APIs

X-Experience-API-Version

The request header X-Experience-API-Version now accepts 2.0.x in addition to 1.0.x. TRAX LRS negotiates the response accordingly and echoes the matching version on every response: 1.0.3 for a 1.0.x request, 2.0.0 for a 2.0.x request. A missing or unsupported header is rejected.

About Resource — version

The About Resource advertises the versions a store accepts in its version array, letting clients discover and negotiate the highest version they have in common. The array reflects each store's own configuration:

  • a 1.0.3-only store advertises ["1.0.3"],
  • a 2.0-only store advertises ["2.0.0"],
  • a dual-mode store advertises ["1.0.3", "2.0.0"].

CORS

xAPI 2.0 strongly recommends CORS support so that browser-based clients (XHR / fetch) can talk to the LRS directly. TRAX LRS answers preflight requests and exposes the X-Experience-API-Version and X-Experience-API-Consistent-Through response headers so these clients can read them.

Document Resources — JSON merge on POST

When a State, Activity Profile or Agent Profile document is updated with POST and both the stored document and the incoming body are application/json, TRAX LRS merges the two JSON objects rather than overwriting. xAPI 2.0 makes this behaviour required for all three document resources (1.0.3 only required it for State). TRAX LRS applies the merge consistently in both modes.

Document Resources — concurrency

xAPI 2.0 tightens optimistic concurrency for document resources. TRAX LRS uses strong ETags and enforces preconditions across the full PUT / POST / DELETE matrix for State, Activity Profile and Agent Profile:

  • A header-less PUT that creates a document succeeds; a header-less PUT that would overwrite an existing document is rejected with HTTP 409, requiring an If-Match / If-None-Match precondition.
  • POST and DELETE enforce the precondition whenever an If-Match / If-None-Match header is present.
  • If-Match: * is rejected when the target document does not yet exist.

Statements Resource — related_agents

The related_agents=true filter on GET /statements returns Statements that mention a given Agent anywhere significant. In 2.0, TRAX LRS extends the set of locations it inspects to include the new context properties: an Agent matches not only via actor, object, context.instructor, context.team and authority (and the same positions inside a SubStatement), but also via context.contextAgents[].agent and context.contextGroups[].group, including Group members. No special request is needed — Statements stored with these properties become matchable automatically.