Conventions

  • JSON request/response; dates ISO-8601; money as decimal strings "150000.00"
  • Pagination: ?page=0&size=20{ content, totalElements, totalPages }
  • Errors: { "code": "CLAIM_DUPLICATE", "message": "...", "field": "transactionId" }
  • Idempotency: Idempotency-Key header on POST approve/payment

Auth

POST /auth/login          { email|phone, password } → { accessToken, refreshToken }
POST /auth/refresh        { refreshToken }
POST /auth/parent/register { phone, otp, password }  -- invite flow
GET  /auth/me             → user + roles + campuses

Students (student-service)

POST   /students                    -- create student + guardians
GET    /students/{id}
GET    /students?campusId=&classId=&q=
PUT    /students/{id}
POST   /students/{id}/enroll
GET    /students/{id}/guardians
POST   /students/{id}/photo           -- multipart → file-service

Fees & payment claims (fee-service)

GET    /portal/fee-accounts/{studentId}
GET    /portal/invoices?studentId=
POST   /portal/payment-claims
GET    /portal/payment-claims
GET    /portal/payment-claims/{id}
PUT    /portal/payment-claims/{id}              -- only NEEDS_INFO
DELETE /portal/payment-claims/{id}              -- cancel PENDING

GET    /admin/payment-claims?status=&campusId=
POST   /admin/payment-claims/{id}/start-review
POST   /admin/payment-claims/{id}/needs-info    { message }
POST   /admin/payment-claims/{id}/approve       { amount, invoiceIds[] }
POST   /admin/payment-claims/{id}/reject        { reason }
POST   /admin/payments                          -- direct bursar entry
POST   /admin/invoices/generate?termId=&classId=
GET    /admin/defaulters?campusId=&termId=
POST   /admin/payments/{id}/void                { reason }

Health records (health-service)

GET    /portal/health/{studentId}
PUT    /portal/health/{studentId}
POST   /portal/health/{studentId}/confirm
POST   /portal/health/{studentId}/documents

GET    /admin/health/students?campusId=&status=
GET    /admin/health/students/{studentId}      # HEALTH_RECORDS_VIEWER only
POST   /admin/health/students/{studentId}/nurse-note
POST   /admin/health/students/{studentId}/request-reconfirmation
GET    /admin/health/audit

See student health records requirements.

Portal documents (notice-service)

GET  /portal/children
GET  /portal/documents?studentId=&type=
GET  /portal/documents/{id}/download
GET  /portal/homework?studentId=
POST /admin/documents              -- upload metadata + fileId
POST /admin/documents/{id}/publish { audienceType, audienceRef }

Organization

GET  /org/campuses
GET  /org/terms/current?campusId=
GET  /org/classes?campusId=&yearId=

Sample: submit payment claim

POST /api/v1/portal/payment-claims
{
  "studentId": "uuid",
  "invoiceId": "uuid",
  "amount": "380000.00",
  "paymentMethod": "MTN_MOMO",
  "transactionId": "34829103847",
  "payerPhone": "+256770123456",
  "paymentDate": "2026-06-10",
  "notes": "Term 2 day scholar fees",
  "proofFileId": "uuid-or-null"
}

→ 201
{
  "id": "uuid",
  "status": "PENDING_VERIFICATION",
  "submittedAt": "2026-06-10T14:22:00Z"
}

Sample: bursar approve

POST /api/v1/admin/payment-claims/{id}/approve
{
  "amount": "380000.00",
  "invoiceIds": ["uuid"]
}

→ 200
{
  "claimStatus": "APPROVED",
  "paymentId": "uuid",
  "receiptNumber": "RCP-2026-004821",
  "receiptFileId": "uuid"
}

Webhooks (notification-service)

POST /notifications/webhooks/africastalking/delivery
POST /notifications/webhooks/africastalking/inbound  -- future

OpenAPI 3 specs will live in each service repo at /src/main/resources/openapi.yaml — generate via springdoc-openapi at build time.