{"openapi":"3.1.0","info":{"title":"AI Studies board: staff API","version":"1","description":"Read everything staff can see on the board (submissions in any state, the AI pre-review, students, their claims, applications and profiles) and record review decisions.\n\n**Auth.** Every route except this document needs `Authorization: Bearer <key>`. Make a key on the board's Staff page (Staff accounts → API keys). A key acts as the staff member who made it, and reviews made with it are recorded under their email. `read` keys can read everything; `write` keys can also review.\n\n**Not here, on purpose:** wipes and resets, password resets, staff accounts and résumé files. Those stay on the page.\n\n**Rate limit:** 300 requests a minute per key."},"servers":[{"url":"/api/v1"}],"security":[{"bearer":[]}],"paths":{"/me":{"get":{"summary":"Who this key acts as","operationId":"me","responses":{"200":{"description":"The key's owner and scope","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"email":{"type":"string"},"name":{"type":"string"},"scope":{"enum":["read","write"]}}}}}},"401":{"description":"No key, or a revoked one","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/submissions":{"get":{"summary":"List submissions","operationId":"listSubmissions","description":"Compact rows: no long details text and no individual findings. Newest first.","parameters":[{"name":"state","in":"query","schema":{"enum":["queue","all","pending","approved","changes_requested","rejected"],"default":"queue"},"description":"`queue` is what the review queue shows: pending, plus approved projects edited since."},{"name":"owner","in":"query","schema":{"enum":["student","submitter"]},"description":"`student` for students' own proposals, `submitter` for faculty, lab and industry postings."},{"name":"source","in":"query","schema":{"enum":["faculty","lab","industry","student"]}},{"name":"course","in":"query","schema":{"type":"string"},"description":"Roster course of the student owner, e.g. `AI 1010`."},{"name":"q","in":"query","schema":{"type":"string"},"description":"Matches title, owner name, owner email or organisation."},{"name":"has_repo_link","in":"query","schema":{"enum":["true","false"]}}],"responses":{"200":{"description":"Matching submissions","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"count":{"type":"integer"},"submissions":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionSummary"}}}}}}},"400":{"description":"Unknown state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/submissions/{id}":{"get":{"summary":"One submission, in full","operationId":"getSubmission","description":"Every part with its full text, who holds each part, the review decision, the AI pre-review with all its findings, and the links found in the text.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"The submission","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"submission":{"$ref":"#/components/schemas/Submission"}}}}}},"404":{"description":"No such submission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/students":{"get":{"summary":"List or search the roster","operationId":"listStudents","parameters":[{"name":"q","in":"query","schema":{"type":"string"},"description":"Part of a name or email."},{"name":"course","in":"query","schema":{"type":"string"}},{"name":"section","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Students","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"count":{"type":"integer"},"students":{"type":"array","items":{"$ref":"#/components/schemas/StudentSummary"}}}}}}}}}},"/students/{email}":{"get":{"summary":"One student, with everything they submitted","operationId":"getStudent","description":"Profile, résumé metadata (not the file), active claims, applications with their statements, and every proposal of their own in full, AI pre-review included.","parameters":[{"name":"email","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The student","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"student":{"$ref":"#/components/schemas/Student"}}}}}},"404":{"description":"Not on the roster","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/submissions/{id}/review":{"post":{"summary":"Record a review decision (write key)","operationId":"reviewSubmission","description":"The same decision as the review queue's buttons. Approving a student's own proposal assigns it to them. A note is required for anything but approval, and is what the submitter reads.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"verdict":{"enum":["approved","changes_requested","rejected"]},"note":{"type":"string"}},"required":["verdict"]}}}},"responses":{"200":{"description":"Recorded","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"wasEdit":{"type":"boolean"},"assigned":{"type":["string","null"]}}}}}},"400":{"description":"bad_verdict or note_required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"read_only_key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such submission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"has_active_claim: it is live and a student holds it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/submissions/{id}/semester-project":{"post":{"summary":"Mark or unmark a student's proposal as their graded project (write key)","operationId":"setSemesterProject","description":"Re-runs the AI pre-review afterwards, because the answer changes which bar it applies.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"semester_project":{"type":"boolean"}},"required":["semester_project"]}}}},"responses":{"200":{"description":"Set","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"semester_project":{"type":"boolean"},"reviewed":{"type":"boolean"},"verdict":{"type":["string","null"]}}}}}},"400":{"description":"not_student","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"read_only_key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such submission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/submissions/{id}/prereview":{"post":{"summary":"Run the AI pre-review again (write key)","operationId":"rerunPrereview","description":"Never approves anything when run this way. Waits for the reviewer, up to two minutes.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Done, or the reviewer was unavailable (ok false, reason reviewer_unavailable)","content":{"application/json":{"schema":{"type":"object"}}}},"403":{"description":"read_only_key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such submission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"An `ais_...` key from the Staff page."}},"schemas":{"Error":{"type":"object","properties":{"ok":{"const":false},"error":{"type":"string"}}},"Owner":{"type":"object","properties":{"type":{"enum":["student","submitter"]},"email":{"type":"string"},"name":{"type":"string"},"course":{"type":["string","null"]},"section":{"type":["string","null"]},"title":{"type":["string","null"]},"org":{"type":["string","null"]}}},"Link":{"type":"object","properties":{"url":{"type":"string"},"host":{"type":"string"},"repo":{"type":"boolean","description":"GitHub, GitLab, Bitbucket, Hugging Face or Codeberg."}}},"AiReview":{"type":"object","properties":{"verdict":{"enum":["pass","flag","error"]},"confidence":{"type":["string","null"]},"summary":{"type":["string","null"]},"findings":{"type":"array","items":{},"description":"Full view only."},"notes":{"type":"array","items":{},"description":"Full view only."},"findings_count":{"type":"integer","description":"List view only."},"error":{"type":["string","null"]},"model":{"type":["string","null"]},"reviewed_at":{"type":["string","null"]},"auto_approved":{"type":"boolean"}}},"SubmissionSummary":{"type":"object","properties":{"id":{"type":"integer"},"title":{"type":"string"},"source":{"enum":["faculty","lab","industry","student"]},"state":{"enum":["pending","approved","changes_requested","rejected"]},"needs_review":{"type":"boolean"},"edited_after_approval":{"type":"boolean"},"semester_project":{"type":"boolean"},"owner":{"$ref":"#/components/schemas/Owner"},"submitted_at":{"type":"string"},"age_days":{"type":"integer"},"edited_at":{"type":["string","null"]},"review":{"oneOf":[{"type":"null"},{"type":"object","properties":{"by":{"type":["string","null"]},"at":{"type":["string","null"]},"note":{"type":["string","null"]}}}]},"ai_review":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AiReview"}]},"links":{"type":"array","items":{"$ref":"#/components/schemas/Link"}},"has_repo_link":{"type":"boolean"},"part_count":{"type":"integer"},"summary":{"type":["string","null"]}}},"Submission":{"allOf":[{"$ref":"#/components/schemas/SubmissionSummary"},{"type":"object","properties":{"parts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"part_index":{"type":"integer"},"part_total":{"type":"integer"},"title":{"type":"string"},"sub_title":{"type":["string","null"]},"sub_description":{"type":["string","null"]},"description":{"type":"string"},"details":{"type":"string"},"mode":{"enum":["claim","apply"]},"source":{"type":"string"},"org":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"audience":{"type":"array","items":{"type":"string"}},"team_size":{"type":"integer"},"starts_on":{"type":["string","null"]},"duration":{"type":["string","null"]},"ends_on":{"type":["string","null"]},"hours_per_week":{"type":["string","null"]},"meetings_per_week":{"type":["string","null"]},"looking_for":{"type":["string","null"]},"us_citizen_only":{"type":"boolean"},"semester_project":{"type":"boolean"},"paid":{"type":"boolean"},"claimed_by":{"type":"array","items":{"type":"object","properties":{"email":{"type":"string"},"name":{"type":"string"},"since":{"type":"string"}}}}}}},"before_edit":{"type":"object","description":"Present when an approved project was edited and awaits re-review."}}}]},"StudentSummary":{"type":"object","properties":{"email":{"type":"string"},"name":{"type":"string"},"course":{"type":"string"},"section":{"type":"string"},"signed_up":{"type":"boolean"},"last_login_at":{"type":["string","null"]},"active_claims":{"type":"integer"},"applications":{"type":"integer"},"own_submissions":{"type":"integer"}}},"Student":{"type":"object","properties":{"email":{"type":"string"},"name":{"type":"string"},"course":{"type":["string","null"]},"section":{"type":["string","null"]},"signed_up":{"type":"boolean"},"last_login_at":{"type":["string","null"]},"profile":{"type":"object","properties":{"study_year":{"type":["string","null"]},"major":{"type":["string","null"]},"experience":{"type":["string","null"]},"interests":{"type":["string","null"]},"updated_at":{"type":["string","null"]}}},"resume":{"oneOf":[{"type":"null"},{"type":"object","properties":{"filename":{"type":"string"},"mime":{"type":"string"},"size":{"type":"integer"},"uploaded_at":{"type":"string"}}}]},"claims":{"type":"array","items":{"type":"object","properties":{"project_id":{"type":"integer"},"group_id":{"type":"integer"},"title":{"type":"string"},"sub_title":{"type":["string","null"]},"source":{"type":"string"},"mode":{"type":"string"},"since":{"type":"string"},"own_proposal":{"type":"boolean"}}}},"applications":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"project_id":{"type":"integer"},"group_id":{"type":"integer"},"title":{"type":"string"},"sub_title":{"type":["string","null"]},"state":{"type":"string"},"statement":{"type":"string"},"created_at":{"type":"string"},"decided_at":{"type":["string","null"]}}}},"submissions":{"type":"array","items":{"$ref":"#/components/schemas/Submission"}},"has_submission":{"type":"boolean"},"has_repo_link":{"type":"boolean"}}}}}}