Enter one of your students for an exam
- Bearer token
- Permission
application/create - Per organization
Enters one of your students for one exam in this organization, and creates the payment record the application is paid through.
Before you call it: register the student (registerStudent), have them sign in to this organization once through a sign-in link (createSigninLink), which creates the organization’s own record of them, and pick the exam from listAvailableExams: a leaf’s matchedExam._id is the examId to send. An exam that list leaves out for this student is refused here too.
Checks, in order. The first that fails decides the answer.
- The student is yours (
404). Your students are the ones your account registered; anyone else’s answers like an unknown id. - The organization holds a record of the student (
409). - The exam exists in this organization (
404). - The exam is offered to this student: it is open, and it fits their grade (
400for a student with no grade), their country and has a language (409, naming the rule). - The student already has this exact exam:
200"Application already exists." with that application, and nothing is created. - The student holds no other exam in the same category on the same sitting (
409).
Side effects. A payment record for the exam’s price: paid with amount 0 for a free exam, pending otherwise. This API never takes or refunds money. An application for a make-up sitting is removed automatically 6 hours after it is made (see removeAfter).
Safe to retry for the same student and exam: a repeat answers 200 with the application already there. Only while the exam is still offered to the student, though, because check 4 runs first: once the sitting date has passed, a repeat answers 409 and leaves the existing application as it was.
data is the application as stored. exam, payment and user are ids, and user is the organization’s own id for the student, not your studentId; getApplication resolves them.
Parameters
Path parameters
| Name | Type | Description |
|---|---|---|
organizationIdrequired | stringpattern ^[0-9a-f]{24}$ | The organization’s Example |
Request body
examIdstringrequiredThe exam’s
_idin this organization, 24 hexadecimal digits: alistAvailableExamsleaf’smatchedExam._id, or an_idfromlistExams. It has to be an examlistAvailableExamsoffers this student.example
6650a1b2c3d4e5f6a7b8c9e1studentIdstringrequiredThe student’s
_id, 24 hexadecimal digits: the idregisterStudentreturned. It has to be one of your students, the ones your account registered.example
6650a1b2c3d4e5f6a7b8c9d0
Responses
200 OK
Nothing new had to be created: message is "Application already exists.".
Body
successbooleanrequiredAlways
trueon a success.one of
truemessagestringrequiredexample
Application already exists.dataobject · ApplicationRecordResponserequiredAn application as stored, with
exam,userandpaymentas ids. Read the application to have them resolved.16 fields of data
_idstringrequiredThe application’s id: what
applicationIdtakes.example
6650a1b2c3d4e5f6a7b8c9e5examstringrequiredThe id of the exam applied for.
example
6650a1b2c3d4e5f6a7b8c9e1userstringrequiredThe organization’s own id for the student, not the id you registered them with. The application reads resolve it into a record carrying both.
example
6650a1b2c3d4e5f6a7b8c9e9paymentstringThe id of the application’s payment.
example
6650a1b2c3d4e5f6a7b8c9e6partnersarray of ApplicationPartnerResponserequiredOn a team exam, the other members of the team. Empty for an exam sat alone.
2 fields of each item
userstringThe team member’s id in this organization. Not one of your students’ ids, and not resolved.
example
6650a1b2c3d4e5f6a7b8c9d6acceptedbooleanWhether they have accepted the invitation to the team.
example
true
participatedbooleanrequiredWhether the student has started the exam. A started application cannot be moved to another exam.
example
falseexamStartstringWhen the student started the exam; absent until then.
format
date-timeexample2026-11-14T10:04:12.000ZexamSubmittedbooleanWhether the student has handed the exam in. A handed-in application cannot be moved to another exam.
example
falsesubmitDatestringWhen the student handed the exam in; absent until then.
format
date-timeexample2026-11-14T11:12:40.000ZsimulationStartedbooleanrequiredWhether the student has started the practice run.
example
falsesimulationStartstringWhen the student started the practice run; absent until then.
format
date-timeexample2026-11-07T10:00:00.000ZsimulationSubmittedbooleanrequiredWhether the student has handed the practice run in.
example
falseremoveAfterstringOnly on an application to an exam on a make-up sitting: when it will be removed, 6 hours after it was made.
format
date-timeexample2026-09-02T14:05:00.000ZuuidstringrequiredA short reference code for the application.
example
a3f-09c-7e1createdAtstringrequiredWhen the application was made.
format
date-timeexample2026-09-01T09:30:00.000ZupdatedAtstringrequiredWhen the application last changed.
format
date-timeexample2026-09-02T14:05:00.000Z
Headers
X-RateLimit-LimitintegerRequests your account may make to this operation per window (100).
X-RateLimit-RemainingintegerRequests left in the current window.
X-RateLimit-ResetintegerSeconds until the current window ends.
Example
{
"success": true,
"message": "Application already exists.",
"data": {
"_id": "6650a1b2c3d4e5f6a7b8c9e5",
"exam": "6650a1b2c3d4e5f6a7b8c9e1",
"user": "6650a1b2c3d4e5f6a7b8c9e9",
"payment": "6650a1b2c3d4e5f6a7b8c9e6",
"partners": [
{
"user": "6650a1b2c3d4e5f6a7b8c9d6",
"accepted": true
}
],
"participated": false,
"examStart": "2026-11-14T10:04:12.000Z",
"examSubmitted": false,
"submitDate": "2026-11-14T11:12:40.000Z",
"simulationStarted": false,
"simulationStart": "2026-11-07T10:00:00.000Z",
"simulationSubmitted": false,
"removeAfter": "2026-09-02T14:05:00.000Z",
"uuid": "a3f-09c-7e1",
"createdAt": "2026-09-01T09:30:00.000Z",
"updatedAt": "2026-09-02T14:05:00.000Z"
}
}201 Created
Created: message is "Application created successfully.".
Body
successbooleanrequiredAlways
trueon a success.one of
truemessagestringrequiredexample
Application created successfully.dataobject · ApplicationRecordResponserequiredAn application as stored, with
exam,userandpaymentas ids. Read the application to have them resolved.16 fields of data
_idstringrequiredThe application’s id: what
applicationIdtakes.example
6650a1b2c3d4e5f6a7b8c9e5examstringrequiredThe id of the exam applied for.
example
6650a1b2c3d4e5f6a7b8c9e1userstringrequiredThe organization’s own id for the student, not the id you registered them with. The application reads resolve it into a record carrying both.
example
6650a1b2c3d4e5f6a7b8c9e9paymentstringThe id of the application’s payment.
example
6650a1b2c3d4e5f6a7b8c9e6partnersarray of ApplicationPartnerResponserequiredOn a team exam, the other members of the team. Empty for an exam sat alone.
2 fields of each item
userstringThe team member’s id in this organization. Not one of your students’ ids, and not resolved.
example
6650a1b2c3d4e5f6a7b8c9d6acceptedbooleanWhether they have accepted the invitation to the team.
example
true
participatedbooleanrequiredWhether the student has started the exam. A started application cannot be moved to another exam.
example
falseexamStartstringWhen the student started the exam; absent until then.
format
date-timeexample2026-11-14T10:04:12.000ZexamSubmittedbooleanWhether the student has handed the exam in. A handed-in application cannot be moved to another exam.
example
falsesubmitDatestringWhen the student handed the exam in; absent until then.
format
date-timeexample2026-11-14T11:12:40.000ZsimulationStartedbooleanrequiredWhether the student has started the practice run.
example
falsesimulationStartstringWhen the student started the practice run; absent until then.
format
date-timeexample2026-11-07T10:00:00.000ZsimulationSubmittedbooleanrequiredWhether the student has handed the practice run in.
example
falseremoveAfterstringOnly on an application to an exam on a make-up sitting: when it will be removed, 6 hours after it was made.
format
date-timeexample2026-09-02T14:05:00.000ZuuidstringrequiredA short reference code for the application.
example
a3f-09c-7e1createdAtstringrequiredWhen the application was made.
format
date-timeexample2026-09-01T09:30:00.000ZupdatedAtstringrequiredWhen the application last changed.
format
date-timeexample2026-09-02T14:05:00.000Z
Headers
X-RateLimit-LimitintegerRequests your account may make to this operation per window (100).
X-RateLimit-RemainingintegerRequests left in the current window.
X-RateLimit-ResetintegerSeconds until the current window ends.
Example
{
"success": true,
"message": "Application created successfully.",
"data": {
"_id": "6650a1b2c3d4e5f6a7b8c9e5",
"exam": "6650a1b2c3d4e5f6a7b8c9e1",
"user": "6650a1b2c3d4e5f6a7b8c9e9",
"payment": "6650a1b2c3d4e5f6a7b8c9e6",
"partners": [
{
"user": "6650a1b2c3d4e5f6a7b8c9d6",
"accepted": true
}
],
"participated": false,
"examStart": "2026-11-14T10:04:12.000Z",
"examSubmitted": false,
"submitDate": "2026-11-14T11:12:40.000Z",
"simulationStarted": false,
"simulationStart": "2026-11-07T10:00:00.000Z",
"simulationSubmitted": false,
"removeAfter": "2026-09-02T14:05:00.000Z",
"uuid": "a3f-09c-7e1",
"createdAt": "2026-09-01T09:30:00.000Z",
"updatedAt": "2026-09-02T14:05:00.000Z"
}
}400 Bad request
bad_request: The body is not valid JSON, breaks a field’s rules, or has a field this operation does not accept ("property <name> should not exist").bad_request: The exam is open, but the student has no grade. Set one withupdateOrgStudentorupdateStudent, then try again.
When
bad_requestThe body is not valid JSON, breaks a field’s rules, or has a field this operation does not accept ("property <name> should not exist").
bad_requestThe exam is open, but the student has no grade. Set one with
updateOrgStudentorupdateStudent, then try again.
Example
{
"error": {
"code": "bad_request",
"documentation_url": "https://hub.main-team.org/api/errors#bad_request",
"message": "The request was malformed or contained invalid parameters.",
"request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
}
}Fields of the error body
errorobject · ErrorDetailrequiredWhat went wrong.
5 fields of error
codestringrequiredA stable machine code: branch on this. Each one is explained at
documentation_url.one of
invalid_emailbad_requestunauthorizedforbiddennot_foundconflictpayload_too_largeunsupported_media_typeunprocessable_entitytoo_many_requestsinternal_errorservice_unavailableexample
not_foundmessagestringrequiredWritten for a person, and may change: never branch on it.
example
Not found!documentation_urlstringrequiredWhere this code is explained.
format
uriexamplehttps://hub.main-team.org/api/errors#not_foundrequest_idstringrequiredThis request’s id, also sent as the
X-Request-Idresponse header: the one you sent, when it was acceptable, or else one the API made. Quote it when you report a problem.example
0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8edetailsobjectPresent only where an operation says so, and then with the shape that operation documents — the rows it could not accept, say. Treat it as absent everywhere else, and ignore a
detailsyou do not recognise.
401 Unauthorized
unauthorized: The token is missing or malformed, is not signed with your account’s apiSecret, breaks the iat and exp rules, has expired or been revoked, or its account is not active. All of these answer the same.
When
unauthorizedThe token is missing or malformed, is not signed with your account’s
apiSecret, breaks theiatandexprules, has expired or been revoked, or its account is not active. All of these answer the same.
Example
{
"error": {
"code": "unauthorized",
"documentation_url": "https://hub.main-team.org/api/errors#unauthorized",
"message": "Authentication is required or the provided credentials are invalid.",
"request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
}
}Fields of the error body
errorobject · ErrorDetailrequiredWhat went wrong.
5 fields of error
codestringrequiredA stable machine code: branch on this. Each one is explained at
documentation_url.one of
invalid_emailbad_requestunauthorizedforbiddennot_foundconflictpayload_too_largeunsupported_media_typeunprocessable_entitytoo_many_requestsinternal_errorservice_unavailableexample
not_foundmessagestringrequiredWritten for a person, and may change: never branch on it.
example
Not found!documentation_urlstringrequiredWhere this code is explained.
format
uriexamplehttps://hub.main-team.org/api/errors#not_foundrequest_idstringrequiredThis request’s id, also sent as the
X-Request-Idresponse header: the one you sent, when it was acceptable, or else one the API made. Quote it when you report a problem.example
0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8edetailsobjectPresent only where an operation says so, and then with the shape that operation documents — the rows it could not accept, say. Treat it as absent everywhere else, and ignore a
detailsyou do not recognise.
403 Forbidden
forbidden: The token is valid, but no role on your account allows application/create on the organization in the path, or a role denies it.
When
forbiddenThe token is valid, but no role on your account allows
application/createon the organization in the path, or a role denies it.
Example
{
"error": {
"code": "forbidden",
"documentation_url": "https://hub.main-team.org/api/errors#forbidden",
"message": "Insufficient role permissions",
"request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
}
}Fields of the error body
errorobject · ErrorDetailrequiredWhat went wrong.
5 fields of error
codestringrequiredA stable machine code: branch on this. Each one is explained at
documentation_url.one of
invalid_emailbad_requestunauthorizedforbiddennot_foundconflictpayload_too_largeunsupported_media_typeunprocessable_entitytoo_many_requestsinternal_errorservice_unavailableexample
not_foundmessagestringrequiredWritten for a person, and may change: never branch on it.
example
Not found!documentation_urlstringrequiredWhere this code is explained.
format
uriexamplehttps://hub.main-team.org/api/errors#not_foundrequest_idstringrequiredThis request’s id, also sent as the
X-Request-Idresponse header: the one you sent, when it was acceptable, or else one the API made. Quote it when you report a problem.example
0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8edetailsobjectPresent only where an operation says so, and then with the shape that operation documents — the rows it could not accept, say. Treat it as absent everywhere else, and ignore a
detailsyou do not recognise.
404 Not found
not_found:organizationIdis not the_idof an organization.not_found: No student of yours has this id: it matches nobody, or another account registered the student.not_found:examIdis not the_idof an exam in this organization.
When
Example
{
"error": {
"code": "not_found",
"documentation_url": "https://hub.main-team.org/api/errors#not_found",
"message": "Organization not found!",
"request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
}
}Fields of the error body
errorobject · ErrorDetailrequiredWhat went wrong.
5 fields of error
codestringrequiredA stable machine code: branch on this. Each one is explained at
documentation_url.one of
invalid_emailbad_requestunauthorizedforbiddennot_foundconflictpayload_too_largeunsupported_media_typeunprocessable_entitytoo_many_requestsinternal_errorservice_unavailableexample
not_foundmessagestringrequiredWritten for a person, and may change: never branch on it.
example
Not found!documentation_urlstringrequiredWhere this code is explained.
format
uriexamplehttps://hub.main-team.org/api/errors#not_foundrequest_idstringrequiredThis request’s id, also sent as the
X-Request-Idresponse header: the one you sent, when it was acceptable, or else one the API made. Quote it when you report a problem.example
0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8edetailsobjectPresent only where an operation says so, and then with the shape that operation documents — the rows it could not accept, say. Treat it as absent everywhere else, and ignore a
detailsyou do not recognise.
409 Conflict
conflict: The student has never signed in to this organization, so it holds no record of them yet. Send them a sign-in link (createSigninLink), and try again once they have used it.conflict: The exam is not open: applications to it are switched off, its sitting date has passed, or its category is inactive.conflict: The exam does not accept the student’s grade. The message names the grades it does accept, such as "Exam is not available for grade 8. It accepts grade 9, 10."conflict: The exam is restricted to countries the student is not in. The message names the countries it is offered in.conflict: The exam has no language set, so it is offered to no student.conflict: For any other reason,listAvailableExamsleaves the exam out for this student.conflict: The student already holds another exam in the same category on the same sitting, and nobody can sit both. Move that application (moveApplication) instead.
When
conflictThe student has never signed in to this organization, so it holds no record of them yet. Send them a sign-in link (
createSigninLink), and try again once they have used it.conflictThe exam is not open: applications to it are switched off, its sitting date has passed, or its category is inactive.
conflictThe exam does not accept the student’s grade. The message names the grades it does accept, such as "Exam is not available for grade 8. It accepts grade 9, 10."
conflictThe exam is restricted to countries the student is not in. The message names the countries it is offered in.
conflictThe exam has no language set, so it is offered to no student.
conflictFor any other reason,
listAvailableExamsleaves the exam out for this student.conflictThe student already holds another exam in the same category on the same sitting, and nobody can sit both. Move that application (
moveApplication) instead.
Example
{
"error": {
"code": "conflict",
"documentation_url": "https://hub.main-team.org/api/errors#conflict",
"message": "The request conflicts with the current state of the resource.",
"request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
}
}Fields of the error body
errorobject · ErrorDetailrequiredWhat went wrong.
5 fields of error
codestringrequiredA stable machine code: branch on this. Each one is explained at
documentation_url.one of
invalid_emailbad_requestunauthorizedforbiddennot_foundconflictpayload_too_largeunsupported_media_typeunprocessable_entitytoo_many_requestsinternal_errorservice_unavailableexample
not_foundmessagestringrequiredWritten for a person, and may change: never branch on it.
example
Not found!documentation_urlstringrequiredWhere this code is explained.
format
uriexamplehttps://hub.main-team.org/api/errors#not_foundrequest_idstringrequiredThis request’s id, also sent as the
X-Request-Idresponse header: the one you sent, when it was acceptable, or else one the API made. Quote it when you report a problem.example
0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8edetailsobjectPresent only where an operation says so, and then with the shape that operation documents — the rows it could not accept, say. Treat it as absent everywhere else, and ignore a
detailsyou do not recognise.
413 Payload too large
payload_too_large: The body is larger than 100 kB.
When
payload_too_largeThe body is larger than 100 kB.
Example
{
"error": {
"code": "payload_too_large",
"documentation_url": "https://hub.main-team.org/api/errors#payload_too_large",
"message": "request entity too large",
"request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
}
}Fields of the error body
errorobject · ErrorDetailrequiredWhat went wrong.
5 fields of error
codestringrequiredA stable machine code: branch on this. Each one is explained at
documentation_url.one of
invalid_emailbad_requestunauthorizedforbiddennot_foundconflictpayload_too_largeunsupported_media_typeunprocessable_entitytoo_many_requestsinternal_errorservice_unavailableexample
not_foundmessagestringrequiredWritten for a person, and may change: never branch on it.
example
Not found!documentation_urlstringrequiredWhere this code is explained.
format
uriexamplehttps://hub.main-team.org/api/errors#not_foundrequest_idstringrequiredThis request’s id, also sent as the
X-Request-Idresponse header: the one you sent, when it was acceptable, or else one the API made. Quote it when you report a problem.example
0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8edetailsobjectPresent only where an operation says so, and then with the shape that operation documents — the rows it could not accept, say. Treat it as absent everywhere else, and ignore a
detailsyou do not recognise.
415 Unsupported media type
unsupported_media_type: The body declares a charset that is not a UTF one (send UTF-8), or a Content-Encoding other than gzip, deflate or br.
When
unsupported_media_typeThe body declares a charset that is not a UTF one (send UTF-8), or a
Content-Encodingother than gzip, deflate or br.
Example
{
"error": {
"code": "unsupported_media_type",
"documentation_url": "https://hub.main-team.org/api/errors#unsupported_media_type",
"message": "unsupported charset \"ISO-8859-1\"",
"request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
}
}Fields of the error body
errorobject · ErrorDetailrequiredWhat went wrong.
5 fields of error
codestringrequiredA stable machine code: branch on this. Each one is explained at
documentation_url.one of
invalid_emailbad_requestunauthorizedforbiddennot_foundconflictpayload_too_largeunsupported_media_typeunprocessable_entitytoo_many_requestsinternal_errorservice_unavailableexample
not_foundmessagestringrequiredWritten for a person, and may change: never branch on it.
example
Not found!documentation_urlstringrequiredWhere this code is explained.
format
uriexamplehttps://hub.main-team.org/api/errors#not_foundrequest_idstringrequiredThis request’s id, also sent as the
X-Request-Idresponse header: the one you sent, when it was acceptable, or else one the API made. Quote it when you report a problem.example
0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8edetailsobjectPresent only where an operation says so, and then with the shape that operation documents — the rows it could not accept, say. Treat it as absent everywhere else, and ignore a
detailsyou do not recognise.
429 Too many requests
too_many_requests: Your account has made more than 100 requests to this operation in the current 60-second window. Wait the seconds in Retry-After before sending again.
When
too_many_requestsYour account has made more than 100 requests to this operation in the current 60-second window. Wait the seconds in
Retry-Afterbefore sending again.
Headers
Retry-AfterintegerSeconds to wait before sending again; a request sent sooner is refused too.
Example
{
"error": {
"code": "too_many_requests",
"documentation_url": "https://hub.main-team.org/api/errors#too_many_requests",
"message": "Too many requests to this operation. Wait the number of seconds in Retry-After, then try again.",
"request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
}
}Fields of the error body
errorobject · ErrorDetailrequiredWhat went wrong.
5 fields of error
codestringrequiredA stable machine code: branch on this. Each one is explained at
documentation_url.one of
invalid_emailbad_requestunauthorizedforbiddennot_foundconflictpayload_too_largeunsupported_media_typeunprocessable_entitytoo_many_requestsinternal_errorservice_unavailableexample
not_foundmessagestringrequiredWritten for a person, and may change: never branch on it.
example
Not found!documentation_urlstringrequiredWhere this code is explained.
format
uriexamplehttps://hub.main-team.org/api/errors#not_foundrequest_idstringrequiredThis request’s id, also sent as the
X-Request-Idresponse header: the one you sent, when it was acceptable, or else one the API made. Quote it when you report a problem.example
0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8edetailsobjectPresent only where an operation says so, and then with the shape that operation documents — the rows it could not accept, say. Treat it as absent everywhere else, and ignore a
detailsyou do not recognise.
500 Internal error
internal_error: Something failed on our side. Retry later, and quote request_id if it goes on.
When
internal_errorSomething failed on our side. Retry later, and quote
request_idif it goes on.
Example
{
"error": {
"code": "internal_error",
"documentation_url": "https://hub.main-team.org/api/errors#internal_error",
"message": "An unexpected error occurred.",
"request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
}
}Fields of the error body
errorobject · ErrorDetailrequiredWhat went wrong.
5 fields of error
codestringrequiredA stable machine code: branch on this. Each one is explained at
documentation_url.one of
invalid_emailbad_requestunauthorizedforbiddennot_foundconflictpayload_too_largeunsupported_media_typeunprocessable_entitytoo_many_requestsinternal_errorservice_unavailableexample
not_foundmessagestringrequiredWritten for a person, and may change: never branch on it.
example
Not found!documentation_urlstringrequiredWhere this code is explained.
format
uriexamplehttps://hub.main-team.org/api/errors#not_foundrequest_idstringrequiredThis request’s id, also sent as the
X-Request-Idresponse header: the one you sent, when it was acceptable, or else one the API made. Quote it when you report a problem.example
0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8edetailsobjectPresent only where an operation says so, and then with the shape that operation documents — the rows it could not accept, say. Treat it as absent everywhere else, and ignore a
detailsyou do not recognise.