Skip to main content
There is no API key. Callers send the NextAuth session cookie. The edge proxy may reject a request before the route handler runs. Typical JSON body:
Some form routes add message and a details object for debugging (never secrets). Rate-limited responses include Retry-After.

Status codes

success
Handler succeeded. Body is JSON unless the route streams PDF, DOCX, CSV, or HTML.
client error
Invalid JSON, missing stepKey, bad schoolYear, or a validation failure on create/update.
auth
No session, inactive user, denied jti after logout, or JWT failed getToken in the proxy.
authz
Authenticated but not allowed: wrong level, Super Admin-only route, or form RBAC (not owner, not same-school principal, not assigned).
client error
Unknown User, FormSubmission, or step number with no matching bank key.
conflict
School already has a plan for that year; step save revision conflict; next-year settings already exist.
rate limit
Too many requests. Respect Retry-After (seconds). In production, some limits fail closed if Redis is down.
server error
Uncaught exception. Logged with console.error; optionally SENTRY_DSN via src/lib/reportError.js. Do not send form answers to Sentry.

Rate limits

Counters live in Redis as rl:* keys (60-second windows unless noted). The proxy applies the auth limit by client IP (x-forwarded-for). Application limits use session.user.id.
Header: Retry-After: 60
Hitting a 403 on /api/forms/:id/editors while the form page itself loads usually means the route’s RBAC is stricter than GET /api/forms/:id. Principals need same-school level 4 (or matching principalEmail). Assistant Principals need assignment or share. See Troubleshooting.