Skip to content

SIS API Documentation ​

Base URL: http://localhost:8000/api/v1/

This documentation covers all REST API endpoints for the Student Information System (SIS).

The machine-readable inventory ​

openapi.yaml is an OpenAPI 3.0.3 description of every registered route — 1,397 operations across 902 paths — with the permission node and access level guarding each one.

It is generated from the router, not written by hand:

bash
cd sis_backendgo
go test ./internal/router/ -run TestEmitOpenAPI -v   -args -openapi-out=<repo>/docs/public/openapi.yaml

The pages below carry what the router cannot know — request and response body shapes, worked examples, and the reasoning behind an endpoint. The spec carries what prose keeps getting wrong: what exists, and what guards it. When the two disagree, the spec is right about the route table and the page is right about the payload.

Reading x-authz

Each operation records kind (gated, selfservice, public, custom), and for a gated route the node and level required. x-router-pattern carries the pattern exactly as registered, which differs from the path key in the two places where the router spells the same wildcard two ways.

To see the same inventory as a table instead:

bash
go test ./internal/router/ -run TestDumpRoutes -v            # grouped text
go test ./internal/router/ -run TestDumpRoutes -v -args -format=md

Table of Contents ​

Core ​

DomainDescriptionDocumentation
AuthenticationLogin, logout, sessions, user management/auth/
CoreLookup values, currencies, settings, countries, email templates/core/

People & Roles ​

DomainDescriptionDocumentation
PeoplePerson records, names, addresses, phones, emails, photos/people/
RolesStudents, teachers, advisors, cohorts, student lists/roles/
AuthorizationRoles, permissions, impersonation, dashboard tiles/authorization/
Proxy AccessProxy access, invitations, student data access/proxy/
CommunicationsStaff-to-student email: compose, sent mail, sender policy/communications/

Academics ​

DomainDescriptionDocumentation
AcademicYears, terms, courses, sections, programs, requirements, materials/academic/
RegistrationEnrollments, transactions, holds, planning, waitlists/registration/
GradingGrades, GPA calculations, transcripts/grading/
Degree AuditDegree progress, waivers, transfer credits, graduation/audit/
Test ScoresTest types, scores, policies, superscoring/testscores/

Administration ​

DomainDescriptionDocumentation
OrganizationCampuses, buildings, rooms, departments, colleges, course codes/organization/
BillingTuition billing, fees, payment plans, refund schedules, transactions/billing/
Financial AidRetired — endpoints return 403/financial-aid/
Form BuilderDynamic application forms, templates, responses/formbuilder/
WorkflowRegistration workflows, automation rules/workflow/
Workflow EngineGeneric configurable approval workflows/workflow-engine/
LMSLMS connectors, instances, shells, sync, CSV export/lms/
ReportingIPEDS reports, student reports, grade reports/reporting/

Admissions

The admissions domain was removed in August 2026 — a different system is taking it over. Its database schema is untouched, but there are no /admissions/ endpoints. Applicant records are still served by Roles at /roles/applicants.

Authentication ​

All API endpoints (except where noted) require a valid session. The API uses cookie-based session authentication, not Bearer tokens.

Logging In ​

bash
# Login to get a session
curl -X POST http://localhost:8000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -c cookies.txt \
  -d '{"email": "user@example.com", "password": "yourpassword"}'

Response: The server sets three cookies:

  • sis_session (HttpOnly) - Opaque session ID
  • sis_csrf - CSRF token for state-changing requests
  • sis_logged_in - Flag for JavaScript to check auth state
json
{
  "success": true,
  "data": {
    "user": {
      "auth_user_id": 1,
      "email": "user@example.com",
      "person_id": "550e8400-e29b-41d4-a716-446655440000",
      "is_superuser": false,
      "is_staff": true
    },
    "csrf_token": "<csrf_token>"
  }
}

The user object is abbreviated here; Authentication has the full shape. Its roles labels are not authorization: the roles a user holds and is acting as come from GET /authorization/user-roles/my_roles.

Making Authenticated Requests ​

Include the session cookie with each request:

bash
curl -X GET http://localhost:8000/api/v1/academic/courses \
  -b cookies.txt

CSRF Protection ​

State-changing requests (POST, PUT, PATCH, DELETE) require a CSRF token:

bash
# Read the sis_csrf cookie value, then send it as a header
curl -X POST http://localhost:8000/api/v1/academic/courses \
  -b cookies.txt \
  -H "X-CSRF-Token: <csrf_token_from_cookie>" \
  -H "Content-Type: application/json" \
  -d '{"course_code": "CS101", "title": "Intro to CS"}'

Common Patterns ​

Pagination ​

List endpoints return paginated results:

json
{
  "count": 245,
  "page": 1,
  "page_size": 20,
  "total_pages": 13,
  "results": [...]
}

Query Parameters:

  • page - Page number (default: 1)
  • page_size - Results per page (default: 20)

Filtering ​

Most list endpoints support filtering by specific fields:

bash
# Filter by department
GET /api/v1/academic/courses?department_id=5

# Filter by status
GET /api/v1/academic/courses?status_id=1

