Skip to main content
This page explains how the SMS codebase is organized so you can find behaviour quickly and understand what runs in public vs tenant schemas.

Top-level layout

PathRole
manage.pyDjango entrypoint
requirements.txtPython dependencies
.env.dev / .envEnvironment-specific configuration (not committed with secrets)
sms/Project package: URLs, settings, WSGI/ASGI, middleware, schema hooks, exception handlers, storage
core/Tenant app: academic domain, admissions, exams, marks, reports, RBAC, audit, most REST viewsets
clients/Shared app: tenants (Client), domains, system-level school CRUD
authentication/Shared app: custom User, platform & school auth, JWT views
chatbot/Tenant app: WhatsApp flows, webhooks, Celery tasks, chatbot-specific endpoints
docs/Mintlify documentation (this site) and generated api-reference/openapi.json
Static and media paths follow normal Django conventions (staticfiles/, media/ in dev).

Shared vs tenant apps

Configuration in sms/settings.py splits apps into:
  • SHARED_APPS — Migrations and models in the public schema. Includes django_tenants, clients, authentication, admin, DRF, Spectacular, Celery beat, etc.
  • TENANT_APPScore and chatbot. Each school schema gets its own copies of these tables.
Anything that must be global (platform users, API keys, tenant registry) belongs in shared apps. Anything per school (students, sessions, marks, chatbot state) belongs in tenant apps.

URL routing

All API routes are wired in sms/urls.py:
  • /api/v1/system/system_router from core/api_router.py (schools, platform users, roles, system auth).
  • /api/v1/school/school_router plus nested routers for sessions, academic classes, exam sessions, exams, session setup, etc.
  • /api/v1/chatbot/chatbot.urls.
Viewsets and router registration live mainly in core/api_router.py. Nested resources use rest_framework_nested (for example sessions → academic-classes → registration). Schema and interactive docs:
  • /api/schema/ — OpenAPI JSON (drf-spectacular)
  • /api/docs/ — Swagger UI
  • /api/redoc/ — ReDoc

Cross-cutting project modules (sms/)

ModulePurpose
sms/middleware.pySmartTenantMiddleware — resolves public vs tenant schema from host, JWT, or X-Schema-Name
sms/handler.pyglobal_exception_handler — consistent API errors; special handling for Swagger auth
sms/schema_hooks.pyOpenAPI preprocessing/postprocessing (filter paths, tenant header, tag order)
sms/schema_custom.pyCustom Spectacular schema class (ListRetrieveAutoSchema)
sms/swagger_auth.pyPermission for who can open Swagger/ReDoc/schema
sms/storage.pyProduction static/media (e.g. hybrid + object storage) when enabled

Core app internals (core/)

High-signal locations:
  • core/views.py — Large collection of ModelViewSets and related API views
  • core/serializers.py — DRF serializers for school-facing resources
  • core/models.py — Tenant models (sessions, admissions, exams, etc.)
  • core/permissions.pyDynamicModelPermissions (HTTP method → Django model permissions)
  • core/middleware.pyAuditMiddleware (runs after auth)
  • core/backends.pyTenantPermissionBackend for school-schema role permissions

Authentication app (authentication/)

  • Custom User model (AUTH_USER_MODEL)
  • System vs school login viewsets registered on the routers in core/api_router.py
  • Backends in authentication/backends.py (e.g. identifier-based login) complement Django’s defaults

Clients app (clients/)

  • Client tenant model and Domain mapping hostnames to tenants
  • ClientViewSet exposed under /api/v1/system/schools/

Chatbot app (chatbot/)

  • URLconf, views, tasks (Celery), and models for WhatsApp and automated replies
  • Runs as a tenant app; views may resolve tenant context using host, JWT, phone numbers, or other chatbot-specific rules (see Architecture and the Chatbot module page)

Documentation assets

  • docs/docs.json — Mintlify navigation and theme; api.baseUrl is the default server for the Try it experience in the published API reference
  • docs/api-reference/openapi.json — Committed snapshot of the schema; regenerate from Django when endpoints change (see API Reference)