cursor means students are still being turned off/on: send it back to carry on.| Location | Name | Required | Meaning |
|---|---|---|---|
| path | orgId | Yes | Use the ID from an authorized staff response; source validators reject malformed IDs. |
| header | X-Admin-CSRF | Yes | Session-bound token from the staff page; not a developer credential. |
{
"oneOf": [
{
"type": "object",
"properties": {
"suspended": {
"type": "boolean"
},
"cursor": {
"type": "string",
"minLength": 1,
"maxLength": 2048
}
},
"required": [
"suspended"
]
},
{
"type": "object",
"properties": {
"reinvite": {
"const": true
}
},
"required": [
"reinvite"
]
}
]
}cursor means students are still being turned off/on: send it back to carry on.{error:{code,message}} for CloudError failures, attaches Request-Id, and disables caching. Handle 401 by signing in; show role refusal for 403 forbidden, reauthenticate only for step_up_required, and collect a reason for 400 reason_required. A CSRF failure needs a current page/session token, not blind retries. Operation-specific 404/409/413/422/502/503 errors retain their status. Honor Retry-After on 429./**
* PATCH /api/admin/schools/:orgId {suspended, cursor?} | {reinvite: true} (schools.manage, step-up); each audited. A
* status answer with a `cursor` means students are still being turned off/on: send it back to carry on.
*/
export const PATCH = adminRoute("schools.manage", async (request, ctx, { params }: Context) => {
const orgId = schoolOrgParam((await params).orgId);
const body = await readBody(request, PATCH_BODY);
if (body.reinvite) return adminJson(await reinvite(ctx, orgId));
return adminJson(await setSchoolStatus(ctx, orgId, body.suspended!, body.cursor));
}, { stepUp: true, rateLimit: "school-manage" });