Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/block-prs-from-release-5.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ permissions:
jobs:
block-release-5:
name: Block PRs from release/5.*
runs-on: ubuntu-22.04
runs-on: ubuntu-latest

steps:
# Fail the workflow when the source branch starts with release/5.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/check_links_lychee.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:

steps:
# Checkout the repository.
- uses: actions/checkout@v4
- uses: actions/checkout@v6

# Check documentation links with Lychee.
- uses: lycheeverse/lychee-action@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint_markdown.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:

steps:
# Checkout the repository.
- uses: actions/checkout@v4
- uses: actions/checkout@v6

# Run markdownlint on all documentation files.
- name: Run markdownlint-cli2
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/publish_release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:

steps:
# Checkout this repository containing the documentation.
- uses: actions/checkout@v4
- uses: actions/checkout@v6

# Extract the version from the branch name (release/x.y → x.y).
- name: Determine release version
Expand All @@ -27,7 +27,7 @@ jobs:
# Create a GitHub App token used to push to the publication repository.
- name: Create GitHub App installation token
id: app-token
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
app-id: ${{ vars.OOAPI_APP_PUBLISH_ID }}
private-key: ${{ secrets.OOAPI_APP_PUBLISH_SECRET }}
Expand All @@ -37,7 +37,7 @@ jobs:

# Checkout the documentation website repository.
- name: Checkout target repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
repository: open-education-api/open-education-api.github.io
token: ${{ steps.app-token.outputs.token }}
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/publish_unreleased.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ jobs:

steps:
# Checkout this repository containing the documentation.
- uses: actions/checkout@v4
- uses: actions/checkout@v6

# Create a GitHub App token used to push to the publication repository.
- name: Create GitHub App installation token
id: app-token
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
app-id: ${{ vars.OOAPI_APP_PUBLISH_ID }}
private-key: ${{ secrets.OOAPI_APP_PUBLISH_SECRET }}
Expand All @@ -29,7 +29,7 @@ jobs:

