Version 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.
Release notes
Version 1.1.0 adds three operations and three fields, and removes and changes nothing. Code written against 1.0.1 keeps working without a change, and every request you send today answers as it did before.
Register a class in one request
createStudentImport takes 30 to 1000 students in one
request. Every row follows registerStudent's rules and takes
everything it takes except password, plus an optional externalRef of your own that comes back
on the row.
The whole batch is checked before anything is queued. If every row passes, the answer is 202 with
the import and a Location header, and the students are registered in the background. The batch
is written in one transaction: it registers every row or it registers nothing, so you never have
half a class to reconcile. Each student gets the welcome email registerStudent sends.
Follow the import with getStudentImport, which returns the
import and one page of its rows, with page and limit as every list takes them. status is
queued, running, succeeded, failed or cancelled. On succeeded every row carries the
student's _id; on anything else no student of that batch was registered. An import stops being
readable 30 days after it finishes, and then answers 404, as does an import another account sent.
Both need student/create on mto, the permission registration already needs, so an account that
registers students today can import them without a new role.
Limits, which are stricter than the rest of the API:
| Limit | Value |
|---|---|
| Rows in one request | 30 to 1000 |
| Requests | 10 an hour |
| Body | 1.5 MB on this operation, where every other takes 100 kB |
| Unfinished imports per account | 1; a second answers 409 |
Sending the same rows again inside 24 hours answers with the import you already have, rather than starting a second one, so a retry after a timeout is safe. The whole workflow, with a worked run, is in Registering many students at once.
A 422 that names the rows
When a batch is refused, nothing is queued and the answer is 422 unprocessable_entity with every
row it will not register. That list arrives in a new, optional details object on the error body:
{
"error": {
"code": "unprocessable_entity",
"message": "The import was refused.",
"documentation_url": "https://hub.main-team.org/api/errors#unprocessable_entity",
"request_id": "0199d2d6-6f04-7a31-9f2a-0f4d2a17c2b1",
"details": {
"rows": [
{ "row": 4, "field": "grade", "code": "unknown_reference", "message": "Grade not found!" }
]
}
}
}
details is optional and operation-specific: createStudentImport is the only operation that
sends one today, and every other error body is exactly what it was in 1.0.1. Ignore a details you
do not recognize, as you ignore any field you do not recognize. The four fields your code already
reads — code, message, documentation_url and request_id — are unchanged and always there.
See Errors and Requests and responses.
Check a registration before you send it
checkStudentRegistration runs the checks
registerStudent makes before it creates anything, on the same
body without password, and answers 200 with what they found:
valid, whether registration would go through;- every
country,grade,cityorschoolthat matches nothing, with the message registration would refuse it with, and the_ideach reference that does match resolves to; - whether one of your own students already has the email address, and that student's
_id.
Nothing is created, no username is issued, and it needs student/create on mto, the permission
registration needs. Only your own students are checked for the address: an address a student on
another account holds is still refused by registerStudent itself, so a valid answer is a strong
hint, never a promise. See Students.
Find students by address, and see who has signed in
listStudents takes an optional email query parameter: up to 100
addresses, separated by commas, matched exactly and without regard to case. It lists only your own
students, as it always has. A value that is not such a list is refused with 400 bad_request.
listOrgStudents carries a new signedIn field on every
student, whether that student has signed in to that organization at least once, and takes signedIn
as an optional filter, true or false. Any other value is refused with 400 bad_request. This is
the field to look at when an entry or a supervisor link is refused: both
createApplication and
linkStudentSupervisor need the student to have signed in
to that organization once, which a sign-in link does.
Nothing to change
- No operation, request field, response field or error code was removed or narrowed.
- No request field became required, and no response field became optional.
- The new response field,
signedInonlistOrgStudents, is an addition: a client that rejects fields it does not know is the only one that notices it.