Conventions and errors
The rules every Next Exams endpoint follows, so an integration can be written once and work across all of them.
Requests
| Rule | Detail |
|---|---|
| Base address | https://your-site.example/api/index.php/v1/nextexams |
| Bodies | JSON, sent with Content-Type: application/json. A body that is not a JSON object is refused with 400 INVALID_JSON |
Accept |
application/json. A request with no Accept header is treated the same way |
| Ids | Whole numbers, in the path: /questions/42 |
| Booleans | true / false in responses. Requests also accept 1 / 0 |
Create, update and replace
| Verb | Meaning |
|---|---|
POST /things |
Create. Answers 201 with the new id |
PATCH /things/{id} |
Update the fields you send and keep the rest (subjects, tags) |
PUT /questions/{id} |
Replace the whole question |
POST /things/{id}/status |
Publish ({"published": true}) or unpublish (false) |
DELETE /things/{id} |
Delete. Answers 204 with no body |
Questions are replaced, not patched. A question is saved as a whole: its options, answer key and tags go together, and anything you leave out is removed. Read the question, change what you need, and send the whole thing back. The response of GET /questions/{id} is accepted as a PUT body unchanged.
Next Exams refuses a PUT that has no options key while the question still has options (422 OPTIONS_REQUIRED). That stops an update meant to fix a typo in the title from deleting every choice. Send "options": [] if you really mean to remove them.
Responses
Success responses wrap their content in data:
{ "data": { "id": 42, "title": "Two plus two", "published": true } }
Lists add the total for paging in meta:
{ "data": [ … ], "meta": { "total": 137 } }
Paging and filters
| Parameter | Default | Meaning |
|---|---|---|
limit |
20 | Page size |
offset |
0 | Rows to skip |
search |
— | Matches the title (and a question's stem) |
published |
— | 1 published only, 0 unpublished only |
Attempts also take exam_id and status.
Field types
- Dates are ISO 8601 in UTC, such as
2026-10-18T12:51:24Z, ornullwhen not set. Convert to your viewers' time zone yourself. - Numbers are numbers. Scores, points and percentages are JSON numbers, not strings.
- Every documented field is always present, with
nullwhen it has no value. A field is never silently missing. - Responses carry exactly the documented fields. They are a stable contract, not raw database rows. Internal values such as a guest's result token or an exam's access code are never returned.
Ignore fields you do not recognise. New fields may be added in a later release; existing ones are not removed or retyped within v1.
Errors
A failure answers with an HTTP error status and an errors list, in the same shape the Joomla Web Services application uses for its own errors:
{
"errors": [
{ "title": "The token is missing the required scope: exams:delete", "code": "MISSING_SCOPE" }
]
}
Match on code, never on title. The title is written for a person and may be reworded. Some codes carry extra detail in meta.
| Status | Code | Meaning |
|---|---|---|
| 400 | INVALID_JSON |
The body is not a JSON object |
| 400 | BAD_REQUEST |
Validation failed. The title says which field |
| 401 | MISSING_TOKEN |
No token arrived (see the Authorization header) |
| 401 | INVALID_TOKEN |
Wrong, expired or switched-off token |
| 403 | MISSING_SCOPE |
The token lacks the scope this endpoint needs |
| 403 | FORBIDDEN |
The token owner's Next Exams role does not allow it |
| 403 | ACCESS_DENIED |
The token owner cannot manage Next Exams in Joomla |
| 403 | ACCOUNT_UNUSABLE |
The token owner is blocked or must reset their password |
| 403 | API_DISABLED |
The API Tokens app is switched off |
| 403 | APP_DISABLED |
The feature's app is switched off. meta.app names it, for example analytics |
| 403 | DEMO_LOCKED |
A demo site, where changes are disabled |
| 404 | NOT_FOUND |
No record has this id |
| 404 | QUESTION_UNAVAILABLE |
The question does not exist, or is not one you can use here |
| 409 | QUESTION_IN_USE |
Delete refused: exams still use the question. meta.exams lists them, or only meta.exam_count when the token owner may not view exams |
| 409 | TYPE_LOCKED |
The question type cannot change once it has been answered |
| 409 | STALE |
Someone saved since you read it (you sent expected_version) |
| 422 | OPTIONS_REQUIRED |
A question PUT with no options key |
A request to an address no route matches answers 404 from Joomla itself, with a numeric code (404). Treat code as either a string or a number.
Questions that have been answered
Next Exams never changes a question under answers that were already given to it.
- Wording changes save in place. Fixing a typo in the stem is fine.
- Changes that affect grading are refused with
409 ANSWERED_NEEDS_VERSION: which option is correct, the options themselves, the answer key. Send the same body with"new_version": {"exam_ids": [3, 7]}to save it as a new version. The listed exams switch to the new version and past attempts keep grading against the old one. - Option ids survive a save when you send them back. Keep each option's
idin yourPUTbody and answers already given to it stay attached.
To protect against two people editing at once, send the version you read as expected_version. Next Exams answers 409 STALE if the stored version has moved on.
OpenAPI
Every Next Exams site serves its own OpenAPI 3.1 document, matching the version installed there:
https://your-site.example/media/com_nextexams/openapi.yaml
The API tokens screen links to it. It is the same document this documentation's API Reference is generated from, and it is checked against a running site on every Next Exams release: each documented response is validated against the document with undocumented fields forbidden, so the document cannot quietly drift from what the API returns.
Use it with any OpenAPI tool:
# Browse it as interactive documentation
npx @redocly/cli preview-docs https://your-site.example/media/com_nextexams/openapi.yaml
# Generate TypeScript types for a client
npx openapi-typescript https://your-site.example/media/com_nextexams/openapi.yaml -o nextexams.d.ts
The document's servers entry is templated (protocol, host, basePath), so set it to your site once and generated clients call the right address, subfolder installs included.
The older task lane
Before the REST API, integrations called the administrator JSON endpoint with a task parameter:
https://your-site.example/administrator/index.php?option=com_nextexams&format=json&task=exam.getList
It still works with the same tokens, scopes and checks, and the Next Exams admin screens use it themselves. But it returns raw records and its own envelope ({success, data}), and it cannot be described in OpenAPI, because every operation shares one address. Use the REST API for new integrations.