Help
Changelog
Every change to the API contract, by version, newest first: operations, request and response fields, error codes, and the rules your integration relies on. A change that can break code written against these pages is marked Breaking.
Versions follow semantic versioning. A minor version adds without breaking, a patch version fixes, and a new major version gets a new path prefix; every 1.x version is served under /v1. Ignore response fields your code does not recognise: new ones can arrive in any minor version.
1.2.0
Nine new operations under one new tag, Group challenges, behind two new permissions, group-challenge/read and group-challenge/submit. Nothing that worked against 1.1.1 changes.
Added
- Group challenges, a new resource under
/v1/{organizationId}/group-challengewith the tag "Group challenges" and the permissiongroup-challenge/read(also granted by*/read,*/*and*):listGroupChallengesandgetGroupChallengeread the challenges an organization runs;listGroupChallengeStudentsandgetGroupChallengeStudentsay where each of your students stands (eligible by grade, in a group, the group's state) and give thepanelPathto send them to withcreateSigninLink;listGroupChallengeGroupsandgetGroupChallengeGroupread the groups your students are in, with each step and its files;listGroupChallengeActivitylists what happened in a group. Only your own students are named: every other person is their role alone. The operations answer404on an organization where group challenges are not switched on. - Submitting group challenge work for one of your students, behind the new permission
group-challenge/submit(also granted by*/*and*, not by*/read):submitGroupChallengeStepsubmits a group's open step and opens the next, andsubmitGroupChallengeWorksends the group's finished work and e-mails its members and teacher. Both take{ "studentId": "<studentId>" }, a student of yours who is an active member of the group, and are recorded in the group's history as your account acting for them. Repeating a submit that already happened answers200withchanged: false.
Changed
submitGroupChallengeStepandsubmitGroupChallengeWorkare the first operations whose409 conflictcarrieserror.details.reason(challenge_closed,window_closed,payment_pending,group_not_confirmed,step_locked,step_empty,steps_incomplete), and whose503 service_unavailablecarriesdetails.reason: busywithRetry-After: 1when the group is being changed by someone else. The error reference for both codes says so.- The agent kit has a seventh skill,
following-group-challenges-via-api: reading where your students stand withlistGroupChallengeStudentsandgetGroupChallengeGroupbefore submitting withsubmitGroupChallengeSteporsubmitGroupChallengeWork, never looping on a409, and sending a student to a challenge withcreateSigninLinkandpanelPath.
1.1.1
A documentation release. The published agent kit now carries six skills instead of two, including one written against bulk registration, and none of them describes anything but this API. Every call that worked against 1.1.0 works unchanged.
Changed
- The agent kit published at https://hub.main-team.org/api/skills now has six skills instead of two, all of them about this REST API:
integrating-main-team-api(tokens, the envelopes, pagination, rate limits, retries and idempotency, polling, every error code),registering-a-main-team-student,registering-main-team-students-from-spreadsheets,enrolling-students-in-olympiad-exams-via-api,downloading-results-and-certificates-via-apiandmanaging-api-access-and-tokens. The spreadsheet skill is now written againstcreateStudentImportandgetStudentImport: it builds one request body of 30 to 1000 rows, reads the422row list, and polls the import until it has succeeded or failed. Nothing in the kit describes the MCP server, which is a separate service. No operation, field, permission or error code changed.
1.1.0
Three new operations — bulk registration, its status, and a registration pre-check — with an email filter on listStudents, a signedIn field and filter on listOrgStudents, and an optional details object on error bodies. Nothing that worked against 1.0.1 changes.
Added
createStudentImport:POST /v1/student/importregisters 30 to 1000 students in one request. Every row followsregisterStudent's rules and takes everything it takes exceptpassword, plus an optionalexternalRefof your own that is echoed back. The whole batch is checked before anything is queued; the answer is202with the import and aLocationheader, and the students are registered in the background. They are written in one transaction: a batch registers every row or registers nothing, so there is no partial import to reconcile. Each student gets the same welcome emailregisterStudentsends. Limits: one unfinished import per account (409otherwise), 10 requests an hour, and bodies up to 1.5 MB on this operation where every other takes 100 kB. Sending the same rows again inside 24 hours answers with the import you already have rather than a second one. It needsstudent/createonmto, the permission registration needs.getStudentImport:GET /v1/student/import/{importId}returns one of your imports and one page of its rows, withpageandlimit.statusisqueued,running,succeeded,failedorcancelled; onsucceededevery row carries the student's_id, and on anything else no student of that batch was registered. An import stops being readable 30 days after it finishes and then answers404, as does one another account sent. It needsstudent/createonmto.- Error bodies may carry an optional
detailsobject, andcreateStudentImportis the first operation to send one: its422puts every row it cannot register inerror.details.rows, each with its position, the property at fault and a code to branch on. Ignore adetailsyou do not recognise; no other operation sends one. checkStudentRegistration:POST /v1/student/checkruns the checksregisterStudentmakes before it creates anything, on the same body withoutpassword, and answers200with what they found:valid; everycountry,grade,cityorschoolthat matches nothing, with the message registration would refuse it with; the_ideach reference resolves to; and whether one of your students already has the email address, with that student's_id. Nothing is created and no username is issued. It needsstudent/createonmto, the permission registration needs. Only your own students are checked for the address, so an address a student on another account holds is still refused byregisterStudentalone.listStudents: an optionalemailquery parameter lists only your students who have one of up to 100 addresses, separated by commas, matched exactly and without regard to case. A value that is not such a list is refused with400 bad_request.listOrgStudents: each student carriessignedIn, whether they have signed in to that organization at least once, and an optionalsignedInquery parameter,trueorfalse, lists only the students who have or only those who have not. Any other value is refused with400 bad_request.createApplicationandlinkStudentSupervisoron an organization need the student to have signed in there once.
1.0.1
The sandbox is live and the contract names it as a second server, and the panel now requires students to confirm their email address. No operation, field or error code changes.
Added
- The contract's server list names the sandbox,
https://apisnd.main-team.org, after production, markedx-environment: sandbox. Production stays the first entry, so a tool that takes the first server still calls production. No operation changes.
Changed
- The sandbox is live at
https://apisnd.main-team.org/v1. It runs the same release as production, with separate accounts issued by an operator, seeded reference data with production's organization ids, no emails and test-mode payments. Registration is open there, with the same permissions and rate limits as in production. The official client libraries accept only the production base URL, so call the sandbox over HTTPS directly. The "Try it" console on the reference pages works against the sandbox with a token you paste. Students pay in the sandbox's panels with Stripe's test cards, such as 4242 4242 4242 4242; the Environments page lists them. Test cards never work in production. createSigninLink: a student whose email address is not confirmed must now confirm it before they can use the panel, My Exams included, so they confirm before they can start an exam. The panel asks for a 6-digit code on every page until the address is confirmed, and the student can no longer put it off; signing out is the only way out. A student already inside an exam room is not interrupted. Only the confirmation on the student's core record counts. A code is valid for 15 minutes, and a new one can be requested after 60 seconds. The sandbox sends no emails, so its panels don't ask. Have your students confirm well before an exam day, for example right after their first sign-in. The request, its answers and the link are unchanged.- Documentation corrections: the pages that called the sandbox planned now describe it as it runs, and every page that called the panel's email confirmation prompt optional now describes it as required. The pages no longer write a current version number into their text: the API reference shows the version they describe, and the changelog lists every version.
1.0.0
Initial release of the v1 partner API, with 35 operations for students, sign-in links, exams, applications, certificates and reports.
Added
- Health:
getHealth. - API account:
getCurrentApiAccount,revokeToken. - Reference data:
listCountries,getCountry,listGrades,getGrade,listOrganizations,getOrganization. - Students:
listStudents,getStudent,registerStudent,updateStudent,setStudentPassword,listOrgStudents,getOrgStudent,updateOrgStudent,linkStudentSupervisor. - Sign-in links:
createSigninLink. - Exams:
listExamCategories,getExamCategory,listExams,listAvailableExams,getExam. - Applications:
listApplications,listExamApplications,listStudentApplications,getApplication,createApplication,moveApplication,deleteApplication. - Documents:
listStudentCertificates,downloadCertificate,listStudentReports,downloadReport. - Authentication with self-signed HS256 tokens, where
kidandsubare the apiKey, andiatandexpare required with a lifetime of at most 3600 seconds and 30 seconds of clock tolerance. Every authentication failure answers the same401 unauthorized. - Permissions by role, checked against the organization each request acts on. An operation with no permission granted answers
403 forbidden. - Response envelopes:
{ success, message, data, pagination? }on success, and{ error: { code, message, documentation_url, request_id } }on failure, with anX-Request-Idheader on every response.documentation_urlis the code's entry on the partner documentation,https://hub.main-team.org/api/errors#<code>. - Pagination with
page(default 1) andlimit(default 20, at most 100). - A rate limit of 100 requests per 60 seconds for each account and each operation, with
Retry-AfterandX-RateLimit-*headers. Each operation has its own budget. Going over answers429 too_many_requestswith the message "Too many requests to this operation. Wait the number of seconds in Retry-After, then try again." createSigninLink: sign-in links that are valid for 120 seconds and work once, for a student with access to that organization.getStudent,getOrgStudent,getCountry,getGrade,getOrganizationandgetExamCategoryanswer404 not_foundfor an id they cannot show, as every single read does. A student of another account answers like a missing one.getApplication,moveApplicationanddeleteApplicationanswer404with "Application not found!" alike for a missing application and one of another account's student.listOrganizationsandgetOrganizationserve the five organizations that run exams:stem,hilingua,neo,gmathandcoding. On every operation withorganizationIdin its path, the id of any other organization,mto's included, answers404 not_foundwith "Organization not found!", as an id that names no organization does.getCurrentApiAccountreturns the account'sroles, each witheffect,action,targetand, when set,authorized, to compare with an operation'sx-permission.registerStudentrequiresfirstName,lastName,email,birth,sex,country,grade,cityandschool, withbirtha date that exists, as DD/MM/YYYY, and names a missing field in its400("lastName should not be empty").updateStudentandupdateOrgStudenthold the fields they are sent to the same rules, and refusenullforfirstName,lastName,birth,sexandphone.registerStudentrefuses anullphonetoo.activatedPlatformsThisSeasononregisterStudent,updateStudentandupdateOrgStudenttakescommon,stem,hilingua,neo,gmathandcoding. The two updates add to the student's list and never remove from it.updateOrgStudentwithout the field, even with an empty body, adds its own organization, unless the list already holdscommon. All three refusenullfor it with400;registerStudentwithout it stores["common"].- Passwords: a
passwordatregisterStudent, andsetStudentPassword, needauth/signinonmto. A password is 5 characters to 72 bytes and contains none of the student's own details.setStudentPasswordanswers409once the student has confirmed their email address. linkStudentSupervisoranswers every refusal with the same404"Not found!".createApplicationis safe to repeat: the same student and exam answer200with the existing application.