Submit one step of a group for one of your students
- Bearer token
- Permission
group-challenge/submit - Per organization
Submits the open step of a group, for one of your students who is a member of it, exactly as the student would in the panel: the files uploaded for the step are submitted with it, and the next step opens. The members upload the work in the panel or the app; this API does not upload. Read the group first: a step’s canSubmit says whether this would be accepted now.
The body names the student you act for: { "studentId": "<studentId>" }. The group’s history records the submit as your account (partner) acting for them.
Checks, in order. The first that fails decides the answer, and nothing is written for any of them.
- The student is yours (
404), and has signed in to this organization once (409). - The challenge exists and is published or closed (
404). - The group belongs to the challenge and the student is an active member of it (
404). - The step has not been submitted already. If it has, the answer is
200withchanged: falseand "Step already submitted.", and nothing changes, so a retry after a timeout is safe. - The challenge is published (
409,challenge_closed) and now is betweenwindowStartandwindowEnd(409,window_closed). - The teacher has confirmed the group (
409,payment_pendingorgroup_not_confirmed). - The group has this step (
404). - The step is open, not
locked(409,step_locked). - At least one file has been uploaded for it (
409,step_empty). Do not retry this one in a loop: a member uploads the work first.
Every 409 carries error.details.reason, one of the codes above; branch on it, not on the message.
Side effects. The step becomes submitted, and its uploaded files with it. An upload still running for the step is stopped. The next step, if there is one, opens. An entry is added to the group’s history, and a second one when the next step opens.
Busy. A group is changed by one request at a time. If someone is changing it at that moment the answer is 503 with Retry-After: 1 and details.reason: busy, and nothing was written: send the same request again.
Parameters
Path parameters
| Name | Type | Description |
|---|---|---|
organizationIdrequired | stringpattern ^[0-9a-f]{24}$ | The organization’s Example |
challengeIdrequired | string | The group challenge’s |
groupIdrequired | string | The group’s |
stepIdrequired | string | The step’s |
Request body
studentIdstringrequiredThe student the submit is made for: their
_id, asregisterStudentreturned it. They must be one of your students and an active member of the group; the group’s log records the submit as your account acting for them.example
6650a1b2c3d4e5f6a7b8c9d0
Responses
200 OK
Success: message is "Step submitted.".
Or message is "Step already submitted.": The step had already been submitted, by anyone. changed is false and nothing changed.
Body
successbooleanrequiredAlways
trueon a success.one of
truemessagestringrequiredexample
Step submitted.dataobject · GroupChallengeStepSubmitResponserequiredA step submitted for one of your students.
5 fields of data
groupIdstringrequiredThe group’s id.
example
6650a1b2c3d4e5f6a7b8c9edgroupStatusstringrequiredThe group’s state now:
finalizedwhile steps remain open.one of
awaiting_paymentdraftfinalizedcompletedexample
finalizedstepobject · GroupChallengeSubmittedStepResponserequiredThe step you submitted.
4 fields of step
_idstringrequiredThe step’s id.
example
6650a1b2c3d4e5f6a7b8c9eeorderobjectnullablerequiredIts position, from 1.
example
1statestringrequiredAlways
submitted.one of
submittedsubmittedAtstringnullablerequiredWhen it was submitted: now, or the first time on a repeat.
format
date-timeexample2026-10-20T16:02:11.000Z
nextStepobject · GroupChallengeOpenedStepResponsenullablerequiredThe step this submit opened, or
nullwhen it was the last one, or on a repeat.3 fields of nextStep
_idstringrequiredThe step’s id.
example
6650a1b2c3d4e5f6a7b8c9f1ordernumberrequiredIts position.
example
2statestringrequiredAlways
open: the group can work on it now.one of
open
changedbooleanrequiredtruewhen this request submitted the step;falsewhen it had already been submitted, and nothing changed.example
true
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
{
"data": {
"changed": true,
"groupId": "6650a1b2c3d4e5f6a7b8c9ed",
"groupStatus": "finalized",
"nextStep": {
"_id": "6650a1b2c3d4e5f6a7b8c9f1",
"order": 2,
"state": "open"
},
"step": {
"_id": "6650a1b2c3d4e5f6a7b8c9ee",
"order": 1,
"state": "submitted",
"submittedAt": "2026-10-20T16:02:11.000Z"
}
},
"message": "Step submitted.",
"success": true
}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:challengeIdis not 24 hexadecimal digits.bad_request:groupIdis not 24 hexadecimal digits.bad_request:stepIdis not 24 hexadecimal digits.
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_requestchallengeIdis not 24 hexadecimal digits.bad_requestgroupIdis not 24 hexadecimal digits.bad_requeststepIdis not 24 hexadecimal digits.
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 group-challenge/submit on the organization in the path, or a role denies it.
When
forbiddenThe token is valid, but no role on your account allows
group-challenge/submiton 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: Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read.not_found: No student of yours has this id: it matches nobody, or another account registered the student.not_found: No group challenge of this organization has this id, or it is not published: a draft, archived or deleted challenge answers the same.not_found: No group of this challenge has this id with the student as an active member: a missing group, a deleted one and one the student is not in get the same answer.not_found: The group has no step with this id. A group has its steps once the teacher confirms it.
When
not_foundorganizationIdis not the_idof an organization.not_foundGroup challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read.
not_foundNo student of yours has this id: it matches nobody, or another account registered the student.
not_foundNo group challenge of this organization has this id, or it is not published: a draft, archived or deleted challenge answers the same.
not_foundNo group of this challenge has this id with the student as an active member: a missing group, a deleted one and one the student is not in get the same answer.
not_foundThe group has no step with this id. A group has its steps once the teacher confirms it.
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 organizers have closed the challenge (status: closed).conflict: Now is beforewindowStartor afterwindowEnd.detailscarries both.conflict: The teacher is still preparing the group (status: awaiting_payment).conflict: The teacher has not confirmed the group yet (status: draft), so it has no steps.conflict: The step is still locked: the steps before it are not all submitted.conflict: Nothing has been uploaded for the step yet. A member uploads the work in the panel or the app first; retrying does not help.
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 organizers have closed the challenge (
status: closed).conflictNow is before
windowStartor afterwindowEnd.detailscarries both.conflictThe teacher is still preparing the group (
status: awaiting_payment).conflictThe teacher has not confirmed the group yet (
status: draft), so it has no steps.conflictThe step is still locked: the steps before it are not all submitted.
conflictNothing has been uploaded for the step yet. A member uploads the work in the panel or the app first; retrying does not help.
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.
503 Service unavailable
service_unavailable: Someone else — a member in the panel or the app, or another request of yours — is changing the group at this moment. Nothing was written. Wait the second in Retry-After and send the same request again.
When
service_unavailableSomeone else — a member in the panel or the app, or another request of yours — is changing the group at this moment. Nothing was written. Wait the second in
Retry-Afterand send the same request again.
Headers
Retry-AfterintegerSeconds to wait before sending again; a request sent sooner is refused too.
Example
{
"error": {
"code": "service_unavailable",
"details": {
"reason": "busy"
},
"documentation_url": "https://hub.main-team.org/api/errors#service_unavailable",
"message": "The group is being changed by someone else. Try again in a second.",
"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.