Create Question
POST /v1/nextexams/questions
Create a question
Creates a bank question. question_type is one of the values
GET /v1/nextexams/question-types returns; choice types carry options
(mark the right one with is_correct), the others an answer_key.
Scope questions:write; the role needs questions.create. Creating a
question private to an exam (owner_exam_id) also needs exams:write.
Request
POST https://your-site.example/api/index.php/v1/nextexams/questions
Request body
Content type: application/json
| Field | Type | Required | Description |
|---|---|---|---|
title |
string |
No | |
question_type |
string |
No | |
stem |
string |
No | |
explanation |
string |
No | |
subject_id |
integer |
No | |
difficulty |
integer |
No | (min 1, max 5) |
default_points |
number |
No | |
shuffle_options |
boolean|integer |
No | |
published |
boolean|integer |
No | |
options |
object[] |
No | |
options.id |
integer |
No | Send it back to keep the option (and the answers given to it). |
options.option_text |
string |
No | |
options.is_correct |
boolean|integer |
No | |
options.match_key |
string|null |
No | |
options.feedback |
string|null |
No | |
answer_key |
object |
No | |
tags |
array |
No | Tag ids, tag titles, or {id, title} objects; a new title creates the tag. |
owner_exam_id |
integer |
No | Create the question private to this exam. Needs exams:write too. |
expected_version |
integer |
No | Refuse with 409 STALE if the stored version differs. |
new_version |
object |
No | Save as a new version, swapped into the listed exams. |
new_version.exam_ids |
integer[] |
No |
{
"title": "Two plus two",
"question_type": "mcq_single",
"stem": "<p>What is 2 + 2?</p>",
"default_points": 1,
"difficulty": 1,
"options": [
{
"option_text": "3",
"is_correct": false
},
{
"option_text": "4",
"is_correct": true
}
]
}
Responses
201 — Created. data.id is the new record.
{
"data": {
"id": 42
}
}
| Field | Type | Meaning |
|---|---|---|
data.id |
integer |
Errors
| Status | Code | When |
|---|---|---|
400 |
BAD_REQUEST |
The body failed validation, or is not a JSON object (INVALID_JSON). |
401 |
INVALID_TOKEN |
No token (MISSING_TOKEN), or one that is wrong, expired, revoked, or whose owner can no longer sign in (INVALID_TOKEN). |
403 |
MISSING_SCOPE |
The token is valid but this request is not allowed, and code says why: MISSING_SCOPE (the token lacks the scope), FORBIDDEN (the owner's role does not allow it), ACCESS_DENIED (the owner cannot manage Next Exams), API_DISABLED (the API Tokens app is off), APP_DISABLED (the feature's app is off — meta.app names it), ACCOUNT_UNUSABLE (the owner is blocked or must reset their password), DEMO_LOCKED (a demo site). |
Examples
curl
curl -s -X POST "https://your-site.example/api/index.php/v1/nextexams/questions" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"Two plus two","question_type":"mcq_single","stem":"<p>What is 2 + 2?</p>","default_points":1,"difficulty":1,"options":[{"option_text":"3","is_correct":false},{"option_text":"4","is_correct":true}]}'
PHP
<?php
$ch = curl_init('https://your-site.example/api/index.php/v1/nextexams/questions');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_TOKEN'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => '{"title":"Two plus two","question_type":"mcq_single","stem":"<p>What is 2 + 2?</p>","default_points":1,"difficulty":1,"options":[{"option_text":"3","is_correct":false},{"option_text":"4","is_correct":true}]}',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
JavaScript
const API_TOKEN = process.env.API_TOKEN;
const response = await fetch("https://your-site.example/api/index.php/v1/nextexams/questions", {
method: 'POST',
headers: {
"Authorization": `Bearer ${API_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"title": "Two plus two",
"question_type": "mcq_single",
"stem": "<p>What is 2 + 2?</p>",
"default_points": 1,
"difficulty": 1,
"options": [
{
"option_text": "3",
"is_correct": false
},
{
"option_text": "4",
"is_correct": true
}
]
}),
});
const data = await response.json();