{
  "components": {
    "schemas": {
      "ApiAccountResponse": {
        "description": "The API account a token belongs to.",
        "properties": {
          "_id": {
            "description": "Your account’s id.",
            "example": "6650a1b2c3d4e5f6a7b8c9f0",
            "type": "string"
          },
          "apiKey": {
            "description": "Your account’s `apiKey`: what goes in a token’s `kid` header and `sub` claim.",
            "example": "key_EXAMPLEexample0123456789",
            "type": "string"
          },
          "companyName": {
            "description": "The company the account was issued to.",
            "example": "Example Learning Ltd",
            "type": "string"
          },
          "scopes": {
            "description": "Labels an operator set on the account. Informational: no operation checks them, and what your account may do is decided by its roles.",
            "example": [],
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "roles": {
            "description": "What your account may do, as an operator set it. Compare them with an operation’s `x-permission` to see why it answers `403 forbidden`. Only an operator can change them, and a change reaches every operation within 60 seconds.",
            "items": {
              "$ref": "#/components/schemas/RoleResponse"
            },
            "type": "array"
          },
          "isActive": {
            "description": "Always `true` here: a token of an account that is not active is refused with `401` before this is reached.",
            "example": true,
            "type": "boolean"
          }
        },
        "required": [
          "_id",
          "apiKey",
          "companyName",
          "scopes",
          "roles",
          "isActive"
        ],
        "type": "object"
      },
      "ApplicationPartnerResponse": {
        "description": "Another member of a team application.",
        "properties": {
          "user": {
            "description": "The team member’s id in this organization. Not one of your students’ ids, and not resolved.",
            "example": "6650a1b2c3d4e5f6a7b8c9d6",
            "type": "string"
          },
          "accepted": {
            "description": "Whether they have accepted the invitation to the team.",
            "example": true,
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "ApplicationRecordResponse": {
        "description": "An application as stored, with `exam`, `user` and `payment` as ids. Read the application to have them resolved.",
        "properties": {
          "_id": {
            "description": "The application’s id: what `applicationId` takes.",
            "example": "6650a1b2c3d4e5f6a7b8c9e5",
            "type": "string"
          },
          "exam": {
            "description": "The id of the exam applied for.",
            "example": "6650a1b2c3d4e5f6a7b8c9e1",
            "type": "string"
          },
          "user": {
            "description": "The 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": "6650a1b2c3d4e5f6a7b8c9e9",
            "type": "string"
          },
          "payment": {
            "description": "The id of the application’s payment.",
            "example": "6650a1b2c3d4e5f6a7b8c9e6",
            "type": "string"
          },
          "partners": {
            "description": "On a team exam, the other members of the team. Empty for an exam sat alone.",
            "items": {
              "$ref": "#/components/schemas/ApplicationPartnerResponse"
            },
            "type": "array"
          },
          "participated": {
            "description": "Whether the student has started the exam. A started application cannot be moved to another exam.",
            "example": false,
            "type": "boolean"
          },
          "examStart": {
            "description": "When the student started the exam; absent until then.",
            "example": "2026-11-14T10:04:12.000Z",
            "format": "date-time",
            "type": "string"
          },
          "examSubmitted": {
            "description": "Whether the student has handed the exam in. A handed-in application cannot be moved to another exam.",
            "example": false,
            "type": "boolean"
          },
          "submitDate": {
            "description": "When the student handed the exam in; absent until then.",
            "example": "2026-11-14T11:12:40.000Z",
            "format": "date-time",
            "type": "string"
          },
          "simulationStarted": {
            "description": "Whether the student has started the practice run.",
            "example": false,
            "type": "boolean"
          },
          "simulationStart": {
            "description": "When the student started the practice run; absent until then.",
            "example": "2026-11-07T10:00:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "simulationSubmitted": {
            "description": "Whether the student has handed the practice run in.",
            "example": false,
            "type": "boolean"
          },
          "removeAfter": {
            "description": "Only on an application to an exam on a make-up sitting: when it will be removed, 6 hours after it was made.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "uuid": {
            "description": "A short reference code for the application.",
            "example": "a3f-09c-7e1",
            "type": "string"
          },
          "createdAt": {
            "description": "When the application was made.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the application last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "_id",
          "exam",
          "user",
          "partners",
          "participated",
          "simulationStarted",
          "simulationSubmitted",
          "uuid",
          "createdAt",
          "updatedAt"
        ],
        "type": "object"
      },
      "ApplicationResponse": {
        "description": "An application of one of your students, with its `exam`, `user` (the student) and `payment` resolved into records.",
        "properties": {
          "_id": {
            "description": "The application’s id: what `applicationId` takes.",
            "example": "6650a1b2c3d4e5f6a7b8c9e5",
            "type": "string"
          },
          "exam": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ExamRecordResponse"
              }
            ],
            "description": "The exam applied for. Its own `session`, `category` and `language` are ids. `null` if the exam no longer exists.",
            "nullable": true,
            "type": "object"
          },
          "user": {
            "allOf": [
              {
                "$ref": "#/components/schemas/StudentRefResponse"
              }
            ],
            "description": "The student the application is for."
          },
          "payment": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PaymentResponse"
              }
            ],
            "description": "The application’s payment. `null` if the stored reference no longer resolves; absent when none is set.",
            "nullable": true,
            "type": "object"
          },
          "partners": {
            "description": "On a team exam, the other members of the team. Empty for an exam sat alone.",
            "items": {
              "$ref": "#/components/schemas/ApplicationPartnerResponse"
            },
            "type": "array"
          },
          "participated": {
            "description": "Whether the student has started the exam. A started application cannot be moved to another exam.",
            "example": false,
            "type": "boolean"
          },
          "examStart": {
            "description": "When the student started the exam; absent until then.",
            "example": "2026-11-14T10:04:12.000Z",
            "format": "date-time",
            "type": "string"
          },
          "examSubmitted": {
            "description": "Whether the student has handed the exam in. A handed-in application cannot be moved to another exam.",
            "example": false,
            "type": "boolean"
          },
          "submitDate": {
            "description": "When the student handed the exam in; absent until then.",
            "example": "2026-11-14T11:12:40.000Z",
            "format": "date-time",
            "type": "string"
          },
          "simulationStarted": {
            "description": "Whether the student has started the practice run.",
            "example": false,
            "type": "boolean"
          },
          "simulationStart": {
            "description": "When the student started the practice run; absent until then.",
            "example": "2026-11-07T10:00:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "simulationSubmitted": {
            "description": "Whether the student has handed the practice run in.",
            "example": false,
            "type": "boolean"
          },
          "removeAfter": {
            "description": "Only on an application to an exam on a make-up sitting: when it will be removed, 6 hours after it was made.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "uuid": {
            "description": "A short reference code for the application.",
            "example": "a3f-09c-7e1",
            "type": "string"
          },
          "createdAt": {
            "description": "When the application was made.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the application last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "_id",
          "exam",
          "user",
          "partners",
          "participated",
          "simulationStarted",
          "simulationSubmitted",
          "uuid",
          "createdAt",
          "updatedAt"
        ],
        "type": "object"
      },
      "AvailableExamCategoryResponse": {
        "description": "A category with at least one exam the student can apply to: the top of the available-exams tree.",
        "properties": {
          "_id": {
            "description": "The category’s id.",
            "example": "6650a1b2c3d4e5f6a7b8c9e3",
            "type": "string"
          },
          "name": {
            "description": "The category’s name, as students see it.",
            "example": "Mathematics",
            "type": "string"
          },
          "altName": {
            "description": "A second name for the category, where one is set.",
            "example": "Maths",
            "type": "string"
          },
          "order": {
            "description": "Where the category sorts among the others, lowest first. The available-exams tree is in this order.",
            "example": 1,
            "type": "number"
          },
          "isActive": {
            "description": "Whether the category is live. No exam in an inactive category is open for applications.",
            "example": true,
            "type": "boolean"
          },
          "nonAcceptedReplacements": {
            "description": "Ids of the categories an application in this one may not be moved to: `moveApplication` refuses such a move.",
            "example": [
              "6650a1b2c3d4e5f6a7b8c9ea"
            ],
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "studyMaterialLinks": {
            "description": "Links to study material for the category.",
            "example": [
              "https://example.org/study/mathematics"
            ],
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "createdAt": {
            "description": "When the record was created.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the record last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "sessions": {
            "description": "The sittings in this category the student can apply to, soonest first. Never empty. A sitting the student already holds an application for in this category is left out.",
            "items": {
              "$ref": "#/components/schemas/AvailableExamSessionResponse"
            },
            "type": "array"
          }
        },
        "required": [
          "_id",
          "sessions"
        ],
        "type": "object"
      },
      "AvailableExamLanguageResponse": {
        "description": "A language one sitting can be taken in by this student: a leaf of the available-exams tree.",
        "properties": {
          "_id": {
            "description": "The language’s id.",
            "example": "6650a1b2c3d4e5f6a7b8c9e4",
            "type": "string"
          },
          "name": {
            "description": "The language’s name.",
            "example": "English",
            "type": "string"
          },
          "code": {
            "description": "A short language code.",
            "example": "en",
            "type": "string"
          },
          "order": {
            "description": "Where the language sorts among the others, lowest first. The available-exams tree is in this order.",
            "example": 1,
            "type": "number"
          },
          "createdAt": {
            "description": "When the record was created.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the record last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "matchedExam": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ExamRecordResponse"
              }
            ],
            "description": "The one exam this category, sitting and language make up. Its `_id` is the `examId` that `createApplication` takes; its `session`, `category` and `language` are the ids of the levels above."
          }
        },
        "required": [
          "_id",
          "matchedExam"
        ],
        "type": "object"
      },
      "AvailableExamSessionResponse": {
        "description": "A sitting in one category the student can apply to: a branch of the available-exams tree.",
        "properties": {
          "_id": {
            "description": "The sitting’s id.",
            "example": "6650a1b2c3d4e5f6a7b8c9e2",
            "type": "string"
          },
          "sessionName": {
            "description": "The sitting’s name.",
            "example": "November 2026",
            "type": "string"
          },
          "date": {
            "description": "When the sitting takes place. Its exams are open for applications until this moment, and not after.",
            "example": "2026-11-14T10:00:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "startTime": {
            "description": "The start time as the organization wrote it. For the moment itself, read `date`.",
            "example": "10:00",
            "type": "string"
          },
          "tz": {
            "description": "`global`: the sitting starts at one moment everywhere. `local`: it starts at the same clock time in each student’s own time zone, their country’s `tz`.",
            "enum": [
              "global",
              "local"
            ],
            "example": "global",
            "type": "string"
          },
          "sessionAlias": {
            "description": "A second name for the sitting, shown to students.",
            "example": "Autumn round",
            "type": "string"
          },
          "sessionNote": {
            "description": "A note about the sitting, shown to students.",
            "example": "Please join ten minutes early.",
            "type": "string"
          },
          "enableSimulation": {
            "description": "Whether students get a practice run before the sitting.",
            "example": true,
            "type": "boolean"
          },
          "simulationDate": {
            "description": "When the practice run opens.",
            "example": "2026-11-07T10:00:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "simulationEndDate": {
            "description": "When the practice run closes.",
            "example": "2026-11-08T10:00:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "relatedSession": {
            "description": "Set on a make-up sitting: the id of the sitting it belongs to. An application to an exam on a make-up sitting is removed 6 hours after it is made.",
            "example": "6650a1b2c3d4e5f6a7b8c9e2",
            "type": "string"
          },
          "createdAt": {
            "description": "When the record was created.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the record last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "languages": {
            "description": "The languages the student can take this sitting in, in the organization’s order. Never empty.",
            "items": {
              "$ref": "#/components/schemas/AvailableExamLanguageResponse"
            },
            "type": "array"
          }
        },
        "required": [
          "_id",
          "languages"
        ],
        "type": "object"
      },
      "CertificateResponse": {
        "description": "A released certificate of one of your students. Download the PDF with `downloadCertificate`.",
        "properties": {
          "_id": {
            "description": "The certificate’s id. `downloadCertificate` takes it, or `shortId`.",
            "example": "6650a1b2c3d4e5f6a7b8c9e7",
            "type": "string"
          },
          "shortId": {
            "description": "A ten-character code for the certificate. `downloadCertificate` takes it too.",
            "example": "K7Q2M9X4TB",
            "type": "string"
          },
          "title": {
            "description": "The certificate’s title.",
            "example": "Certificate of Participation",
            "type": "string"
          },
          "application": {
            "description": "The id of the application it was issued for, when it was issued for one.",
            "example": "6650a1b2c3d4e5f6a7b8c9e5",
            "type": "string"
          },
          "user": {
            "description": "The organization’s own id for the student, when the certificate names the student directly rather than through `application`.",
            "example": "6650a1b2c3d4e5f6a7b8c9e9",
            "type": "string"
          },
          "active": {
            "description": "Always `true`: only released certificates are listed.",
            "example": true,
            "type": "boolean"
          },
          "createdAt": {
            "description": "When the certificate was created.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the certificate last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "_id",
          "shortId",
          "active"
        ],
        "type": "object"
      },
      "CheckStudentRegistrationRequest": {
        "description": "The body of `checkStudentRegistration`: the body of `registerStudent` without `password`, with the same rules.",
        "properties": {
          "firstName": {
            "description": "The student’s first name. Printed on certificates and reports, followed by `lastName`.",
            "example": "Jane",
            "type": "string"
          },
          "lastName": {
            "description": "The student’s surname. Printed on certificates and reports after `firstName`. It cannot be empty.",
            "example": "Doe",
            "type": "string"
          },
          "birth": {
            "description": "Date of birth as `DD/MM/YYYY`, and a date that exists: `31/02/2008` is refused. An ISO date such as `2008-05-14` is refused.",
            "example": "14/05/2008",
            "pattern": "^(0[1-9]|[12]\\d|3[01])\\/(0[1-9]|1[0-2])\\/\\d{4}$",
            "type": "string"
          },
          "sex": {
            "description": "One of `m`, `f` or `n`.",
            "enum": [
              "m",
              "f",
              "n"
            ],
            "example": "f",
            "type": "string"
          },
          "email": {
            "description": "The student’s email address, stored in lower case. An address belongs to one student on the whole platform, so one already registered, by your account or another, is refused with 409.",
            "example": "jane.doe@example.com",
            "format": "email",
            "type": "string"
          },
          "email2": {
            "description": "Leave this out. Any value but an empty one is refused with 400.",
            "type": "string"
          },
          "phone": {
            "description": "A phone number, stored as you send it. No format is checked. On an update, `\"\"` clears it.",
            "example": "+1 555 0100",
            "type": "string"
          },
          "country": {
            "description": "The `_id` of a country, from `listCountries` (`GET /v1/country`). An id only: a name or an ISO code is refused. Its two-letter code starts the student’s `username`.",
            "example": "6650a1b2c3d4e5f6a7b8c9d1",
            "pattern": "^[0-9a-fA-F]{24}$",
            "type": "string"
          },
          "grade": {
            "description": "The `_id` of a grade, from `listGrades` (`GET /v1/grade`), or its name, `1` to `12`. Either way the grade’s `_id` is what is stored. One that matches no grade is refused with 400. The grade decides which exams the student is offered.",
            "example": "10",
            "type": "string"
          },
          "school": {
            "description": "The `_id` of a school, or its name within `country` and `city`, matched without regard to case. There is no list of schools to look one up in: send the name your records hold. One that matches no school is refused with 400; no school is ever created.",
            "example": "Springfield High School",
            "type": "string"
          },
          "city": {
            "description": "The `_id` of a city, or its name within `country`, matched without regard to case. There is no list of cities to look one up in: send the name your records hold. One that matches no city is refused with 400; no city is ever created.",
            "example": "Springfield",
            "type": "string"
          },
          "activatedPlatformsThisSeason": {
            "description": "The organizations the student takes part in this season, by `slug` (as `listOrganizations` gives it, except `mto`: the core record, which every student is on), or `common` for every organization. Registration sets `[\"common\"]` when you leave it out; `null` is refused with 400. At most 6 entries.",
            "example": [
              "common"
            ],
            "items": {
              "enum": [
                "common",
                "stem",
                "hilingua",
                "neo",
                "gmath",
                "coding"
              ],
              "type": "string"
            },
            "maxItems": 6,
            "type": "array"
          }
        },
        "required": [
          "firstName",
          "lastName",
          "birth",
          "sex",
          "email",
          "country",
          "grade",
          "school",
          "city"
        ],
        "type": "object"
      },
      "CityResponse": {
        "description": "A city, as a student’s `city`.",
        "properties": {
          "_id": {
            "description": "The city’s id.",
            "example": "6650a1b2c3d4e5f6a7b8c9d2",
            "type": "string"
          },
          "name": {
            "description": "The city’s name, in capitals.",
            "example": "SPRINGFIELD",
            "type": "string"
          },
          "country": {
            "description": "The id of the country the city is in, where one is recorded.",
            "example": "6650a1b2c3d4e5f6a7b8c9d1",
            "type": "string"
          },
          "stateCode": {
            "description": "A state or region code, where one is recorded.",
            "example": "IL",
            "type": "string"
          },
          "createdAt": {
            "description": "When the record was created.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the record last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "_id"
        ],
        "type": "object"
      },
      "CountryResponse": {
        "description": "A country a student can be registered in.",
        "properties": {
          "_id": {
            "description": "The country’s id: what `country` takes when you register or update a student.",
            "example": "6650a1b2c3d4e5f6a7b8c9d1",
            "type": "string"
          },
          "name": {
            "description": "The country’s name, in capitals.",
            "example": "UNITED STATES",
            "type": "string"
          },
          "iso3": {
            "description": "The ISO 3166-1 alpha-3 code, in capitals.",
            "example": "USA",
            "type": "string"
          },
          "iso2": {
            "description": "The ISO 3166-1 alpha-2 code, in capitals. A student’s `username` starts with it.",
            "example": "US",
            "type": "string"
          },
          "tz": {
            "description": "The IANA time zone a sitting on local time is read in for students in this country (see `tz` on a sitting).",
            "example": "America/New_York",
            "type": "string"
          },
          "dialCode": {
            "description": "The international dialling code, with its `+`.",
            "example": "+1",
            "type": "string"
          },
          "flag": {
            "description": "The country’s flag, as an emoji.",
            "example": "🇺🇸",
            "type": "string"
          },
          "createdAt": {
            "description": "When the record was created.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the record last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "_id"
        ],
        "type": "object"
      },
      "CreateApplicationRequest": {
        "description": "The body of `createApplication`: the exam, and the student to enter for it.",
        "properties": {
          "examId": {
            "description": "The exam’s `_id` in this organization, 24 hexadecimal digits: a `listAvailableExams` leaf’s `matchedExam._id`, or an `_id` from `listExams`. It has to be an exam `listAvailableExams` offers this student.",
            "example": "6650a1b2c3d4e5f6a7b8c9e1",
            "type": "string"
          },
          "studentId": {
            "description": "The student’s `_id`, 24 hexadecimal digits: the id `registerStudent` returned. It has to be one of your students, the ones your account registered.",
            "example": "6650a1b2c3d4e5f6a7b8c9d0",
            "type": "string"
          }
        },
        "required": [
          "examId",
          "studentId"
        ],
        "type": "object"
      },
      "CreateSigninLinkRequest": {
        "description": "The body of `createSigninLink`: the student to sign in, and where they land.",
        "properties": {
          "studentId": {
            "description": "The id (`_id`) of the student to sign in, as `registerStudent` returned it. It must be one of your students.",
            "example": "6650a1b2c3d4e5f6a7b8c9d0",
            "type": "string"
          },
          "redirect": {
            "description": "Where the student lands once signed in: a path on the organization’s panel, starting with a single `/`, with no whitespace and no backslash anywhere. Full URLs, `//host` and `/\\host` are refused, so a link can never send anyone off the panel. `{userId}` in the path is replaced with the student’s id on that organization. Leave it out to land on the organization’s `defaultRedirect`.",
            "example": "/dashboard",
            "type": "string"
          }
        },
        "required": [
          "studentId"
        ],
        "type": "object"
      },
      "CreateStudentImportRequest": {
        "description": "The body of `createStudentImport`.",
        "properties": {
          "students": {
            "description": "The students to register, 30 to 1000 of them. Below that, call `registerStudent` per student: an import is queued and answered before it runs, and for a handful of students the round trip is not worth the wait.",
            "items": {
              "$ref": "#/components/schemas/StudentImportRow"
            },
            "maxItems": 1000,
            "minItems": 30,
            "type": "array"
          },
          "clientReference": {
            "description": "Your own name for this batch, echoed back when you read the import. At most 64 characters.",
            "example": "year-10-autumn-2026",
            "maxLength": 64,
            "type": "string"
          }
        },
        "required": [
          "students"
        ],
        "type": "object"
      },
      "ErrorDetail": {
        "description": "What went wrong.",
        "properties": {
          "code": {
            "description": "A stable machine code: branch on this. Each one is explained at `documentation_url`.",
            "enum": [
              "invalid_email",
              "bad_request",
              "unauthorized",
              "forbidden",
              "not_found",
              "conflict",
              "payload_too_large",
              "unsupported_media_type",
              "unprocessable_entity",
              "too_many_requests",
              "internal_error",
              "service_unavailable"
            ],
            "example": "not_found",
            "type": "string"
          },
          "message": {
            "description": "Written for a person, and may change: never branch on it.",
            "example": "Not found!",
            "type": "string"
          },
          "documentation_url": {
            "description": "Where this code is explained.",
            "example": "https://hub.main-team.org/api/errors#not_found",
            "format": "uri",
            "type": "string"
          },
          "request_id": {
            "description": "This request’s id, also sent as the `X-Request-Id` response 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-3f4a5b6c7d8e",
            "type": "string"
          },
          "details": {
            "additionalProperties": true,
            "description": "Present 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 `details` you do not recognise.",
            "type": "object"
          }
        },
        "required": [
          "code",
          "message",
          "documentation_url",
          "request_id"
        ],
        "type": "object"
      },
      "ErrorEnvelope": {
        "description": "How every error response is wrapped.",
        "properties": {
          "error": {
            "$ref": "#/components/schemas/ErrorDetail"
          }
        },
        "required": [
          "error"
        ],
        "type": "object"
      },
      "ExamApplicationResponse": {
        "description": "An application on the list for one exam: `exam` is the id you asked about, and `user` and `payment` are resolved into records.",
        "properties": {
          "_id": {
            "description": "The application’s id: what `applicationId` takes.",
            "example": "6650a1b2c3d4e5f6a7b8c9e5",
            "type": "string"
          },
          "exam": {
            "description": "The id of the exam applied for: the one you asked about.",
            "example": "6650a1b2c3d4e5f6a7b8c9e1",
            "type": "string"
          },
          "user": {
            "allOf": [
              {
                "$ref": "#/components/schemas/StudentRefResponse"
              }
            ],
            "description": "The student the application is for."
          },
          "payment": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PaymentResponse"
              }
            ],
            "description": "The application’s payment. `null` if the stored reference no longer resolves; absent when none is set.",
            "nullable": true,
            "type": "object"
          },
          "partners": {
            "description": "On a team exam, the other members of the team. Empty for an exam sat alone.",
            "items": {
              "$ref": "#/components/schemas/ApplicationPartnerResponse"
            },
            "type": "array"
          },
          "participated": {
            "description": "Whether the student has started the exam. A started application cannot be moved to another exam.",
            "example": false,
            "type": "boolean"
          },
          "examStart": {
            "description": "When the student started the exam; absent until then.",
            "example": "2026-11-14T10:04:12.000Z",
            "format": "date-time",
            "type": "string"
          },
          "examSubmitted": {
            "description": "Whether the student has handed the exam in. A handed-in application cannot be moved to another exam.",
            "example": false,
            "type": "boolean"
          },
          "submitDate": {
            "description": "When the student handed the exam in; absent until then.",
            "example": "2026-11-14T11:12:40.000Z",
            "format": "date-time",
            "type": "string"
          },
          "simulationStarted": {
            "description": "Whether the student has started the practice run.",
            "example": false,
            "type": "boolean"
          },
          "simulationStart": {
            "description": "When the student started the practice run; absent until then.",
            "example": "2026-11-07T10:00:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "simulationSubmitted": {
            "description": "Whether the student has handed the practice run in.",
            "example": false,
            "type": "boolean"
          },
          "removeAfter": {
            "description": "Only on an application to an exam on a make-up sitting: when it will be removed, 6 hours after it was made.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "uuid": {
            "description": "A short reference code for the application.",
            "example": "a3f-09c-7e1",
            "type": "string"
          },
          "createdAt": {
            "description": "When the application was made.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the application last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "_id",
          "exam",
          "user",
          "partners",
          "participated",
          "simulationStarted",
          "simulationSubmitted",
          "uuid",
          "createdAt",
          "updatedAt"
        ],
        "type": "object"
      },
      "ExamCategoryResponse": {
        "description": "A subject an organization runs exams in.",
        "properties": {
          "_id": {
            "description": "The category’s id.",
            "example": "6650a1b2c3d4e5f6a7b8c9e3",
            "type": "string"
          },
          "name": {
            "description": "The category’s name, as students see it.",
            "example": "Mathematics",
            "type": "string"
          },
          "altName": {
            "description": "A second name for the category, where one is set.",
            "example": "Maths",
            "type": "string"
          },
          "order": {
            "description": "Where the category sorts among the others, lowest first. The available-exams tree is in this order.",
            "example": 1,
            "type": "number"
          },
          "isActive": {
            "description": "Whether the category is live. No exam in an inactive category is open for applications.",
            "example": true,
            "type": "boolean"
          },
          "nonAcceptedReplacements": {
            "description": "Ids of the categories an application in this one may not be moved to: `moveApplication` refuses such a move.",
            "example": [
              "6650a1b2c3d4e5f6a7b8c9ea"
            ],
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "studyMaterialLinks": {
            "description": "Links to study material for the category.",
            "example": [
              "https://example.org/study/mathematics"
            ],
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "createdAt": {
            "description": "When the record was created.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the record last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "_id"
        ],
        "type": "object"
      },
      "ExamRecordResponse": {
        "description": "An exam as stored, with `session`, `category`, `language`, `grades` and `countries` as ids.",
        "properties": {
          "_id": {
            "description": "The exam’s id: the `examId` that `createApplication` and `moveApplication` take.",
            "example": "6650a1b2c3d4e5f6a7b8c9e1",
            "type": "string"
          },
          "session": {
            "description": "The id of the sitting the exam is on.",
            "example": "6650a1b2c3d4e5f6a7b8c9e2",
            "type": "string"
          },
          "category": {
            "description": "The id of the exam’s category.",
            "example": "6650a1b2c3d4e5f6a7b8c9e3",
            "type": "string"
          },
          "language": {
            "description": "The id of the language the exam is sat in, when it has one.",
            "example": "6650a1b2c3d4e5f6a7b8c9e4",
            "type": "string"
          },
          "grades": {
            "description": "The grades that may sit the exam. A student in any other grade is not offered it and cannot apply to it. Grade ids are the same in every organization.",
            "example": [
              "6650a1b2c3d4e5f6a7b8c9d4"
            ],
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "countries": {
            "description": "The countries the exam is restricted to; empty or absent, it is open to every country. Ids of the organization’s own country records, which need not match the ones `listCountries` gives.",
            "example": [],
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "examType": {
            "description": "`standard` or `essay`.",
            "enum": [
              "standard",
              "essay"
            ],
            "example": "standard",
            "type": "string"
          },
          "examTime": {
            "description": "The time limit, in minutes, from when the student starts.",
            "example": 75,
            "type": "number"
          },
          "duration": {
            "description": "How many hours the exam can be started in, counted from the start of the sitting.",
            "example": 15,
            "type": "number"
          },
          "questionCount": {
            "description": "How many questions the exam has.",
            "example": 30,
            "type": "number"
          },
          "price": {
            "description": "What an application costs, as a number; no currency is given. Absent when the exam has no price: an application to it is charged 0.",
            "example": 25,
            "type": "number"
          },
          "preventApplication": {
            "description": "Whether the exam is closed to new applications. A closed exam is never listed, so on the exam reads this is `false` or absent.",
            "example": false,
            "type": "boolean"
          },
          "createdAt": {
            "description": "When the record was created.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the record last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "_id"
        ],
        "type": "object"
      },
      "ExamResponse": {
        "description": "An exam that is open for applications, with its sitting, category, language, grades and countries resolved into records.",
        "properties": {
          "_id": {
            "description": "The exam’s id: the `examId` that `createApplication` and `moveApplication` take.",
            "example": "6650a1b2c3d4e5f6a7b8c9e1",
            "type": "string"
          },
          "session": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ExamSessionResponse"
              }
            ],
            "description": "The sitting the exam is on."
          },
          "category": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ExamCategoryResponse"
              }
            ],
            "description": "The exam’s category."
          },
          "language": {
            "allOf": [
              {
                "$ref": "#/components/schemas/LanguageResponse"
              }
            ],
            "description": "The language the exam is sat in. Absent or `null` when it has none: such an exam is listed, but not offered to students, and cannot be applied to.",
            "nullable": true,
            "type": "object"
          },
          "grades": {
            "description": "The grades that may sit the exam. A student in any other grade is not offered it and cannot apply to it.",
            "items": {
              "$ref": "#/components/schemas/GradeResponse"
            },
            "type": "array"
          },
          "countries": {
            "description": "The countries the exam is restricted to; empty or absent, it is open to every country. These are the organization’s own country records, whose ids need not match the ones `listCountries` gives: compare them with a student’s country by `iso2`.",
            "items": {
              "$ref": "#/components/schemas/CountryResponse"
            },
            "type": "array"
          },
          "examType": {
            "description": "`standard` or `essay`.",
            "enum": [
              "standard",
              "essay"
            ],
            "example": "standard",
            "type": "string"
          },
          "examTime": {
            "description": "The time limit, in minutes, from when the student starts.",
            "example": 75,
            "type": "number"
          },
          "duration": {
            "description": "How many hours the exam can be started in, counted from the start of the sitting.",
            "example": 15,
            "type": "number"
          },
          "questionCount": {
            "description": "How many questions the exam has.",
            "example": 30,
            "type": "number"
          },
          "price": {
            "description": "What an application costs, as a number; no currency is given. Absent when the exam has no price: an application to it is charged 0.",
            "example": 25,
            "type": "number"
          },
          "preventApplication": {
            "description": "Whether the exam is closed to new applications. A closed exam is never listed, so on the exam reads this is `false` or absent.",
            "example": false,
            "type": "boolean"
          },
          "createdAt": {
            "description": "When the record was created.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the record last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "_id",
          "session",
          "category",
          "grades",
          "countries"
        ],
        "type": "object"
      },
      "ExamSessionResponse": {
        "description": "A sitting: the date an exam takes place.",
        "properties": {
          "_id": {
            "description": "The sitting’s id.",
            "example": "6650a1b2c3d4e5f6a7b8c9e2",
            "type": "string"
          },
          "sessionName": {
            "description": "The sitting’s name.",
            "example": "November 2026",
            "type": "string"
          },
          "date": {
            "description": "When the sitting takes place. Its exams are open for applications until this moment, and not after.",
            "example": "2026-11-14T10:00:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "startTime": {
            "description": "The start time as the organization wrote it. For the moment itself, read `date`.",
            "example": "10:00",
            "type": "string"
          },
          "tz": {
            "description": "`global`: the sitting starts at one moment everywhere. `local`: it starts at the same clock time in each student’s own time zone, their country’s `tz`.",
            "enum": [
              "global",
              "local"
            ],
            "example": "global",
            "type": "string"
          },
          "sessionAlias": {
            "description": "A second name for the sitting, shown to students.",
            "example": "Autumn round",
            "type": "string"
          },
          "sessionNote": {
            "description": "A note about the sitting, shown to students.",
            "example": "Please join ten minutes early.",
            "type": "string"
          },
          "enableSimulation": {
            "description": "Whether students get a practice run before the sitting.",
            "example": true,
            "type": "boolean"
          },
          "simulationDate": {
            "description": "When the practice run opens.",
            "example": "2026-11-07T10:00:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "simulationEndDate": {
            "description": "When the practice run closes.",
            "example": "2026-11-08T10:00:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "relatedSession": {
            "description": "Set on a make-up sitting: the id of the sitting it belongs to. An application to an exam on a make-up sitting is removed 6 hours after it is made.",
            "example": "6650a1b2c3d4e5f6a7b8c9e2",
            "type": "string"
          },
          "createdAt": {
            "description": "When the record was created.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the record last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "_id"
        ],
        "type": "object"
      },
      "GradeResponse": {
        "description": "A school grade: a student has one, an exam accepts a set.",
        "properties": {
          "_id": {
            "description": "The grade’s id: what `grade` takes when you register or update a student. A grade has the same id in every organization.",
            "example": "6650a1b2c3d4e5f6a7b8c9d4",
            "type": "string"
          },
          "name": {
            "description": "The grade, as a number in a string, `1` to `12`. `grade` accepts this name as well as the id.",
            "example": "10",
            "type": "string"
          },
          "createdAt": {
            "description": "When the record was created. The grade list is in this order.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the record last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "_id"
        ],
        "type": "object"
      },
      "GroupChallengeActivityFileResponse": {
        "description": "The file an activity entry is about.",
        "properties": {
          "_id": {
            "description": "The file’s id.",
            "example": "6650a1b2c3d4e5f6a7b8c9ef",
            "type": "string"
          },
          "name": {
            "description": "Its name, only when one of your students did what the entry records; otherwise `null`.",
            "example": "proposal.pdf",
            "nullable": true,
            "type": "object"
          }
        },
        "required": [
          "_id",
          "name"
        ],
        "type": "object"
      },
      "GroupChallengeActivityResponse": {
        "description": "Something that happened in a group. The entries every member of the group can read; notes the organizers keep are not included.",
        "properties": {
          "_id": {
            "description": "The entry’s id.",
            "example": "6650a1b2c3d4e5f6a7b8c9f0",
            "type": "string"
          },
          "at": {
            "description": "When it happened (UTC).",
            "example": "2026-10-20T16:02:11.000Z",
            "format": "date-time",
            "type": "string"
          },
          "event": {
            "description": "What happened. New values may be added; ignore one you do not know.",
            "enum": [
              "group_created",
              "group_renamed",
              "member_added",
              "member_removed",
              "group_finalized",
              "group_deleted",
              "group_restored",
              "step_unlocked",
              "file_uploaded",
              "file_replaced",
              "file_deleted",
              "file_rejected",
              "step_submitted",
              "step_reopened",
              "final_submitted",
              "final_reverted"
            ],
            "example": "step_submitted",
            "type": "string"
          },
          "actor": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupChallengePersonResponse"
              }
            ],
            "description": "Who did it. Your account acting for a student is `partner` with `yours: true`."
          },
          "via": {
            "description": "Where it was done: the panel, the app, an AI assistant (`mcp`), this API, or the system itself. New values may be added; ignore one you do not know.",
            "enum": [
              "panel",
              "app",
              "mcp",
              "api",
              "system"
            ],
            "example": "api",
            "type": "string"
          },
          "onBehalfOf": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupChallengePersonResponse"
              }
            ],
            "description": "Whom it was done for: the student a partner acted for, or the teacher an administrator acted for. `null` otherwise.",
            "nullable": true,
            "type": "object"
          },
          "subject": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupChallengePersonResponse"
              }
            ],
            "description": "The student it was about (added or removed), or `null`.",
            "nullable": true,
            "type": "object"
          },
          "step": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupChallengeActivityStepResponse"
              }
            ],
            "description": "The step it was about, or `null`.",
            "nullable": true,
            "type": "object"
          },
          "file": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupChallengeActivityFileResponse"
              }
            ],
            "description": "The file it was about, or `null`.",
            "nullable": true,
            "type": "object"
          }
        },
        "required": [
          "_id",
          "at",
          "event",
          "actor",
          "via",
          "onBehalfOf",
          "subject",
          "step",
          "file"
        ],
        "type": "object"
      },
      "GroupChallengeActivityStepResponse": {
        "description": "The step an activity entry is about.",
        "properties": {
          "_id": {
            "description": "The step’s id.",
            "example": "6650a1b2c3d4e5f6a7b8c9ee",
            "type": "string"
          },
          "order": {
            "description": "Its position when the entry was written.",
            "example": 1,
            "nullable": true,
            "type": "object"
          }
        },
        "required": [
          "_id",
          "order"
        ],
        "type": "object"
      },
      "GroupChallengeFileResponse": {
        "description": "A file a member uploaded for a step.",
        "properties": {
          "_id": {
            "description": "The file’s id.",
            "example": "6650a1b2c3d4e5f6a7b8c9ef",
            "type": "string"
          },
          "status": {
            "description": "`uploading` while the upload runs, `draft` once uploaded, `submitted` once its step is submitted. New values may be added.",
            "enum": [
              "uploading",
              "draft",
              "submitted"
            ],
            "example": "submitted",
            "type": "string"
          },
          "name": {
            "description": "The file’s name, only when one of your students uploaded it; otherwise `null`.",
            "example": "proposal.pdf",
            "nullable": true,
            "type": "object"
          },
          "fileType": {
            "description": "Its type, by extension.",
            "example": "pdf",
            "nullable": true,
            "type": "object"
          },
          "size": {
            "description": "Its size in bytes, once uploaded; `null` while uploading.",
            "example": 1048576,
            "nullable": true,
            "type": "object"
          },
          "uploadedBy": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupChallengePersonResponse"
              }
            ],
            "description": "Who uploaded it."
          },
          "completedAt": {
            "description": "When the upload finished.",
            "example": "2026-10-20T15:47:03.000Z",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "submittedAt": {
            "description": "When its step was submitted with it.",
            "example": "2026-10-20T16:02:11.000Z",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "_id",
          "status",
          "name",
          "fileType",
          "size",
          "uploadedBy",
          "completedAt",
          "submittedAt"
        ],
        "type": "object"
      },
      "GroupChallengeGradeGroupRefResponse": {
        "description": "A grade group of the challenge, by id and name.",
        "properties": {
          "_id": {
            "description": "The grade group’s id within the challenge.",
            "example": "6650a1b2c3d4e5f6a7b8c9ec",
            "type": "string"
          },
          "label": {
            "description": "Its name.",
            "example": "Group 7-8-9",
            "type": "string"
          }
        },
        "required": [
          "_id",
          "label"
        ],
        "type": "object"
      },
      "GroupChallengeGradeGroupResponse": {
        "description": "A set of grades that form groups together: every member of a group is in the same grade group.",
        "properties": {
          "_id": {
            "description": "The grade group’s id within the challenge.",
            "example": "6650a1b2c3d4e5f6a7b8c9ec",
            "type": "string"
          },
          "label": {
            "description": "Its name, for a person.",
            "example": "Group 7-8-9",
            "type": "string"
          },
          "grades": {
            "description": "The grades it takes. A student whose grade is in none of the grade groups cannot join the challenge.",
            "items": {
              "$ref": "#/components/schemas/GroupChallengeGradeResponse"
            },
            "type": "array"
          }
        },
        "required": [
          "_id",
          "label",
          "grades"
        ],
        "type": "object"
      },
      "GroupChallengeGradeResponse": {
        "description": "One grade of a grade group.",
        "properties": {
          "_id": {
            "description": "The grade’s id, the same `_id` `listGrades` returns and a student’s `grade` holds.",
            "example": "6650a1b2c3d4e5f6a7b8c9d4",
            "type": "string"
          },
          "name": {
            "description": "The grade’s name, as it was when the challenge was set up.",
            "example": "10",
            "nullable": true,
            "type": "object"
          }
        },
        "required": [
          "_id",
          "name"
        ],
        "type": "object"
      },
      "GroupChallengeGroupResponse": {
        "description": "A group one or more of your students are in, with its steps.",
        "properties": {
          "_id": {
            "description": "The group’s id: the `groupId` of the group operations.",
            "example": "6650a1b2c3d4e5f6a7b8c9ed",
            "type": "string"
          },
          "challengeId": {
            "description": "The challenge it belongs to.",
            "example": "6650a1b2c3d4e5f6a7b8c9eb",
            "type": "string"
          },
          "shortCode": {
            "description": "A six-character code for the group, unique in the organization.",
            "example": "7KQ2MX",
            "type": "string"
          },
          "name": {
            "description": "The name the teacher gave it, or `null`.",
            "example": null,
            "nullable": true,
            "type": "object"
          },
          "displayName": {
            "description": "`name`, or \"Group\" and the short code when it has none.",
            "example": "Group 7KQ2MX",
            "type": "string"
          },
          "status": {
            "description": "`awaiting_payment` and `draft` while the teacher prepares it; `finalized` once confirmed, with its steps open one by one; `completed` once its work has been sent. New values may be added.",
            "enum": [
              "awaiting_payment",
              "draft",
              "finalized",
              "completed"
            ],
            "example": "finalized",
            "type": "string"
          },
          "gradeGroup": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupChallengeGradeGroupRefResponse"
              }
            ],
            "description": "The grade group its members come from."
          },
          "memberCount": {
            "description": "How many students are in it, yours and others.",
            "example": 3,
            "type": "number"
          },
          "yourStudents": {
            "description": "Your students in it. The other members are counted in `memberCount` and not listed.",
            "items": {
              "$ref": "#/components/schemas/GroupChallengeMemberResponse"
            },
            "type": "array"
          },
          "stepCount": {
            "description": "How many steps the group works through.",
            "example": 3,
            "type": "number"
          },
          "stepsSubmitted": {
            "description": "How many of them are submitted.",
            "example": 1,
            "type": "number"
          },
          "createdAt": {
            "description": "When the teacher created it.",
            "example": "2026-10-05T13:20:00.000Z",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "finalizedAt": {
            "description": "When the teacher confirmed it, or `null`.",
            "example": "2026-10-12T08:15:00.000Z",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "finalSubmittedAt": {
            "description": "When its work was sent, or `null`.",
            "example": null,
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "finalSubmittedBy": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupChallengePersonResponse"
              }
            ],
            "description": "Who sent the group’s work, or `null` while it has not been sent.",
            "nullable": true,
            "type": "object"
          },
          "canFinalSubmit": {
            "description": "Whether `submitGroupChallengeWork` would send the work now: every step is submitted and the challenge is open.",
            "example": false,
            "type": "boolean"
          },
          "steps": {
            "description": "The steps, in order, once the teacher has confirmed the group; empty before that.",
            "items": {
              "$ref": "#/components/schemas/GroupChallengeGroupStepResponse"
            },
            "type": "array"
          }
        },
        "required": [
          "_id",
          "challengeId",
          "shortCode",
          "name",
          "displayName",
          "status",
          "gradeGroup",
          "memberCount",
          "yourStudents",
          "stepCount",
          "stepsSubmitted",
          "createdAt",
          "finalizedAt",
          "finalSubmittedAt",
          "finalSubmittedBy",
          "canFinalSubmit",
          "steps"
        ],
        "type": "object"
      },
      "GroupChallengeGroupStepResponse": {
        "description": "One step of a group, with its files.",
        "properties": {
          "_id": {
            "description": "The step’s id: the `stepId` `submitGroupChallengeStep` takes.",
            "example": "6650a1b2c3d4e5f6a7b8c9ee",
            "type": "string"
          },
          "order": {
            "description": "Its position, from 1.",
            "example": 1,
            "type": "number"
          },
          "title": {
            "description": "Its title.",
            "example": "Project proposal",
            "type": "string"
          },
          "state": {
            "description": "`locked` until the step before it is submitted, `open` while the group works on it, `submitted` once it is sent. New values may be added.",
            "enum": [
              "locked",
              "open",
              "submitted"
            ],
            "example": "submitted",
            "type": "string"
          },
          "openedAt": {
            "description": "When it opened.",
            "example": "2026-10-12T08:15:00.000Z",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "submittedAt": {
            "description": "When it was submitted, or `null`.",
            "example": "2026-10-20T16:02:11.000Z",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "submittedBy": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupChallengePersonResponse"
              }
            ],
            "description": "Who submitted it, or `null`. `partner` with `yours: true` is your account, acting for one of your students.",
            "nullable": true,
            "type": "object"
          },
          "reopenCount": {
            "description": "How many times the organizers reopened it after a submit.",
            "example": 0,
            "type": "number"
          },
          "canSubmit": {
            "description": "Whether `submitGroupChallengeStep` would submit it now: it is open, it has a file, and the challenge is open.",
            "example": false,
            "type": "boolean"
          },
          "files": {
            "description": "Its files, uploading, uploaded or submitted.",
            "items": {
              "$ref": "#/components/schemas/GroupChallengeFileResponse"
            },
            "type": "array"
          }
        },
        "required": [
          "_id",
          "order",
          "title",
          "state",
          "openedAt",
          "submittedAt",
          "submittedBy",
          "reopenCount",
          "canSubmit",
          "files"
        ],
        "type": "object"
      },
      "GroupChallengeGroupSummaryResponse": {
        "description": "A group one or more of your students are in.",
        "properties": {
          "_id": {
            "description": "The group’s id: the `groupId` of the group operations.",
            "example": "6650a1b2c3d4e5f6a7b8c9ed",
            "type": "string"
          },
          "challengeId": {
            "description": "The challenge it belongs to.",
            "example": "6650a1b2c3d4e5f6a7b8c9eb",
            "type": "string"
          },
          "shortCode": {
            "description": "A six-character code for the group, unique in the organization.",
            "example": "7KQ2MX",
            "type": "string"
          },
          "name": {
            "description": "The name the teacher gave it, or `null`.",
            "example": null,
            "nullable": true,
            "type": "object"
          },
          "displayName": {
            "description": "`name`, or \"Group\" and the short code when it has none.",
            "example": "Group 7KQ2MX",
            "type": "string"
          },
          "status": {
            "description": "`awaiting_payment` and `draft` while the teacher prepares it; `finalized` once confirmed, with its steps open one by one; `completed` once its work has been sent. New values may be added.",
            "enum": [
              "awaiting_payment",
              "draft",
              "finalized",
              "completed"
            ],
            "example": "finalized",
            "type": "string"
          },
          "gradeGroup": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupChallengeGradeGroupRefResponse"
              }
            ],
            "description": "The grade group its members come from."
          },
          "memberCount": {
            "description": "How many students are in it, yours and others.",
            "example": 3,
            "type": "number"
          },
          "yourStudents": {
            "description": "Your students in it. The other members are counted in `memberCount` and not listed.",
            "items": {
              "$ref": "#/components/schemas/GroupChallengeMemberResponse"
            },
            "type": "array"
          },
          "stepCount": {
            "description": "How many steps the group works through.",
            "example": 3,
            "type": "number"
          },
          "stepsSubmitted": {
            "description": "How many of them are submitted.",
            "example": 1,
            "type": "number"
          },
          "createdAt": {
            "description": "When the teacher created it.",
            "example": "2026-10-05T13:20:00.000Z",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "finalizedAt": {
            "description": "When the teacher confirmed it, or `null`.",
            "example": "2026-10-12T08:15:00.000Z",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "finalSubmittedAt": {
            "description": "When its work was sent, or `null`.",
            "example": null,
            "format": "date-time",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "_id",
          "challengeId",
          "shortCode",
          "name",
          "displayName",
          "status",
          "gradeGroup",
          "memberCount",
          "yourStudents",
          "stepCount",
          "stepsSubmitted",
          "createdAt",
          "finalizedAt",
          "finalSubmittedAt"
        ],
        "type": "object"
      },
      "GroupChallengeMemberResponse": {
        "description": "One of your students in a group.",
        "properties": {
          "studentId": {
            "description": "The student’s `_id`, as `registerStudent` returned it.",
            "example": "6650a1b2c3d4e5f6a7b8c9d0",
            "type": "string"
          },
          "firstName": {
            "description": "Their first name when they joined the group.",
            "example": "Ada",
            "type": "string"
          },
          "lastName": {
            "description": "Their last name then.",
            "example": "Lovelace",
            "type": "string"
          },
          "gradeName": {
            "description": "Their grade’s name then.",
            "example": "8",
            "nullable": true,
            "type": "object"
          },
          "addedAt": {
            "description": "When the teacher added them.",
            "example": "2026-10-05T13:20:00.000Z",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "studentId",
          "firstName",
          "lastName",
          "gradeName",
          "addedAt"
        ],
        "type": "object"
      },
      "GroupChallengeOpenedStepResponse": {
        "description": "The step a submit opened.",
        "properties": {
          "_id": {
            "description": "The step’s id.",
            "example": "6650a1b2c3d4e5f6a7b8c9f1",
            "type": "string"
          },
          "order": {
            "description": "Its position.",
            "example": 2,
            "type": "number"
          },
          "state": {
            "description": "Always `open`: the group can work on it now.",
            "enum": [
              "open"
            ],
            "example": "open",
            "type": "string"
          }
        },
        "required": [
          "_id",
          "order",
          "state"
        ],
        "type": "object"
      },
      "GroupChallengePersonResponse": {
        "description": "A person in a group challenge. Only your own students are named: everyone else — other members, the teacher, the organizers, another partner — is their role and nothing more.",
        "properties": {
          "role": {
            "description": "`student`, `teacher`, `administrator` (the organizers), `partner` (an API account, acting for a student) or `system`. New values may be added.",
            "enum": [
              "student",
              "teacher",
              "administrator",
              "partner",
              "system"
            ],
            "example": "student",
            "type": "string"
          },
          "studentId": {
            "description": "The student’s `_id`, only when they are yours; otherwise `null`.",
            "example": "6650a1b2c3d4e5f6a7b8c9d0",
            "nullable": true,
            "type": "object"
          },
          "name": {
            "description": "Their name, only when they are yours; otherwise `null`.",
            "example": "Ada Lovelace",
            "nullable": true,
            "type": "object"
          },
          "yours": {
            "description": "Whether this is one of your students, or your account acting for one.",
            "example": true,
            "type": "boolean"
          }
        },
        "required": [
          "role",
          "studentId",
          "name",
          "yours"
        ],
        "type": "object"
      },
      "GroupChallengeResponse": {
        "description": "A group challenge: groups of students, formed by their teacher, work through a set of steps between two dates.",
        "properties": {
          "_id": {
            "description": "The challenge’s id: the `challengeId` of every other operation.",
            "example": "6650a1b2c3d4e5f6a7b8c9eb",
            "type": "string"
          },
          "name": {
            "description": "Its name.",
            "example": "STEM Maker Challenge",
            "type": "string"
          },
          "status": {
            "description": "`published` while it runs; `closed` once the organizers have closed it, when it is still readable and takes no more work. New values may be added; treat an unknown one as closed.",
            "enum": [
              "published",
              "closed"
            ],
            "example": "published",
            "type": "string"
          },
          "isOpen": {
            "description": "Whether it takes work right now: `published`, and now is between `windowStart` and `windowEnd`.",
            "example": true,
            "type": "boolean"
          },
          "windowStart": {
            "description": "When it opens (UTC).",
            "example": "2026-10-01T00:00:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "windowEnd": {
            "description": "When it closes (UTC), inclusive. Nothing is submitted after it: not a step, not the final work.",
            "example": "2026-12-21T23:59:59.999Z",
            "format": "date-time",
            "type": "string"
          },
          "minStudents": {
            "description": "The fewest students a group may have.",
            "example": 2,
            "nullable": true,
            "type": "object"
          },
          "maxStudents": {
            "description": "The most students a group may have.",
            "example": 3,
            "nullable": true,
            "type": "object"
          },
          "isPaid": {
            "description": "Whether taking part costs a fee. The teacher settles it in the panel when they create the group; this API neither shows nor takes it.",
            "example": false,
            "type": "boolean"
          },
          "gradeGroups": {
            "description": "The grade groups, in their order.",
            "items": {
              "$ref": "#/components/schemas/GroupChallengeGradeGroupResponse"
            },
            "type": "array"
          },
          "steps": {
            "description": "The steps, in their order.",
            "items": {
              "$ref": "#/components/schemas/GroupChallengeStepDefinitionResponse"
            },
            "type": "array"
          }
        },
        "required": [
          "_id",
          "name",
          "status",
          "isOpen",
          "windowStart",
          "windowEnd",
          "minStudents",
          "maxStudents",
          "isPaid",
          "gradeGroups",
          "steps"
        ],
        "type": "object"
      },
      "GroupChallengeStepDefinitionResponse": {
        "description": "One step a group works through, in order.",
        "properties": {
          "_id": {
            "description": "The step’s id: what `submitGroupChallengeStep` takes.",
            "example": "6650a1b2c3d4e5f6a7b8c9ee",
            "type": "string"
          },
          "order": {
            "description": "Its position, from 1. Steps open one after another.",
            "example": 1,
            "type": "number"
          },
          "title": {
            "description": "Its title, for a person.",
            "example": "Project proposal",
            "type": "string"
          },
          "fileTypes": {
            "description": "The file types a member may upload for it, by extension, such as `pdf` or `mp4`.",
            "example": [
              "pdf",
              "docx"
            ],
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "maxFileBytes": {
            "description": "The largest file a member may upload for it, in bytes.",
            "example": 20971520,
            "nullable": true,
            "type": "object"
          },
          "maxFiles": {
            "description": "How many files the step holds at most.",
            "example": 1,
            "nullable": true,
            "type": "object"
          }
        },
        "required": [
          "_id",
          "order",
          "title",
          "fileTypes",
          "maxFileBytes",
          "maxFiles"
        ],
        "type": "object"
      },
      "GroupChallengeStepSubmitResponse": {
        "description": "A step submitted for one of your students.",
        "properties": {
          "groupId": {
            "description": "The group’s id.",
            "example": "6650a1b2c3d4e5f6a7b8c9ed",
            "type": "string"
          },
          "groupStatus": {
            "description": "The group’s state now: `finalized` while steps remain open.",
            "enum": [
              "awaiting_payment",
              "draft",
              "finalized",
              "completed"
            ],
            "example": "finalized",
            "type": "string"
          },
          "step": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupChallengeSubmittedStepResponse"
              }
            ],
            "description": "The step you submitted."
          },
          "nextStep": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupChallengeOpenedStepResponse"
              }
            ],
            "description": "The step this submit opened, or `null` when it was the last one, or on a repeat.",
            "nullable": true,
            "type": "object"
          },
          "changed": {
            "description": "`true` when this request submitted the step; `false` when it had already been submitted, and nothing changed.",
            "example": true,
            "type": "boolean"
          }
        },
        "required": [
          "groupId",
          "groupStatus",
          "step",
          "nextStep",
          "changed"
        ],
        "type": "object"
      },
      "GroupChallengeStudentGradeResponse": {
        "description": "A student’s grade, as the organization holds it.",
        "properties": {
          "_id": {
            "description": "The grade’s id.",
            "example": "6650a1b2c3d4e5f6a7b8c9d4",
            "type": "string"
          },
          "name": {
            "description": "Its name.",
            "example": "10",
            "nullable": true,
            "type": "object"
          }
        },
        "required": [
          "_id",
          "name"
        ],
        "type": "object"
      },
      "GroupChallengeStudentGroupResponse": {
        "description": "The group a student is an active member of.",
        "properties": {
          "_id": {
            "description": "The group’s id: the `groupId` of the group operations.",
            "example": "6650a1b2c3d4e5f6a7b8c9ed",
            "type": "string"
          },
          "displayName": {
            "description": "The group’s name, or \"Group\" and its short code when it has none.",
            "example": "Group 7KQ2MX",
            "type": "string"
          },
          "status": {
            "description": "`awaiting_payment` and `draft` while the teacher prepares it; `finalized` once they have confirmed it and its steps are open; `completed` once its work has been sent. New values may be added.",
            "enum": [
              "awaiting_payment",
              "draft",
              "finalized",
              "completed"
            ],
            "example": "finalized",
            "type": "string"
          }
        },
        "required": [
          "_id",
          "displayName",
          "status"
        ],
        "type": "object"
      },
      "GroupChallengeStudentResponse": {
        "description": "One of your students, and where they stand in a group challenge.",
        "properties": {
          "studentId": {
            "description": "The student’s `_id`, as `registerStudent` returned it.",
            "example": "6650a1b2c3d4e5f6a7b8c9d0",
            "type": "string"
          },
          "firstName": {
            "description": "Their first name, as the organization holds it.",
            "example": "Ada",
            "type": "string"
          },
          "lastName": {
            "description": "Their last name.",
            "example": "Lovelace",
            "type": "string"
          },
          "grade": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupChallengeStudentGradeResponse"
              }
            ],
            "description": "Their grade in the organization, or `null` when none is set.",
            "nullable": true,
            "type": "object"
          },
          "eligible": {
            "description": "Whether their grade is in one of the challenge’s grade groups.",
            "example": true,
            "type": "boolean"
          },
          "gradeGroup": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupChallengeGradeGroupRefResponse"
              }
            ],
            "description": "The grade group their grade belongs to, or `null`.",
            "nullable": true,
            "type": "object"
          },
          "teacherLinked": {
            "description": "Whether a teacher is linked to them in this organization. Only a teacher can put a student in a group; link one with `linkStudentSupervisor`.",
            "example": true,
            "type": "boolean"
          },
          "state": {
            "description": "`not_eligible`: their grade is in no grade group. `not_in_group`: eligible, in no group yet. `group_pending`: in a group the teacher has not confirmed. `group_active`: in a confirmed group, working through the steps. `group_completed`: their group has sent its work. A group wins over the grade: a student in a group shows it even if their grade changed since. New values may be added.",
            "enum": [
              "not_eligible",
              "not_in_group",
              "group_pending",
              "group_active",
              "group_completed"
            ],
            "example": "group_active",
            "type": "string"
          },
          "group": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupChallengeStudentGroupResponse"
              }
            ],
            "description": "The group they are an active member of, or `null`.",
            "nullable": true,
            "type": "object"
          },
          "panelPath": {
            "description": "Where the challenge’s page is on the organization’s panel. Send it as `redirect` to `createSigninLink` to take the student straight there: `{userId}` is filled in for you.",
            "example": "/stadia/{userId}/group-challenges/6650a1b2c3d4e5f6a7b8c9eb",
            "type": "string"
          }
        },
        "required": [
          "studentId",
          "firstName",
          "lastName",
          "grade",
          "eligible",
          "gradeGroup",
          "teacherLinked",
          "state",
          "group",
          "panelPath"
        ],
        "type": "object"
      },
      "GroupChallengeSubmittedStepResponse": {
        "description": "The step a submit was for.",
        "properties": {
          "_id": {
            "description": "The step’s id.",
            "example": "6650a1b2c3d4e5f6a7b8c9ee",
            "type": "string"
          },
          "order": {
            "description": "Its position, from 1.",
            "example": 1,
            "nullable": true,
            "type": "object"
          },
          "state": {
            "description": "Always `submitted`.",
            "enum": [
              "submitted"
            ],
            "example": "submitted",
            "type": "string"
          },
          "submittedAt": {
            "description": "When it was submitted: now, or the first time on a repeat.",
            "example": "2026-10-20T16:02:11.000Z",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "_id",
          "order",
          "state",
          "submittedAt"
        ],
        "type": "object"
      },
      "GroupChallengeWorkSubmitResponse": {
        "description": "A group’s work sent for one of your students.",
        "properties": {
          "groupId": {
            "description": "The group’s id.",
            "example": "6650a1b2c3d4e5f6a7b8c9ed",
            "type": "string"
          },
          "groupStatus": {
            "description": "The group’s state now: `completed`.",
            "enum": [
              "awaiting_payment",
              "draft",
              "finalized",
              "completed"
            ],
            "example": "completed",
            "type": "string"
          },
          "finalSubmittedAt": {
            "description": "When the work was sent: now, or the first time on a repeat.",
            "example": "2026-10-20T16:02:11.000Z",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "changed": {
            "description": "`true` when this request sent the work; `false` when it had already been sent, and nothing changed.",
            "example": true,
            "type": "boolean"
          }
        },
        "required": [
          "groupId",
          "groupStatus",
          "finalSubmittedAt",
          "changed"
        ],
        "type": "object"
      },
      "HealthResponse": {
        "description": "The service is up.",
        "properties": {
          "status": {
            "description": "Always `ok`: the service is running and answering. It says nothing about any particular operation.",
            "enum": [
              "ok"
            ],
            "example": "ok",
            "type": "string"
          }
        },
        "required": [
          "status"
        ],
        "type": "object"
      },
      "LanguageResponse": {
        "description": "A language an exam is sat in.",
        "properties": {
          "_id": {
            "description": "The language’s id.",
            "example": "6650a1b2c3d4e5f6a7b8c9e4",
            "type": "string"
          },
          "name": {
            "description": "The language’s name.",
            "example": "English",
            "type": "string"
          },
          "code": {
            "description": "A short language code.",
            "example": "en",
            "type": "string"
          },
          "order": {
            "description": "Where the language sorts among the others, lowest first. The available-exams tree is in this order.",
            "example": 1,
            "type": "number"
          },
          "createdAt": {
            "description": "When the record was created.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the record last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "_id"
        ],
        "type": "object"
      },
      "LinkSupervisorRequest": {
        "description": "The body of `linkStudentSupervisor`.",
        "properties": {
          "supervisorUsername": {
            "description": "The username of a supervisor on this organization, as they sign in with it. Letter case and surrounding spaces do not matter.",
            "example": "XXT1003",
            "type": "string"
          }
        },
        "required": [
          "supervisorUsername"
        ],
        "type": "object"
      },
      "LinkedSupervisorResponse": {
        "description": "The supervisor a student was just linked to.",
        "properties": {
          "_id": {
            "description": "The supervisor’s id in this organization.",
            "example": "6650a1b2c3d4e5f6a7b8c9d5",
            "type": "string"
          },
          "username": {
            "description": "The supervisor’s username, in capitals.",
            "example": "XXT1003",
            "type": "string"
          }
        },
        "required": [
          "_id",
          "username"
        ],
        "type": "object"
      },
      "MoveApplicationRequest": {
        "description": "The body of `moveApplication`: the exam to move the application to.",
        "properties": {
          "examId": {
            "description": "The `_id` of the exam to move the application to, 24 hexadecimal digits: usually a `listAvailableExams` leaf’s `matchedExam._id`. It has to be an exam that list offers the application’s student.",
            "example": "6650a1b2c3d4e5f6a7b8c9e1",
            "type": "string"
          }
        },
        "required": [
          "examId"
        ],
        "type": "object"
      },
      "OrgStudentResponse": {
        "description": "One of your students as `listOrgStudents` lists them: the fields of `StudentResponse`, and `signedIn` for this organization.",
        "properties": {
          "_id": {
            "description": "The student’s id: what every `studentId` path parameter and body field takes.",
            "example": "6650a1b2c3d4e5f6a7b8c9d0",
            "type": "string"
          },
          "mainId": {
            "description": "Only on an organization’s own copy of a student, which `linkStudentSupervisor` answers with: there it is the student’s id, and `_id` is the organization’s own. Absent on every other operation.",
            "example": "6650a1b2c3d4e5f6a7b8c9d0",
            "type": "string"
          },
          "username": {
            "description": "Issued at registration, never chosen by you: the country’s two-letter code, a letter, then a number. It is how the student signs in and how support refers to the account.",
            "example": "XXB1045",
            "type": "string"
          },
          "firstName": {
            "description": "The student’s first name.",
            "example": "Jane",
            "type": "string"
          },
          "lastName": {
            "description": "The student’s surname.",
            "example": "Doe",
            "type": "string"
          },
          "fullName": {
            "description": "`firstName` and `lastName` joined by a space, kept in step when either changes.",
            "example": "Jane Doe",
            "type": "string"
          },
          "email": {
            "description": "The student’s email address, in lower case.",
            "example": "jane.doe@example.com",
            "type": "string"
          },
          "emailConfirmed": {
            "description": "Whether the student has confirmed `email`. It starts `false`, only the student can make it `true`, and changing `email` sets it back to `false`. Once it is `true`, `setStudentPassword` is refused: the account is the student’s.",
            "example": false,
            "type": "boolean"
          },
          "phone": {
            "description": "A phone number, as you sent it.",
            "example": "+1 555 0100",
            "type": "string"
          },
          "birth": {
            "description": "Date of birth, `DD/MM/YYYY`.",
            "example": "14/05/2008",
            "type": "string"
          },
          "sex": {
            "description": "`m`, `f` or `n`.",
            "enum": [
              "m",
              "f",
              "n"
            ],
            "example": "f",
            "type": "string"
          },
          "country": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CountryResponse"
              }
            ],
            "description": "The student’s country. `null` if the stored reference no longer resolves; absent when none is set.",
            "nullable": true,
            "type": "object"
          },
          "city": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CityResponse"
              }
            ],
            "description": "The student’s city. `null` if the stored reference no longer resolves; absent when none is set.",
            "nullable": true,
            "type": "object"
          },
          "school": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SchoolResponse"
              }
            ],
            "description": "The student’s school. `null` if the stored reference no longer resolves; absent when none is set.",
            "nullable": true,
            "type": "object"
          },
          "grade": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GradeResponse"
              }
            ],
            "description": "The student’s grade, which decides the exams they are offered. `null` if the stored reference no longer resolves; absent when none is set.",
            "nullable": true,
            "type": "object"
          },
          "supervisor": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PersonRefResponse"
              }
            ],
            "description": "The supervisor the student is attached to across the platform. A supervisor you link with `linkStudentSupervisor` is set on that one organization’s copy of the student, and does not appear here. `null` if the stored reference no longer resolves; absent when none is set.",
            "nullable": true,
            "type": "object"
          },
          "partner": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PersonRefResponse"
              }
            ],
            "description": "The partner the student is attached to. `null` if the stored reference no longer resolves; absent when none is set.",
            "nullable": true,
            "type": "object"
          },
          "activatedPlatformsThisSeason": {
            "description": "The organizations the student is active on this season, by `slug`; `common` means every organization. Registration sets `common` unless you send a list. The organization-scoped student operations see a student only when this lists that organization or `common`, and updating a student through one adds that organization.",
            "example": [
              "common"
            ],
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "createdAt": {
            "description": "When the student was registered.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the record last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "signedIn": {
            "description": "Whether the student has signed in to this organization at least once. The organization keeps its own record of a student from that first sign-in, and until then `createApplication` and `linkStudentSupervisor` there are refused. Send them a link from `createSigninLink` to change it.",
            "example": true,
            "type": "boolean"
          }
        },
        "required": [
          "_id",
          "username",
          "firstName",
          "lastName",
          "fullName",
          "email",
          "emailConfirmed",
          "activatedPlatformsThisSeason",
          "createdAt",
          "updatedAt",
          "signedIn"
        ],
        "type": "object"
      },
      "OrganizationResponse": {
        "description": "An organization you act on. Its `_id` is the `organizationId` of every organization-scoped operation.",
        "properties": {
          "_id": {
            "description": "The organization’s id: the `organizationId` every organization-scoped operation takes.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "type": "string"
          },
          "name": {
            "description": "The organization’s name.",
            "example": "Example Science Olympiad",
            "type": "string"
          },
          "slug": {
            "description": "Its short code. A student’s `activatedPlatformsThisSeason` lists organizations by it, and the sign-in and supervisor links name the organization they acted on by it.",
            "example": "stem",
            "type": "string"
          },
          "logo": {
            "description": "The address of the organization’s logo image.",
            "example": "https://cdn.example.org/logos/example-science-olympiad.png",
            "type": "string"
          },
          "desc": {
            "description": "A short description of the organization.",
            "example": "An international olympiad in science and mathematics.",
            "type": "string"
          },
          "defaultRedirect": {
            "description": "The address of the organization’s student panel. A sign-in link made without a `redirect` lands here.",
            "example": "https://my.example-olympiad.org",
            "type": "string"
          }
        },
        "required": [
          "_id"
        ],
        "type": "object"
      },
      "PaginationMeta": {
        "description": "Where one page sits in the whole list.",
        "properties": {
          "page": {
            "description": "The page returned, counting from 1.",
            "example": 1,
            "minimum": 1,
            "type": "number"
          },
          "limit": {
            "description": "Items per page: the `limit` you sent, 20 if you sent none, and never more than 100.",
            "example": 20,
            "maximum": 100,
            "minimum": 1,
            "type": "number"
          },
          "total": {
            "description": "Items across every page.",
            "example": 57,
            "minimum": 0,
            "type": "number"
          },
          "totalPages": {
            "description": "Pages at this `limit`: `total / limit`, rounded up.",
            "example": 3,
            "minimum": 0,
            "type": "number"
          }
        },
        "required": [
          "page",
          "limit",
          "total",
          "totalPages"
        ],
        "type": "object"
      },
      "PaymentResponse": {
        "description": "The payment an application is paid through. Every application made through this API gets one.",
        "properties": {
          "_id": {
            "description": "The payment’s id.",
            "example": "6650a1b2c3d4e5f6a7b8c9e6",
            "type": "string"
          },
          "status": {
            "description": "`pending`: not paid yet. `paid`: settled; a free exam’s payment is `paid` from the start, with `amount` 0. `canceled`: will not be paid. An application whose payment is `paid` with an `amount` above 0 cannot be deleted, and can move only to an exam of the same price.",
            "enum": [
              "pending",
              "paid",
              "canceled"
            ],
            "example": "pending",
            "type": "string"
          },
          "amount": {
            "description": "What is due, or was paid, as a number; no currency is given. 0 for a free exam. Moving an unpaid application to another exam changes it to that exam’s price.",
            "example": 25,
            "type": "number"
          },
          "application": {
            "description": "The id of the application it pays for.",
            "example": "6650a1b2c3d4e5f6a7b8c9e5",
            "type": "string"
          },
          "exam": {
            "description": "The id of the exam it pays for.",
            "example": "6650a1b2c3d4e5f6a7b8c9e1",
            "type": "string"
          },
          "for": {
            "description": "The organization’s own id for the student it is for.",
            "example": "6650a1b2c3d4e5f6a7b8c9e9",
            "type": "string"
          },
          "createdAt": {
            "description": "When the payment was created.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the payment last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "_id",
          "status"
        ],
        "type": "object"
      },
      "PersonRefResponse": {
        "description": "Another person a student is attached to: enough to say who they are, and nothing more.",
        "properties": {
          "_id": {
            "description": "Their id.",
            "example": "6650a1b2c3d4e5f6a7b8c9d5",
            "type": "string"
          },
          "mainId": {
            "description": "Their main id, when this is an organization’s own copy of them. Absent otherwise.",
            "example": "6650a1b2c3d4e5f6a7b8c9d5",
            "type": "string"
          },
          "firstName": {
            "description": "Their first name.",
            "example": "John",
            "type": "string"
          },
          "lastName": {
            "description": "Their surname.",
            "example": "Smith",
            "type": "string"
          },
          "fullName": {
            "description": "Their first name and surname, joined by a space.",
            "example": "John Smith",
            "type": "string"
          },
          "username": {
            "description": "Their username, in capitals. For a supervisor, the value `linkStudentSupervisor` takes.",
            "example": "XXT1003",
            "type": "string"
          }
        },
        "required": [
          "_id"
        ],
        "type": "object"
      },
      "RegisterStudentRequest": {
        "description": "The body of `registerStudent`.",
        "properties": {
          "firstName": {
            "description": "The student’s first name. Printed on certificates and reports, followed by `lastName`.",
            "example": "Jane",
            "type": "string"
          },
          "lastName": {
            "description": "The student’s surname. Printed on certificates and reports after `firstName`. It cannot be empty.",
            "example": "Doe",
            "type": "string"
          },
          "birth": {
            "description": "Date of birth as `DD/MM/YYYY`, and a date that exists: `31/02/2008` is refused. An ISO date such as `2008-05-14` is refused.",
            "example": "14/05/2008",
            "pattern": "^(0[1-9]|[12]\\d|3[01])\\/(0[1-9]|1[0-2])\\/\\d{4}$",
            "type": "string"
          },
          "sex": {
            "description": "One of `m`, `f` or `n`.",
            "enum": [
              "m",
              "f",
              "n"
            ],
            "example": "f",
            "type": "string"
          },
          "email": {
            "description": "The student’s email address, stored in lower case. An address belongs to one student on the whole platform, so one already registered, by your account or another, is refused with 409.",
            "example": "jane.doe@example.com",
            "format": "email",
            "type": "string"
          },
          "email2": {
            "description": "Leave this out. Any value but an empty one is refused with 400.",
            "type": "string"
          },
          "phone": {
            "description": "A phone number, stored as you send it. No format is checked. On an update, `\"\"` clears it.",
            "example": "+1 555 0100",
            "type": "string"
          },
          "country": {
            "description": "The `_id` of a country, from `listCountries` (`GET /v1/country`). An id only: a name or an ISO code is refused. Its two-letter code starts the student’s `username`.",
            "example": "6650a1b2c3d4e5f6a7b8c9d1",
            "pattern": "^[0-9a-fA-F]{24}$",
            "type": "string"
          },
          "grade": {
            "description": "The `_id` of a grade, from `listGrades` (`GET /v1/grade`), or its name, `1` to `12`. Either way the grade’s `_id` is what is stored. One that matches no grade is refused with 400. The grade decides which exams the student is offered.",
            "example": "10",
            "type": "string"
          },
          "school": {
            "description": "The `_id` of a school, or its name within `country` and `city`, matched without regard to case. There is no list of schools to look one up in: send the name your records hold. One that matches no school is refused with 400; no school is ever created.",
            "example": "Springfield High School",
            "type": "string"
          },
          "city": {
            "description": "The `_id` of a city, or its name within `country`, matched without regard to case. There is no list of cities to look one up in: send the name your records hold. One that matches no city is refused with 400; no city is ever created.",
            "example": "Springfield",
            "type": "string"
          },
          "password": {
            "description": "The student’s sign-in password. Accepted only from an account that holds `auth/signin` on `mto`; from any other the registration is refused with 403. At least 5 characters, the minimum the sign-up form applies, and at most 72 bytes, and not containing the student’s own name or email address. Stored hashed, and never returned by any operation. Omit it to register the student without one.",
            "example": "correct horse battery staple",
            "minLength": 5,
            "type": "string"
          },
          "activatedPlatformsThisSeason": {
            "description": "The organizations the student takes part in this season, by `slug` (as `listOrganizations` gives it, except `mto`: the core record, which every student is on), or `common` for every organization. Registration sets `[\"common\"]` when you leave it out; `null` is refused with 400. At most 6 entries.",
            "example": [
              "common"
            ],
            "items": {
              "enum": [
                "common",
                "stem",
                "hilingua",
                "neo",
                "gmath",
                "coding"
              ],
              "type": "string"
            },
            "maxItems": 6,
            "type": "array"
          }
        },
        "required": [
          "firstName",
          "lastName",
          "birth",
          "sex",
          "email",
          "country",
          "grade",
          "school",
          "city"
        ],
        "type": "object"
      },
      "RegistrationCheckResponse": {
        "description": "What `checkStudentRegistration` found: whether `registerStudent` with the same body would pass the checks it makes before it writes, and why not.",
        "properties": {
          "valid": {
            "description": "`true` when no student of yours has the email address and every reference resolved: `duplicate.sameAccount` is `false` and `problems` is empty.",
            "example": false,
            "type": "boolean"
          },
          "problems": {
            "description": "Every reference field that matched nothing, in the order `country`, `grade`, `city`, `school`, and the country again when its students cannot be given a username. A city or school name inside a country or city that matched nothing is not looked up, so not listed. Empty when the email address is one of your students’.",
            "example": [
              {
                "field": "school",
                "message": "school is not a known school. This API does not create reference data."
              }
            ],
            "items": {
              "$ref": "#/components/schemas/RegistrationProblemResponse"
            },
            "type": "array"
          },
          "resolved": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ResolvedReferencesResponse"
              }
            ],
            "description": "The `_id` each reference resolved to. All four are `null` when the email address is one of your students’: nothing is looked up then.",
            "example": {
              "city": "6650a1b2c3d4e5f6a7b8c9d2",
              "country": "6650a1b2c3d4e5f6a7b8c9d1",
              "grade": "6650a1b2c3d4e5f6a7b8c9d4",
              "school": null
            }
          },
          "duplicate": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RegistrationDuplicateResponse"
              }
            ],
            "description": "Whether one of your students already has the address.",
            "example": {
              "sameAccount": false
            }
          }
        },
        "required": [
          "valid",
          "problems",
          "resolved",
          "duplicate"
        ],
        "type": "object"
      },
      "RegistrationDuplicateResponse": {
        "description": "Whether one of your students already has the email address. Students of other accounts are never looked at.",
        "properties": {
          "sameAccount": {
            "description": "`true` when one of your students already has this email address, so `registerStudent` would answer `409 conflict`.",
            "example": false,
            "type": "boolean"
          },
          "studentId": {
            "description": "That student’s `_id`, only when `sameAccount` is `true`: the student to use instead of registering a second one.",
            "example": "6650a1b2c3d4e5f6a7b8c9d0",
            "type": "string"
          }
        },
        "required": [
          "sameAccount"
        ],
        "type": "object"
      },
      "RegistrationProblemResponse": {
        "description": "A field `registerStudent` would refuse, with the message it would refuse it with.",
        "properties": {
          "field": {
            "description": "The field: `country`, `grade`, `city` or `school`.",
            "enum": [
              "country",
              "grade",
              "city",
              "school"
            ],
            "example": "school",
            "type": "string"
          },
          "message": {
            "description": "The message `registerStudent` answers `400 bad_request` with for this field.",
            "example": "school is not a known school. This API does not create reference data.",
            "type": "string"
          }
        },
        "required": [
          "field",
          "message"
        ],
        "type": "object"
      },
      "ReportResponse": {
        "description": "A released result report of one of your students. Download the PDF with `downloadReport`.",
        "properties": {
          "_id": {
            "description": "The report’s id. `downloadReport` takes it, or `shortId`.",
            "example": "6650a1b2c3d4e5f6a7b8c9e8",
            "type": "string"
          },
          "shortId": {
            "description": "A ten-character code for the report. `downloadReport` takes it too.",
            "example": "R4N8W2P6ZD",
            "type": "string"
          },
          "application": {
            "description": "The id of the application the report is for.",
            "example": "6650a1b2c3d4e5f6a7b8c9e5",
            "type": "string"
          },
          "isActive": {
            "description": "Always `true`: only released reports are listed.",
            "example": true,
            "type": "boolean"
          },
          "isCanceled": {
            "description": "Always `false`: a withdrawn report is not listed.",
            "example": false,
            "type": "boolean"
          },
          "createdAt": {
            "description": "When the report was created.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the report last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "_id",
          "shortId",
          "application",
          "isActive",
          "isCanceled"
        ],
        "type": "object"
      },
      "ResolvedReferencesResponse": {
        "description": "The `_id` each reference field resolved to: what `registerStudent` would store.",
        "properties": {
          "country": {
            "description": "The country’s `_id`; `null` if it matched nothing or was not looked up.",
            "example": "6650a1b2c3d4e5f6a7b8c9d1",
            "nullable": true,
            "type": "string"
          },
          "grade": {
            "description": "The grade’s `_id`, also when you sent its name; `null` if it matched nothing or was not looked up.",
            "example": "6650a1b2c3d4e5f6a7b8c9d4",
            "nullable": true,
            "type": "string"
          },
          "city": {
            "description": "The city’s `_id`, also when you sent its name; `null` if it matched nothing or was not looked up.",
            "example": "6650a1b2c3d4e5f6a7b8c9d2",
            "nullable": true,
            "type": "string"
          },
          "school": {
            "description": "The school’s `_id`, also when you sent its name; `null` if it matched nothing or was not looked up.",
            "example": "6650a1b2c3d4e5f6a7b8c9d3",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "country",
          "grade",
          "city",
          "school"
        ],
        "type": "object"
      },
      "RevokeTokenResponse": {
        "description": "The token you sent is revoked.",
        "properties": {
          "expiresIn": {
            "description": "How many seconds the revocation is held: what was left of the token’s life plus 30 seconds of clock tolerance, and at least 1. The token is refused from now on; after this it would be refused as expired anyway.",
            "example": 1830,
            "minimum": 1,
            "type": "integer"
          }
        },
        "required": [
          "expiresIn"
        ],
        "type": "object"
      },
      "RoleResponse": {
        "description": "One grant on an API account. An operation goes through when an `allow` role matches it and no `disallow` role does.",
        "properties": {
          "effect": {
            "description": "`allow` grants what the role matches; `disallow` refuses it, and wins over every `allow` that matches the same request.",
            "enum": [
              "allow",
              "disallow"
            ],
            "example": "allow",
            "type": "string"
          },
          "action": {
            "description": "The operations it matches, as `<resource>/<operation>`: an exact action such as `student/read`, a `*` for either part such as `student/*` or `*/read`, or `*` for every action. Each operation names the action it needs in `x-permission`.",
            "example": "student/*",
            "type": "string"
          },
          "target": {
            "description": "The organization it applies to: an organization’s `slug`, or `*` for every organization. An operation without `:organizationId` in its path acts on `mto`.",
            "example": "mto",
            "type": "string"
          },
          "authorized": {
            "description": "An account `_id` the role is limited to. Absent, or your own `_id`, and the role applies to your account; any other id and it does nothing, a `disallow` included.",
            "example": "6650a1b2c3d4e5f6a7b8c9f0",
            "type": "string"
          }
        },
        "required": [
          "effect",
          "action",
          "target"
        ],
        "type": "object"
      },
      "SchoolResponse": {
        "description": "A school, as a student’s `school`.",
        "properties": {
          "_id": {
            "description": "The school’s id.",
            "example": "6650a1b2c3d4e5f6a7b8c9d3",
            "type": "string"
          },
          "name": {
            "description": "The school’s name, in capitals.",
            "example": "SPRINGFIELD HIGH SCHOOL",
            "type": "string"
          },
          "country": {
            "description": "The id of the school’s country, where one is recorded.",
            "example": "6650a1b2c3d4e5f6a7b8c9d1",
            "type": "string"
          },
          "city": {
            "description": "The id of the school’s city, where one is recorded.",
            "example": "6650a1b2c3d4e5f6a7b8c9d2",
            "type": "string"
          },
          "createdAt": {
            "description": "When the record was created.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the record last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "_id"
        ],
        "type": "object"
      },
      "SetStudentPasswordRequest": {
        "description": "The body of `setStudentPassword`.",
        "properties": {
          "password": {
            "description": "The student’s new sign-in password. At least 5 characters and at most 72 bytes, and not containing the student’s own first name, surname, username or email address, whatever the case. Stored hashed, and never returned by any operation.",
            "example": "correct horse battery staple",
            "minLength": 5,
            "type": "string"
          }
        },
        "required": [
          "password"
        ],
        "type": "object"
      },
      "SigninLinkResponse": {
        "description": "A link that signs one of your students in to an organization. It works once, within 120 seconds.",
        "properties": {
          "url": {
            "description": "Send the student’s browser here to sign them in. It works once, and only within `expiresIn` seconds; after either, it answers that the access token is invalid or expired, and you make a new link. Treat it as a secret while it lives, and as opaque: do not build, parse or change it.",
            "example": "https://auth.main-team.org/api/user/oauth/invoke?accessToken=exampletokenexampletokenexampletoken&redirectUrl=%2Fdashboard",
            "format": "uri",
            "type": "string"
          },
          "organization": {
            "description": "The `slug` of the organization the student lands in.",
            "example": "stem",
            "type": "string"
          },
          "studentId": {
            "description": "The id of the student the link signs in.",
            "example": "6650a1b2c3d4e5f6a7b8c9d0",
            "type": "string"
          },
          "expiresIn": {
            "description": "Seconds the link stays usable from now: always 120.",
            "example": 120,
            "type": "integer"
          }
        },
        "required": [
          "url",
          "organization",
          "studentId",
          "expiresIn"
        ],
        "type": "object"
      },
      "StudentImportFailureResponse": {
        "description": "Why an import ended the way it did.",
        "properties": {
          "code": {
            "description": "A 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_rejected",
            "type": "string"
          },
          "message": {
            "description": "Written for a person, and may change: never branch on it.",
            "example": "Two rows could no longer be registered. Nothing was registered.",
            "type": "string"
          }
        },
        "required": [
          "code",
          "message"
        ],
        "type": "object"
      },
      "StudentImportRejectionResponse": {
        "description": "What `createStudentImport` puts in `error.details` when it refuses a batch. Nothing was registered and no import was queued.",
        "properties": {
          "total": {
            "description": "How many rows you sent.",
            "example": 250,
            "type": "number"
          },
          "rejected": {
            "description": "How many of them cannot be registered.",
            "example": 3,
            "type": "number"
          },
          "truncated": {
            "description": "True when `rows` is shorter than `rejected`, so fix what is listed and send the batch again to see the rest.",
            "example": false,
            "type": "boolean"
          },
          "rows": {
            "description": "Every refused row, by position and property. Empty, with `rejected` still counted, when the refusal is only that addresses cannot be registered and your account has already been told which rows those were several times today.",
            "items": {
              "$ref": "#/components/schemas/StudentImportRejectionRowResponse"
            },
            "type": "array"
          }
        },
        "required": [
          "total",
          "rejected",
          "truncated",
          "rows"
        ],
        "type": "object"
      },
      "StudentImportRejectionRowResponse": {
        "description": "One row of `error.details.rows`.",
        "properties": {
          "row": {
            "description": "The row’s position in the `students` you sent, counting from 0.",
            "example": 17,
            "type": "number"
          },
          "field": {
            "description": "The property at fault, or `null` when no single property is.",
            "example": "email",
            "nullable": true,
            "type": "object"
          },
          "code": {
            "description": "Why the row was refused; the same codes a row carries.",
            "enum": [
              "invalid_field",
              "unexpected_field",
              "unknown_reference",
              "duplicate_in_request",
              "email_taken_by_your_student",
              "email_unavailable"
            ],
            "example": "email_taken_by_your_student",
            "type": "string"
          },
          "message": {
            "description": "Written for a person, and may change: never branch on it.",
            "example": "One of your students already has this email address.",
            "type": "string"
          },
          "studentId": {
            "description": "On `email_taken_by_your_student`: the `_id` of the student of yours who has the address.",
            "example": "6650a1b2c3d4e5f6a7b8c9d0",
            "type": "string"
          },
          "duplicateOf": {
            "description": "On `duplicate_in_request`: the earlier row with the same address.",
            "example": 12,
            "type": "number"
          }
        },
        "required": [
          "row",
          "field",
          "code",
          "message"
        ],
        "type": "object"
      },
      "StudentImportResponse": {
        "description": "A batch of students you asked to register, and what has happened to it.",
        "properties": {
          "_id": {
            "description": "The import’s `_id`. Read it with `getStudentImport`.",
            "example": "65f0c2a1d3e4f5a6b7c8d901",
            "type": "string"
          },
          "status": {
            "description": "`queued` until a server picks it up, `running` while it registers, then `succeeded` or `failed`. `cancelled` means an operator stopped it. There is no partial import: on anything but `succeeded` no student of this batch was registered.",
            "enum": [
              "queued",
              "running",
              "succeeded",
              "failed",
              "cancelled"
            ],
            "example": "queued",
            "type": "string"
          },
          "total": {
            "description": "How many rows you sent.",
            "example": 250,
            "type": "number"
          },
          "registered": {
            "description": "How many students were registered: `total` once the import has succeeded, and 0 until then and for ever after a failure.",
            "example": 250,
            "type": "number"
          },
          "clientReference": {
            "description": "The `clientReference` you sent, if you sent one.",
            "example": "year-10-autumn-2026",
            "type": "string"
          },
          "failure": {
            "allOf": [
              {
                "$ref": "#/components/schemas/StudentImportFailureResponse"
              }
            ],
            "description": "Set once the import has failed or been cancelled."
          },
          "students": {
            "description": "One page of the rows, in the order you sent them. Page it with `page` and `limit`; `pagination.total` counts every row. Absent from the answer to `createStudentImport`, which has nothing to report yet.",
            "items": {
              "$ref": "#/components/schemas/StudentImportRowResponse"
            },
            "type": "array"
          },
          "createdAt": {
            "description": "When you sent it.",
            "example": "2026-10-06T08:15:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "startedAt": {
            "description": "When a server picked it up.",
            "example": "2026-10-06T08:15:04.000Z",
            "format": "date-time",
            "type": "string"
          },
          "finishedAt": {
            "description": "When it ended, whichever way it ended.",
            "example": "2026-10-06T08:15:09.000Z",
            "format": "date-time",
            "type": "string"
          },
          "expiresAt": {
            "description": "When this import stops being readable. After it, `getStudentImport` answers 404, exactly as it does for an import that never existed. The students stay registered.",
            "example": "2026-11-05T08:15:09.000Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "_id",
          "status",
          "total",
          "registered",
          "students",
          "createdAt",
          "expiresAt"
        ],
        "type": "object"
      },
      "StudentImportRow": {
        "description": "One student in `createStudentImport`: the body of `registerStudent` without `password`, with the same rules, plus your own `externalRef`.",
        "properties": {
          "firstName": {
            "description": "The student’s first name. Printed on certificates and reports, followed by `lastName`.",
            "example": "Jane",
            "type": "string"
          },
          "lastName": {
            "description": "The student’s surname. Printed on certificates and reports after `firstName`. It cannot be empty.",
            "example": "Doe",
            "type": "string"
          },
          "birth": {
            "description": "Date of birth as `DD/MM/YYYY`, and a date that exists: `31/02/2008` is refused. An ISO date such as `2008-05-14` is refused.",
            "example": "14/05/2008",
            "pattern": "^(0[1-9]|[12]\\d|3[01])\\/(0[1-9]|1[0-2])\\/\\d{4}$",
            "type": "string"
          },
          "sex": {
            "description": "One of `m`, `f` or `n`.",
            "enum": [
              "m",
              "f",
              "n"
            ],
            "example": "f",
            "type": "string"
          },
          "email": {
            "description": "The student’s email address, stored in lower case. An address belongs to one student on the whole platform, so one already registered, by your account or another, is refused with 409.",
            "example": "jane.doe@example.com",
            "format": "email",
            "type": "string"
          },
          "email2": {
            "description": "Leave this out. Any value but an empty one is refused with 400.",
            "type": "string"
          },
          "phone": {
            "description": "A phone number, stored as you send it. No format is checked. On an update, `\"\"` clears it.",
            "example": "+1 555 0100",
            "type": "string"
          },
          "country": {
            "description": "The `_id` of a country, from `listCountries` (`GET /v1/country`). An id only: a name or an ISO code is refused. Its two-letter code starts the student’s `username`.",
            "example": "6650a1b2c3d4e5f6a7b8c9d1",
            "pattern": "^[0-9a-fA-F]{24}$",
            "type": "string"
          },
          "grade": {
            "description": "The `_id` of a grade, from `listGrades` (`GET /v1/grade`), or its name, `1` to `12`. Either way the grade’s `_id` is what is stored. One that matches no grade is refused with 400. The grade decides which exams the student is offered.",
            "example": "10",
            "type": "string"
          },
          "school": {
            "description": "The `_id` of a school, or its name within `country` and `city`, matched without regard to case. There is no list of schools to look one up in: send the name your records hold. One that matches no school is refused with 400; no school is ever created.",
            "example": "Springfield High School",
            "type": "string"
          },
          "city": {
            "description": "The `_id` of a city, or its name within `country`, matched without regard to case. There is no list of cities to look one up in: send the name your records hold. One that matches no city is refused with 400; no city is ever created.",
            "example": "Springfield",
            "type": "string"
          },
          "activatedPlatformsThisSeason": {
            "description": "The organizations the student takes part in this season, by `slug` (as `listOrganizations` gives it, except `mto`: the core record, which every student is on), or `common` for every organization. Registration sets `[\"common\"]` when you leave it out; `null` is refused with 400. At most 6 entries.",
            "example": [
              "common"
            ],
            "items": {
              "enum": [
                "common",
                "stem",
                "hilingua",
                "neo",
                "gmath",
                "coding"
              ],
              "type": "string"
            },
            "maxItems": 6,
            "type": "array"
          },
          "externalRef": {
            "description": "Your own reference for this row, echoed back when you read the import. Not stored on the student and not required to be unique. At most 64 characters.",
            "example": "roster-2026-114",
            "maxLength": 64,
            "type": "string"
          }
        },
        "required": [
          "firstName",
          "lastName",
          "birth",
          "sex",
          "email",
          "country",
          "grade",
          "school",
          "city"
        ],
        "type": "object"
      },
      "StudentImportRowErrorResponse": {
        "description": "Why one row of an import could not be registered.",
        "properties": {
          "code": {
            "description": "A stable machine code: branch on this. `invalid_field` and `unexpected_field` are the row’s own rules; `unknown_reference` is a `country`, `grade`, `city` or `school` that matches nothing; `duplicate_in_request` is the same address twice in your own body; `email_taken_by_your_student` is one of your students; `email_unavailable` is an address that cannot be registered, and the answer never says who holds it.",
            "enum": [
              "invalid_field",
              "unexpected_field",
              "unknown_reference",
              "duplicate_in_request",
              "email_taken_by_your_student",
              "email_unavailable"
            ],
            "example": "email_taken_by_your_student",
            "type": "string"
          },
          "message": {
            "description": "Written for a person, and may change: never branch on it.",
            "example": "One of your students already has this email address.",
            "type": "string"
          },
          "studentId": {
            "description": "On `email_taken_by_your_student`: the `_id` of the student of yours who has the address, so you can update them instead.",
            "example": "6650a1b2c3d4e5f6a7b8c9d0",
            "type": "string"
          },
          "duplicateOf": {
            "description": "On `duplicate_in_request`: the earlier row in your own body with the same address.",
            "example": 12,
            "type": "number"
          }
        },
        "required": [
          "code",
          "message"
        ],
        "type": "object"
      },
      "StudentImportRowResponse": {
        "description": "One student of an import.",
        "properties": {
          "row": {
            "description": "The row’s position in the `students` you sent, counting from 0.",
            "example": 0,
            "type": "number"
          },
          "email": {
            "description": "The address you sent for this row, in lower case.",
            "example": "jane.doe@example.com",
            "format": "email",
            "type": "string"
          },
          "externalRef": {
            "description": "The `externalRef` you sent for this row, if you sent one.",
            "example": "roster-2026-114",
            "type": "string"
          },
          "status": {
            "description": "`pending` until the import runs, then `registered` for every row when it succeeds, or `skipped` for every row when it does not. An import registers all of its students or none of them.",
            "enum": [
              "pending",
              "registered",
              "skipped"
            ],
            "example": "registered",
            "type": "string"
          },
          "studentId": {
            "description": "The student’s `_id`, on a `registered` row. Use it with `getStudent`, `createSigninLink` and `createApplication`.",
            "example": "6650a1b2c3d4e5f6a7b8c9d0",
            "type": "string"
          },
          "error": {
            "allOf": [
              {
                "$ref": "#/components/schemas/StudentImportRowErrorResponse"
              }
            ],
            "description": "Why this row could not be registered, when it could not."
          }
        },
        "required": [
          "row",
          "email",
          "status"
        ],
        "type": "object"
      },
      "StudentRecordResponse": {
        "description": "One of your students as stored, with `country`, `city`, `school`, `grade`, `supervisor` and `partner` as ids. What registration, the updates and `setStudentPassword` answer with; read the student to have them resolved.",
        "properties": {
          "_id": {
            "description": "The student’s id: what every `studentId` path parameter and body field takes.",
            "example": "6650a1b2c3d4e5f6a7b8c9d0",
            "type": "string"
          },
          "mainId": {
            "description": "Only on an organization’s own copy of a student, which `linkStudentSupervisor` answers with: there it is the student’s id, and `_id` is the organization’s own. Absent on every other operation.",
            "example": "6650a1b2c3d4e5f6a7b8c9d0",
            "type": "string"
          },
          "username": {
            "description": "Issued at registration, never chosen by you: the country’s two-letter code, a letter, then a number. It is how the student signs in and how support refers to the account.",
            "example": "XXB1045",
            "type": "string"
          },
          "firstName": {
            "description": "The student’s first name.",
            "example": "Jane",
            "type": "string"
          },
          "lastName": {
            "description": "The student’s surname.",
            "example": "Doe",
            "type": "string"
          },
          "fullName": {
            "description": "`firstName` and `lastName` joined by a space, kept in step when either changes.",
            "example": "Jane Doe",
            "type": "string"
          },
          "email": {
            "description": "The student’s email address, in lower case.",
            "example": "jane.doe@example.com",
            "type": "string"
          },
          "emailConfirmed": {
            "description": "Whether the student has confirmed `email`. It starts `false`, only the student can make it `true`, and changing `email` sets it back to `false`. Once it is `true`, `setStudentPassword` is refused: the account is the student’s.",
            "example": false,
            "type": "boolean"
          },
          "phone": {
            "description": "A phone number, as you sent it.",
            "example": "+1 555 0100",
            "type": "string"
          },
          "birth": {
            "description": "Date of birth, `DD/MM/YYYY`.",
            "example": "14/05/2008",
            "type": "string"
          },
          "sex": {
            "description": "`m`, `f` or `n`.",
            "enum": [
              "m",
              "f",
              "n"
            ],
            "example": "f",
            "type": "string"
          },
          "country": {
            "description": "The id of the student’s country.",
            "example": "6650a1b2c3d4e5f6a7b8c9d1",
            "type": "string"
          },
          "city": {
            "description": "The id of the student’s city. A city you sent by name is stored as the id it resolved to.",
            "example": "6650a1b2c3d4e5f6a7b8c9d2",
            "type": "string"
          },
          "school": {
            "description": "The id of the student’s school. A school you sent by name is stored as the id it resolved to.",
            "example": "6650a1b2c3d4e5f6a7b8c9d3",
            "type": "string"
          },
          "grade": {
            "description": "The id of the student’s grade. A grade you sent by name is stored as the id it resolved to.",
            "example": "6650a1b2c3d4e5f6a7b8c9d4",
            "type": "string"
          },
          "supervisor": {
            "description": "The id of the student’s supervisor, when one is attached.",
            "example": "6650a1b2c3d4e5f6a7b8c9d5",
            "type": "string"
          },
          "partner": {
            "description": "The id of the student’s partner, when one is attached.",
            "example": "6650a1b2c3d4e5f6a7b8c9d6",
            "type": "string"
          },
          "activatedPlatformsThisSeason": {
            "description": "The organizations the student is active on this season, by `slug`; `common` means every organization. Registration sets `common` unless you send a list. The organization-scoped student operations see a student only when this lists that organization or `common`, and updating a student through one adds that organization.",
            "example": [
              "common"
            ],
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "createdAt": {
            "description": "When the student was registered.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the record last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "_id",
          "username",
          "firstName",
          "lastName",
          "fullName",
          "email",
          "emailConfirmed",
          "activatedPlatformsThisSeason",
          "createdAt",
          "updatedAt"
        ],
        "type": "object"
      },
      "StudentRefResponse": {
        "description": "The student on an application: enough to recognise the row and to match it to your own records.",
        "properties": {
          "_id": {
            "description": "The organization’s own id for the student. It differs from the id you registered them with: match on `mainId`.",
            "example": "6650a1b2c3d4e5f6a7b8c9e9",
            "type": "string"
          },
          "mainId": {
            "description": "The student’s id: the one you registered them with, and what every `studentId` takes.",
            "example": "6650a1b2c3d4e5f6a7b8c9d0",
            "type": "string"
          },
          "firstName": {
            "description": "The student’s first name.",
            "example": "Jane",
            "type": "string"
          },
          "lastName": {
            "description": "The student’s surname.",
            "example": "Doe",
            "type": "string"
          }
        },
        "required": [
          "_id",
          "mainId"
        ],
        "type": "object"
      },
      "StudentResponse": {
        "description": "One of your students, with `country`, `city`, `school`, `grade`, `supervisor` and `partner` resolved into records. What the student reads answer with.",
        "properties": {
          "_id": {
            "description": "The student’s id: what every `studentId` path parameter and body field takes.",
            "example": "6650a1b2c3d4e5f6a7b8c9d0",
            "type": "string"
          },
          "mainId": {
            "description": "Only on an organization’s own copy of a student, which `linkStudentSupervisor` answers with: there it is the student’s id, and `_id` is the organization’s own. Absent on every other operation.",
            "example": "6650a1b2c3d4e5f6a7b8c9d0",
            "type": "string"
          },
          "username": {
            "description": "Issued at registration, never chosen by you: the country’s two-letter code, a letter, then a number. It is how the student signs in and how support refers to the account.",
            "example": "XXB1045",
            "type": "string"
          },
          "firstName": {
            "description": "The student’s first name.",
            "example": "Jane",
            "type": "string"
          },
          "lastName": {
            "description": "The student’s surname.",
            "example": "Doe",
            "type": "string"
          },
          "fullName": {
            "description": "`firstName` and `lastName` joined by a space, kept in step when either changes.",
            "example": "Jane Doe",
            "type": "string"
          },
          "email": {
            "description": "The student’s email address, in lower case.",
            "example": "jane.doe@example.com",
            "type": "string"
          },
          "emailConfirmed": {
            "description": "Whether the student has confirmed `email`. It starts `false`, only the student can make it `true`, and changing `email` sets it back to `false`. Once it is `true`, `setStudentPassword` is refused: the account is the student’s.",
            "example": false,
            "type": "boolean"
          },
          "phone": {
            "description": "A phone number, as you sent it.",
            "example": "+1 555 0100",
            "type": "string"
          },
          "birth": {
            "description": "Date of birth, `DD/MM/YYYY`.",
            "example": "14/05/2008",
            "type": "string"
          },
          "sex": {
            "description": "`m`, `f` or `n`.",
            "enum": [
              "m",
              "f",
              "n"
            ],
            "example": "f",
            "type": "string"
          },
          "country": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CountryResponse"
              }
            ],
            "description": "The student’s country. `null` if the stored reference no longer resolves; absent when none is set.",
            "nullable": true,
            "type": "object"
          },
          "city": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CityResponse"
              }
            ],
            "description": "The student’s city. `null` if the stored reference no longer resolves; absent when none is set.",
            "nullable": true,
            "type": "object"
          },
          "school": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SchoolResponse"
              }
            ],
            "description": "The student’s school. `null` if the stored reference no longer resolves; absent when none is set.",
            "nullable": true,
            "type": "object"
          },
          "grade": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GradeResponse"
              }
            ],
            "description": "The student’s grade, which decides the exams they are offered. `null` if the stored reference no longer resolves; absent when none is set.",
            "nullable": true,
            "type": "object"
          },
          "supervisor": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PersonRefResponse"
              }
            ],
            "description": "The supervisor the student is attached to across the platform. A supervisor you link with `linkStudentSupervisor` is set on that one organization’s copy of the student, and does not appear here. `null` if the stored reference no longer resolves; absent when none is set.",
            "nullable": true,
            "type": "object"
          },
          "partner": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PersonRefResponse"
              }
            ],
            "description": "The partner the student is attached to. `null` if the stored reference no longer resolves; absent when none is set.",
            "nullable": true,
            "type": "object"
          },
          "activatedPlatformsThisSeason": {
            "description": "The organizations the student is active on this season, by `slug`; `common` means every organization. Registration sets `common` unless you send a list. The organization-scoped student operations see a student only when this lists that organization or `common`, and updating a student through one adds that organization.",
            "example": [
              "common"
            ],
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "createdAt": {
            "description": "When the student was registered.",
            "example": "2026-09-01T09:30:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the record last changed.",
            "example": "2026-09-02T14:05:00.000Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "_id",
          "username",
          "firstName",
          "lastName",
          "fullName",
          "email",
          "emailConfirmed",
          "activatedPlatformsThisSeason",
          "createdAt",
          "updatedAt"
        ],
        "type": "object"
      },
      "SubmitGroupChallengeRequest": {
        "description": "The body of a group challenge submit: the student of yours it is made for.",
        "properties": {
          "studentId": {
            "description": "The student the submit is made for: their `_id`, as `registerStudent` returned 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",
            "type": "string"
          }
        },
        "required": [
          "studentId"
        ],
        "type": "object"
      },
      "SuccessEnvelope": {
        "description": "How every successful JSON response is wrapped. `data` is the result, and each operation declares its shape; list operations add `pagination`. `GET /v1/api-account/validate-me` is the one operation that answers without this envelope.",
        "properties": {
          "success": {
            "description": "Always `true` on a success.",
            "enum": [
              true
            ],
            "example": true,
            "type": "boolean"
          },
          "message": {
            "description": "A sentence saying what happened, written for a person. Branch on the status and on `data`, not on this.",
            "example": "Request completed successfully.",
            "type": "string"
          },
          "pagination": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ],
            "description": "On list operations only."
          }
        },
        "required": [
          "success",
          "message"
        ],
        "type": "object"
      },
      "SupervisorLinkResponse": {
        "description": "A supervisor link, made on one organization only: the other organizations’ records of the student are left as they were.",
        "properties": {
          "organization": {
            "description": "The `slug` of the organization the link was made on.",
            "example": "stem",
            "type": "string"
          },
          "student": {
            "allOf": [
              {
                "$ref": "#/components/schemas/StudentRecordResponse"
              }
            ],
            "description": "The organization’s own copy of the student, after the link. Its `_id` is that organization’s id for the student, and `mainId` is the id you use everywhere else. `supervisor` is the supervisor’s id in the organization, and the other references are ids as that organization stores them."
          },
          "supervisor": {
            "allOf": [
              {
                "$ref": "#/components/schemas/LinkedSupervisorResponse"
              }
            ],
            "description": "The supervisor the student is now linked to."
          }
        },
        "required": [
          "organization",
          "student",
          "supervisor"
        ],
        "type": "object"
      },
      "UpdateOrgStudentRequest": {
        "description": "The body of `updateOrgStudent`. Every field is optional.",
        "properties": {
          "firstName": {
            "description": "The student’s first name. Printed on certificates and reports, followed by `lastName`.",
            "example": "Jane",
            "type": "string"
          },
          "lastName": {
            "description": "The student’s surname. Printed on certificates and reports after `firstName`. It cannot be empty.",
            "example": "Doe",
            "type": "string"
          },
          "birth": {
            "description": "Date of birth as `DD/MM/YYYY`, and a date that exists: `31/02/2008` is refused. An ISO date such as `2008-05-14` is refused.",
            "example": "14/05/2008",
            "pattern": "^(0[1-9]|[12]\\d|3[01])\\/(0[1-9]|1[0-2])\\/\\d{4}$",
            "type": "string"
          },
          "sex": {
            "description": "One of `m`, `f` or `n`.",
            "enum": [
              "m",
              "f",
              "n"
            ],
            "example": "f",
            "type": "string"
          },
          "email": {
            "description": "The student’s email address, stored in lower case. An address belongs to one student on the whole platform, so one already registered, by your account or another, is refused with 409.",
            "example": "jane.doe@example.com",
            "format": "email",
            "type": "string"
          },
          "email2": {
            "description": "Leave this out. Any value but an empty one is refused with 400.",
            "type": "string"
          },
          "phone": {
            "description": "A phone number, stored as you send it. No format is checked. On an update, `\"\"` clears it.",
            "example": "+1 555 0100",
            "type": "string"
          },
          "country": {
            "description": "The `_id` of a country, from `listCountries` (`GET /v1/country`). An id only: a name or an ISO code is refused. Its two-letter code starts the student’s `username`.",
            "example": "6650a1b2c3d4e5f6a7b8c9d1",
            "pattern": "^[0-9a-fA-F]{24}$",
            "type": "string"
          },
          "grade": {
            "description": "The `_id` of a grade, from `listGrades` (`GET /v1/grade`), or its name, `1` to `12`. Either way the grade’s `_id` is what is stored. One that matches no grade is refused with 400. The grade decides which exams the student is offered.",
            "example": "10",
            "type": "string"
          },
          "school": {
            "description": "The `_id` of a school, or its name within `country` and `city`, matched without regard to case. There is no list of schools to look one up in: send the name your records hold. One that matches no school is refused with 400; no school is ever created.",
            "example": "Springfield High School",
            "type": "string"
          },
          "city": {
            "description": "The `_id` of a city, or its name within `country`, matched without regard to case. There is no list of cities to look one up in: send the name your records hold. One that matches no city is refused with 400; no city is ever created.",
            "example": "Springfield",
            "type": "string"
          },
          "activatedPlatformsThisSeason": {
            "description": "Organization slugs, or 'common' for every organization, to add to the student's list. Nothing is ever removed: a value the list already holds is not added twice, and `[]` adds nothing. Leave it out to add this organization, which adds nothing if the list holds 'common'. Send it and only what you name is added, so this organization is added only if you name it.",
            "example": [
              "stem"
            ],
            "items": {
              "enum": [
                "common",
                "stem",
                "hilingua",
                "neo",
                "gmath",
                "coding"
              ],
              "type": "string"
            },
            "maxItems": 6,
            "type": "array"
          }
        },
        "type": "object"
      },
      "UpdateStudentRequest": {
        "description": "The body of `updateStudent`. Every field is optional.",
        "properties": {
          "firstName": {
            "description": "The student’s first name. Printed on certificates and reports, followed by `lastName`.",
            "example": "Jane",
            "type": "string"
          },
          "lastName": {
            "description": "The student’s surname. Printed on certificates and reports after `firstName`. It cannot be empty.",
            "example": "Doe",
            "type": "string"
          },
          "birth": {
            "description": "Date of birth as `DD/MM/YYYY`, and a date that exists: `31/02/2008` is refused. An ISO date such as `2008-05-14` is refused.",
            "example": "14/05/2008",
            "pattern": "^(0[1-9]|[12]\\d|3[01])\\/(0[1-9]|1[0-2])\\/\\d{4}$",
            "type": "string"
          },
          "sex": {
            "description": "One of `m`, `f` or `n`.",
            "enum": [
              "m",
              "f",
              "n"
            ],
            "example": "f",
            "type": "string"
          },
          "email": {
            "description": "The student’s email address, stored in lower case. An address belongs to one student on the whole platform, so one already registered, by your account or another, is refused with 409.",
            "example": "jane.doe@example.com",
            "format": "email",
            "type": "string"
          },
          "email2": {
            "description": "Leave this out. Any value but an empty one is refused with 400.",
            "type": "string"
          },
          "phone": {
            "description": "A phone number, stored as you send it. No format is checked. On an update, `\"\"` clears it.",
            "example": "+1 555 0100",
            "type": "string"
          },
          "country": {
            "description": "The `_id` of a country, from `listCountries` (`GET /v1/country`). An id only: a name or an ISO code is refused. Its two-letter code starts the student’s `username`.",
            "example": "6650a1b2c3d4e5f6a7b8c9d1",
            "pattern": "^[0-9a-fA-F]{24}$",
            "type": "string"
          },
          "grade": {
            "description": "The `_id` of a grade, from `listGrades` (`GET /v1/grade`), or its name, `1` to `12`. Either way the grade’s `_id` is what is stored. One that matches no grade is refused with 400. The grade decides which exams the student is offered.",
            "example": "10",
            "type": "string"
          },
          "school": {
            "description": "The `_id` of a school, or its name within `country` and `city`, matched without regard to case. There is no list of schools to look one up in: send the name your records hold. One that matches no school is refused with 400; no school is ever created.",
            "example": "Springfield High School",
            "type": "string"
          },
          "city": {
            "description": "The `_id` of a city, or its name within `country`, matched without regard to case. There is no list of cities to look one up in: send the name your records hold. One that matches no city is refused with 400; no city is ever created.",
            "example": "Springfield",
            "type": "string"
          },
          "activatedPlatformsThisSeason": {
            "description": "Organization slugs, or 'common' for every organization, to add to the student's list. Nothing is ever removed: a value the list already holds is not added twice, and `[]` adds nothing. Leave it out and the list stays as it is.",
            "example": [
              "neo"
            ],
            "items": {
              "enum": [
                "common",
                "stem",
                "hilingua",
                "neo",
                "gmath",
                "coding"
              ],
              "type": "string"
            },
            "maxItems": 6,
            "type": "array"
          }
        },
        "type": "object"
      }
    },
    "securitySchemes": {
      "access-token": {
        "bearerFormat": "JWT",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "contact": {},
    "description": "Register your students, enter them for exams, follow their applications, download their certificates and reports, and sign them in to an organization’s panel.\n\nEvery operation except the health check needs a bearer token: a JWT you sign yourself with HS256 and your account’s `apiSecret`. Put your `apiKey` in both the `kid` header and the `sub` claim, and set `iat` and `exp` at most 3600 seconds apart. Every authentication failure answers the same `401 unauthorized`.",
    "title": "Main Team API",
    "version": "1.2.0"
  },
  "openapi": "3.0.0",
  "paths": {
    "/v1/api-account/revoke-token": {
      "post": {
        "description": "Revokes the bearer token this request carries. Send no body. From now on that token answers `401 unauthorized` everywhere, this operation included, so sending it again with the same token answers `401`. Only this token is revoked: your other tokens work until they expire.\n\n`data.expiresIn` is how many seconds the revocation is held: what was left of the token’s life plus 30 seconds of clock tolerance, at least 1 and at most 3660. After that the token would be refused as expired anyway.\n\nUse it when a token may have leaked, or when the session it served ends. It needs the `api/*` permission, which only a role whose action is `api/*`, `*/*` or `*` grants; a role for the student or exam operations does not. An account without it cannot revoke its tokens, so keep them short-lived.",
        "operationId": "revokeToken",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Token revoked successfully",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/RevokeTokenResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Token revoked successfully\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "bad_request",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Invalid token format.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`bad_request`: The token could not be read back or has no `exp`. A token that passed authentication always can, so you should never see this; nothing was revoked."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `api/*` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Revoke the token you send, before it expires",
        "tags": [
          "API account"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid token format.",
            "status": 400,
            "when": "The token could not be read back or has no `exp`. A token that passed authentication always can, so you should never see this; nothing was revoked."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `api/*` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "api/*:$org:$ID"
        ]
      }
    },
    "/v1/api-account/validate-me": {
      "get": {
        "description": "The account whose `apiKey` signed the token: its `_id`, `apiKey`, `companyName`, `scopes`, `roles` and `isActive`, never its `apiSecret`. Call it to check that your tokens are accepted and which account they name.\n\n**The one operation without the envelope.** The account is the whole body, not `data` inside `{ success, message, data }`.\n\n`roles` are the grants an operator gave your account, each `{ effect, action, target, authorized? }`. When an operation answers `403 forbidden` with \"Insufficient role permissions\", compare them with its `x-permission`: no `allow` role matched it on that organization, or a `disallow` role did.\n\nIt needs the `api/*` permission, which only a role whose action is `api/*`, `*/*` or `*` grants; a role for the student or exam operations does not. A change an operator makes to your account, to its roles or deactivating it, can take up to 60 seconds to reach this and every other operation.",
        "operationId": "getCurrentApiAccount",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiAccountResponse"
                }
              }
            },
            "description": "The object itself, not wrapped in the `{ success, message, data }` envelope every other operation answers with.",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `api/*` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Fetch the API account your token belongs to",
        "tags": [
          "API account"
        ],
        "x-errors": [
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `api/*` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "api/*:$org:$ID"
        ]
      }
    },
    "/v1/country": {
      "get": {
        "description": "Every country you can give as a student’s `country`, a page at a time (`limit` up to 100). Reference data: the same list whichever organization you work with.\n\n`country` on the student operations takes the `_id` from here and nothing else: not the name, `iso2` or `iso3`. A few countries are reserved for internal use; they are not listed, and a student cannot be registered in one.\n\nAn exam’s `countries` are the organization’s own records, whose ids need not match these: compare them by `iso2`.\n\nThe list is in no guaranteed order and rarely changes, so fetch it once and keep it.",
        "operationId": "listCountries",
        "parameters": [
          {
            "description": "Page number. Defaults to 1.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "example": 1,
              "type": "number"
            }
          },
          {
            "description": "Items per page. Defaults to 20, max 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Countries fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "items": {
                            "$ref": "#/components/schemas/CountryResponse"
                          },
                          "type": "array"
                        },
                        "pagination": {
                          "$ref": "#/components/schemas/PaginationMeta"
                        }
                      },
                      "required": [
                        "data",
                        "pagination"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Countries fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `country/read` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "List the countries a student can be registered in",
        "tags": [
          "Reference data"
        ],
        "x-errors": [
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `country/read` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "country/read:$org:$ID"
        ]
      }
    },
    "/v1/country/{id}": {
      "get": {
        "description": "The country with this `_id`, as `listCountries` lists it.\n\nAn id that no country has and the id of one of the reserved countries get the same answer, `404 not_found`.",
        "operationId": "getCountry",
        "parameters": [
          {
            "description": "The country’s `_id`, from `listCountries`: 24 hexadecimal digits.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Country fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/CountryResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Country fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "bad_request",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`bad_request`: `id` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `country/read` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "not_found",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Country not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`not_found`: No country `listCountries` lists has this `_id`: it matches no country, or it names one of the reserved ones."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Fetch one country by its id",
        "tags": [
          "Reference data"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for '_id': expected ObjectId.",
            "status": 400,
            "when": "`id` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `country/read` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Country not found!",
            "status": 404,
            "when": "No country `listCountries` lists has this `_id`: it matches no country, or it names one of the reserved ones."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "country/read:$org:$ID"
        ]
      }
    },
    "/v1/grade": {
      "get": {
        "description": "Every grade a student can have, in the order they were created, a page at a time (`limit` up to 100). Reference data: the same for every organization, and a grade has the same `_id` in each of them.\n\n`grade` on the student operations takes either a grade’s `_id` or its `name` as listed here (`\"10\"`); any other value is refused with `400 bad_request`. An exam accepts a set of grades, and a student is only offered the exams that accept theirs.",
        "operationId": "listGrades",
        "parameters": [
          {
            "description": "Page number. Defaults to 1.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "example": 1,
              "type": "number"
            }
          },
          {
            "description": "Items per page. Defaults to 20, max 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Grades fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "items": {
                            "$ref": "#/components/schemas/GradeResponse"
                          },
                          "type": "array"
                        },
                        "pagination": {
                          "$ref": "#/components/schemas/PaginationMeta"
                        }
                      },
                      "required": [
                        "data",
                        "pagination"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Grades fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `grade/read` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "List the grades a student can be registered with",
        "tags": [
          "Reference data"
        ],
        "x-errors": [
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `grade/read` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "grade/read:$org:$ID"
        ]
      }
    },
    "/v1/grade/{id}": {
      "get": {
        "description": "The grade with this `_id`, as `listGrades` lists it.\n\nAn id that no grade has answers `404 not_found`.",
        "operationId": "getGrade",
        "parameters": [
          {
            "description": "The grade’s `_id`, from `listGrades`: 24 hexadecimal digits.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Grade fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/GradeResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Grade fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "bad_request",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`bad_request`: `id` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `grade/read` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "not_found",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Grade not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`not_found`: No grade has this `_id`."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Fetch one grade by its id",
        "tags": [
          "Reference data"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for '_id': expected ObjectId.",
            "status": 400,
            "when": "`id` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `grade/read` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Grade not found!",
            "status": 404,
            "when": "No grade has this `_id`."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "grade/read:$org:$ID"
        ]
      }
    },
    "/v1/health": {
      "get": {
        "description": "Answers `200` with `data.status` `ok` whenever the service is running. It needs no token and is not counted against your account’s rate limit, so it is the first call to make against an environment.\n\nIt says only that the service answers: it checks nothing the other operations depend on, so one of them can still fail while this answers `ok`.",
        "operationId": "getHealth",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Request completed successfully.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/HealthResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Request completed successfully.\"."
          }
        },
        "summary": "Check that the API is up",
        "tags": [
          "Health"
        ]
      }
    },
    "/v1/organization": {
      "get": {
        "description": "Every organization that runs exams, a page at a time (`limit` up to 100). Each one’s `_id` is the `organizationId` in the path of every organization-scoped operation: exams, applications, certificates, reports, sign-in links and the organization’s own student records.\n\n`mto`, the organization that holds your students’ main records, is not listed. The operations without `organizationId` in the path act on it, so you never need its id.\n\nBeing listed does not mean your account may act on an organization. That is set by the roles an operator gave your account, and an operation on any other organization answers `403 forbidden`.\n\nThe list is in no guaranteed order and rarely changes, so fetch it once and keep it.",
        "operationId": "listOrganizations",
        "parameters": [
          {
            "description": "Page number. Defaults to 1.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "example": 1,
              "type": "number"
            }
          },
          {
            "description": "Items per page. Defaults to 20, max 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Organizations fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "items": {
                            "$ref": "#/components/schemas/OrganizationResponse"
                          },
                          "type": "array"
                        },
                        "pagination": {
                          "$ref": "#/components/schemas/PaginationMeta"
                        }
                      },
                      "required": [
                        "data",
                        "pagination"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Organizations fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `organization/read` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "List the organizations and their ids",
        "tags": [
          "Reference data"
        ],
        "x-errors": [
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `organization/read` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "organization/read:$org:$ID"
        ]
      }
    },
    "/v1/organization/{id}": {
      "get": {
        "description": "The organization with this `_id`, as `listOrganizations` lists it.\n\nAn id that names no organization in that list, `mto` included, answers `404 not_found`: the same answer that id gets as `organizationId` in another operation’s path.",
        "operationId": "getOrganization",
        "parameters": [
          {
            "description": "The organization’s `_id`, from `listOrganizations`: 24 hexadecimal digits.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Organization fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/OrganizationResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Organization fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "bad_request",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`bad_request`: `id` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `organization/read` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "not_found",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`not_found`: No organization `listOrganizations` lists has this `_id`."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Fetch one organization by its id",
        "tags": [
          "Reference data"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for '_id': expected ObjectId.",
            "status": 400,
            "when": "`id` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `organization/read` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "No organization `listOrganizations` lists has this `_id`."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "organization/read:$org:$ID"
        ]
      }
    },
    "/v1/student": {
      "get": {
        "description": "Lists the students your account registered, with `country`, `city`, `school`, `grade`, `supervisor` and `partner` resolved into records. A student another account registered never appears, even one with the same name.\n\nTwenty students to a page unless you set `limit`, which is at most 100; a larger one is read as 100, and a `page` or `limit` that is not a number is read as the default rather than refused. `pagination.total` counts every one of your students, and `pagination.totalPages` says when to stop.\n\nThe students come in no guaranteed order, so a student registered while you page through can move others between pages. To find one student, keep the `_id` `registerStudent` answered with and call `getStudent`.\n\n**Find students by email address.** `email` takes one address, or up to 100 separated by commas, and lists only your students who have one of them, matched exactly and without regard to case. Use it to find the `_id` of a student you registered earlier, such as after `registerStudent` answered `409` for an address that is already one of your students’. An address none of your students has matches nothing, whoever else holds it: the filter never looks beyond your own students. `pagination.total` counts the matches.",
        "operationId": "listStudents",
        "parameters": [
          {
            "description": "Only the students with one of these email addresses, separated by commas: at most 100. Matched exactly, without regard to case. An address none of your students has matches nothing. Refused with `400` when it is empty, given twice, holds a value that is not an address, or lists more than 100.",
            "in": "query",
            "name": "email",
            "required": false,
            "schema": {
              "example": "ada.lovelace@example.org,grace.hopper@example.org",
              "type": "string"
            }
          },
          {
            "description": "Page number. Defaults to 1.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "example": 1,
              "type": "number"
            }
          },
          {
            "description": "Items per page. Defaults to 20, max 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Students fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "items": {
                            "$ref": "#/components/schemas/StudentResponse"
                          },
                          "type": "array"
                        },
                        "pagination": {
                          "$ref": "#/components/schemas/PaginationMeta"
                        }
                      },
                      "required": [
                        "data",
                        "pagination"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Students fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "`email` is empty, is given twice, or holds a value that is not an email address.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "email must be given once, as one or more email addresses separated by commas",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "bad_request_2": {
                    "summary": "`email` lists more than 100 addresses.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "email must list at most 100 addresses",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `bad_request`: `email` is empty, is given twice, or holds a value that is not an email address.\n- `bad_request`: `email` lists more than 100 addresses."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `student/read` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "List your students",
        "tags": [
          "Students"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "email must be given once, as one or more email addresses separated by commas",
            "status": 400,
            "when": "`email` is empty, is given twice, or holds a value that is not an email address."
          },
          {
            "code": "bad_request",
            "message": "email must list at most 100 addresses",
            "status": 400,
            "when": "`email` lists more than 100 addresses."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `student/read` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "student/read:$org:$ID"
        ]
      },
      "post": {
        "description": "Creates a student under your account. From then on the student is one of yours: you can read and update them, send them a sign-in link, and apply them to exams.\n\n**What to look up first**\n- `country` is the `_id` of a country from `listCountries`. A name or an ISO code is refused.\n- `grade` is the `_id` of a grade from `listGrades`, or its name, `1` to `12`.\n- `city` is a city’s `_id` or its name within `country`, and `school` a school’s `_id` or its name within that country and city. There is no list of cities or schools: send the names your records hold. Names are matched without regard to case, and one that matches nothing is refused with 400. No country, grade, city or school is ever created.\n\n**What is set for you**\n- `username`: the country’s two-letter code, a letter and a number, such as `XXB1045` with a real code in place of `XX`. You cannot choose it, and it is how the student signs in and how support finds the account.\n- `fullName`, from `firstName` and `lastName`.\n- `activatedPlatformsThisSeason`: `[\"common\"]` unless you send a list.\n- `emailConfirmed`: `false`. Only the student can confirm their address.\n\n**A password is optional, and needs a second permission.** Sending `password` needs `auth/signin` on `mto` as well as `student/create`; without it the request is refused with 403 before anything is read or written. It is stored hashed and never returned, and hashing is deliberately slow, so a registration that carries one takes a second or more longer. Leave it out to sign the student in with `createSigninLink` instead, or set one later with `setStudentPassword`.\n\n**Not idempotent.** Registering an email address one of your students already has is refused with 409, and the message ends with that student’s `_id`, so a replayed batch can fetch the student rather than create a second one. An address another account registered is refused with 409 as well, without an id.\n\nThe answer has `country`, `city`, `school` and `grade` as ids; `getStudent` resolves them. An organization holds no record of the student until they have signed in there once, for instance through a link from `createSigninLink`, and until then `createApplication` on that organization is refused with 409.",
        "operationId": "registerStudent",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterStudentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "User registered successfully.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/StudentRecordResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Created: `message` is \"User registered successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "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\").",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "bad_request_2": {
                    "summary": "`country` is not the `_id` of a country `listCountries` lists, or the country has no two-letter code to begin a username with.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "country is not a known country.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "bad_request_3": {
                    "summary": "`lastName` is missing or empty.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "lastName should not be empty",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "bad_request_4": {
                    "summary": "`birth` is not `DD/MM/YYYY`, or is not a date that exists, such as `31/02/2008`.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "birth must be a real date in DD/MM/YYYY format",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `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\").\n- `bad_request`: `country` is not the `_id` of a country `listCountries` lists, or the country has no two-letter code to begin a username with.\n- `bad_request`: `grade`, `city` or `school` matches nothing: no record has that `_id`, and none has that name (within `country`, and for a school within `city` too).\n- `bad_request`: `lastName` is missing or empty.\n- `bad_request`: `birth` is not `DD/MM/YYYY`, or is not a date that exists, such as `31/02/2008`.\n- `bad_request`: `password` is shorter than 5 characters, longer than 72 bytes, or contains the student’s own name or email address."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "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.",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "forbidden_2": {
                    "summary": "`password` was sent, and your account does not hold `auth/signin` on `mto`. Nothing was written.",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Setting a student's password needs the auth/signin permission on mto, the same grant a sign-in link needs.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `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.\n- `forbidden`: `password` was sent, and your account does not hold `auth/signin` on `mto`. Nothing was written."
          },
          "409": {
            "content": {
              "application/json": {
                "examples": {
                  "conflict": {
                    "summary": "One of your students already has this email address. The message ends with that student’s `_id`.",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "A student with that email is already registered to this account (6650a1b2c3d4e5f6a7b8c9d0).",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "conflict_2": {
                    "summary": "Another account registered this email address. The message does not say whose it is.",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "That email address is already registered.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `conflict`: One of your students already has this email address. The message ends with that student’s `_id`.\n- `conflict`: Another account registered this email address. The message does not say whose it is."
          },
          "413": {
            "content": {
              "application/json": {
                "examples": {
                  "payload_too_large": {
                    "summary": "payload_too_large",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`payload_too_large`: The body is larger than 100 kB."
          },
          "415": {
            "content": {
              "application/json": {
                "examples": {
                  "unsupported_media_type": {
                    "summary": "unsupported_media_type",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Register a student",
        "tags": [
          "Students"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "status": 400,
            "when": "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\")."
          },
          {
            "code": "bad_request",
            "message": "country is not a known country.",
            "status": 400,
            "when": "`country` is not the `_id` of a country `listCountries` lists, or the country has no two-letter code to begin a username with."
          },
          {
            "code": "bad_request",
            "status": 400,
            "when": "`grade`, `city` or `school` matches nothing: no record has that `_id`, and none has that name (within `country`, and for a school within `city` too)."
          },
          {
            "code": "bad_request",
            "message": "lastName should not be empty",
            "status": 400,
            "when": "`lastName` is missing or empty."
          },
          {
            "code": "bad_request",
            "message": "birth must be a real date in DD/MM/YYYY format",
            "status": 400,
            "when": "`birth` is not `DD/MM/YYYY`, or is not a date that exists, such as `31/02/2008`."
          },
          {
            "code": "bad_request",
            "status": 400,
            "when": "`password` is shorter than 5 characters, longer than 72 bytes, or contains the student’s own name or email address."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Setting a student's password needs the auth/signin permission on mto, the same grant a sign-in link needs.",
            "status": 403,
            "when": "`password` was sent, and your account does not hold `auth/signin` on `mto`. Nothing was written."
          },
          {
            "code": "conflict",
            "message": "A student with that email is already registered to this account (6650a1b2c3d4e5f6a7b8c9d0).",
            "status": 409,
            "when": "One of your students already has this email address. The message ends with that student’s `_id`."
          },
          {
            "code": "conflict",
            "message": "That email address is already registered.",
            "status": 409,
            "when": "Another account registered this email address. The message does not say whose it is."
          },
          {
            "code": "payload_too_large",
            "message": "request entity too large",
            "status": 413,
            "when": "The body is larger than 100 kB."
          },
          {
            "code": "unsupported_media_type",
            "message": "unsupported charset \"ISO-8859-1\"",
            "status": 415,
            "when": "The body declares a charset that is not a UTF one (send UTF-8), or a `Content-Encoding` other than gzip, deflate or br."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "student/create:$org:$ID"
        ]
      }
    },
    "/v1/student/check": {
      "post": {
        "description": "Runs the checks `registerStudent` makes before it creates anything, on the same body without `password`, and tells you what they found. Nothing is created or changed and no username is issued, so sending it any number of times has no effect. Use it to validate a batch before you register it.\n\n**The body** follows `registerStudent`’s rules, and one that breaks a rule is refused the same way, with `400`: a missing or malformed field, `null` for `phone` or `activatedPlatformsThisSeason`, or a property the operation does not accept, `password` included.\n\n**The answer is `200` whatever the lookups found.** `data.valid` is `true` when the registration would pass every check made here:\n- `data.duplicate.sameAccount` is `true` when one of your students already has this email address, and `data.duplicate.studentId` is that student’s `_id`: `registerStudent` would answer `409`. Nothing else is looked up then, as registration does not look further either.\n- `data.problems` lists every `country`, `grade`, `city` and `school` that matches nothing, each as `{ field, message }` with the message `registerStudent` would answer `400` with, where registration names only the first. A `city` or `school` named inside a country or city that matched nothing is not looked up, so fix the field above it first. A country whose students cannot be given a username is listed as well.\n- `data.resolved` has the `_id` each of the four resolved to, what registration would store, or `null`.\n\n**Only your own students are checked for the email address.** An address a student on another account holds is not looked for and not reported, so `valid: true` is not a promise: `registerStudent` still answers `409` for such an address, without saying whose it is. Nor can the check foresee a student registered, or reference data changed, between the check and the registration.",
        "operationId": "checkStudentRegistration",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckStudentRegistrationRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Registration checked. Nothing was created.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/RegistrationCheckResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Registration checked. Nothing was created.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "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\").",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "bad_request_2": {
                    "summary": "`password` was sent. The check never takes one: leave it out, and send it with `registerStudent` only.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "property password should not exist",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "bad_request_3": {
                    "summary": "`lastName` is missing or empty.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "lastName should not be empty",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "bad_request_4": {
                    "summary": "`birth` is not `DD/MM/YYYY`, or is not a date that exists, such as `31/02/2008`.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "birth must be a real date in DD/MM/YYYY format",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `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\").\n- `bad_request`: `password` was sent. The check never takes one: leave it out, and send it with `registerStudent` only.\n- `bad_request`: `lastName` is missing or empty.\n- `bad_request`: `birth` is not `DD/MM/YYYY`, or is not a date that exists, such as `31/02/2008`."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "413": {
            "content": {
              "application/json": {
                "examples": {
                  "payload_too_large": {
                    "summary": "payload_too_large",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`payload_too_large`: The body is larger than 100 kB."
          },
          "415": {
            "content": {
              "application/json": {
                "examples": {
                  "unsupported_media_type": {
                    "summary": "unsupported_media_type",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Check a registration without registering the student",
        "tags": [
          "Students"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "status": 400,
            "when": "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\")."
          },
          {
            "code": "bad_request",
            "message": "property password should not exist",
            "status": 400,
            "when": "`password` was sent. The check never takes one: leave it out, and send it with `registerStudent` only."
          },
          {
            "code": "bad_request",
            "message": "lastName should not be empty",
            "status": 400,
            "when": "`lastName` is missing or empty."
          },
          {
            "code": "bad_request",
            "message": "birth must be a real date in DD/MM/YYYY format",
            "status": 400,
            "when": "`birth` is not `DD/MM/YYYY`, or is not a date that exists, such as `31/02/2008`."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "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."
          },
          {
            "code": "payload_too_large",
            "message": "request entity too large",
            "status": 413,
            "when": "The body is larger than 100 kB."
          },
          {
            "code": "unsupported_media_type",
            "message": "unsupported charset \"ISO-8859-1\"",
            "status": 415,
            "when": "The body declares a charset that is not a UTF one (send UTF-8), or a `Content-Encoding` other than gzip, deflate or br."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "student/create:$org:$ID"
        ]
      }
    },
    "/v1/student/import": {
      "post": {
        "description": "Registers 30 to 1000 students in one request. Every row follows `registerStudent`’s rules exactly, so read that operation first: this one is the same registration, a class at a time.\n\n**It answers before it registers anybody.** The whole batch is checked while you wait — every field, every `country`, `grade`, `city` and `school`, and every email address — and then `202` with an import you read to follow it. The `Location` header is where to read it. Nothing exists yet when you get that answer: poll `getStudentImport` every few seconds until `status` is no longer `queued` or `running`.\n\n**All of them or none of them.** The students are written in one transaction, so a batch either registers every row or registers nothing. There is no partial import to reconcile and nothing to undo. A batch that fails says why, and you fix the rows and send it again.\n\n**No passwords.** A row takes everything `registerStudent` takes except `password`: hashing is deliberately slow, and a thousand of them would keep the batch waiting and leave the plaintext queued meanwhile. Sign the students in with `createSigninLink`, or set a password per student afterwards with `setStudentPassword`.\n\n**The welcome email is the one `registerStudent` sends**, one per student, with the same text.\n\n**Limits.** One unfinished import per account: send the next batch when this one has finished. 10 requests an hour. Bodies up to 1.5 MB here, where every other operation takes 100 kB.\n\n**Sending the same batch twice is safe.** The same rows from your account inside 24 hours answer with the import you already have, not a second one, so a request whose answer you never saw can simply be sent again.\n\n**If anything is wrong with the rows** the answer is `422`, nothing is queued, and `error.details.rows` lists every row at fault with its position, property and a code to branch on. Fix them and send the batch again.",
        "operationId": "createStudentImport",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateStudentImportRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "examples": {
                  "accepted": {
                    "summary": "Accepted",
                    "value": {
                      "data": {
                        "_id": "65f0c2a1d3e4f5a6b7c8d901",
                        "clientReference": "year-10-autumn-2026",
                        "createdAt": "2026-10-06T08:15:00.000Z",
                        "expiresAt": "2026-11-05T08:15:09.000Z",
                        "failure": {
                          "code": "rows_rejected",
                          "message": "Two rows could no longer be registered. Nothing was registered."
                        },
                        "finishedAt": "2026-10-06T08:15:09.000Z",
                        "registered": 250,
                        "startedAt": "2026-10-06T08:15:04.000Z",
                        "status": "queued",
                        "students": [
                          {
                            "email": "jane.doe@example.com",
                            "error": {
                              "code": "email_taken_by_your_student",
                              "duplicateOf": 12,
                              "message": "One of your students already has this email address.",
                              "studentId": "6650a1b2c3d4e5f6a7b8c9d0"
                            },
                            "externalRef": "roster-2026-114",
                            "row": 0,
                            "status": "registered",
                            "studentId": "6650a1b2c3d4e5f6a7b8c9d0"
                          }
                        ],
                        "total": 250
                      },
                      "message": "Import accepted. The students are registered in the background; read the import to follow it.",
                      "success": true
                    }
                  },
                  "alreadySent": {
                    "summary": "You sent these exact rows inside the last 24 hours. The import in `data` is the one you already have; no second one was made.",
                    "value": {
                      "data": {
                        "_id": "65f0c2a1d3e4f5a6b7c8d901",
                        "clientReference": "year-10-autumn-2026",
                        "createdAt": "2026-10-06T08:15:00.000Z",
                        "expiresAt": "2026-11-05T08:15:09.000Z",
                        "failure": {
                          "code": "rows_rejected",
                          "message": "Two rows could no longer be registered. Nothing was registered."
                        },
                        "finishedAt": "2026-10-06T08:15:09.000Z",
                        "registered": 250,
                        "startedAt": "2026-10-06T08:15:04.000Z",
                        "status": "queued",
                        "students": [
                          {
                            "email": "jane.doe@example.com",
                            "error": {
                              "code": "email_taken_by_your_student",
                              "duplicateOf": 12,
                              "message": "One of your students already has this email address.",
                              "studentId": "6650a1b2c3d4e5f6a7b8c9d0"
                            },
                            "externalRef": "roster-2026-114",
                            "row": 0,
                            "status": "registered",
                            "studentId": "6650a1b2c3d4e5f6a7b8c9d0"
                          }
                        ],
                        "total": 250
                      },
                      "message": "You already sent this batch. Its import is unchanged.",
                      "success": true
                    }
                  }
                },
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Import accepted. The students are registered in the background; read the import to follow it.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/StudentImportResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Accepted: `message` is \"Import accepted. The students are registered in the background; read the import to follow it.\".\n\nOr `message` is \"You already sent this batch. Its import is unchanged.\": You sent these exact rows inside the last 24 hours. The import in `data` is the one you already have; no second one was made.",
            "headers": {
              "Location": {
                "description": "The import’s own path, `/v1/student/import/{importId}`. Read it to follow the batch.",
                "schema": {
                  "type": "string"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (10 per 3600 seconds).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "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\").",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "bad_request_2": {
                    "summary": "A row breaks one of `registerStudent`’s rules. The message names the row and the property, counting rows from 0.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "students.4.birth birth must be a real date in DD/MM/YYYY format",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "bad_request_3": {
                    "summary": "`students` has fewer than 30 rows or more than 1000. Below 30, call `registerStudent` per student.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "students must contain at least 30 elements",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "bad_request_4": {
                    "summary": "A row carries `password`, which this operation does not take, or any other property it does not accept.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "property password should not exist",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `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\").\n- `bad_request`: A row breaks one of `registerStudent`’s rules. The message names the row and the property, counting rows from 0.\n- `bad_request`: `students` has fewer than 30 rows or more than 1000. Below 30, call `registerStudent` per student.\n- `bad_request`: A row carries `password`, which this operation does not take, or any other property it does not accept."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "not_found",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "The requested resource could not be found.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`not_found`: Bulk registration is not switched on for the environment you are calling. Nothing was queued. Ask support before you build against it."
          },
          "409": {
            "content": {
              "application/json": {
                "examples": {
                  "conflict": {
                    "summary": "conflict",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "You already have an import that has not finished. Read it, and send the next one when it has. (65f0c2a1d3e4f5a6b7c8d901)",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`conflict`: You already have an import that has not finished. Read it, and send the next one when it has."
          },
          "413": {
            "content": {
              "application/json": {
                "examples": {
                  "payload_too_large": {
                    "summary": "payload_too_large",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`payload_too_large`: The body is larger than 1.5 MB."
          },
          "415": {
            "content": {
              "application/json": {
                "examples": {
                  "unsupported_media_type": {
                    "summary": "unsupported_media_type",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "422": {
            "content": {
              "application/json": {
                "examples": {
                  "unprocessable_entity": {
                    "summary": "unprocessable_entity",
                    "value": {
                      "error": {
                        "code": "unprocessable_entity",
                        "details": {
                          "rejected": 3,
                          "rows": [
                            {
                              "code": "invalid_field",
                              "field": "birth",
                              "message": "birth must be a real date in DD/MM/YYYY format",
                              "row": 4
                            },
                            {
                              "code": "email_taken_by_your_student",
                              "field": "email",
                              "message": "One of your students already has this email address.",
                              "row": 17,
                              "studentId": "652f1c9b8e4b2a0012a3c4d5"
                            },
                            {
                              "code": "duplicate_in_request",
                              "duplicateOf": 12,
                              "field": "email",
                              "message": "Row 12 has the same email address.",
                              "row": 31
                            }
                          ],
                          "total": 250,
                          "truncated": false
                        },
                        "documentation_url": "https://hub.main-team.org/api/errors#unprocessable_entity",
                        "message": "3 of 250 rows cannot be registered. Nothing was registered and no import was queued; error.details.rows lists every problem.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`unprocessable_entity`: One or more rows cannot be registered. Nothing was queued. `error.details.rows` lists every row at fault with a `code` to branch on: `invalid_field`, `unexpected_field`, `unknown_reference`, `duplicate_in_request`, `email_taken_by_your_student` (with that student’s `_id`) and `email_unavailable`, which never says who holds the address. The list is left out, and only counted, when the only problem is unavailable addresses and your account has already been shown those rows several times today."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`too_many_requests`: Your account has made more than 10 requests to this operation in the current hour. Wait the seconds in `Retry-After` before sending again. This operation has a budget of its own, lower than the 100 per 60 seconds every other operation gets.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          },
          "503": {
            "content": {
              "application/json": {
                "examples": {
                  "service_unavailable": {
                    "summary": "This server is already checking another batch. Nothing was queued; wait the seconds in `Retry-After` and send it again.",
                    "value": {
                      "error": {
                        "code": "service_unavailable",
                        "documentation_url": "https://hub.main-team.org/api/errors#service_unavailable",
                        "message": "This server is already checking another import. Send it again in a few seconds.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "service_unavailable_2": {
                    "summary": "Bulk registration is paused for a scheduled window, such as an exam morning. Nothing was queued; `Retry-After` is how long the window lasts. An import already queued is not lost — it waits and then runs.",
                    "value": {
                      "error": {
                        "code": "service_unavailable",
                        "documentation_url": "https://hub.main-team.org/api/errors#service_unavailable",
                        "message": "Bulk registration is paused for a scheduled window. Nothing was queued; send it again after the time in Retry-After.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "service_unavailable_3": {
                    "summary": "Checking the batch took too long. Nothing was queued; send it again, or in smaller batches.",
                    "value": {
                      "error": {
                        "code": "service_unavailable",
                        "documentation_url": "https://hub.main-team.org/api/errors#service_unavailable",
                        "message": "Checking this batch took too long. Nothing was registered and no import was queued. Send it again later, or in smaller batches.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `service_unavailable`: This server is already checking another batch. Nothing was queued; wait the seconds in `Retry-After` and send it again.\n- `service_unavailable`: Bulk registration is paused for a scheduled window, such as an exam morning. Nothing was queued; `Retry-After` is how long the window lasts. An import already queued is not lost — it waits and then runs.\n- `service_unavailable`: Checking the batch took too long. Nothing was queued; send it again, or in smaller batches.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Register many students at once",
        "tags": [
          "Students"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "status": 400,
            "when": "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\")."
          },
          {
            "code": "bad_request",
            "message": "students.4.birth birth must be a real date in DD/MM/YYYY format",
            "status": 400,
            "when": "A row breaks one of `registerStudent`’s rules. The message names the row and the property, counting rows from 0."
          },
          {
            "code": "bad_request",
            "message": "students must contain at least 30 elements",
            "status": 400,
            "when": "`students` has fewer than 30 rows or more than 1000. Below 30, call `registerStudent` per student."
          },
          {
            "code": "bad_request",
            "message": "property password should not exist",
            "status": 400,
            "when": "A row carries `password`, which this operation does not take, or any other property it does not accept."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "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."
          },
          {
            "code": "not_found",
            "status": 404,
            "when": "Bulk registration is not switched on for the environment you are calling. Nothing was queued. Ask support before you build against it."
          },
          {
            "code": "conflict",
            "message": "You already have an import that has not finished. Read it, and send the next one when it has. (65f0c2a1d3e4f5a6b7c8d901)",
            "status": 409,
            "when": "You already have an import that has not finished. Read it, and send the next one when it has."
          },
          {
            "code": "payload_too_large",
            "message": "request entity too large",
            "status": 413,
            "when": "The body is larger than 1.5 MB."
          },
          {
            "code": "unsupported_media_type",
            "message": "unsupported charset \"ISO-8859-1\"",
            "status": 415,
            "when": "The body declares a charset that is not a UTF one (send UTF-8), or a `Content-Encoding` other than gzip, deflate or br."
          },
          {
            "code": "unprocessable_entity",
            "details": {
              "rejected": 3,
              "rows": [
                {
                  "code": "invalid_field",
                  "field": "birth",
                  "message": "birth must be a real date in DD/MM/YYYY format",
                  "row": 4
                },
                {
                  "code": "email_taken_by_your_student",
                  "field": "email",
                  "message": "One of your students already has this email address.",
                  "row": 17,
                  "studentId": "652f1c9b8e4b2a0012a3c4d5"
                },
                {
                  "code": "duplicate_in_request",
                  "duplicateOf": 12,
                  "field": "email",
                  "message": "Row 12 has the same email address.",
                  "row": 31
                }
              ],
              "total": 250,
              "truncated": false
            },
            "message": "3 of 250 rows cannot be registered. Nothing was registered and no import was queued; error.details.rows lists every problem.",
            "status": 422,
            "when": "One or more rows cannot be registered. Nothing was queued. `error.details.rows` lists every row at fault with a `code` to branch on: `invalid_field`, `unexpected_field`, `unknown_reference`, `duplicate_in_request`, `email_taken_by_your_student` (with that student’s `_id`) and `email_unavailable`, which never says who holds the address. The list is left out, and only counted, when the only problem is unavailable addresses and your account has already been shown those rows several times today."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "Your account has made more than 10 requests to this operation in the current hour. Wait the seconds in `Retry-After` before sending again. This operation has a budget of its own, lower than the 100 per 60 seconds every other operation gets."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          },
          {
            "code": "service_unavailable",
            "message": "This server is already checking another import. Send it again in a few seconds.",
            "status": 503,
            "when": "This server is already checking another batch. Nothing was queued; wait the seconds in `Retry-After` and send it again."
          },
          {
            "code": "service_unavailable",
            "message": "Bulk registration is paused for a scheduled window. Nothing was queued; send it again after the time in Retry-After.",
            "status": 503,
            "when": "Bulk registration is paused for a scheduled window, such as an exam morning. Nothing was queued; `Retry-After` is how long the window lasts. An import already queued is not lost — it waits and then runs."
          },
          {
            "code": "service_unavailable",
            "message": "Checking this batch took too long. Nothing was registered and no import was queued. Send it again later, or in smaller batches.",
            "status": 503,
            "when": "Checking the batch took too long. Nothing was queued; send it again, or in smaller batches."
          }
        ],
        "x-permission": [
          "student/create:$org:$ID"
        ],
        "x-rate-limit": {
          "limit": 10,
          "windowSeconds": 3600
        }
      }
    },
    "/v1/student/import/{importId}": {
      "get": {
        "description": "Returns one of your imports and one page of its rows, in the order you sent them.\n\n`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.\n\n**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`.\n\nAn 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.",
        "operationId": "getStudentImport",
        "parameters": [
          {
            "description": "The import’s `_id`, as `createStudentImport` returned it.",
            "in": "path",
            "name": "importId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Page number. Defaults to 1.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "example": 1,
              "type": "number"
            }
          },
          {
            "description": "Items per page. Defaults to 20, max 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Import fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/StudentImportResponse"
                        },
                        "pagination": {
                          "$ref": "#/components/schemas/PaginationMeta"
                        }
                      },
                      "required": [
                        "data",
                        "pagination"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Import fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "bad_request",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`bad_request`: `importId` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "not_found",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Follow a batch of students you sent",
        "tags": [
          "Students"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for '_id': expected ObjectId.",
            "status": 400,
            "when": "`importId` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "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."
          },
          {
            "code": "not_found",
            "message": "Import not found!",
            "status": 404,
            "when": "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."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-pagination": {
          "of": "students"
        },
        "x-permission": [
          "student/create:$org:$ID"
        ]
      }
    },
    "/v1/student/{studentId}": {
      "get": {
        "description": "Returns one student your account registered, with `country`, `city`, `school`, `grade`, `supervisor` and `partner` resolved into records.\n\nA `studentId` no student has, and the `_id` of a student another account registered, both answer `404 not_found`, the same way. A `studentId` that is not an id at all is refused with `400`.",
        "operationId": "getStudent",
        "parameters": [
          {
            "description": "The student’s `_id`, as `registerStudent` returned it: 24 hexadecimal digits. A student another account registered answers exactly like one that does not exist.",
            "in": "path",
            "name": "studentId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Student fetched successfully",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/StudentResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Student fetched successfully\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "bad_request",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`bad_request`: `studentId` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `student/read` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "not_found",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Student not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`not_found`: No student of yours has this `studentId`. A student another account registered answers the same."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Fetch one of your students",
        "tags": [
          "Students"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for '_id': expected ObjectId.",
            "status": 400,
            "when": "`studentId` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `student/read` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Student not found!",
            "status": 404,
            "when": "No student of yours has this `studentId`. A student another account registered answers the same."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "student/read:$org:$ID"
        ]
      },
      "put": {
        "description": "Changes the fields you send on one of your students and leaves every other field as it is. Every field is optional here, and each one you send follows the same rules as in `registerStudent`. `password` is not accepted (400): use `setStudentPassword`.\n\n- A `city` or `school` sent by name is looked up within the student’s country and city: the ones in this request, or else the ones already stored.\n- Changing `firstName` or `lastName` updates `fullName`.\n- `firstName`, `lastName`, `birth` and `sex` can be changed but not cleared: an empty value or `null` is refused. `phone` is cleared with `\"\"`. `birth` has to be a date that exists: `31/02/2008` is refused.\n- `activatedPlatformsThisSeason` is added to the stored list, never written over it: nothing is removed, a value already there is not added twice, and `[]` adds nothing.\n- **Changing `email` to a different address sets `emailConfirmed` back to `false`.** The student has to confirm the new address, and until they do `setStudentPassword` accepts a password for them again. Sending the address already stored, in any case, is not a change.\n\nA student another account registered answers 404, exactly like one that does not exist. Sending the same body twice has the effect of sending it once. The answer has `country`, `city`, `school`, `grade`, `supervisor` and `partner` as ids; `getStudent` resolves them.",
        "operationId": "updateStudent",
        "parameters": [
          {
            "description": "The student’s `_id`, as `registerStudent` returned it: 24 hexadecimal digits. A student another account registered answers exactly like one that does not exist.",
            "in": "path",
            "name": "studentId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateStudentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Student updated successfully.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/StudentRecordResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Student updated successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "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\").",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "bad_request_2": {
                    "summary": "`studentId` is not 24 hexadecimal digits.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "bad_request_3": {
                    "summary": "`email` is `null`: an address can be changed, not removed.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "email must be a valid email address",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "bad_request_4": {
                    "summary": "`firstName`, `lastName`, `birth`, `sex`, `phone` or `activatedPlatformsThisSeason` is `null`, or `firstName` or `lastName` is empty. These can be changed, not removed; clear `phone` with `\"\"`.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "lastName should not be empty",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `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\").\n- `bad_request`: `studentId` is not 24 hexadecimal digits.\n- `bad_request`: `email` is `null`: an address can be changed, not removed.\n- `bad_request`: `firstName`, `lastName`, `birth`, `sex`, `phone` or `activatedPlatformsThisSeason` is `null`, or `firstName` or `lastName` is empty. These can be changed, not removed; clear `phone` with `\"\"`.\n- `bad_request`: `country`, `grade`, `city` or `school` matches nothing or is `null`, or a `city` or `school` name cannot be looked up because the student has no country, or no city for a school."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `student/update` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "not_found",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Student not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`not_found`: No student of yours has this `studentId`. A student another account registered answers the same."
          },
          "409": {
            "content": {
              "application/json": {
                "examples": {
                  "conflict": {
                    "summary": "conflict",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "That email address is already registered.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`conflict`: `email` is changed to an address already registered, by your account or another. The message does not say whose it is."
          },
          "413": {
            "content": {
              "application/json": {
                "examples": {
                  "payload_too_large": {
                    "summary": "payload_too_large",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`payload_too_large`: The body is larger than 100 kB."
          },
          "415": {
            "content": {
              "application/json": {
                "examples": {
                  "unsupported_media_type": {
                    "summary": "unsupported_media_type",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Update one of your students",
        "tags": [
          "Students"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "status": 400,
            "when": "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\")."
          },
          {
            "code": "bad_request",
            "message": "Invalid value for '_id': expected ObjectId.",
            "status": 400,
            "when": "`studentId` is not 24 hexadecimal digits."
          },
          {
            "code": "bad_request",
            "message": "email must be a valid email address",
            "status": 400,
            "when": "`email` is `null`: an address can be changed, not removed."
          },
          {
            "code": "bad_request",
            "message": "lastName should not be empty",
            "status": 400,
            "when": "`firstName`, `lastName`, `birth`, `sex`, `phone` or `activatedPlatformsThisSeason` is `null`, or `firstName` or `lastName` is empty. These can be changed, not removed; clear `phone` with `\"\"`."
          },
          {
            "code": "bad_request",
            "status": 400,
            "when": "`country`, `grade`, `city` or `school` matches nothing or is `null`, or a `city` or `school` name cannot be looked up because the student has no country, or no city for a school."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `student/update` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Student not found!",
            "status": 404,
            "when": "No student of yours has this `studentId`. A student another account registered answers the same."
          },
          {
            "code": "conflict",
            "message": "That email address is already registered.",
            "status": 409,
            "when": "`email` is changed to an address already registered, by your account or another. The message does not say whose it is."
          },
          {
            "code": "payload_too_large",
            "message": "request entity too large",
            "status": 413,
            "when": "The body is larger than 100 kB."
          },
          {
            "code": "unsupported_media_type",
            "message": "unsupported charset \"ISO-8859-1\"",
            "status": 415,
            "when": "The body declares a charset that is not a UTF one (send UTF-8), or a `Content-Encoding` other than gzip, deflate or br."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "student/update:$org:$ID"
        ]
      }
    },
    "/v1/student/{studentId}/password": {
      "put": {
        "description": "Replaces the password one of your students signs in with, whichever organization they sign in to. A password you choose lets whoever holds it sign in as the student, like a link from `createSigninLink`, but it never expires. So it needs the permission a sign-in link needs: `auth/signin` on `mto` or on `*`. A `student/*` role does not grant it.\n\nChecked in this order, before anything is written:\n1. **400** for a password shorter than 5 characters or longer than 72 bytes. These are rules of the body, so they are checked before the student is looked up.\n2. **404** for a student another account registered, answered exactly like one that does not exist.\n3. **409** once the student has confirmed their email address. The account is theirs from then on, and a password set over theirs would lock them out. Send them a sign-in link with `createSigninLink` instead.\n4. **400** for a password that contains the student’s first name, surname, username or email address (or the part of it before `@`), whatever the case. Parts shorter than 4 characters are not checked.\n\nThe password is stored hashed and never returned by any operation, and hashing is deliberately slow, so allow a second or more. Setting it again replaces it. The answer is the student, with references as ids.",
        "operationId": "setStudentPassword",
        "parameters": [
          {
            "description": "The student’s `_id`, as `registerStudent` returned it: 24 hexadecimal digits. A student another account registered answers exactly like one that does not exist.",
            "in": "path",
            "name": "studentId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetStudentPasswordRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Password set.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/StudentRecordResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Password set.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "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\").",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "bad_request_2": {
                    "summary": "`studentId` is not 24 hexadecimal digits.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "bad_request_3": {
                    "summary": "`password` contains the student’s first name, surname, username or email address, as stored.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "password must not contain the student's own name, username or email address",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `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\").\n- `bad_request`: `studentId` is not 24 hexadecimal digits.\n- `bad_request`: `password` contains the student’s first name, surname, username or email address, as stored."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `auth/signin` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "not_found",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Student not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`not_found`: No student of yours has this `studentId`. A student another account registered answers the same."
          },
          "409": {
            "content": {
              "application/json": {
                "examples": {
                  "conflict": {
                    "summary": "conflict",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "This student has confirmed their email address, so the password is theirs to change. Send them a sign-in link with POST /v1/:organizationId/auth/signin.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`conflict`: The student has confirmed their email address, so the password is theirs to change."
          },
          "413": {
            "content": {
              "application/json": {
                "examples": {
                  "payload_too_large": {
                    "summary": "payload_too_large",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`payload_too_large`: The body is larger than 100 kB."
          },
          "415": {
            "content": {
              "application/json": {
                "examples": {
                  "unsupported_media_type": {
                    "summary": "unsupported_media_type",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Set the sign-in password of one of your students",
        "tags": [
          "Students"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "status": 400,
            "when": "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\")."
          },
          {
            "code": "bad_request",
            "message": "Invalid value for '_id': expected ObjectId.",
            "status": 400,
            "when": "`studentId` is not 24 hexadecimal digits."
          },
          {
            "code": "bad_request",
            "message": "password must not contain the student's own name, username or email address",
            "status": 400,
            "when": "`password` contains the student’s first name, surname, username or email address, as stored."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `auth/signin` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Student not found!",
            "status": 404,
            "when": "No student of yours has this `studentId`. A student another account registered answers the same."
          },
          {
            "code": "conflict",
            "message": "This student has confirmed their email address, so the password is theirs to change. Send them a sign-in link with POST /v1/:organizationId/auth/signin.",
            "status": 409,
            "when": "The student has confirmed their email address, so the password is theirs to change."
          },
          {
            "code": "payload_too_large",
            "message": "request entity too large",
            "status": 413,
            "when": "The body is larger than 100 kB."
          },
          {
            "code": "unsupported_media_type",
            "message": "unsupported charset \"ISO-8859-1\"",
            "status": 415,
            "when": "The body declares a charset that is not a UTF one (send UTF-8), or a `Content-Encoding` other than gzip, deflate or br."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "auth/signin:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/application": {
      "get": {
        "description": "Lists the applications of your students in this organization, with `exam`, `payment` and the student (`user`) resolved into records. Inside `exam`, `session`, `category` and `language` stay ids. Applications for closed and past exams are included.\n\nA page at a time: `limit` is 20 by default and at most 100. No order is guaranteed, so to take a complete snapshot walk every page and remove duplicates by `_id`.\n\nYour students are the ones your account registered. A student appears here only while they are activated for this organization (their `activatedPlatformsThisSeason` lists its slug or `common`) and once they have signed in to it. An application of a student no longer activated here is left out of this list, though `listStudentApplications` and `getApplication` still return it.\n\nMatch a row to your records by `user.mainId`, the id you registered the student with; `user._id` is the organization’s own id for them.",
        "operationId": "listApplications",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "Page number. Defaults to 1.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "example": 1,
              "type": "number"
            }
          },
          {
            "description": "Items per page. Defaults to 20, max 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Applications fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "items": {
                            "$ref": "#/components/schemas/ApplicationResponse"
                          },
                          "type": "array"
                        },
                        "pagination": {
                          "$ref": "#/components/schemas/PaginationMeta"
                        }
                      },
                      "required": [
                        "data",
                        "pagination"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Applications fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `application/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "not_found",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`not_found`: `organizationId` is not the `_id` of an organization."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "List your students’ applications in this organization",
        "tags": [
          "Applications"
        ],
        "x-errors": [
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `application/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "application/read:$org:$ID"
        ]
      },
      "post": {
        "description": "Enters one of your students for one exam in this organization, and creates the payment record the application is paid through.\n\n**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.\n\n**Checks, in order.** The first that fails decides the answer.\n\n1. The student is yours (`404`). Your students are the ones your account registered; anyone else’s answers like an unknown id.\n2. The organization holds a record of the student (`409`).\n3. The exam exists in this organization (`404`).\n4. The exam is offered to this student: it is open, and it fits their grade (`400` for a student with no grade), their country and has a language (`409`, naming the rule).\n5. The student already has this exact exam: `200` \"Application already exists.\" with that application, and nothing is created.\n6. The student holds no other exam in the same category on the same sitting (`409`).\n\n**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`).\n\n**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.\n\n`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.",
        "operationId": "createApplication",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateApplicationRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Application already exists.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/ApplicationRecordResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Nothing new had to be created: `message` is \"Application already exists.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Application created successfully.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/ApplicationRecordResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Created: `message` is \"Application created successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "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\").",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "bad_request_2": {
                    "summary": "The exam is open, but the student has no grade. Set one with `updateOrgStudent` or `updateStudent`, then try again.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Student has no grade set, and every exam is restricted to a set of grades. Set one with PUT /:organizationId/student/:studentId before applying.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `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\").\n- `bad_request`: The exam is open, but the student has no grade. Set one with `updateOrgStudent` or `updateStudent`, then try again."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "No student of yours has this id: it matches nobody, or another account registered the student.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Student not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "not_found_3": {
                    "summary": "`examId` is not the `_id` of an exam in this organization.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Exam not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: No student of yours has this id: it matches nobody, or another account registered the student.\n- `not_found`: `examId` is not the `_id` of an exam in this organization."
          },
          "409": {
            "content": {
              "application/json": {
                "examples": {
                  "conflict": {
                    "summary": "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.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "conflict_2": {
                    "summary": "The exam is not open: applications to it are switched off, its sitting date has passed, or its category is inactive.",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "Exam is not open for application. Only exams returned by GET /:organizationId/exam can be applied to.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "conflict_3": {
                    "summary": "The exam has no language set, so it is offered to no student.",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "Exam has no language set, so it is not offered to students and cannot be applied to.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "conflict_4": {
                    "summary": "For any other reason, `listAvailableExams` leaves the exam out for this student.",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "Exam is not available to this student. Only exams returned by GET /:organizationId/exam/available/:studentId can be applied to.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `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.\n- `conflict`: The exam is not open: applications to it are switched off, its sitting date has passed, or its category is inactive.\n- `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.\"\n- `conflict`: The exam is restricted to countries the student is not in. The message names the countries it is offered in.\n- `conflict`: The exam has no language set, so it is offered to no student.\n- `conflict`: For any other reason, `listAvailableExams` leaves the exam out for this student.\n- `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."
          },
          "413": {
            "content": {
              "application/json": {
                "examples": {
                  "payload_too_large": {
                    "summary": "payload_too_large",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`payload_too_large`: The body is larger than 100 kB."
          },
          "415": {
            "content": {
              "application/json": {
                "examples": {
                  "unsupported_media_type": {
                    "summary": "unsupported_media_type",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Enter one of your students for an exam",
        "tags": [
          "Applications"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "status": 400,
            "when": "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\")."
          },
          {
            "code": "bad_request",
            "message": "Student has no grade set, and every exam is restricted to a set of grades. Set one with PUT /:organizationId/student/:studentId before applying.",
            "status": 400,
            "when": "The exam is open, but the student has no grade. Set one with `updateOrgStudent` or `updateStudent`, then try again."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `application/create` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "message": "Student not found!",
            "status": 404,
            "when": "No student of yours has this id: it matches nobody, or another account registered the student."
          },
          {
            "code": "not_found",
            "message": "Exam not found!",
            "status": 404,
            "when": "`examId` is not the `_id` of an exam in this organization."
          },
          {
            "code": "conflict",
            "status": 409,
            "when": "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."
          },
          {
            "code": "conflict",
            "message": "Exam is not open for application. Only exams returned by GET /:organizationId/exam can be applied to.",
            "status": 409,
            "when": "The exam is not open: applications to it are switched off, its sitting date has passed, or its category is inactive."
          },
          {
            "code": "conflict",
            "status": 409,
            "when": "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.\""
          },
          {
            "code": "conflict",
            "status": 409,
            "when": "The exam is restricted to countries the student is not in. The message names the countries it is offered in."
          },
          {
            "code": "conflict",
            "message": "Exam has no language set, so it is not offered to students and cannot be applied to.",
            "status": 409,
            "when": "The exam has no language set, so it is offered to no student."
          },
          {
            "code": "conflict",
            "message": "Exam is not available to this student. Only exams returned by GET /:organizationId/exam/available/:studentId can be applied to.",
            "status": 409,
            "when": "For any other reason, `listAvailableExams` leaves the exam out for this student."
          },
          {
            "code": "conflict",
            "status": 409,
            "when": "The student already holds another exam in the same category on the same sitting, and nobody can sit both. Move that application (`moveApplication`) instead."
          },
          {
            "code": "payload_too_large",
            "message": "request entity too large",
            "status": 413,
            "when": "The body is larger than 100 kB."
          },
          {
            "code": "unsupported_media_type",
            "message": "unsupported charset \"ISO-8859-1\"",
            "status": 415,
            "when": "The body declares a charset that is not a UTF one (send UTF-8), or a `Content-Encoding` other than gzip, deflate or br."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "application/create:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/application/exam-applications/{examId}": {
      "get": {
        "description": "Lists your students’ applications for one exam, with `payment` and the student (`user`) resolved into records. `exam` stays the id you asked about. It covers the same students as `listApplications`.\n\nA page at a time: `limit` is 20 by default and at most 100. No order is guaranteed, so to take a complete snapshot walk every page and remove duplicates by `_id`.\n\nThe exam itself is not looked up: an id that matches no exam answers an empty list rather than `404`, and past sittings work too.",
        "operationId": "listExamApplications",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The exam’s `_id` in this organization, from `listExams`, `listAvailableExams` or an application’s `exam`.",
            "in": "path",
            "name": "examId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Page number. Defaults to 1.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "example": 1,
              "type": "number"
            }
          },
          {
            "description": "Items per page. Defaults to 20, max 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Applications fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "items": {
                            "$ref": "#/components/schemas/ExamApplicationResponse"
                          },
                          "type": "array"
                        },
                        "pagination": {
                          "$ref": "#/components/schemas/PaginationMeta"
                        }
                      },
                      "required": [
                        "data",
                        "pagination"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Applications fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "bad_request",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Invalid value for 'exam': expected ObjectId.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`bad_request`: `examId` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `application/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "not_found",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`not_found`: `organizationId` is not the `_id` of an organization."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "List your students’ applications for one exam",
        "tags": [
          "Applications"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for 'exam': expected ObjectId.",
            "status": 400,
            "when": "`examId` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `application/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "application/read:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/application/student-applications/{studentId}": {
      "get": {
        "description": "Lists every application one of your students holds in this organization, with `exam`, `payment` and the student (`user`) resolved into records. Applications for closed and past exams are included.\n\nA page at a time: `limit` is 20 by default and at most 100. No order is guaranteed, so to take a complete snapshot walk every page and remove duplicates by `_id`.\n\nYour students are the ones your account registered: anyone else’s student answers `404`, exactly as an unknown id does. A student who has never signed in to this organization answers `409`, since they can hold no application there yet.",
        "operationId": "listStudentApplications",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The student’s `_id`: the id `registerStudent` returned, which an application carries as `user.mainId`.",
            "in": "path",
            "name": "studentId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Page number. Defaults to 1.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "example": 1,
              "type": "number"
            }
          },
          {
            "description": "Items per page. Defaults to 20, max 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Applications fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "items": {
                            "$ref": "#/components/schemas/ApplicationResponse"
                          },
                          "type": "array"
                        },
                        "pagination": {
                          "$ref": "#/components/schemas/PaginationMeta"
                        }
                      },
                      "required": [
                        "data",
                        "pagination"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Applications fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "bad_request",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`bad_request`: `studentId` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `application/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "No student of yours has this id: it matches nobody, or another account registered the student.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Student not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: No student of yours has this id: it matches nobody, or another account registered the student."
          },
          "409": {
            "content": {
              "application/json": {
                "examples": {
                  "conflict": {
                    "summary": "conflict",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "List one of your students’ applications in this organization",
        "tags": [
          "Applications"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for '_id': expected ObjectId.",
            "status": 400,
            "when": "`studentId` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `application/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "message": "Student not found!",
            "status": 404,
            "when": "No student of yours has this id: it matches nobody, or another account registered the student."
          },
          {
            "code": "conflict",
            "status": 409,
            "when": "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."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "application/read:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/application/{applicationId}": {
      "delete": {
        "description": "Deletes an application of one of your students. `data` is the application as it was, with `exam`, `payment` and `user` as ids.\n\nAn application whose payment is settled (`status` `paid`, with an `amount` above 0) cannot be deleted: deleting refunds nothing, and this API never moves money. A free exam’s payment (`paid`, `amount` 0) and a `pending` or `canceled` one do not block it.\n\nThe payment is the only state checked. Whether the student has started the exam, or its sitting has passed, is not: read `participated` and `examSubmitted` first if that matters to you.\n\nYour students are the ones your account registered: an application of anyone else’s student answers `404`, as an unknown id does. Deleting again answers `404` too, so a retried delete that gets `404` has already succeeded.",
        "operationId": "deleteApplication",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The application’s `_id`, from `createApplication` or one of the application lists.",
            "in": "path",
            "name": "applicationId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Application deleted successfully.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/ApplicationRecordResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Application deleted successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "bad_request",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`bad_request`: `applicationId` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `application/delete` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "No application of your students has this id: it matches nothing, or the application belongs to another account’s student. Both get the same answer.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Application not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: No application of your students has this id: it matches nothing, or the application belongs to another account’s student. Both get the same answer."
          },
          "409": {
            "content": {
              "application/json": {
                "examples": {
                  "conflict": {
                    "summary": "conflict",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "Application has been paid for and cannot be deleted. Cancelling a paid application requires a refund, which this API does not perform.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`conflict`: The payment is settled. Cancelling a paid application is a refund, which happens outside this API."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Withdraw one of your students from an exam",
        "tags": [
          "Applications"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for '_id': expected ObjectId.",
            "status": 400,
            "when": "`applicationId` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `application/delete` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "message": "Application not found!",
            "status": 404,
            "when": "No application of your students has this id: it matches nothing, or the application belongs to another account’s student. Both get the same answer."
          },
          {
            "code": "conflict",
            "message": "Application has been paid for and cannot be deleted. Cancelling a paid application requires a refund, which this API does not perform.",
            "status": 409,
            "when": "The payment is settled. Cancelling a paid application is a refund, which happens outside this API."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "application/delete:$org:$ID"
        ]
      },
      "get": {
        "description": "Reads one application of one of your students, with `exam`, `payment` and the student (`user`) resolved into records. Inside `exam`, `session`, `category` and `language` stay ids.\n\nYour students are the ones your account registered: an application of anyone else’s student answers `404`, as an unknown id does.",
        "operationId": "getApplication",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The application’s `_id`, from `createApplication` or one of the application lists.",
            "in": "path",
            "name": "applicationId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Application fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/ApplicationResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Application fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "bad_request",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`bad_request`: `applicationId` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `application/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "No application of your students has this id: it matches nothing, or the application belongs to another account’s student. Both get the same answer.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Application not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: No application of your students has this id: it matches nothing, or the application belongs to another account’s student. Both get the same answer."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Fetch one of your students’ applications",
        "tags": [
          "Applications"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for '_id': expected ObjectId.",
            "status": 400,
            "when": "`applicationId` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `application/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "message": "Application not found!",
            "status": 404,
            "when": "No application of your students has this id: it matches nothing, or the application belongs to another account’s student. Both get the same answer."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "application/read:$org:$ID"
        ]
      },
      "put": {
        "description": "Points an application of one of your students at a different exam. It keeps its `_id` and its payment record. Use it to change the language, the sitting or, where the category allows, the category. The student is not in the body: the application already says whose it is.\n\nThe new exam has to be one `listAvailableExams` offers the student, by the same rules and messages as `createApplication`. The application being moved does not count against itself, so changing the language on the same sitting is allowed; every other application the student holds still counts.\n\n**Checks, in order.** The first that fails decides the answer.\n\n1. The application belongs to one of your students (`404`, as for an unknown id).\n2. The exam has not been started or handed in (`409`).\n3. The new exam exists in this organization (`404`).\n4. It is the exam the application already has: `200` \"Application already uses that exam.\" and nothing changes.\n5. The new exam is offered to the student (`409`, or `400` for a student with no grade).\n6. The old exam’s category accepts the new exam’s category (`409`).\n7. The student holds no other exam in that category on that sitting (`409`).\n8. An application whose current exam is in the AI Challenge category cannot move once the student has used any of their image quota (`409`).\n9. A settled payment (`paid`, with an `amount` above 0) moves only to an exam of the same price (`409`, naming both figures).\n\n**Side effects.** A payment that is not settled is re-priced to the new exam: `paid` with `amount` 0 for a free exam, `pending` at its price otherwise, so a move from a free exam to a priced one leaves the student owing that price. A settled payment keeps its amount and status. A move onto a make-up sitting makes the application expire 6 hours later (`removeAfter`); a move off one clears that.\n\nSafe to retry: sending the exam it already has answers `200` and changes nothing. `data` is the application as stored, with `exam`, `payment` and `user` as ids.",
        "operationId": "moveApplication",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The application’s `_id`, from `createApplication` or one of the application lists.",
            "in": "path",
            "name": "applicationId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MoveApplicationRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "success": {
                    "summary": "Success",
                    "value": {
                      "data": {
                        "_id": "6650a1b2c3d4e5f6a7b8c9e5",
                        "createdAt": "2026-09-01T09:30:00.000Z",
                        "exam": "6650a1b2c3d4e5f6a7b8c9e1",
                        "examStart": "2026-11-14T10:04:12.000Z",
                        "examSubmitted": false,
                        "participated": false,
                        "partners": [
                          {
                            "accepted": true,
                            "user": "6650a1b2c3d4e5f6a7b8c9d6"
                          }
                        ],
                        "payment": "6650a1b2c3d4e5f6a7b8c9e6",
                        "removeAfter": "2026-09-02T14:05:00.000Z",
                        "simulationStart": "2026-11-07T10:00:00.000Z",
                        "simulationStarted": false,
                        "simulationSubmitted": false,
                        "submitDate": "2026-11-14T11:12:40.000Z",
                        "updatedAt": "2026-09-02T14:05:00.000Z",
                        "user": "6650a1b2c3d4e5f6a7b8c9e9",
                        "uuid": "a3f-09c-7e1"
                      },
                      "message": "Application updated successfully.",
                      "success": true
                    }
                  },
                  "unchanged": {
                    "summary": "The application already has that exam; nothing changed.",
                    "value": {
                      "data": {
                        "_id": "6650a1b2c3d4e5f6a7b8c9e5",
                        "createdAt": "2026-09-01T09:30:00.000Z",
                        "exam": "6650a1b2c3d4e5f6a7b8c9e1",
                        "examStart": "2026-11-14T10:04:12.000Z",
                        "examSubmitted": false,
                        "participated": false,
                        "partners": [
                          {
                            "accepted": true,
                            "user": "6650a1b2c3d4e5f6a7b8c9d6"
                          }
                        ],
                        "payment": "6650a1b2c3d4e5f6a7b8c9e6",
                        "removeAfter": "2026-09-02T14:05:00.000Z",
                        "simulationStart": "2026-11-07T10:00:00.000Z",
                        "simulationStarted": false,
                        "simulationSubmitted": false,
                        "submitDate": "2026-11-14T11:12:40.000Z",
                        "updatedAt": "2026-09-02T14:05:00.000Z",
                        "user": "6650a1b2c3d4e5f6a7b8c9e9",
                        "uuid": "a3f-09c-7e1"
                      },
                      "message": "Application already uses that exam.",
                      "success": true
                    }
                  }
                },
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Application updated successfully.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/ApplicationRecordResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Application updated successfully.\".\n\nOr `message` is \"Application already uses that exam.\": The application already has that exam; nothing changed.",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "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\").",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "bad_request_2": {
                    "summary": "`applicationId` is not 24 hexadecimal digits.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "bad_request_3": {
                    "summary": "The exam is open, but the student has no grade. Set one with `updateOrgStudent` or `updateStudent`, then try again.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Student has no grade set, and every exam is restricted to a set of grades. Set one with PUT /:organizationId/student/:studentId before applying.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `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\").\n- `bad_request`: `applicationId` is not 24 hexadecimal digits.\n- `bad_request`: The exam is open, but the student has no grade. Set one with `updateOrgStudent` or `updateStudent`, then try again."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `application/update` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "No application of your students has this id: it matches nothing, or the application belongs to another account’s student. Both get the same answer.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Application not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "not_found_3": {
                    "summary": "`examId` is not the `_id` of an exam in this organization.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Exam not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: No application of your students has this id: it matches nothing, or the application belongs to another account’s student. Both get the same answer.\n- `not_found`: `examId` is not the `_id` of an exam in this organization."
          },
          "409": {
            "content": {
              "application/json": {
                "examples": {
                  "conflict": {
                    "summary": "The student has started or handed in the exam. Their answers belong to its questions, so the application stays where it is.",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "This exam has already been started and can no longer be changed.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "conflict_2": {
                    "summary": "The exam is not open: applications to it are switched off, its sitting date has passed, or its category is inactive.",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "Exam is not open for application. Only exams returned by GET /:organizationId/exam can be applied to.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "conflict_3": {
                    "summary": "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.\"",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "conflict_4": {
                    "summary": "The exam has no language set, so it is offered to no student.",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "Exam has no language set, so it is not offered to students and cannot be applied to.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "conflict_5": {
                    "summary": "For any other reason, `listAvailableExams` leaves the exam out for this student.",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "Exam is not available to this student. Only exams returned by GET /:organizationId/exam/available/:studentId can be applied to.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "conflict_6": {
                    "summary": "The current exam is in the AI Challenge category and the student has already used some of their image quota.",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "Training has already started for this AI Challenge application, so it can no longer be moved.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `conflict`: The student has started or handed in the exam. Their answers belong to its questions, so the application stays where it is.\n- `conflict`: The exam is not open: applications to it are switched off, its sitting date has passed, or its category is inactive.\n- `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.\"\n- `conflict`: The exam is restricted to countries the student is not in. The message names the countries it is offered in.\n- `conflict`: The exam has no language set, so it is offered to no student.\n- `conflict`: For any other reason, `listAvailableExams` leaves the exam out for this student.\n- `conflict`: The category of the application’s current exam does not accept the new exam’s category as a replacement. The message names the current category.\n- `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.\n- `conflict`: The current exam is in the AI Challenge category and the student has already used some of their image quota.\n- `conflict`: The payment is settled and the new exam costs something different. This API neither charges a difference nor refunds; the message names both figures."
          },
          "413": {
            "content": {
              "application/json": {
                "examples": {
                  "payload_too_large": {
                    "summary": "payload_too_large",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`payload_too_large`: The body is larger than 100 kB."
          },
          "415": {
            "content": {
              "application/json": {
                "examples": {
                  "unsupported_media_type": {
                    "summary": "unsupported_media_type",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Move one of your students’ applications to another exam",
        "tags": [
          "Applications"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "status": 400,
            "when": "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\")."
          },
          {
            "code": "bad_request",
            "message": "Invalid value for '_id': expected ObjectId.",
            "status": 400,
            "when": "`applicationId` is not 24 hexadecimal digits."
          },
          {
            "code": "bad_request",
            "message": "Student has no grade set, and every exam is restricted to a set of grades. Set one with PUT /:organizationId/student/:studentId before applying.",
            "status": 400,
            "when": "The exam is open, but the student has no grade. Set one with `updateOrgStudent` or `updateStudent`, then try again."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `application/update` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "message": "Application not found!",
            "status": 404,
            "when": "No application of your students has this id: it matches nothing, or the application belongs to another account’s student. Both get the same answer."
          },
          {
            "code": "not_found",
            "message": "Exam not found!",
            "status": 404,
            "when": "`examId` is not the `_id` of an exam in this organization."
          },
          {
            "code": "conflict",
            "message": "This exam has already been started and can no longer be changed.",
            "status": 409,
            "when": "The student has started or handed in the exam. Their answers belong to its questions, so the application stays where it is."
          },
          {
            "code": "conflict",
            "message": "Exam is not open for application. Only exams returned by GET /:organizationId/exam can be applied to.",
            "status": 409,
            "when": "The exam is not open: applications to it are switched off, its sitting date has passed, or its category is inactive."
          },
          {
            "code": "conflict",
            "status": 409,
            "when": "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.\""
          },
          {
            "code": "conflict",
            "status": 409,
            "when": "The exam is restricted to countries the student is not in. The message names the countries it is offered in."
          },
          {
            "code": "conflict",
            "message": "Exam has no language set, so it is not offered to students and cannot be applied to.",
            "status": 409,
            "when": "The exam has no language set, so it is offered to no student."
          },
          {
            "code": "conflict",
            "message": "Exam is not available to this student. Only exams returned by GET /:organizationId/exam/available/:studentId can be applied to.",
            "status": 409,
            "when": "For any other reason, `listAvailableExams` leaves the exam out for this student."
          },
          {
            "code": "conflict",
            "status": 409,
            "when": "The category of the application’s current exam does not accept the new exam’s category as a replacement. The message names the current category."
          },
          {
            "code": "conflict",
            "status": 409,
            "when": "The student already holds another exam in the same category on the same sitting, and nobody can sit both. Move that application (`moveApplication`) instead."
          },
          {
            "code": "conflict",
            "message": "Training has already started for this AI Challenge application, so it can no longer be moved.",
            "status": 409,
            "when": "The current exam is in the AI Challenge category and the student has already used some of their image quota."
          },
          {
            "code": "conflict",
            "status": 409,
            "when": "The payment is settled and the new exam costs something different. This API neither charges a difference nor refunds; the message names both figures."
          },
          {
            "code": "payload_too_large",
            "message": "request entity too large",
            "status": 413,
            "when": "The body is larger than 100 kB."
          },
          {
            "code": "unsupported_media_type",
            "message": "unsupported charset \"ISO-8859-1\"",
            "status": 415,
            "when": "The body declares a charset that is not a UTF one (send UTF-8), or a `Content-Encoding` other than gzip, deflate or br."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "application/update:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/auth/signin": {
      "post": {
        "description": "Returns a URL that signs the student in to this organization’s student panel. **It works once, and for 120 seconds** (`expiresIn`) from when it is issued, whether or not anyone opened it. Whoever opens it first is signed in as the student, and a second visit is refused, so:\n\n- redirect the student’s browser to it straight away;\n- never log it, email it or show it anywhere else;\n- ask for a new link for every sign-in.\n\nEach call issues a new link and leaves the earlier ones as they were until they expire. Nothing is issued to your account: no token, cookie or session, only the URL.\n\n**The student needs access to this organization first:** their `activatedPlatformsThisSeason` must hold this organization’s `slug` or `common`. `registerStudent` gives `common` unless you send a list; otherwise grant access with `updateOrgStudent`. Without it the answer is 403 with a message saying so, and nothing is changed.\n\nThe student does not need a confirmed email address: a student you have just registered can be sent a link straight away.\n\n`redirect` chooses where on the panel the student lands. Leave it out to land on the organization’s `defaultRedirect`, as `getOrganization` shows it.\n\n**Make this the first call for a student new to this organization.** The organization creates its own record of a student the first time they open a link to it, and `linkStudentSupervisor` and the application, certificate and report operations on this organization need that record.\n\nOnly students your account registered can be signed in; another account’s student answers 404, like an unknown id.",
        "operationId": "createSigninLink",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSigninLinkRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Sign-in link generated successfully.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/SigninLinkResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Sign-in link generated successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "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\").",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "bad_request_2": {
                    "summary": "`redirect` is not a path on the panel: it needs one leading `/`, and no whitespace or backslash anywhere.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "redirect must be a site-relative path starting with \"/\" (e.g. \"/dashboard\")",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `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\").\n- `bad_request`: `redirect` is not a path on the panel: it needs one leading `/`, and no whitespace or backslash anywhere."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "The token is valid, but no role on your account allows `auth/signin` on the organization in the path, or a role denies it.",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "forbidden_2": {
                    "summary": "The student has no access to this organization: their `activatedPlatformsThisSeason` holds neither its `slug` nor `common`. Grant it with `updateOrgStudent`, then ask again.",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Student is not activated for organization stem.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `forbidden`: The token is valid, but no role on your account allows `auth/signin` on the organization in the path, or a role denies it.\n- `forbidden`: The student has no access to this organization: their `activatedPlatformsThisSeason` holds neither its `slug` nor `common`. Grant it with `updateOrgStudent`, then ask again."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "No student of yours has this `studentId`. A student registered by another account answers the same.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Student not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: No student of yours has this `studentId`. A student registered by another account answers the same."
          },
          "413": {
            "content": {
              "application/json": {
                "examples": {
                  "payload_too_large": {
                    "summary": "payload_too_large",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`payload_too_large`: The body is larger than 100 kB."
          },
          "415": {
            "content": {
              "application/json": {
                "examples": {
                  "unsupported_media_type": {
                    "summary": "unsupported_media_type",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "Something failed on our side. Retry later, and quote `request_id` if it goes on.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "internal_error_2": {
                    "summary": "The link could not be issued. Send the request again.",
                    "value": {
                      "error": {
                        "code": "internal_error",
                        "documentation_url": "https://hub.main-team.org/api/errors#internal_error",
                        "message": "Could not issue a sign-in token, please retry.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on.\n- `internal_error`: The link could not be issued. Send the request again."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Create a single-use sign-in link for one of your students",
        "tags": [
          "Sign-in links"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "status": 400,
            "when": "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\")."
          },
          {
            "code": "bad_request",
            "message": "redirect must be a site-relative path starting with \"/\" (e.g. \"/dashboard\")",
            "status": 400,
            "when": "`redirect` is not a path on the panel: it needs one leading `/`, and no whitespace or backslash anywhere."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `auth/signin` on the organization in the path, or a role denies it."
          },
          {
            "code": "forbidden",
            "message": "Student is not activated for organization stem.",
            "status": 403,
            "when": "The student has no access to this organization: their `activatedPlatformsThisSeason` holds neither its `slug` nor `common`. Grant it with `updateOrgStudent`, then ask again."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "message": "Student not found!",
            "status": 404,
            "when": "No student of yours has this `studentId`. A student registered by another account answers the same."
          },
          {
            "code": "payload_too_large",
            "message": "request entity too large",
            "status": 413,
            "when": "The body is larger than 100 kB."
          },
          {
            "code": "unsupported_media_type",
            "message": "unsupported charset \"ISO-8859-1\"",
            "status": 415,
            "when": "The body declares a charset that is not a UTF one (send UTF-8), or a `Content-Encoding` other than gzip, deflate or br."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          },
          {
            "code": "internal_error",
            "message": "Could not issue a sign-in token, please retry.",
            "status": 500,
            "when": "The link could not be issued. Send the request again."
          }
        ],
        "x-permission": [
          "auth/signin:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/certificate/download/{certificateId}": {
      "get": {
        "description": "Sends the file of a released certificate of one of your students: the bytes themselves, not JSON. `Content-Disposition` carries its name.\n\nEvery refusal of the certificate itself is the same `404` \"Not found!\": no certificate has this id or `shortId`, it is not released, it belongs to another account’s student, or it has no file. So a download never shows whether a certificate exists in someone else’s hands.\n\nErrors always arrive as JSON before the first byte. A transfer that ends early, or with fewer bytes than `Content-Length`, has failed: discard what you received.",
        "operationId": "downloadCertificate",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The certificate’s `_id` (24 hexadecimal digits) or its 10-character `shortId`, both from `listStudentCertificates`. A `shortId` is matched exactly, case included.",
            "in": "path",
            "name": "certificateId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/octet-stream": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              },
              "application/pdf": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "The certificate file, streamed. Content-Type is whatever the object was stored with. Content-Disposition carries an ASCII `filename` and the real name as RFC 5987 `filename*=UTF-8''…`; prefer the latter.",
            "headers": {
              "Content-Disposition": {
                "description": "attachment; filename=\"<ascii>\"; filename*=UTF-8''<percent-encoded>",
                "schema": {
                  "type": "string"
                }
              },
              "Content-Length": {
                "description": "Present when storage reports the size.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `certificate/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "No certificate has this id or shortId, it is not released, it belongs to another account's student, it has no file attached, or its file is missing from storage. Every one of these answers the same, so the status says nothing about another account's ids.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: No certificate has this id or shortId, it is not released, it belongs to another account's student, it has no file attached, or its file is missing from storage. Every one of these answers the same, so the status says nothing about another account's ids."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on.\n- `internal_error`: File storage is not configured or could not be reached."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Download a certificate file",
        "tags": [
          "Documents"
        ],
        "x-errors": [
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `certificate/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "message": "Not found!",
            "status": 404,
            "when": "No certificate has this id or shortId, it is not released, it belongs to another account's student, it has no file attached, or its file is missing from storage. Every one of these answers the same, so the status says nothing about another account's ids."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "File storage is not configured or could not be reached."
          }
        ],
        "x-permission": [
          "certificate/read:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/certificate/{userId}": {
      "get": {
        "description": "Lists the certificates this organization has released for one of your students, across every season. A certificate the organization has not released yet is not listed. One is listed whether it was issued for one of the student’s applications or to the student directly. Download the file with `downloadCertificate`, by `_id` or `shortId`.\n\nA page at a time: `limit` is 20 by default and at most 100. No order is guaranteed.\n\nYour students are the ones your account registered: anyone else’s student answers `404`, exactly as an unknown id does. A student who has never signed in to this organization answers `409`; they cannot have sat its exams, so there is nothing to collect.",
        "operationId": "listStudentCertificates",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The student’s `_id`: the id `registerStudent` returned.",
            "in": "path",
            "name": "userId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Page number. Defaults to 1.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "example": 1,
              "type": "number"
            }
          },
          {
            "description": "Items per page. Defaults to 20, max 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Certificates fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "items": {
                            "$ref": "#/components/schemas/CertificateResponse"
                          },
                          "type": "array"
                        },
                        "pagination": {
                          "$ref": "#/components/schemas/PaginationMeta"
                        }
                      },
                      "required": [
                        "data",
                        "pagination"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Certificates fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "bad_request",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`bad_request`: `userId` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `certificate/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "No student of yours has this id: it matches nobody, or another account registered the student.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Student not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: No student of yours has this id: it matches nobody, or another account registered the student."
          },
          "409": {
            "content": {
              "application/json": {
                "examples": {
                  "conflict": {
                    "summary": "conflict",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`conflict`: The student has never signed in to this organization, so it holds no record of them."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "List one of your students’ released certificates",
        "tags": [
          "Documents"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for '_id': expected ObjectId.",
            "status": 400,
            "when": "`userId` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `certificate/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "message": "Student not found!",
            "status": 404,
            "when": "No student of yours has this id: it matches nobody, or another account registered the student."
          },
          {
            "code": "conflict",
            "status": 409,
            "when": "The student has never signed in to this organization, so it holds no record of them."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "certificate/read:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/exam": {
      "get": {
        "description": "Lists the organization’s exams that are open for applications, the soonest sitting first.\n\nAn exam is **open for applications** while all three hold: it is not closed to applications (`preventApplication` is not `true`), its sitting (`session.date`) is still to come, and its category is active. It stops being open at the moment its sitting starts. An exam that is not open is not listed, and `getExam` and `createApplication` refuse it too.\n\n`session`, `category`, `language`, `grades` and `countries` come back as records rather than ids. An exam with no language is listed, but no student can apply to it.\n\nThis list is the same for every student: it does not look at grades, countries or what a student already applied for. To see what one of your students can actually apply to, call `listAvailableExams`.\n\nA page holds 20 exams unless you ask for up to 100 with `limit`. A read only: nothing changes, and it is safe to repeat.",
        "operationId": "listExams",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "Page number. Defaults to 1.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "example": 1,
              "type": "number"
            }
          },
          {
            "description": "Items per page. Defaults to 20, max 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Exams fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "items": {
                            "$ref": "#/components/schemas/ExamResponse"
                          },
                          "type": "array"
                        },
                        "pagination": {
                          "$ref": "#/components/schemas/PaginationMeta"
                        }
                      },
                      "required": [
                        "data",
                        "pagination"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Exams fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `exam/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "not_found",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`not_found`: `organizationId` is not the `_id` of an organization."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "List the exams open for applications",
        "tags": [
          "Exams"
        ],
        "x-errors": [
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `exam/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "exam/read:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/exam-category": {
      "get": {
        "description": "Lists every exam category the organization has, active or not: `isActive` says which. No exam in an inactive category is open for applications.\n\nCategories belong to the organization, not to your account, so every account allowed to read them sees the same list. The order is not specified; sort by `order` yourself if you show them.\n\nA page holds 20 categories unless you ask for up to 100 with `limit`. A read only: nothing changes, and it is safe to repeat.",
        "operationId": "listExamCategories",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "Page number. Defaults to 1.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "example": 1,
              "type": "number"
            }
          },
          {
            "description": "Items per page. Defaults to 20, max 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Categories fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "items": {
                            "$ref": "#/components/schemas/ExamCategoryResponse"
                          },
                          "type": "array"
                        },
                        "pagination": {
                          "$ref": "#/components/schemas/PaginationMeta"
                        }
                      },
                      "required": [
                        "data",
                        "pagination"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Categories fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `exam-category/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "not_found",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`not_found`: `organizationId` is not the `_id` of an organization."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "List an organization’s exam categories",
        "tags": [
          "Exams"
        ],
        "x-errors": [
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `exam-category/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "exam-category/read:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/exam-category/{categoryId}": {
      "get": {
        "description": "Returns one of the organization’s exam categories, active or not.\n\nAn id that names no category in this organization answers `404 not_found`, and an id that is not 24 hexadecimal digits is refused with `400`.\n\nAn exam’s `category` on `listExams` and `getExam` is already this record, so you only need this call for a category id you hold on its own. A read only: nothing changes, and it is safe to repeat.",
        "operationId": "getExamCategory",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The category’s `_id`, as `listExamCategories` lists it or as an exam’s `category._id` carries it.",
            "in": "path",
            "name": "categoryId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Category fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/ExamCategoryResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Category fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "bad_request",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`bad_request`: `categoryId` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `exam-category/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "`categoryId` names no category in this organization.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Category not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: `categoryId` names no category in this organization."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Fetch one exam category",
        "tags": [
          "Exams"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for '_id': expected ObjectId.",
            "status": 400,
            "when": "`categoryId` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `exam-category/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "message": "Category not found!",
            "status": 404,
            "when": "`categoryId` names no category in this organization."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "exam-category/read:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/exam/available/{studentId}": {
      "get": {
        "description": "The exams one of your students can apply to in this organization, as a tree: categories, each holding its sittings, each holding the languages it can be sat in. Every leaf carries `matchedExam`, and `matchedExam._id` is the `examId` that `createApplication` takes.\n\n**Only your students.** A student is yours when your account registered it. Another account’s student answers `404`, exactly like an id that is nobody’s.\n\n**What is offered.** An exam is in the tree when all of these hold:\n\n- it is open for applications, as `listExams` lists it. An exam is **open for applications** while all three hold: it is not closed to applications (`preventApplication` is not `true`), its sitting (`session.date`) is still to come, and its category is active. It stops being open at the moment its sitting starts.\n- the student’s grade is one of the exam’s `grades`;\n- the exam is open to the student’s country, or to every country. When the student has no country, or the organization does not list it, only exams open to every country are offered;\n- the exam has a language;\n- the student holds no application in this organization for the same category on the same sitting.\n\n`createApplication` refuses an exam by the same rules, so what is listed here is what it accepts, as long as nothing changes in between. It also needs the student to have signed in to the organization once (`createSigninLink`); this list does not.\n\n**Order and size.** Categories in the organization’s `order`, sittings soonest first, languages in `order`. Not paginated: `data` is the whole tree, and `[]` when nothing is offered. No branch is ever empty.\n\n**Call first:** `registerStudent`, or `listStudents`, for the `studentId`. The student needs a grade: one without is refused with `400` until you set it with `updateStudent` or `updateOrgStudent`. A read only: nothing changes, and it is safe to repeat.",
        "operationId": "listAvailableExams",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The `_id` of one of your students, as `registerStudent` answered and `listStudents` lists it.",
            "in": "path",
            "name": "studentId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Available exams fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "items": {
                            "$ref": "#/components/schemas/AvailableExamCategoryResponse"
                          },
                          "type": "array"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Available exams fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "`studentId` is not 24 hexadecimal digits.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "bad_request_2": {
                    "summary": "The student has no grade. Every exam is restricted to a set of grades, so no exam could be offered; set one first.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Student has no grade set, and every exam is restricted to a set of grades. Set one with PUT /:organizationId/student/:studentId before listing exams.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `bad_request`: `studentId` is not 24 hexadecimal digits.\n- `bad_request`: The student has no grade. Every exam is restricted to a set of grades, so no exam could be offered; set one first."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `exam/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "No student of yours has this id: it is nobody’s, or another account’s. Both answer the same.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Student not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: No student of yours has this id: it is nobody’s, or another account’s. Both answer the same."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "List the exams one of your students can apply to",
        "tags": [
          "Exams"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for '_id': expected ObjectId.",
            "status": 400,
            "when": "`studentId` is not 24 hexadecimal digits."
          },
          {
            "code": "bad_request",
            "message": "Student has no grade set, and every exam is restricted to a set of grades. Set one with PUT /:organizationId/student/:studentId before listing exams.",
            "status": 400,
            "when": "The student has no grade. Every exam is restricted to a set of grades, so no exam could be offered; set one first."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `exam/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "message": "Student not found!",
            "status": 404,
            "when": "No student of yours has this id: it is nobody’s, or another account’s. Both answer the same."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "exam/read:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/exam/{examId}": {
      "get": {
        "description": "Returns one exam, in the shape `listExams` lists it, provided it is open for applications. An exam is **open for applications** while all three hold: it is not closed to applications (`preventApplication` is not `true`), its sitting (`session.date`) is still to come, and its category is active. It stops being open at the moment its sitting starts.\n\nAn exam `listExams` would not list cannot be read here either: an exam that exists but is not open answers `404`, with a message that tells it apart from an id that names no exam. Like the list, this says nothing about whether a given student may apply; `listAvailableExams` does.\n\nA read only: nothing changes, and it is safe to repeat.",
        "operationId": "getExam",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The exam’s `_id`, as `listExams` lists it or as `matchedExam._id` on a leaf of `listAvailableExams` carries it.",
            "in": "path",
            "name": "examId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Exam fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/ExamResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Exam fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "bad_request",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`bad_request`: `examId` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `exam/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "No exam in the organization has this id.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Exam not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "not_found_3": {
                    "summary": "The exam exists but is not open for applications: it is closed to applications, its sitting has started or passed, or its category is inactive.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Exam is not open for application, so it is not available through this API.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: No exam in the organization has this id.\n- `not_found`: The exam exists but is not open for applications: it is closed to applications, its sitting has started or passed, or its category is inactive."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Fetch one exam that is open for applications",
        "tags": [
          "Exams"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for '_id': expected ObjectId.",
            "status": 400,
            "when": "`examId` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `exam/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "message": "Exam not found!",
            "status": 404,
            "when": "No exam in the organization has this id."
          },
          {
            "code": "not_found",
            "message": "Exam is not open for application, so it is not available through this API.",
            "status": 404,
            "when": "The exam exists but is not open for applications: it is closed to applications, its sitting has started or passed, or its category is inactive."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "exam/read:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/group-challenge": {
      "get": {
        "description": "Lists the group challenges of this organization that are running or have closed, the one whose dates start latest first. A group challenge is a project students do in small groups: their teacher forms a group from their own students in the panel, and the group works through the challenge’s steps between `windowStart` and `windowEnd`, uploading its work for each step in the panel or the app.\n\n`isOpen` says whether it takes work right now. Challenges the organizers have not published are not listed.\n\nA page at a time: `limit` is 20 by default and at most 100.",
        "operationId": "listGroupChallenges",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "Page number. Defaults to 1.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "example": 1,
              "type": "number"
            }
          },
          {
            "description": "Items per page. Defaults to 20, max 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Group challenges fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "items": {
                            "$ref": "#/components/schemas/GroupChallengeResponse"
                          },
                          "type": "array"
                        },
                        "pagination": {
                          "$ref": "#/components/schemas/PaginationMeta"
                        }
                      },
                      "required": [
                        "data",
                        "pagination"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Group challenges fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `group-challenge/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "The requested resource could not be found.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "List the group challenges an organization runs",
        "tags": [
          "Group challenges"
        ],
        "x-errors": [
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `group-challenge/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "status": 404,
            "when": "Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "group-challenge/read:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/group-challenge/{challengeId}": {
      "get": {
        "description": "Reads one group challenge of this organization: its dates, how many students a group takes, the grade groups it is open to and the steps a group works through, in order.",
        "operationId": "getGroupChallenge",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The group challenge’s `_id`, from `listGroupChallenges`.",
            "in": "path",
            "name": "challengeId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Group challenge fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/GroupChallengeResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Group challenge fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "bad_request",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Invalid value for 'challengeId': expected ObjectId.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`bad_request`: `challengeId` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `group-challenge/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "The requested resource could not be found.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "not_found_3": {
                    "summary": "No group challenge of this organization has this id, or it is not published: a draft, archived or deleted challenge answers the same.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Group challenge not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read.\n- `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."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Fetch one group challenge",
        "tags": [
          "Group challenges"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for 'challengeId': expected ObjectId.",
            "status": 400,
            "when": "`challengeId` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `group-challenge/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "status": 404,
            "when": "Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read."
          },
          {
            "code": "not_found",
            "message": "Group challenge not found!",
            "status": 404,
            "when": "No group challenge of this organization has this id, or it is not published: a draft, archived or deleted challenge answers the same."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "group-challenge/read:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/group-challenge/{challengeId}/group": {
      "get": {
        "description": "Lists the groups of the challenge that at least one of your students is an active member of, oldest first: their state, how far through the steps they are, and your students in them. Deleted groups are not listed.\n\nOnly your own students are named. Every other person — the other members, the teacher, the organizers — is shown by role alone, with `studentId` and `name` set to `null`, and a file’s name only when one of your students uploaded it. The members who are not yours are counted in `memberCount` and not listed.\n\nA page at a time: `limit` is 20 by default and at most 100.",
        "operationId": "listGroupChallengeGroups",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The group challenge’s `_id`, from `listGroupChallenges`.",
            "in": "path",
            "name": "challengeId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Page number. Defaults to 1.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "example": 1,
              "type": "number"
            }
          },
          {
            "description": "Items per page. Defaults to 20, max 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Groups fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "items": {
                            "$ref": "#/components/schemas/GroupChallengeGroupSummaryResponse"
                          },
                          "type": "array"
                        },
                        "pagination": {
                          "$ref": "#/components/schemas/PaginationMeta"
                        }
                      },
                      "required": [
                        "data",
                        "pagination"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Groups fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "bad_request",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Invalid value for 'challengeId': expected ObjectId.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`bad_request`: `challengeId` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `group-challenge/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "The requested resource could not be found.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "not_found_3": {
                    "summary": "No group challenge of this organization has this id, or it is not published: a draft, archived or deleted challenge answers the same.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Group challenge not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read.\n- `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."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "List the groups your students are in for a group challenge",
        "tags": [
          "Group challenges"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for 'challengeId': expected ObjectId.",
            "status": 400,
            "when": "`challengeId` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `group-challenge/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "status": 404,
            "when": "Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read."
          },
          {
            "code": "not_found",
            "message": "Group challenge not found!",
            "status": 404,
            "when": "No group challenge of this organization has this id, or it is not published: a draft, archived or deleted challenge answers the same."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "group-challenge/read:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/group-challenge/{challengeId}/group/{groupId}": {
      "get": {
        "description": "Reads one group that one of your students is an active member of, with each step, its state and its files. Steps appear once the teacher has confirmed the group (`finalized`).\n\n`canSubmit` on a step and `canFinalSubmit` on the group are the answers `submitGroupChallengeStep` and `submitGroupChallengeWork` would give right now, so read the group before you submit.\n\nOnly your own students are named. Every other person — the other members, the teacher, the organizers — is shown by role alone, with `studentId` and `name` set to `null`, and a file’s name only when one of your students uploaded it.",
        "operationId": "getGroupChallengeGroup",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The group challenge’s `_id`, from `listGroupChallenges`.",
            "in": "path",
            "name": "challengeId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The group’s `_id`, from `listGroupChallengeGroups` or a student’s `group`.",
            "in": "path",
            "name": "groupId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Group fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/GroupChallengeGroupResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Group fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "`challengeId` is not 24 hexadecimal digits.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Invalid value for 'challengeId': expected ObjectId.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "bad_request_2": {
                    "summary": "`groupId` is not 24 hexadecimal digits.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Invalid value for 'groupId': expected ObjectId.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `bad_request`: `challengeId` is not 24 hexadecimal digits.\n- `bad_request`: `groupId` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `group-challenge/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "The requested resource could not be found.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "not_found_3": {
                    "summary": "No group challenge of this organization has this id, or it is not published: a draft, archived or deleted challenge answers the same.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Group challenge not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "not_found_4": {
                    "summary": "No group of this challenge has this id that one of your students is an active member of: a missing group, a deleted one and another account’s get the same answer.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Group not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read.\n- `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.\n- `not_found`: No group of this challenge has this id that one of your students is an active member of: a missing group, a deleted one and another account’s get the same answer."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Fetch one group, with its steps and files",
        "tags": [
          "Group challenges"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for 'challengeId': expected ObjectId.",
            "status": 400,
            "when": "`challengeId` is not 24 hexadecimal digits."
          },
          {
            "code": "bad_request",
            "message": "Invalid value for 'groupId': expected ObjectId.",
            "status": 400,
            "when": "`groupId` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `group-challenge/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "status": 404,
            "when": "Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read."
          },
          {
            "code": "not_found",
            "message": "Group challenge not found!",
            "status": 404,
            "when": "No group challenge of this organization has this id, or it is not published: a draft, archived or deleted challenge answers the same."
          },
          {
            "code": "not_found",
            "message": "Group not found!",
            "status": 404,
            "when": "No group of this challenge has this id that one of your students is an active member of: a missing group, a deleted one and another account’s get the same answer."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "group-challenge/read:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/group-challenge/{challengeId}/group/{groupId}/activity": {
      "get": {
        "description": "Lists what has happened in one group that one of your students is an active member of, newest first: the group created, confirmed, members added, files uploaded, steps submitted, the work sent. It is the log every member of the group can read; notes and details the organizers keep are not included.\n\nOnly your own students are named. Every other person — the other members, the teacher, the organizers — is shown by role alone, with `studentId` and `name` set to `null`, and a file’s name only when one of your students uploaded it. What your account did for a student is `actor.role: partner` with `yours: true`, and the student in `onBehalfOf`.\n\nA page at a time: `limit` is 20 by default and at most 100.",
        "operationId": "listGroupChallengeActivity",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The group challenge’s `_id`, from `listGroupChallenges`.",
            "in": "path",
            "name": "challengeId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The group’s `_id`, from `listGroupChallengeGroups` or a student’s `group`.",
            "in": "path",
            "name": "groupId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Page number. Defaults to 1.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "example": 1,
              "type": "number"
            }
          },
          {
            "description": "Items per page. Defaults to 20, max 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Activity fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "items": {
                            "$ref": "#/components/schemas/GroupChallengeActivityResponse"
                          },
                          "type": "array"
                        },
                        "pagination": {
                          "$ref": "#/components/schemas/PaginationMeta"
                        }
                      },
                      "required": [
                        "data",
                        "pagination"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Activity fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "`challengeId` is not 24 hexadecimal digits.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Invalid value for 'challengeId': expected ObjectId.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "bad_request_2": {
                    "summary": "`groupId` is not 24 hexadecimal digits.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Invalid value for 'groupId': expected ObjectId.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `bad_request`: `challengeId` is not 24 hexadecimal digits.\n- `bad_request`: `groupId` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `group-challenge/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "The requested resource could not be found.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "not_found_3": {
                    "summary": "No group challenge of this organization has this id, or it is not published: a draft, archived or deleted challenge answers the same.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Group challenge not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "not_found_4": {
                    "summary": "No group of this challenge has this id that one of your students is an active member of: a missing group, a deleted one and another account’s get the same answer.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Group not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read.\n- `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.\n- `not_found`: No group of this challenge has this id that one of your students is an active member of: a missing group, a deleted one and another account’s get the same answer."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "List what has happened in one group",
        "tags": [
          "Group challenges"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for 'challengeId': expected ObjectId.",
            "status": 400,
            "when": "`challengeId` is not 24 hexadecimal digits."
          },
          {
            "code": "bad_request",
            "message": "Invalid value for 'groupId': expected ObjectId.",
            "status": 400,
            "when": "`groupId` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `group-challenge/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "status": 404,
            "when": "Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read."
          },
          {
            "code": "not_found",
            "message": "Group challenge not found!",
            "status": 404,
            "when": "No group challenge of this organization has this id, or it is not published: a draft, archived or deleted challenge answers the same."
          },
          {
            "code": "not_found",
            "message": "Group not found!",
            "status": 404,
            "when": "No group of this challenge has this id that one of your students is an active member of: a missing group, a deleted one and another account’s get the same answer."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "group-challenge/read:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/group-challenge/{challengeId}/group/{groupId}/final-submit": {
      "post": {
        "description": "Sends a group’s finished work, once every step is submitted, for one of your students who is a member of the group, exactly as the student would in the panel. The group becomes `completed`. Read the group first: `canFinalSubmit` says whether this would be accepted now.\n\nThe body names the student you act for: `{ \"studentId\": \"<studentId>\" }`. The group’s history records the submit as your account (`partner`) acting for them.\n\n**Checks, in order.** The first that fails decides the answer, and nothing is written for any of them.\n\n1. The student is yours (`404`), and has signed in to this organization once (`409`).\n2. The challenge exists and is published or closed (`404`).\n3. The group belongs to the challenge and the student is an active member of it (`404`).\n4. The work has not been sent already. If it has, the answer is `200` with `changed: false` and \"Group work already submitted.\", and nothing changes, so a retry after a timeout is safe.\n5. The challenge is published (`409`, `challenge_closed`) and now is between `windowStart` and `windowEnd` (`409`, `window_closed`).\n6. The teacher has confirmed the group (`409`, `payment_pending` or `group_not_confirmed`).\n7. Every step is submitted (`409`, `steps_incomplete`, with `stepsSubmitted` and `stepCount` in `details`).\n\nEvery `409` carries `error.details.reason`; branch on it, not on the message.\n\n**Side effects.** The group becomes `completed`, and an entry is added to its history. Every member of the group and its teacher receive an e-mail saying the group has sent its work.\n\n**Busy.** As for `submitGroupChallengeStep`: `503` with `Retry-After: 1` and `details.reason: busy` when someone is changing the group at that moment; nothing was written, so send it again.",
        "operationId": "submitGroupChallengeWork",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The group challenge’s `_id`, from `listGroupChallenges`.",
            "in": "path",
            "name": "challengeId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The group’s `_id`, from `listGroupChallengeGroups` or a student’s `group`.",
            "in": "path",
            "name": "groupId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubmitGroupChallengeRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "success": {
                    "summary": "Success",
                    "value": {
                      "data": {
                        "changed": true,
                        "finalSubmittedAt": "2026-10-20T16:02:11.000Z",
                        "groupId": "6650a1b2c3d4e5f6a7b8c9ed",
                        "groupStatus": "completed"
                      },
                      "message": "Group work submitted.",
                      "success": true
                    }
                  },
                  "unchanged": {
                    "summary": "The work had already been sent, by anyone. `changed` is `false` and nothing changed.",
                    "value": {
                      "data": {
                        "changed": true,
                        "finalSubmittedAt": "2026-10-20T16:02:11.000Z",
                        "groupId": "6650a1b2c3d4e5f6a7b8c9ed",
                        "groupStatus": "completed"
                      },
                      "message": "Group work already submitted.",
                      "success": true
                    }
                  }
                },
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Group work submitted.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/GroupChallengeWorkSubmitResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Group work submitted.\".\n\nOr `message` is \"Group work already submitted.\": The work had already been sent, by anyone. `changed` is `false` and nothing changed.",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "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\").",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "bad_request_2": {
                    "summary": "`challengeId` is not 24 hexadecimal digits.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Invalid value for 'challengeId': expected ObjectId.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "bad_request_3": {
                    "summary": "`groupId` is not 24 hexadecimal digits.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Invalid value for 'groupId': expected ObjectId.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `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\").\n- `bad_request`: `challengeId` is not 24 hexadecimal digits.\n- `bad_request`: `groupId` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "The requested resource could not be found.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "not_found_3": {
                    "summary": "No student of yours has this id: it matches nobody, or another account registered the student.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Student not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "not_found_4": {
                    "summary": "No group challenge of this organization has this id, or it is not published: a draft, archived or deleted challenge answers the same.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Group challenge not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "not_found_5": {
                    "summary": "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.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Group not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read.\n- `not_found`: No student of yours has this id: it matches nobody, or another account registered the student.\n- `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.\n- `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."
          },
          "409": {
            "content": {
              "application/json": {
                "examples": {
                  "conflict": {
                    "summary": "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.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "conflict_2": {
                    "summary": "The organizers have closed the challenge (`status: closed`).",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "details": {
                          "reason": "challenge_closed"
                        },
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "This group challenge is closed.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "conflict_3": {
                    "summary": "Now is before `windowStart` or after `windowEnd`. `details` carries both.",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "details": {
                          "reason": "window_closed",
                          "windowEnd": "2026-12-21T23:59:59.999Z",
                          "windowStart": "2026-10-01T00:00:00.000Z"
                        },
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "The group challenge takes no submissions outside its dates (windowStart to windowEnd).",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "conflict_4": {
                    "summary": "The teacher is still preparing the group (`status: awaiting_payment`).",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "details": {
                          "reason": "payment_pending"
                        },
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "The group is still being prepared by its teacher, so it has no steps yet.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "conflict_5": {
                    "summary": "The teacher has not confirmed the group yet (`status: draft`), so it has no steps.",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "details": {
                          "reason": "group_not_confirmed"
                        },
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "The teacher has not confirmed the group yet, so it has no steps yet.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "conflict_6": {
                    "summary": "A step is not submitted yet. `details` counts them.",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "details": {
                          "reason": "steps_incomplete",
                          "stepCount": 3,
                          "stepsSubmitted": 2
                        },
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "Every step has to be submitted before the group’s work can be sent.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `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.\n- `conflict`: The organizers have closed the challenge (`status: closed`).\n- `conflict`: Now is before `windowStart` or after `windowEnd`. `details` carries both.\n- `conflict`: The teacher is still preparing the group (`status: awaiting_payment`).\n- `conflict`: The teacher has not confirmed the group yet (`status: draft`), so it has no steps.\n- `conflict`: A step is not submitted yet. `details` counts them."
          },
          "413": {
            "content": {
              "application/json": {
                "examples": {
                  "payload_too_large": {
                    "summary": "payload_too_large",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`payload_too_large`: The body is larger than 100 kB."
          },
          "415": {
            "content": {
              "application/json": {
                "examples": {
                  "unsupported_media_type": {
                    "summary": "unsupported_media_type",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          },
          "503": {
            "content": {
              "application/json": {
                "examples": {
                  "service_unavailable": {
                    "summary": "service_unavailable",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Send a group’s finished work for one of your students",
        "tags": [
          "Group challenges"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "status": 400,
            "when": "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\")."
          },
          {
            "code": "bad_request",
            "message": "Invalid value for 'challengeId': expected ObjectId.",
            "status": 400,
            "when": "`challengeId` is not 24 hexadecimal digits."
          },
          {
            "code": "bad_request",
            "message": "Invalid value for 'groupId': expected ObjectId.",
            "status": 400,
            "when": "`groupId` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "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."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "status": 404,
            "when": "Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read."
          },
          {
            "code": "not_found",
            "message": "Student not found!",
            "status": 404,
            "when": "No student of yours has this id: it matches nobody, or another account registered the student."
          },
          {
            "code": "not_found",
            "message": "Group challenge not found!",
            "status": 404,
            "when": "No group challenge of this organization has this id, or it is not published: a draft, archived or deleted challenge answers the same."
          },
          {
            "code": "not_found",
            "message": "Group not found!",
            "status": 404,
            "when": "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."
          },
          {
            "code": "conflict",
            "status": 409,
            "when": "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."
          },
          {
            "code": "conflict",
            "details": {
              "reason": "challenge_closed"
            },
            "message": "This group challenge is closed.",
            "status": 409,
            "when": "The organizers have closed the challenge (`status: closed`)."
          },
          {
            "code": "conflict",
            "details": {
              "reason": "window_closed",
              "windowEnd": "2026-12-21T23:59:59.999Z",
              "windowStart": "2026-10-01T00:00:00.000Z"
            },
            "message": "The group challenge takes no submissions outside its dates (windowStart to windowEnd).",
            "status": 409,
            "when": "Now is before `windowStart` or after `windowEnd`. `details` carries both."
          },
          {
            "code": "conflict",
            "details": {
              "reason": "payment_pending"
            },
            "message": "The group is still being prepared by its teacher, so it has no steps yet.",
            "status": 409,
            "when": "The teacher is still preparing the group (`status: awaiting_payment`)."
          },
          {
            "code": "conflict",
            "details": {
              "reason": "group_not_confirmed"
            },
            "message": "The teacher has not confirmed the group yet, so it has no steps yet.",
            "status": 409,
            "when": "The teacher has not confirmed the group yet (`status: draft`), so it has no steps."
          },
          {
            "code": "conflict",
            "details": {
              "reason": "steps_incomplete",
              "stepCount": 3,
              "stepsSubmitted": 2
            },
            "message": "Every step has to be submitted before the group’s work can be sent.",
            "status": 409,
            "when": "A step is not submitted yet. `details` counts them."
          },
          {
            "code": "payload_too_large",
            "message": "request entity too large",
            "status": 413,
            "when": "The body is larger than 100 kB."
          },
          {
            "code": "unsupported_media_type",
            "message": "unsupported charset \"ISO-8859-1\"",
            "status": 415,
            "when": "The body declares a charset that is not a UTF one (send UTF-8), or a `Content-Encoding` other than gzip, deflate or br."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          },
          {
            "code": "service_unavailable",
            "details": {
              "reason": "busy"
            },
            "message": "The group is being changed by someone else. Try again in a second.",
            "status": 503,
            "when": "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."
          }
        ],
        "x-permission": [
          "group-challenge/submit:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/group-challenge/{challengeId}/group/{groupId}/step/{stepId}/submit": {
      "post": {
        "description": "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.\n\nThe body names the student you act for: `{ \"studentId\": \"<studentId>\" }`. The group’s history records the submit as your account (`partner`) acting for them.\n\n**Checks, in order.** The first that fails decides the answer, and nothing is written for any of them.\n\n1. The student is yours (`404`), and has signed in to this organization once (`409`).\n2. The challenge exists and is published or closed (`404`).\n3. The group belongs to the challenge and the student is an active member of it (`404`).\n4. The step has not been submitted already. If it has, the answer is `200` with `changed: false` and \"Step already submitted.\", and nothing changes, so a retry after a timeout is safe.\n5. The challenge is published (`409`, `challenge_closed`) and now is between `windowStart` and `windowEnd` (`409`, `window_closed`).\n6. The teacher has confirmed the group (`409`, `payment_pending` or `group_not_confirmed`).\n7. The group has this step (`404`).\n8. The step is open, not `locked` (`409`, `step_locked`).\n9. 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.\n\nEvery `409` carries `error.details.reason`, one of the codes above; branch on it, not on the message.\n\n**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.\n\n**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.",
        "operationId": "submitGroupChallengeStep",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The group challenge’s `_id`, from `listGroupChallenges`.",
            "in": "path",
            "name": "challengeId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The group’s `_id`, from `listGroupChallengeGroups` or a student’s `group`.",
            "in": "path",
            "name": "groupId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The step’s `_id`, from the group’s `steps` (`getGroupChallengeGroup`) or the challenge’s.",
            "in": "path",
            "name": "stepId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubmitGroupChallengeRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "success": {
                    "summary": "Success",
                    "value": {
                      "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
                    }
                  },
                  "unchanged": {
                    "summary": "The step had already been submitted, by anyone. `changed` is `false` and nothing changed.",
                    "value": {
                      "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 already submitted.",
                      "success": true
                    }
                  }
                },
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Step submitted.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/GroupChallengeStepSubmitResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Step submitted.\".\n\nOr `message` is \"Step already submitted.\": The step had already been submitted, by anyone. `changed` is `false` and nothing changed.",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "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\").",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "bad_request_2": {
                    "summary": "`challengeId` is not 24 hexadecimal digits.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Invalid value for 'challengeId': expected ObjectId.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "bad_request_3": {
                    "summary": "`groupId` is not 24 hexadecimal digits.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Invalid value for 'groupId': expected ObjectId.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "bad_request_4": {
                    "summary": "`stepId` is not 24 hexadecimal digits.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Invalid value for 'stepId': expected ObjectId.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `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\").\n- `bad_request`: `challengeId` is not 24 hexadecimal digits.\n- `bad_request`: `groupId` is not 24 hexadecimal digits.\n- `bad_request`: `stepId` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "The requested resource could not be found.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "not_found_3": {
                    "summary": "No student of yours has this id: it matches nobody, or another account registered the student.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Student not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "not_found_4": {
                    "summary": "No group challenge of this organization has this id, or it is not published: a draft, archived or deleted challenge answers the same.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Group challenge not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "not_found_5": {
                    "summary": "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.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Group not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "not_found_6": {
                    "summary": "The group has no step with this id. A group has its steps once the teacher confirms it.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Step not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read.\n- `not_found`: No student of yours has this id: it matches nobody, or another account registered the student.\n- `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.\n- `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.\n- `not_found`: The group has no step with this id. A group has its steps once the teacher confirms it."
          },
          "409": {
            "content": {
              "application/json": {
                "examples": {
                  "conflict": {
                    "summary": "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.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "conflict_2": {
                    "summary": "The organizers have closed the challenge (`status: closed`).",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "details": {
                          "reason": "challenge_closed"
                        },
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "This group challenge is closed.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "conflict_3": {
                    "summary": "Now is before `windowStart` or after `windowEnd`. `details` carries both.",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "details": {
                          "reason": "window_closed",
                          "windowEnd": "2026-12-21T23:59:59.999Z",
                          "windowStart": "2026-10-01T00:00:00.000Z"
                        },
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "The group challenge takes no submissions outside its dates (windowStart to windowEnd).",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "conflict_4": {
                    "summary": "The teacher is still preparing the group (`status: awaiting_payment`).",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "details": {
                          "reason": "payment_pending"
                        },
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "The group is still being prepared by its teacher, so it has no steps yet.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "conflict_5": {
                    "summary": "The teacher has not confirmed the group yet (`status: draft`), so it has no steps.",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "details": {
                          "reason": "group_not_confirmed"
                        },
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "The teacher has not confirmed the group yet, so it has no steps yet.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "conflict_6": {
                    "summary": "The step is still locked: the steps before it are not all submitted.",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "details": {
                          "reason": "step_locked"
                        },
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "This step is not open yet: the steps before it come first.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "conflict_7": {
                    "summary": "Nothing has been uploaded for the step yet. A member uploads the work in the panel or the app first; retrying does not help.",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "details": {
                          "reason": "step_empty"
                        },
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "Nothing has been uploaded for this step yet. A member of the group uploads the work first.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `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.\n- `conflict`: The organizers have closed the challenge (`status: closed`).\n- `conflict`: Now is before `windowStart` or after `windowEnd`. `details` carries both.\n- `conflict`: The teacher is still preparing the group (`status: awaiting_payment`).\n- `conflict`: The teacher has not confirmed the group yet (`status: draft`), so it has no steps.\n- `conflict`: The step is still locked: the steps before it are not all submitted.\n- `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."
          },
          "413": {
            "content": {
              "application/json": {
                "examples": {
                  "payload_too_large": {
                    "summary": "payload_too_large",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`payload_too_large`: The body is larger than 100 kB."
          },
          "415": {
            "content": {
              "application/json": {
                "examples": {
                  "unsupported_media_type": {
                    "summary": "unsupported_media_type",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          },
          "503": {
            "content": {
              "application/json": {
                "examples": {
                  "service_unavailable": {
                    "summary": "service_unavailable",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Submit one step of a group for one of your students",
        "tags": [
          "Group challenges"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "status": 400,
            "when": "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\")."
          },
          {
            "code": "bad_request",
            "message": "Invalid value for 'challengeId': expected ObjectId.",
            "status": 400,
            "when": "`challengeId` is not 24 hexadecimal digits."
          },
          {
            "code": "bad_request",
            "message": "Invalid value for 'groupId': expected ObjectId.",
            "status": 400,
            "when": "`groupId` is not 24 hexadecimal digits."
          },
          {
            "code": "bad_request",
            "message": "Invalid value for 'stepId': expected ObjectId.",
            "status": 400,
            "when": "`stepId` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "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."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "status": 404,
            "when": "Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read."
          },
          {
            "code": "not_found",
            "message": "Student not found!",
            "status": 404,
            "when": "No student of yours has this id: it matches nobody, or another account registered the student."
          },
          {
            "code": "not_found",
            "message": "Group challenge not found!",
            "status": 404,
            "when": "No group challenge of this organization has this id, or it is not published: a draft, archived or deleted challenge answers the same."
          },
          {
            "code": "not_found",
            "message": "Group not found!",
            "status": 404,
            "when": "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."
          },
          {
            "code": "not_found",
            "message": "Step not found!",
            "status": 404,
            "when": "The group has no step with this id. A group has its steps once the teacher confirms it."
          },
          {
            "code": "conflict",
            "status": 409,
            "when": "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."
          },
          {
            "code": "conflict",
            "details": {
              "reason": "challenge_closed"
            },
            "message": "This group challenge is closed.",
            "status": 409,
            "when": "The organizers have closed the challenge (`status: closed`)."
          },
          {
            "code": "conflict",
            "details": {
              "reason": "window_closed",
              "windowEnd": "2026-12-21T23:59:59.999Z",
              "windowStart": "2026-10-01T00:00:00.000Z"
            },
            "message": "The group challenge takes no submissions outside its dates (windowStart to windowEnd).",
            "status": 409,
            "when": "Now is before `windowStart` or after `windowEnd`. `details` carries both."
          },
          {
            "code": "conflict",
            "details": {
              "reason": "payment_pending"
            },
            "message": "The group is still being prepared by its teacher, so it has no steps yet.",
            "status": 409,
            "when": "The teacher is still preparing the group (`status: awaiting_payment`)."
          },
          {
            "code": "conflict",
            "details": {
              "reason": "group_not_confirmed"
            },
            "message": "The teacher has not confirmed the group yet, so it has no steps yet.",
            "status": 409,
            "when": "The teacher has not confirmed the group yet (`status: draft`), so it has no steps."
          },
          {
            "code": "conflict",
            "details": {
              "reason": "step_locked"
            },
            "message": "This step is not open yet: the steps before it come first.",
            "status": 409,
            "when": "The step is still locked: the steps before it are not all submitted."
          },
          {
            "code": "conflict",
            "details": {
              "reason": "step_empty"
            },
            "message": "Nothing has been uploaded for this step yet. A member of the group uploads the work first.",
            "status": 409,
            "when": "Nothing has been uploaded for the step yet. A member uploads the work in the panel or the app first; retrying does not help."
          },
          {
            "code": "payload_too_large",
            "message": "request entity too large",
            "status": 413,
            "when": "The body is larger than 100 kB."
          },
          {
            "code": "unsupported_media_type",
            "message": "unsupported charset \"ISO-8859-1\"",
            "status": 415,
            "when": "The body declares a charset that is not a UTF one (send UTF-8), or a `Content-Encoding` other than gzip, deflate or br."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          },
          {
            "code": "service_unavailable",
            "details": {
              "reason": "busy"
            },
            "message": "The group is being changed by someone else. Try again in a second.",
            "status": 503,
            "when": "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."
          }
        ],
        "x-permission": [
          "group-challenge/submit:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/group-challenge/{challengeId}/student": {
      "get": {
        "description": "Lists your students in this organization — the ones `listOrgStudents` lists: activated for it and signed in to it at least once — by last name, each with where they stand in the challenge: whether their grade lets them take part, whether a teacher is linked to them, and the group they are in, if any.\n\nOnly a teacher puts students in a group, in the panel; this API does not. A student in `not_in_group` with `teacherLinked: false` needs a teacher first (`linkStudentSupervisor`). `panelPath` is where to send a student with `createSigninLink`.\n\nA page at a time: `limit` is 20 by default and at most 100.",
        "operationId": "listGroupChallengeStudents",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The group challenge’s `_id`, from `listGroupChallenges`.",
            "in": "path",
            "name": "challengeId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Page number. Defaults to 1.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "example": 1,
              "type": "number"
            }
          },
          {
            "description": "Items per page. Defaults to 20, max 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Students fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "items": {
                            "$ref": "#/components/schemas/GroupChallengeStudentResponse"
                          },
                          "type": "array"
                        },
                        "pagination": {
                          "$ref": "#/components/schemas/PaginationMeta"
                        }
                      },
                      "required": [
                        "data",
                        "pagination"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Students fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "bad_request",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Invalid value for 'challengeId': expected ObjectId.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`bad_request`: `challengeId` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `group-challenge/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "The requested resource could not be found.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "not_found_3": {
                    "summary": "No group challenge of this organization has this id, or it is not published: a draft, archived or deleted challenge answers the same.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Group challenge not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read.\n- `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."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "List your students’ eligibility and groups for a group challenge",
        "tags": [
          "Group challenges"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for 'challengeId': expected ObjectId.",
            "status": 400,
            "when": "`challengeId` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `group-challenge/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "status": 404,
            "when": "Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read."
          },
          {
            "code": "not_found",
            "message": "Group challenge not found!",
            "status": 404,
            "when": "No group challenge of this organization has this id, or it is not published: a draft, archived or deleted challenge answers the same."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "group-challenge/read:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/group-challenge/{challengeId}/student/{studentId}": {
      "get": {
        "description": "Where one of your students stands in the challenge: the same answer as one row of `listGroupChallengeStudents`, for a student activated for this organization or not.\n\nYour students are the ones your account registered: anyone else’s answers `404`, exactly as an unknown id does. A student who has never signed in to this organization answers `409`.",
        "operationId": "getGroupChallengeStudent",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The group challenge’s `_id`, from `listGroupChallenges`.",
            "in": "path",
            "name": "challengeId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The student’s `_id`, as `registerStudent` returned it.",
            "in": "path",
            "name": "studentId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Student fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/GroupChallengeStudentResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Student fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "`challengeId` is not 24 hexadecimal digits.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Invalid value for 'challengeId': expected ObjectId.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "bad_request_2": {
                    "summary": "`studentId` is not 24 hexadecimal digits.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Invalid value for 'studentId': expected ObjectId.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `bad_request`: `challengeId` is not 24 hexadecimal digits.\n- `bad_request`: `studentId` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `group-challenge/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "The requested resource could not be found.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "not_found_3": {
                    "summary": "No student of yours has this id: it matches nobody, or another account registered the student.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Student not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "not_found_4": {
                    "summary": "No group challenge of this organization has this id, or it is not published: a draft, archived or deleted challenge answers the same.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Group challenge not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read.\n- `not_found`: No student of yours has this id: it matches nobody, or another account registered the student.\n- `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."
          },
          "409": {
            "content": {
              "application/json": {
                "examples": {
                  "conflict": {
                    "summary": "conflict",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Fetch one of your students’ eligibility and group for a group challenge",
        "tags": [
          "Group challenges"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for 'challengeId': expected ObjectId.",
            "status": 400,
            "when": "`challengeId` is not 24 hexadecimal digits."
          },
          {
            "code": "bad_request",
            "message": "Invalid value for 'studentId': expected ObjectId.",
            "status": 400,
            "when": "`studentId` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `group-challenge/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "status": 404,
            "when": "Group challenges are not switched on for this organization. The answer is the one an unknown path gets; nothing was read."
          },
          {
            "code": "not_found",
            "message": "Student not found!",
            "status": 404,
            "when": "No student of yours has this id: it matches nobody, or another account registered the student."
          },
          {
            "code": "not_found",
            "message": "Group challenge not found!",
            "status": 404,
            "when": "No group challenge of this organization has this id, or it is not published: a draft, archived or deleted challenge answers the same."
          },
          {
            "code": "conflict",
            "status": 409,
            "when": "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."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "group-challenge/read:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/report/download/{reportId}": {
      "get": {
        "description": "Sends the file of a released result report of one of your students: the bytes themselves, not JSON. `Content-Disposition` carries its name.\n\nEvery refusal of the report itself is the same `404` \"Not found!\": no report has this id or `shortId`, it is not released or was withdrawn, it belongs to another account’s student, or it has no file. So a download never shows whether a report exists in someone else’s hands.\n\nErrors always arrive as JSON before the first byte. A transfer that ends early, or with fewer bytes than `Content-Length`, has failed: discard what you received.",
        "operationId": "downloadReport",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The report’s `_id` (24 hexadecimal digits) or its 10-character `shortId`, both from `listStudentReports`. A `shortId` is matched exactly, case included.",
            "in": "path",
            "name": "reportId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/octet-stream": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              },
              "application/pdf": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "The report file, streamed. Content-Type is whatever the object was stored with. Content-Disposition carries an ASCII `filename` and the real name as RFC 5987 `filename*=UTF-8''…`; prefer the latter.",
            "headers": {
              "Content-Disposition": {
                "description": "attachment; filename=\"<ascii>\"; filename*=UTF-8''<percent-encoded>",
                "schema": {
                  "type": "string"
                }
              },
              "Content-Length": {
                "description": "Present when storage reports the size.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `report/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "No report has this id or shortId, it is not released, it belongs to another account's student, it has no file attached, or its file is missing from storage. Every one of these answers the same, so the status says nothing about another account's ids.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: No report has this id or shortId, it is not released, it belongs to another account's student, it has no file attached, or its file is missing from storage. Every one of these answers the same, so the status says nothing about another account's ids."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on.\n- `internal_error`: File storage is not configured or could not be reached."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Download a result report file",
        "tags": [
          "Documents"
        ],
        "x-errors": [
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `report/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "message": "Not found!",
            "status": 404,
            "when": "No report has this id or shortId, it is not released, it belongs to another account's student, it has no file attached, or its file is missing from storage. Every one of these answers the same, so the status says nothing about another account's ids."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "File storage is not configured or could not be reached."
          }
        ],
        "x-permission": [
          "report/read:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/report/{userId}": {
      "get": {
        "description": "Lists the result reports this organization has released for one of your students, across every season. A report that is not released yet, or was withdrawn after release, is not listed; if you keep copies, remove one whose report stops appearing. Download the file with `downloadReport`, by `_id` or `shortId`.\n\nA page at a time: `limit` is 20 by default and at most 100. No order is guaranteed.\n\nYour students are the ones your account registered: anyone else’s student answers `404`, exactly as an unknown id does. A student who has never signed in to this organization answers `409`; they cannot have sat its exams, so there is nothing to collect.",
        "operationId": "listStudentReports",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The student’s `_id`: the id `registerStudent` returned.",
            "in": "path",
            "name": "userId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Page number. Defaults to 1.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "example": 1,
              "type": "number"
            }
          },
          {
            "description": "Items per page. Defaults to 20, max 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Reports fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "items": {
                            "$ref": "#/components/schemas/ReportResponse"
                          },
                          "type": "array"
                        },
                        "pagination": {
                          "$ref": "#/components/schemas/PaginationMeta"
                        }
                      },
                      "required": [
                        "data",
                        "pagination"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Reports fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "bad_request",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`bad_request`: `userId` is not 24 hexadecimal digits."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `report/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "No student of yours has this id: it matches nobody, or another account registered the student.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Student not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: No student of yours has this id: it matches nobody, or another account registered the student."
          },
          "409": {
            "content": {
              "application/json": {
                "examples": {
                  "conflict": {
                    "summary": "conflict",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`conflict`: The student has never signed in to this organization, so it holds no record of them."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "List one of your students’ released result reports",
        "tags": [
          "Documents"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for '_id': expected ObjectId.",
            "status": 400,
            "when": "`userId` is not 24 hexadecimal digits."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `report/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "message": "Student not found!",
            "status": 404,
            "when": "No student of yours has this id: it matches nobody, or another account registered the student."
          },
          {
            "code": "conflict",
            "status": 409,
            "when": "The student has never signed in to this organization, so it holds no record of them."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "report/read:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/student": {
      "get": {
        "description": "Lists the students your account registered who can use this organization: those whose `activatedPlatformsThisSeason` holds its `slug` or `common`. A student of yours without access is left out until you grant it with `updateOrgStudent`. Students registered by other accounts are never listed.\n\nEach student is the record you registered, the same one `getStudent` returns, with `country`, `city`, `school`, `grade`, `supervisor` and `partner` resolved into records. A supervisor linked with `linkStudentSupervisor` does not show here: that link is kept on this organization’s own record of the student.\n\n**Signed in or not.** Each student also carries `signedIn`: whether they have signed in to this organization at least once. The organization keeps its own record of a student from that first sign-in, and `createApplication` and `linkStudentSupervisor` there need it. Send `signedIn=false` to list the students who still have to sign in, to send each a link from `createSigninLink`, and `signedIn=true` for the students ready for applications. The filter narrows `pagination.total` too, and anything but `true` or `false` is refused with `400`.\n\nPaginated: `page` starts at 1, and `limit` is 20 unless you ask for between 1 and 100. A value that is not a number falls back to the default, and one out of range to the nearest bound; neither is refused.",
        "operationId": "listOrgStudents",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "`true` for only the students who have signed in to this organization at least once, `false` for only those who have not. Leave it out for both. Anything but `true` or `false` is refused with `400`.",
            "in": "query",
            "name": "signedIn",
            "required": false,
            "schema": {
              "example": false,
              "type": "boolean"
            }
          },
          {
            "description": "Page number. Defaults to 1.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "example": 1,
              "type": "number"
            }
          },
          {
            "description": "Items per page. Defaults to 20, max 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Students fetched successfully.",
                          "type": "string"
                        },
                        "data": {
                          "items": {
                            "$ref": "#/components/schemas/OrgStudentResponse"
                          },
                          "type": "array"
                        },
                        "pagination": {
                          "$ref": "#/components/schemas/PaginationMeta"
                        }
                      },
                      "required": [
                        "data",
                        "pagination"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Students fetched successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "bad_request",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "signedIn must be true or false",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`bad_request`: `signedIn` is given, and is neither `true` nor `false`."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `student/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "not_found",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`not_found`: `organizationId` is not the `_id` of an organization."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "List your students who can use this organization",
        "tags": [
          "Students"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "signedIn must be true or false",
            "status": 400,
            "when": "`signedIn` is given, and is neither `true` nor `false`."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `student/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "student/read:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/student/{studentId}": {
      "get": {
        "description": "Returns one student your account registered, if they can use this organization (their `activatedPlatformsThisSeason` holds its `slug` or `common`), with the references resolved as `listOrgStudents` returns them.\n\nAn unknown id, a student registered by another account and a student of yours without access to this organization all answer `404 not_found`, the same way. A malformed id is a 400.\n\nTo give a student access, use `updateOrgStudent`.",
        "operationId": "getOrgStudent",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The student’s id (`_id`), as `registerStudent` returned it.",
            "in": "path",
            "name": "studentId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Student fetched successfully",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/StudentResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Student fetched successfully\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "bad_request",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "Invalid value for 'mainId': expected ObjectId.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`bad_request`: `studentId` is not a well-formed id."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `student/read` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "No student of yours with this id can use this organization: unknown, another account’s, or not activated here.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Student not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: No student of yours with this id can use this organization: unknown, another account’s, or not activated here."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Fetch one of your students, if they can use this organization",
        "tags": [
          "Students"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "message": "Invalid value for 'mainId': expected ObjectId.",
            "status": 400,
            "when": "`studentId` is not a well-formed id."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `student/read` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "message": "Student not found!",
            "status": 404,
            "when": "No student of yours with this id can use this organization: unknown, another account’s, or not activated here."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "student/read:$org:$ID"
        ]
      },
      "put": {
        "description": "Changes the fields you send and leaves the rest as they are: no field is required, and one you leave out is not cleared. It writes the student’s one record, the one `getStudent` returns and every organization shares, so a change made here shows everywhere.\n\n**Access to this organization.** Leave `activatedPlatformsThisSeason` out and this organization’s `slug` is added to it, unless it already holds `common`. The student then shows in `listOrgStudents` and can be sent a link with `createSigninLink`. This is how you give a student access, so unlike the reads it does not need them to have it already, and an empty body, `{}`, does only that. If you do send `activatedPlatformsThisSeason`, the values you send are added to the stored list, and this organization is added only if you name it. The list only ever grows: nothing is removed, a value it already holds is not added twice, and `[]` adds nothing.\n\n**Fields** follow registration’s rules: `birth` is a date that exists, as `DD/MM/YYYY` (`31/02/2008` is refused), `sex` is `m`, `f` or `n`, `country` is an id from `listCountries`, and `grade`, `city` and `school` take an id or a name. A `city` name is looked up in the student’s country, and a `school` name in their country and city: the ones in this body, or else the stored ones. A name or id that matches nothing is refused; nothing is ever created. `firstName`, `lastName`, `birth` and `sex` can be changed but not cleared: an empty value or `null` is refused. `phone` is cleared with `\"\"`. `password` is refused here: use `setStudentPassword`.\n\n**Email.** A new address sets `emailConfirmed` back to `false`, since nobody has confirmed it yet. Sending the address the student already has, in any letter case, changes nothing. An address another student already has is refused.\n\nSending the same body again leaves the student as the first call did. Answers with the stored record, references as ids; read the student to have them resolved. Only students your account registered can be updated; another account’s student answers 404, like an unknown id.",
        "operationId": "updateOrgStudent",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The student’s id (`_id`), as `registerStudent` returned it.",
            "in": "path",
            "name": "studentId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateOrgStudentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Student updated successfully.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/StudentRecordResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Student updated successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "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\").",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "bad_request_2": {
                    "summary": "`studentId` is not a well-formed id.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "bad_request_3": {
                    "summary": "`country`, `grade`, `city` or `school` matches no record, by id or by name, or a `city` or `school` name was sent for a student with no country (or, for a school, no city) to look it up in.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "school is not a known school. This API does not create reference data.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "bad_request_4": {
                    "summary": "`email` is `null`: an address can be changed, not removed. Any other value that is not an address is refused by the body’s rules (\"email must be an email\").",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "email must be a valid email address",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  },
                  "bad_request_5": {
                    "summary": "`firstName`, `lastName`, `birth`, `sex`, `phone` or `activatedPlatformsThisSeason` is `null`, or `firstName` or `lastName` is empty. These can be changed, not removed; clear `phone` with `\"\"`.",
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "documentation_url": "https://hub.main-team.org/api/errors#bad_request",
                        "message": "lastName should not be empty",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `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\").\n- `bad_request`: `studentId` is not a well-formed id.\n- `bad_request`: `country`, `grade`, `city` or `school` matches no record, by id or by name, or a `city` or `school` name was sent for a student with no country (or, for a school, no city) to look it up in.\n- `bad_request`: `email` is `null`: an address can be changed, not removed. Any other value that is not an address is refused by the body’s rules (\"email must be an email\").\n- `bad_request`: `firstName`, `lastName`, `birth`, `sex`, `phone` or `activatedPlatformsThisSeason` is `null`, or `firstName` or `lastName` is empty. These can be changed, not removed; clear `phone` with `\"\"`."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `student/update` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "No student of yours has this `studentId`. A student registered by another account answers the same.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Student not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: No student of yours has this `studentId`. A student registered by another account answers the same."
          },
          "409": {
            "content": {
              "application/json": {
                "examples": {
                  "conflict": {
                    "summary": "conflict",
                    "value": {
                      "error": {
                        "code": "conflict",
                        "documentation_url": "https://hub.main-team.org/api/errors#conflict",
                        "message": "That email address is already registered.",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`conflict`: `email` is already another student’s address. The answer does not say whose."
          },
          "413": {
            "content": {
              "application/json": {
                "examples": {
                  "payload_too_large": {
                    "summary": "payload_too_large",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`payload_too_large`: The body is larger than 100 kB."
          },
          "415": {
            "content": {
              "application/json": {
                "examples": {
                  "unsupported_media_type": {
                    "summary": "unsupported_media_type",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Update one of your students and give them access to this organization",
        "tags": [
          "Students"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "status": 400,
            "when": "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\")."
          },
          {
            "code": "bad_request",
            "message": "Invalid value for '_id': expected ObjectId.",
            "status": 400,
            "when": "`studentId` is not a well-formed id."
          },
          {
            "code": "bad_request",
            "message": "school is not a known school. This API does not create reference data.",
            "status": 400,
            "when": "`country`, `grade`, `city` or `school` matches no record, by id or by name, or a `city` or `school` name was sent for a student with no country (or, for a school, no city) to look it up in."
          },
          {
            "code": "bad_request",
            "message": "email must be a valid email address",
            "status": 400,
            "when": "`email` is `null`: an address can be changed, not removed. Any other value that is not an address is refused by the body’s rules (\"email must be an email\")."
          },
          {
            "code": "bad_request",
            "message": "lastName should not be empty",
            "status": 400,
            "when": "`firstName`, `lastName`, `birth`, `sex`, `phone` or `activatedPlatformsThisSeason` is `null`, or `firstName` or `lastName` is empty. These can be changed, not removed; clear `phone` with `\"\"`."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `student/update` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "message": "Student not found!",
            "status": 404,
            "when": "No student of yours has this `studentId`. A student registered by another account answers the same."
          },
          {
            "code": "conflict",
            "message": "That email address is already registered.",
            "status": 409,
            "when": "`email` is already another student’s address. The answer does not say whose."
          },
          {
            "code": "payload_too_large",
            "message": "request entity too large",
            "status": 413,
            "when": "The body is larger than 100 kB."
          },
          {
            "code": "unsupported_media_type",
            "message": "unsupported charset \"ISO-8859-1\"",
            "status": 415,
            "when": "The body declares a charset that is not a UTF one (send UTF-8), or a `Content-Encoding` other than gzip, deflate or br."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "student/update:$org:$ID"
        ]
      }
    },
    "/v1/{organizationId}/student/{studentId}/supervisor": {
      "put": {
        "description": "Attaches the student to a supervisor of this organization, found by `supervisorUsername`. Any supervisor of this organization can be linked.\n\n**The link is made on this organization only.** Each organization keeps its own record of a student and the link is written there, so the student’s records on other organizations are left as they were. Neither `getStudent` nor `getOrgStudent` shows it: both read the record you registered. The link stays when the student signs in again. Linking another supervisor replaces the first on this organization, and repeating a link changes nothing.\n\n**The student has to have signed in to this organization once**, with a link from `createSigninLink`. That first sign-in is when the organization creates its record of them, and before it there is nothing to link.\n\n**Every refusal is the same 404, `Not found!`:** a student who is not yours, a student this organization has no record of yet, a username nobody has here, and one whose owner is not a supervisor here. The answer never says which, and never repeats the username. A malformed `studentId`, or a `supervisorUsername` that is missing or empty, is a 400; one of spaces only is the 404.\n\nAnswers with the organization’s `slug`; this organization’s record of the student after the link, whose `_id` is this organization’s own id for the student and whose `mainId` is the id you use everywhere else; and the supervisor’s id and username.",
        "operationId": "linkStudentSupervisor",
        "parameters": [
          {
            "description": "The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it.",
            "example": "64b7f0c2a1d3e4f5a6b7c8d9",
            "in": "path",
            "name": "organizationId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "The student’s id (`_id`), as `registerStudent` returned it.",
            "in": "path",
            "name": "studentId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LinkSupervisorRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessEnvelope"
                    },
                    {
                      "properties": {
                        "message": {
                          "example": "Student linked to supervisor successfully.",
                          "type": "string"
                        },
                        "data": {
                          "$ref": "#/components/schemas/SupervisorLinkResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Success: `message` is \"Student linked to supervisor successfully.\".",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests your account may make to this operation per window (100).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until the current window ends.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "bad_request": {
                    "summary": "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\").",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "bad_request_2": {
                    "summary": "`studentId` is not a well-formed id.",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `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\").\n- `bad_request`: `studentId` is not a well-formed id."
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "unauthorized": {
                    "summary": "unauthorized",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "forbidden": {
                    "summary": "forbidden",
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "documentation_url": "https://hub.main-team.org/api/errors#forbidden",
                        "message": "Insufficient role permissions",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`forbidden`: The token is valid, but no role on your account allows `student/update` on the organization in the path, or a role denies it."
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "not_found": {
                    "summary": "`organizationId` is not the `_id` of an organization.",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "not_found_2": {
                    "summary": "Any refusal: the student is not yours, this organization has no record of them yet (they have never signed in to it), or no supervisor of this organization has this username.",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "documentation_url": "https://hub.main-team.org/api/errors#not_found",
                        "message": "Not found!",
                        "request_id": "0b5c6d0e-8f7a-4b1c-9d2e-3f4a5b6c7d8e"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "- `not_found`: `organizationId` is not the `_id` of an organization.\n- `not_found`: Any refusal: the student is not yours, this organization has no record of them yet (they have never signed in to it), or no supervisor of this organization has this username."
          },
          "413": {
            "content": {
              "application/json": {
                "examples": {
                  "payload_too_large": {
                    "summary": "payload_too_large",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`payload_too_large`: The body is larger than 100 kB."
          },
          "415": {
            "content": {
              "application/json": {
                "examples": {
                  "unsupported_media_type": {
                    "summary": "unsupported_media_type",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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."
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "too_many_requests": {
                    "summary": "too_many_requests",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`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.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again; a request sent sooner is refused too.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "examples": {
                  "internal_error": {
                    "summary": "internal_error",
                    "value": {
                      "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"
                      }
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`internal_error`: Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        },
        "security": [
          {
            "access-token": []
          }
        ],
        "summary": "Link one of your students to a supervisor on this organization",
        "tags": [
          "Students"
        ],
        "x-errors": [
          {
            "code": "bad_request",
            "status": 400,
            "when": "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\")."
          },
          {
            "code": "bad_request",
            "message": "Invalid value for '_id': expected ObjectId.",
            "status": 400,
            "when": "`studentId` is not a well-formed id."
          },
          {
            "code": "unauthorized",
            "status": 401,
            "when": "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."
          },
          {
            "code": "forbidden",
            "message": "Insufficient role permissions",
            "status": 403,
            "when": "The token is valid, but no role on your account allows `student/update` on the organization in the path, or a role denies it."
          },
          {
            "code": "not_found",
            "message": "Organization not found!",
            "status": 404,
            "when": "`organizationId` is not the `_id` of an organization."
          },
          {
            "code": "not_found",
            "message": "Not found!",
            "status": 404,
            "when": "Any refusal: the student is not yours, this organization has no record of them yet (they have never signed in to it), or no supervisor of this organization has this username."
          },
          {
            "code": "payload_too_large",
            "message": "request entity too large",
            "status": 413,
            "when": "The body is larger than 100 kB."
          },
          {
            "code": "unsupported_media_type",
            "message": "unsupported charset \"ISO-8859-1\"",
            "status": 415,
            "when": "The body declares a charset that is not a UTF one (send UTF-8), or a `Content-Encoding` other than gzip, deflate or br."
          },
          {
            "code": "too_many_requests",
            "status": 429,
            "when": "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."
          },
          {
            "code": "internal_error",
            "status": 500,
            "when": "Something failed on our side. Retry later, and quote `request_id` if it goes on."
          }
        ],
        "x-permission": [
          "student/update:$org:$ID"
        ]
      }
    }
  },
  "servers": [
    {
      "url": "https://api.main-team.org",
      "x-environment": "production"
    },
    {
      "description": "Sandbox: separate accounts and data; no e-mail; Stripe test mode",
      "url": "https://apisnd.main-team.org",
      "x-environment": "sandbox"
    }
  ],
  "tags": [
    {
      "description": "Whether the API is up. No token needed, so it is the first call to make against an environment.",
      "name": "Health",
      "x-guide": "/api/environments"
    },
    {
      "description": "The account your token belongs to, and revoking a token before it expires.",
      "name": "API account",
      "x-guide": "/api/guides/api-account"
    },
    {
      "description": "Countries, grades and organizations. A student’s `country` and `grade` take these `_id`s, and so does every `:organizationId` in a path.",
      "name": "Reference data",
      "x-guide": "/api/guides/reference-data"
    },
    {
      "description": "Registering your students, reading and updating them, setting a password, and what each organization holds for them. You only ever see the students your account registered.",
      "name": "Students",
      "x-guide": "/api/guides/students"
    },
    {
      "description": "Links that sign one of your students into an organization’s panel. Each works once and expires two minutes after it is issued.",
      "name": "Sign-in links",
      "x-guide": "/api/guides/sign-in-links"
    },
    {
      "description": "The exam categories and exams an organization runs, and the ones one of your students can apply to.",
      "name": "Exams",
      "x-guide": "/api/guides/exams"
    },
    {
      "description": "Entering your students for exams: creating, reading, moving and deleting their applications.",
      "name": "Applications",
      "x-guide": "/api/guides/applications"
    },
    {
      "description": "Your students’ certificates and reports: listing them and downloading the files.",
      "name": "Documents",
      "x-guide": "/api/guides/certificates-and-reports"
    },
    {
      "description": "Online group challenges your students take part in: the challenges, which of your students can join, the groups they are in, each step and its files, what happened in a group, and submitting on your student’s behalf.",
      "name": "Group challenges",
      "x-guide": "/api/guides/group-challenges"
    }
  ]
}