# Multiple filters
GET /api/v1/academic/courses?department_id=5&is_repeatable=true

Searching ​

Use the search parameter for text search across multiple fields:

bash
GET /api/v1/academic/courses?search=calculus

Ordering ​

Use the ordering parameter to sort results:

bash
# Ascending
GET /api/v1/academic/courses?ordering=course_code

# Descending (prefix with -)
GET /api/v1/academic/courses?ordering=-created_at

# Multiple fields
GET /api/v1/academic/courses?ordering=department,-course_code

Partial updates and edit conflicts ​

A PATCH writes only the fields present in the body. A field you leave out is left alone; a field sent as null is cleared. Two people editing different fields of one record therefore both land.

To be protected against overwriting someone else's change to the same field, send the values you originally loaded for the fields you are changing under _expected:

bash
curl -X PATCH http://localhost:8000/api/v1/people/persons/550e8400-e29b-41d4-a716-446655440000   -b cookies.txt   -H "X-CSRF-Token: <csrf_token_from_cookie>"   -H "Content-Type: application/json"   -d '{
    "gender_id": 3,
    "deceased_date": null,
    "_expected": { "gender_id": 2, "deceased_date": "2020-01-01" }
  }'

The write happens only if every field named in _expected still holds that value. If any has changed since you loaded it, nothing is written and the response is 409, naming each field that moved and what it holds now:

json
{
  "error": {
    "code": "edit_conflict",
    "message": "This record was changed by someone else while you were editing it",
    "message_key": "errors.editConflict",
    "conflicts": [
      { "field": "gender_id", "current": "4" }
    ]
  }
}

current is the stored value as text, or null when the field is empty. Reload, decide, and send again. An expected blank (null or "") matches a field that is either NULL or empty. Without _expected the write is unconditional, so existing scripts keep working.

These endpoints follow the partial-write rule and accept _expected:

ResourceEndpoint
PersonPATCH /people/persons/{personID}
Person names, addresses, phones, emailsPATCH /people/person-names/{nameID} and the matching person-addresses, person-phones, person-emails routes; the caller's own /people/me/addresses, /me/phones, /me/emails
StudentPATCH /roles/students/{studentID}
EnrollmentPATCH /registration/enrollments/{enrollmentID}
User accountPATCH /auth/users/{userID}
Grade definitionPATCH /academic/grades/{gradeID}
Billing method, method config, payment planPATCH /billing/methods/{methodID}, /billing/method-configs/{configID}, /billing/payment-plans/{planID}
Graduation recordPATCH /audit/graduation-records/{recordID}

Other update endpoints do not accept _expected yet. They still load the row, apply the request and write every column back, so a concurrent edit to the same record by someone else can be silently undone.

Response Format ​

Success Response ​

Single resource:

json
{
  "course_id": 1,
  "course_code": "CS101",
  "title": "Introduction to Computer Science",
  ...
}

List endpoint:

json
{
  "count": 100,
  "page": 1,
  "page_size": 20,
  "total_pages": 5,
  "results": [...]
}

Error Response ​

json
{
  "error": {
    "code": "not_found",
    "message": "Course not found"
  }
}

Validation errors:

json
{
  "error": {
    "code": "validation_error",
    "message": "Validation failed",
    "details": {
      "course_code": "This field is required",
      "title": "Must be at most 200 characters"
    }
  }
}

HTTP Status Codes ​

CodeMeaning
200 OKRequest succeeded
201 CreatedResource created successfully
204 No ContentResource deleted successfully
400 Bad RequestInvalid request data or business rule violation
401 UnauthorizedMissing or invalid session
403 ForbiddenInsufficient permissions
404 Not FoundResource not found
409 ConflictDuplicate, or edit_conflict: a field changed since you loaded it (see Partial updates)
429 Too Many RequestsRate limit exceeded (auth endpoints)
500 Internal Server ErrorServer error

Lookup Values ​

The system uses individual lookup tables (lookup_*) for reference data (statuses, types, etc.). Each lookup type has its own table (e.g., lookup_program_status, lookup_course_status). These are accessed via the Core API.

Common Lookup Types ​

Type CodeDescription
PROGRAM_STATUSProgram statuses (Active, Inactive, etc.)
COURSE_STATUSCourse statuses
BLOCK_TYPERequirement block types
REQUISITE_TYPECourse requisite types (Prerequisite, Corequisite)
TERM_TYPEAcademic term types (Fall, Spring, Summer)
NAME_TYPEPerson name types (Legal, Preferred, etc.)
ADDRESS_TYPEAddress types (Home, Mailing, etc.)
PHONE_TYPEPhone types (Mobile, Home, Work)
EMAIL_TYPEEmail types (Personal, Work, Institutional)

Fetching Lookups ​

bash
# Get all lookup types with values
GET /api/v1/core/lookups

# Get values for a specific type
GET /api/v1/core/lookups/by-type/TERM_TYPE

TypeScript Types ​

TypeScript interfaces for all API responses are available in:

sis_frontend/src/api/types/index.ts

Student Information System Documentation