xAPI versions

The packages support two versions of the standard: xAPI 1.0.3 (ADL, 2016) and xAPI 2.0.0, published as IEEE 9274.1.1-2023. The data model and the protocol changed little between them, but enough that an LRS must reject, store or answer some requests differently, so the version is never implicit.

The version is always explicit

XapiVersion has two values, XapiVersion.V1_0_3 and XapiVersion.V2_0_0. Reading, writing and validating JSON in Xapi.Core take one as a parameter. There is no global default, and one process can handle both versions, which the LRS needs.

XapiVersion.TryParseHeader maps the X-Experience-API-Version header: 1.0 and 1.0.x map to 1.0.3, 2.0 and 2.0.x to 2.0.0. Anything else, including 0.95, 1.1.0 and 2.1.0, is not a supported version.

Differences that affect your code

1.0.3 2.0.0
context.contextAgents, context.contextGroups Do not exist. Strict reading reports json.property.version, validation context.version, and writing throws InvalidOperationException. Supported. relevantTypes, when present, must hold at least one IRI.
context.instructor, context.team Supported. Still supported, but deprecated in favour of contextAgents and contextGroups.
Statement version property Must be 1.0.x. Must be 1.0.x or 2.0.0.
Timestamps T between date and time. T or a space.

Both versions require a UTC offset on timestamps and reject -00:00. The library writes timestamps in UTC with a Z and always three (or seven) fraction digits.

1.0.3 compares durations as text, and 2.0 ignores precision beyond 0.01 second. XapiDuration compares component by component and ignores precision beyond 0.01 second in both versions, so PT1.001S equals PT1S and PT60S does not equal PT1M.

Two rules are applied to both versions although only 1.0.3 has them, because the conformance suite tests them in the 2.0.0 battery too: context.revision and context.platform are only allowed when the object is an Activity. See conformance.md.

Differences in LRS behaviour

These are handled by Xapi.Lrs according to the version of the request, and matter to clients that talk to any LRS.

1.0.3 2.0.0
Statement without version The LRS stores 1.0.0. The LRS stores 2.0.0.
State PUT without If-Match or If-None-Match Allowed; replaces the document. 409 when the document exists.
Activity or Agent Profile PUT without a precondition 409 when the document exists, 400 when it does not. 409 when the document exists.
Alternate Request Syntax (a POST with method= for clients that cannot send other methods or headers) Supported. Not part of the standard; such requests are rejected.
X-Experience-API-Version on responses 1.0.3 2.0.0

POST and DELETE of documents never require a precondition in this LRS, in either version. Statements are returned as they were stored, so a 1.0.3 client can receive a statement stored by a 2.0 client, with contextAgents in it. Xapi.Client reads responses leniently and accepts that.

What the client and the outbox do

Xapi.Client negotiates the version through the About resource unless XapiClientOptions.Version is set, and prefers 2.0.0 (see client.md). Statements are validated against the version in use before they are sent, so a statement with contextAgents fails with XapiValidationException when the LRS only speaks 1.0.3, without a request. A statement that is valid in both versions works with both.

StatementOutbox.EnqueueAsync validates with the 2.0.0 rules, which accept everything 1.0.3 does, because the version of the LRS may not be known yet when a statement is queued. If the LRS turns out to speak only 1.0.3 and a queued statement uses a 2.0-only property, delivery fails with XapiValidationException and the statement becomes a dead letter (Rejected).

Which version to use

Use 2.0.0 when the LRS supports it; negotiation does this for you. Set 1.0.3 explicitly when you need identical behaviour against LRSs of both versions, for example while moving from TinCan.NET, which only speaks 1.0.3. An LRS built with Xapi.Lrs serves both versions unless EnableVersion1_0_3 is turned off.