name must be the school's. A cursorclosed is true. Each call is audited.| 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. |
| header | X-Admin-Reason | Yes | URI-encoded written audit reason; Cloud/billing may reuse a fresh target-specific session marker. |
{
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 120
},
"cursor": {
"type": "string",
"minLength": 1,
"maxLength": 2048
}
},
"required": [
"name"
]
}name must be the school's. A cursorclosed is true. Each call is audited.{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./**
* POST /api/admin/schools/:orgId/close {name, cursor?} (schools.close: owners, step-up, X-Admin-Reason; §16) →
* {closed, cursor?}. Erases every student account and then the school's org; `name` must be the school's. A `cursor`
* back means students remain: send it back (with the same name) until `closed` is true. Each call is audited.
*/
export const POST = adminRoute("schools.close", async (request, ctx, { params }: Context) => {
const orgId = schoolOrgParam((await params).orgId);
return adminJson(await closeSchool(ctx, orgId, await readBody(request, BODY)));
}, { rateLimit: "school-close" });