# Checkout the documentation website repository.
- name: Checkout target repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
repository: open-education-api/open-education-api.github.io
token: ${{ steps.app-token.outputs.token }}
Expand Down
3 changes: 0 additions & 3 deletions documentation/abbreviations.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@
| | and private organisations. |
| Community | The OOAPI community consists of parties interested in OOAPI developments. Community members|
| | are known by name and email address, and register with the community site themselves. |
| Slack | A Slack channel is available at: [openonderwijsapi.slack.com](https://openonderwijsapi.slack.com). Since access is on an invite |
| | basis, send an email to [info@openonderwijsapi.nl](mailto:info@openonderwijsapi.nl) and you will receive an invitation if you |
| | would like to participate in this Open Education API Slack channel. |
| Project site | The website at [https://openonderwijsapi.nl/](https://openonderwijsapi.nl/). This website is used mainly to provide |
| | clarification about the project to interested parties. |
| | The site also provides access to the reference API, online documentation and the code |
Expand Down
9 changes: 0 additions & 9 deletions documentation/contact.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,3 @@ Only the members of this mailing list may send email to this list. Send an email
to [openonderwijsapi@list.surf.nl](mailto:openonderwijsapi@list.surf.nl) to
contact the members of this mailing list. If you need support, contact:
[info@openonderwijsapi.nl](mailto:info@openonderwijsapi.nl)

## Slack

A Slack channel is available at:
[openonderwijsapi.slack.com](https://openonderwijsapi.slack.com) Since access is
on an invitation basis, send an email to
[info@openonderwijsapi.nl](mailto:info@openonderwijsapi.nl) and you will receive
an invitation if you would like to participate on this Open education API slack
channel.
3 changes: 2 additions & 1 deletion documentation/technical/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ Since OEAPI describes the information of an institution as resources, each insti

The actual OEAPI specification is specified using the [OpenAPI Specification](https://www.openapis.org/), a standard that specifies how to specify (among other things) REST APIs. The detailed OEAPI specifications can be found here:

* [OEAPI v5](https://open-education-api.github.io/specification/v5/docs.html) (Current version)
* [OEAPI v6](https://openonderwijsapi.nl/specification/v6.0/) (Current version - fully supported)

These are the previous versions:

* [OEAPI v5](https://open-education-api.github.io/specification/v5/docs.html) (limited support)
* [OEAPI v4](https://open-education-api.github.io/specification/v4/docs.html) (limited support)
* [OEAPI v3](https://open-education-api.github.io/specification/v3/docs.html) (unsupported)
* [OEAPI v2](https://open-education-api.github.io/specification/v2/docs.html) (unsupported)
Expand Down
83 changes: 72 additions & 11 deletions documentation/technical/errorcodes.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,37 @@ Servers SHOULD return an `Allow` header indicating supported methods.
Returned when the server cannot provide a representation for the requested
OEAPI or consumer version.

OEAPI uses explicit version negotiation rather than HTTP `Accept` headers.
If neither the requested version nor a lower compatible minor version is
supported, a `406` response MUST be returned.
OEAPI uses header-based version negotiation with an explicit request for a
single OEAPI version and, where applicable, a single consumer and consumer version.

Clients MUST request exactly one OEAPI version via the `Accept` header and
MAY also include exactly one consumer and one consumer version. For example:

```http
Accept: application/vnd.oeapi+json;version=6.0;consumer=mbo-oke-roster-service;consumer-version=2.0
```

The server evaluates whether the requested OEAPI version or a compatible
minor version within the same major version can be provided. The same
principle applies to the optional consumer version: the server may return
the requested consumer version or a compatible minor version.

If a compatible combination can be provided, the server returns the response
using the `Content-Type` header to indicate the actual OEAPI version and
consumer version used. For example:

```http
Content-Type: application/vnd.oeapi+json;version=6.1;consumer=mbo-oke-roster-service;consumer-version=6.1
```

If no compatible version is available, a `406 Not Acceptable` response MUST
be returned.

This behaviour differs from typical HTTP version negotiation:

- exactly one explicit version is requested
- any compatible minor version may be returned
- the same principle applies to the optional consumer and consumer version

This behaviour intentionally deviates from strict HTTP semantics to:

Expand All @@ -183,29 +211,62 @@ This behaviour intentionally deviates from strict HTTP semantics to:

### Example – unsupported OEAPI version

Client:

```http
POST /enrolments
Accept: application/vnd.oeapi+json;version=7.0;consumer=mbo-oke-roster-service;consumer-version=7.0
```

Server:

```http
HTTP/1.1 406 Not Acceptable
```

Response body:

```json
{
"type": "https://api.example.org/problems/version-not-acceptable",
"title": "Version not acceptable",
"status": 406,
"detail": "The requested OEAPI version '5.0' cannot be served.",
"requestedVersion": "5.0",
"supportedVersions": ["6.1", "6.0"],
"detail": "The requested OEAPI version '7.0' cannot be served.",
"requestedVersion": "7.0",
"supportedVersions": ["5.0", "6.1"],
"instance": "https://api.example.org/courses"
}
```

### Example – unsupported consumer version

Client:

```http
POST /enrolments
Content-Type: application/vnd.oeapi+json;version=6.1;consumer=mbo-oke-roster-service;consumer-version=6.1.1
```

Server:

```http
HTTP/1.1 406 Not Acceptable
```

Response body:

```json
{
"type": "https://api.example.org/problems/version-not-acceptable",
"title": "Consumer version not acceptable",
"title": "Version not acceptable",
"status": 406,
"detail": "The consumer version '2.0' is not supported.",
"requestedVersion": "2.0",
"supportedVersions": ["1.0", "0.94"],
"instance": "https://api.example.org/enrolments"
"detail": "The requested consumer version '6.1.1' cannot be served.",
"consumer": {
"consumerKey": "mbo-oke-roster-service"
},
"requestedVersion": "6.1.1",
"supportedVersions": ["0.95", "1.0", "6.1"],
"instance": "https://api.example.org/courses"
}
```

Expand Down
103 changes: 70 additions & 33 deletions documentation/technical/versioning.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
# Versioning: how it works

OEAPI uses an explicit, single-choice versioning model.
OEAPI uses a header-based, explicit, single-choice versioning model.

For each request, the client specifies exactly one OEAPI version and exactly one consumer version using HTTP headers. The server does not negotiate between alternatives.
For each request, the client specifies exactly one OEAPI version and
optionally one consumer with exactly one consumer version using the
`Accept` header. The server does not negotiate between alternatives.

This is referred to as the *closed* versioning approach.

In practice this means:

- the client sends one explicit OEAPI version via `Content-Type`
- the client sends one explicit consumer version via `OEAPI-Consumer-Version`
- the server either accepts that version, or falls back to a lower compatible minor version within the same major
- the client sends one explicit OEAPI version via `Accept`
- the client can include one consumer and one consumer version via `Accept`
- the server evaluates whether the requested version or a compatible minor
version within the same major can be provided (this may be lower or higher)
- the same principle applies to the optional consumer version
- if no compatible version exists, the server rejects the request

Only minor fallback is allowed. Major version fallback is never permitted.

The server MUST always indicate the actual versions used in the response headers.
The server always indicates the actual versions used in the response via
the `Content-Type` header.

If the requested version cannot be satisfied (and no compatible minor version is available), the server MUST return `406 Not Acceptable`, including the requested version and the list of supported versions in the response body.
If the requested version cannot be satisfied (and no compatible minor version
is available), the server returns `406 Not Acceptable`, including the
requested version and the list of supported versions in the response body.

OEAPI deliberately does not use:

- HTTP `Accept` negotiation
- multiple alternative versions in the `Accept` header
- HTTP standard `Accept` negotiation
- query parameters for versioning
- version attributes in the request body

Expand All @@ -37,62 +45,50 @@ Client:

```http
GET /courses
Content-Type: application/vnd.OEAPI.v6.1+json

OEAPI-Consumer-Name: mbo-oke-roster-service
OEAPI-Consumer-Version: 1.0
Accept: application/vnd.oeapi+json;version=6.1;consumer=mbo-oke-roster-service;consumer-version=6.0
```

Server:

```http
HTTP/1.1 200 OK
Content-Type: application/vnd.OEAPI.v6.1+json

OEAPI-Consumer-Name: mbo-oke-roster-service
OEAPI-Consumer-Version: 1.0
Content-Type: application/vnd.oeapi+json;version=6.1;consumer=mbo-oke-roster-service;consumer-version=6.0
```

The requested OEAPI and consumer versions are fully supported.
The server returns exactly the same versions as requested.

---

### Example 2: minor fallback
### Example 2: minor fallback (higher or lower)

Client:

```http
GET /courses
Content-Type: application/vnd.OEAPI.v6.1+json

OEAPI-Consumer-Name: mbo-oke-roster-service
OEAPI-Consumer-Version: 1.0
Accept: application/vnd.oeapi+json;version=6.1;consumer=mbo-oke-roster-service;consumer-version=6.0
```

Server:

```http
HTTP/1.1 200 OK
Content-Type: application/vnd.OEAPI.v6.0+json

OEAPI-Consumer-Name: mbo-oke-roster-service
OEAPI-Consumer-Version: 0.94
Content-Type: application/vnd.oeapi+json;version=6.0;consumer=mbo-oke-roster-service;consumer-version=6.1
```

Because both versions share the same major (6), the server falls back to the highest compatible minor and explicitly reports the versions used.
Because the versions share the same major (6), the server may return a
compatible minor version, higher or lower, and explicitly reports the
versions used.

---

### Example 3: unsupported version
### Example 3: unsupported OEAPI version

Client:

```http
POST /enrolments
Content-Type: application/vnd.OEAPI.v7.0+json

OEAPI-Consumer-Version: 2.0
Accept: application/vnd.oeapi+json;version=7.0;consumer=mbo-oke-roster-service;consumer-version=6.0
```

Server:
Expand All @@ -105,11 +101,52 @@ Response body:

```json
{
"error": "Unsupported OEAPI or consumer version",
"requestedVersion": "2.0",
"supportedVersions": ["0.94", "1.0"]
"type": "https://api.example.org/problems/version-not-acceptable",
"title": "Version not acceptable",
"status": 406,
"detail": "The requested OEAPI version '7.0' cannot be served.",
"requestedVersion": "7.0",
"supportedVersions": ["5.0", "6.1"],
"instance": "https://api.example.org/courses"
}
```

The requested major version is not supported.
Fallback is not permitted and the request is rejected.

---

### Example 4: unsupported consumer version

Client:

```http
POST /enrolments
Accept: application/vnd.oeapi+json;version=6.1;consumer=mbo-oke-roster-service;consumer-version=7.0
```

Server:

```http
HTTP/1.1 406 Not Acceptable
```

Response body:

```json
{
"type": "https://api.example.org/problems/version-not-acceptable",
"title": "Version not acceptable",
"status": 406,
"detail": "The requested consumer version '7.0' cannot be served.",
"consumer": {
"consumerKey": "mbo-oke-roster-service"
},
"requestedVersion": "7.0",
"supportedVersions": ["0.95", "1.0", "6.1"],
"instance": "https://api.example.org/courses"
}
```

The requested consumer version is not supported.
Fallback is not permitted and the request is rejected.
Loading