Machine-readable spec: openapi.json. Agent index: llms.txt.
POST /api/auth/login with your MUJ SLCM credentials sets an encrypted session cookie. Send that cookie with later calls. POST /api/auth/logout clears it. GET /api/auth/session-check reports the current state without needing credentials.
curl -X POST https://slcm.vercel.app/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"NAME.REGNO","password":"..."}' \
-c cookies.txtEvery response is JSON: { success, data?, error?, code? }. Only 401 with code SESSION_EXPIRED means log in again. A 502 means the upstream SLCM portal is struggling — retry later, stay logged in. Unknown /api paths return 404 JSON with a resolution hint.
MUJ SLCM goes down, and when it does it publishes no ETA. A login during an outage returns 503 with code UPSTREAM_UNAVAILABLE, never 401, so an upstream failure is never reported as a bad password.
Honour the Retry-After header on that 503. It escalates with the age of the outage, from 120 seconds up to 1800, because every retry is another request to a server that is already struggling. The ceiling is the interval at which this service's own health verdict can change, so an obedient client never polls faster than new information can arrive. Treat a 120 as a hint rather than a measurement: it is also the answer when no health record is available, which can happen if the probe's record has expired during a long outage.
That 503 also carries verdict, either degraded (the portal answered and redirected) or unreachable (it never answered). It classifies that one login attempt, not the state of the service, so two concurrent callers can get different values. Branch on code, not on it.
GET /api/slcm-status reports the last observed state: healthy, degraded, unreachable, misconfigured, or unknown. It also returns unhealthySince, the Unix time the current run of upstream-attributable bad verdicts began, or null. It carries forward across a degraded/unreachable alternation, because that is one outage rather than two, and resets on recovery or when the verdict becomes misconfigured. That timestamp is a fact from our own probe and is what the login page shows in place of a guessed duration. Read it together with state, since it is null whenever the state is not an upstream outage.
It also returns lastVerdict and lastCheckedAt, from a record that does not expire. unknown is not the same answer as healthy and must not be collapsed into it: the current record carries a 30 minute TTL that only the scheduled probe refreshes, so a stopped probe turns a known outage into unknown with no other signal. A previous degraded or unreachable next to an unknown state means the probe stopped, not that SLCM recovered. Both fields are null before the first probe has ever run.
GET /api/warm — liveness check. GET /api/user — current username or null.
POST /api/contact — anonymous report. 5 per hour per IP, 429 carries Retry-After.
curl -X POST https://slcm.vercel.app/api/contact \
-H 'Content-Type: application/json' \
-d '{"type":"feature","message":"Add a weekly attendance digest view"}'The API is at major version 1. Send API-Version: 1 (optional, the default) and read the API-Version response header. Any other version gets 400 code UNSUPPORTED_VERSION.
Breaking changes ship as a new major version. The old major keeps working for at least 90 days, announced here and in the x-deprecation-policy block of /openapi.json. No major has ever been retired; v1 is the only major and is fully supported.
Sunset and Deprecation response headers are not sent today, and that is deliberate rather than an oversight: both describe something that is going away, and there is nothing to deprecate while v1 is the only major. They arrive in the same change that introduces a second major, so a client that reads them never has to handle their absence after that point.
POST /api/contact allows 5 messages per hour per IP. Every response carries RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset headers; a 429 also carries Retry-After with seconds until reset. Other endpoints are session-gated, not rate-limited.
GET /api/info — profile rows. GET /api/getpersonalattendance — per-subject records. GET /api/data/attendance, /api/data/internal-marks, /api/data/grades, /api/data/cgpa, /api/data/timetable — dashboard payloads. GET /api/data/timetable/fetch-missing — backfill. GET /api/data/timetable/detail?eventId=... — one event.
POST /api/getpersonalattendance is the scheduled job's trigger, not a user endpoint. It logs
into SLCM with a stored service account, records history, and may send email, so it is a
POST rather than a GET. It takes the shared cron secret as Authorization: Bearer or in a JSON body, and needs no session cookie.
curl https://slcm.vercel.app/api/data/attendance \ -b cookies.txt