Follow a batch of students you sent
- Bearer token
- Permission
student/create - Core record
Returns one of your imports and one page of its rows, in the order you sent them.
status is queued until a server picks the batch up, running while it registers, then succeeded or failed. Poll every few seconds; a batch of a thousand takes seconds, not minutes, once it starts.
On succeeded every row is registered and carries the student’s _id: keep them, and use them with getStudent, createSigninLink and createApplication. On anything else no student of this batch was registered, every row is skipped, and failure says why; the rows that explain it carry an error.
An import stops being readable 30 days after it finishes, and then answers 404 exactly like one that never existed. The students stay registered. An import another account sent answers the same way.
Parameters
Path parameters
| Name | Type | Description |
|---|---|---|
importIdrequired | string | The import’s |
Query parameters
| Name | Type | Description |
|---|---|---|
pageoptional | number | Page number. Defaults to 1. Example |
limitoptional | number | Items per page. Defaults to 20, max 100. Example |
Responses
200 OK
Success: message is "Import fetched successfully.".
Body
successbooleanrequiredAlways
trueon a success.one of
truemessagestringrequiredexample
Import fetched successfully.paginationobject · PaginationMetarequiredWhere one page sits in the whole list.
4 fields of pagination
pagenumberrequiredThe page returned, counting from 1.
min
1example1limitnumberrequiredItems per page: the
limityou sent, 20 if you sent none, and never more than 100.min
1max100example20totalnumberrequiredItems across every page.
min
0example57totalPagesnumberrequiredPages at this
limit:total / limit, rounded up.min
0example3
dataobject · StudentImportResponserequiredA batch of students you asked to register, and what has happened to it.
11 fields of data
_idstringrequiredThe import’s
_id. Read it withgetStudentImport.example
65f0c2a1d3e4f5a6b7c8d901statusstringrequiredqueueduntil a server picks it up,runningwhile it registers, thensucceededorfailed.cancelledmeans an operator stopped it. There is no partial import: on anything butsucceededno student of this batch was registered.one of
queuedrunningsucceededfailedcancelledexample
queuedtotalnumberrequiredHow many rows you sent.
example
250registerednumberrequiredHow many students were registered:
totalonce the import has succeeded, and 0 until then and for ever after a failure.example
250clientReferencestringThe
clientReferenceyou sent, if you sent one.example
year-10-autumn-2026failureobject · StudentImportFailureResponseSet once the import has failed or been cancelled.
2 fields of failure
codestringrequiredA stable machine code.
rows_rejected: a row stopped being registrable between the check and the write, most often because its address was registered in between; the rows say which.forbidden: the account may no longer register students.cancelled: an operator stopped it.internal_error: something failed on our side.example
rows_rejectedmessagestringrequiredWritten for a person, and may change: never branch on it.
example
Two rows could no longer be registered. Nothing was registered.
studentsarray of StudentImportRowResponserequiredOne page of the rows, in the order you sent them. Page it with
pageandlimit;pagination.totalcounts every row. Absent from the answer tocreateStudentImport, which has nothing to report yet.6 fields of each item
rownumberrequiredThe row’s position in the
studentsyou sent, counting from 0.example
0emailstringrequiredThe address you sent for this row, in lower case.
format
emailexamplejane.doe@example.comexternalRefstringThe
externalRefyou sent for this row, if you sent one.example
roster-2026-114statusstringrequiredpendinguntil the import runs, thenregisteredfor every row when it succeeds, orskippedfor every row when it does not. An import registers all of its students or none of them.one of
pendingregisteredskippedexample
registeredstudentIdstringThe student’s
_id, on aregisteredrow. Use it withgetStudent,createSigninLinkandcreateApplication.example
6650a1b2c3d4e5f6a7b8c9d0errorobject · StudentImportRowErrorResponseWhy this row could not be registered, when it could not.
4 fields of error
codestringrequiredA stable machine code: branch on this.
invalid_fieldandunexpected_fieldare the row’s own rules;unknown_referenceis acountry,grade,cityorschoolthat matches nothing;duplicate_in_requestis the same address twice in your own body;email_taken_by_your_studentis one of your students;email_unavailableis an address that cannot be registered, and the answer never says who holds it.one of
invalid_fieldunexpected_fieldunknown_referenceduplicate_in_requestemail_taken_by_your_studentemail_unavailableexample
email_taken_by_your_studentmessagestringrequiredWritten for a person, and may change: never branch on it.
example
One of your students already has this email address.studentIdstringOn
email_taken_by_your_student: the_idof the student of yours who has the address, so you can update them instead.example
6650a1b2c3d4e5f6a7b8c9d0duplicateOfnumberOn
duplicate_in_request: the earlier row in your own body with the same address.example
12
createdAtstringrequiredWhen you sent it.
format
date-timeexample2026-10-06T08:15:00.000ZstartedAtstringWhen a server picked it up.
format
date-timeexample2026-10-06T08:15:04.000ZfinishedAtstringWhen it ended, whichever way it ended.
format
date-timeexample2026-10-06T08:15:09.000ZexpiresAtstringrequiredWhen this import stops being readable. After it,
getStudentImportanswers 404, exactly as it does for an import that never existed. The students stay registered.format
date-timeexample2026-11-05T08:15:09.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": "Import fetched successfully.",
"pagination": {
"page": 1,
"limit": 20,
"total": 57,
"totalPages": 3
},
"data": {
"_id": "65f0c2a1d3e4f5a6b7c8d901",
"status": "queued",
"total": 250,
"registered": 250,
"clientReference": "year-10-autumn-2026",
"failure": {
"code": "rows_rejected",
"message": "Two rows could no longer be registered. Nothing was registered."
},
"students": [
{
"row": 0,
"email": "jane.doe@example.com",
"externalRef": "roster-2026-114",
"status": "registered",
"studentId": "6650a1b2c3d4e5f6a7b8c9d0",
"error": {
"code": "email_taken_by_your_student",
"message": "One of your students already has this email address.",
"studentId": "6650a1b2c3d4e5f6a7b8c9d0",
"duplicateOf": 12
}
}
],
"createdAt": "2026-10-06T08:15:00.000Z",
"startedAt": "2026-10-06T08:15:04.000Z",
"finishedAt": "2026-10-06T08:15:09.000Z",
"expiresAt": "2026-11-05T08:15:09.000Z"
}
}400 Bad request
bad_request: importId is not 24 hexadecimal digits.
When
bad_requestimportIdis not 24 hexadecimal digits.
Example
{
"error": {
"code": "bad_request",
"documentation_url": "https://hub.main-team.org/api/errors#bad_request",
"message": "Invalid value for '_id': expected ObjectId.",
"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 student/create on mto, the organization every operation without :organizationId acts on, or a role denies it.
When
forbiddenThe token is valid, but no role on your account allows
student/createonmto, the organization every operation without:organizationIdacts on, 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: No import of yours has this importId, it has expired, or bulk registration is not switched on for the environment you are calling. An import another account sent answers the same.
When
not_foundNo import of yours has this
importId, it has expired, or bulk registration is not switched on for the environment you are calling. An import another account sent answers the same.
Example
{
"error": {
"code": "not_found",
"documentation_url": "https://hub.main-team.org/api/errors#not_found",
"message": "Import 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.
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.