Complete documentation

School Management System

One platform for the whole school — a Laravel 12 API, a React web portal, and a React Native mobile app that share the same live data. This guide takes you from an empty server to a running school, then explains every module, the WhatsApp integration, and how to build and ship the mobile app.

Laravel 12 · PHP 8.2+ React 19 · Vite 7 Expo SDK 54 Multi-branch WhatsApp Cloud API 159 migrations

01 · Install first

What you downloaded

The package contains three separate applications plus this documentation. They are independent codebases that talk to each other over a REST API, which means you can host them together or apart.

School-Management-System/ │ ├── Documentation/ # this guide, with all screenshots │ └── index.html │ ├── Main Files/ │ ├── backend/ # Laravel 12 REST API — the heart of the system │ ├── frontend/ # React 19 + Vite web portal │ ├── mobile/ # React Native (Expo SDK 54) app for Android & iOS │ └── database/ │ ├── sms-clean-install.sql # empty school, ready for real data │ └── sms-demo-data.sql # sample school, for evaluating │ └── Licensing/

Backend — the API

Every rule lives here: permissions, fees, attendance, exams, WhatsApp. Both the web portal and the mobile app are just clients of this API. Install this first.

Frontend — the web portal

A single-page React app. It compiles to plain static files (HTML, JS, CSS) that any web server can serve — no Node required in production.

mobile — the app

One codebase for Android and iOS. Build an installable APK with Expo's cloud service, no Android Studio needed.

Read this section order Install the backend first and confirm it answers on its URL. Nothing else can work until it does — the web portal and the mobile app are both empty shells without it.

02 · Install first

Server requirements

Any standard shared-hosting plan with cPanel meets these. You do not need a VPS, root access, or Docker.

For the backend (required)

RequirementMinimumNotes
PHP8.28.3 recommended. Set it in cPanel under MultiPHP Manager.
MySQL / MariaDB5.7 / 10.3MySQL 8 works. About 60 MB for a school of 1,000 students.
Web serverApache or NginxApache with mod_rewrite enabled — it ships with the required .htaccess.
Composer2.xOnly needed if you install dependencies yourself. See the note below.
Disk space500 MBMore if you upload lecture videos.

Required PHP extensions

All of these are enabled by default on virtually every host. Check them in cPanel under Select PHP Version → Extensions.

bcmath ctype curl dom fileinfo gd json mbstring openssl pcre pdo_mysql tokenizer xml zip

gd and zip are not optional gd renders student photos onto ID cards and certificates; zip drives the Excel exports. If either is missing those features fail with a 500 error and nothing else explains why.

Only for building (your own computer, not the server)

ToolVersionNeeded for
Node.js20 LTS or newerCompiling the web portal, and running the mobile app.
npm10+Ships with Node.
Expo CLIlatestMobile app only. Run via npx, no global install needed.
Android StudiooptionalOnly if you want an emulator or a local APK build. EAS Build does not need it.

03 · Install first

Where the files go on the server

This is the step most installations get wrong, so it is worth understanding before you upload anything. The rule is simple: the web portal goes in your public folder, and the Laravel API goes in a subfolder of it.

The layout you are aiming for

/home/youruser/public_html/ # your domain points here │ ├── index.html # ← the WEB PORTAL (built files) ├── assets/ # ← its JS and CSS bundles ├── .htaccess # ← SPA routing rules (must be present) │ └── apis/ # ← the LARAVEL BACKEND lives here ├── app/ ├── bootstrap/ ├── config/ ├── database/ ├── routes/ ├── storage/ # must be writable ├── vendor/ ├── .env # your settings — create this ├── artisan └── public/ # ← the API is served from HERE ├── index.php ├── .htaccess └── storage → # symlink to ../storage/app/public

With that layout in place your two URLs are:

WhatURL
Web portal (what people visit)https://yourschool.com/
API base URL (what the apps call)https://yourschool.com/apis/public/api/v1

Never put the whole Laravel folder in public_html Only Laravel's public/ directory is meant to be reachable from the internet. If you copy the backend so that .env sits directly in public_html, anyone can download your database password by visiting yourschool.com/.env. Keeping the app in apis/ and serving apis/public is what prevents that.

Prefer a cleaner URL? If your host lets you set the document root of a subdomain, point api.yourschool.com straight at /home/youruser/apis/public and keep the Laravel folder completely outside public_html. That is the tidiest and safest arrangement. Your API base URL then becomes https://api.yourschool.com/api/v1.

Where uploaded files are stored

Everything users upload — student photos, lecture videos, syllabus PDFs, fee receipts — is written to apis/storage/app/public/, organised in folders:

FolderHoldsPublicly readable?
student-pics/, profile-pics/, students/photos/Student and staff photographsNo — signed links only
study_materials/, syllabuses/, lectures/, assignments/Teaching materialNo — signed links only
diaries/, messages/Diary and message attachmentsNo
expenses/, incomes/Accounting documentsNo
branch-logos/, system/School branding, shown on the sign-in pageYes, by design

The .htaccess shipped in apis/public/ enforces that table. Do not remove those rules — see Security for why.

04 · Install first

Installing on cPanel / shared hosting

The common case: one domain, one cPanel login, no terminal. Allow about 30 minutes. If your host gives you SSH access, the VPS instructions are faster.

  1. Create the database

    In cPanel open MySQL® Databases. Create a database, create a user with a strong password, then add the user to the database and tick ALL PRIVILEGES.

    Write down all three values — you need them in step 4. Note that cPanel prefixes them, so a database you name sms really becomes something like youruser_sms.

  2. Set the PHP version

    Open MultiPHP Manager, select your domain, and set it to PHP 8.2 or 8.3. Then open Select PHP Version → Extensions and confirm the extensions listed in Requirements are ticked.

  3. Upload the backend

    In File Manager, go to public_html and create a folder called apis. Upload the contents of Main Files/backend/ into it, then extract.

    Upload the zip rather than thousands of individual files — File Manager's Upload then Extract is dramatically faster and will not silently drop files the way FTP sometimes does.

    About the vendor folder The package already includes vendor/, so you do not need Composer at all. If your copy is missing it and you have SSH, run composer install --no-dev --optimize-autoloader inside apis/.

  4. Create the .env file

    Inside apis/, copy .env.example to .env (File Manager: right-click → Copy). Then edit it and set at least these values:

    APP_NAME="Greenfield School"
    APP_ENV=production
    APP_DEBUG=false
    APP_URL=https://yourschool.com/apis/public
    FRONTEND_URL=https://yourschool.com
    
    DB_CONNECTION=mysql
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=youruser_sms
    DB_USERNAME=youruser_smsuser
    DB_PASSWORD=your-database-password

    APP_DEBUG must be false Left on, Laravel prints your database credentials and file paths onto the error page for anyone who triggers an error. Set it to false before the site goes live.

    APP_URL must include the /apis/public part if you installed into a subfolder — password-reset links and signed file links are built from it, and they break if it is wrong.

  5. Run the installer

    Open cPanel's Terminal (or SSH), move into the folder and run:

    cd ~/public_html/apis
    php artisan key:generate
    php artisan migrate --seed
    php artisan storage:link

    That creates all 159 tables, fills in the default roles, permissions and settings, and links the uploads folder so files can be served.

    No Terminal on your plan? Import Main Files/database/sms-clean-install.sql using phpMyAdmin instead, then set APP_KEY manually in .env to any 32-character random string prefixed with base64:. You will still need the storage symlink — create it with a one-line PHP file containing <?php symlink('/home/youruser/public_html/apis/storage/app/public', '/home/youruser/public_html/apis/public/storage');, visit it once in your browser, then delete the file.

  6. Set folder permissions

    Two folders must be writable by the web server, or every page returns a 500 error:

    chmod -R 775 storage bootstrap/cache

    In File Manager you can do the same with right-click → Change Permissions → 775, ticking Recurse into subdirectories.

  7. Check the API is alive

    Visit https://yourschool.com/apis/public/api/v1/public/settings in your browser. You should see a JSON response. If you do, the backend is working and the hard part is over.

  8. Build and upload the web portal

    On your own computer (not the server), open the Main Files/frontend folder and tell it where your API lives:

    # create a file named .env.production
    VITE_API_BASE_URL=https://yourschool.com/apis/public/api/v1

    Then build it:

    npm install
    npm run build

    This creates a dist/ folder. Upload everything inside dist/ — not the folder itself — into public_html. Make sure the .htaccess file goes up too; File Manager hides dotfiles until you enable Show Hidden Files in its settings.

    Blank white page after upload? That is almost always the missing .htaccess. Without it the server has no idea that /students should be handled by index.html, so every page except the home page 404s. See Troubleshooting.

  9. Add the cron job

    Fee reminders, WhatsApp queues and overdue notices all depend on this. In cPanel open Cron Jobs, choose Once Per Minute, and set the command:

    cd /home/youruser/public_html/apis && php artisan schedule:run >> /dev/null 2>&1

    Details and verification in Cron & queue.

  10. Sign in and secure the account

    Visit https://yourschool.com and sign in with the seeded super admin, then change the password immediately. Continue at First login.

05 · Install first

Installing on a VPS

If you have root access the whole install is a handful of commands. This assumes Ubuntu with Nginx, but Apache works the same way.

Backend

cd /var/www
# upload and extract the package here first
cd /var/www/school/backend

cp .env.example .env
nano .env                        # set APP_URL, FRONTEND_URL and the DB_* values

composer install --no-dev --optimize-autoloader
php artisan key:generate
php artisan migrate --seed
php artisan storage:link

chown -R www-data:www-data storage bootstrap/cache
chmod -R 775 storage bootstrap/cache

Nginx server block

Point the root at Laravel's public directory — never at the app folder itself.

server {
    listen 80;
    server_name api.yourschool.com;
    root /var/www/school/backend/public;

    index index.php;
    client_max_body_size 64M;          # lecture video uploads

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # The .htaccess rules do not apply under Nginx — replicate them:
    location ~ ^/storage/(student-pics|profile-pics|students/photos|diaries|messages|expenses|incomes|study_materials|syllabuses|lectures|lecture-thumbnails|assignments)/ {
        deny all;
    }

    location ~ /\.(?!well-known) { deny all; }
}

Nginx users must copy those deny rules The protection for student photographs and private documents ships as Apache .htaccess rules, and Nginx ignores .htaccess entirely. Without the location block above, every student photo on your server is downloadable by anyone who guesses a filename.

Frontend

cd /var/www/school/frontend
echo "VITE_API_BASE_URL=https://api.yourschool.com/api/v1" > .env.production
npm install
npm run build
cp -r dist/* /var/www/school-web/

Then a second server block for the portal, with the SPA fallback:

server {
    listen 80;
    server_name yourschool.com;
    root /var/www/school-web;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;    # SPA routing
    }

    location ~* \.(js|css|woff2?|png|jpe?g|svg|webp)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    location = /index.html {
        add_header Cache-Control "no-cache, no-store, must-revalidate";
    }
}

Cron

crontab -e -u www-data
# add this single line:
* * * * * cd /var/www/school/backend && php artisan schedule:run >> /dev/null 2>&1

HTTPS

apt install certbot python3-certbot-nginx
certbot --nginx -d yourschool.com -d api.yourschool.com

06 · Install first

Running it locally for development

Useful for evaluating the system, customising it, or developing against it before you buy hosting.

1. Backend

cd backend
composer install
cp .env.example .env
php artisan key:generate

# create a database called sms locally, then set DB_* in .env
php artisan migrate --seed
php artisan storage:link

php artisan serve            # http://127.0.0.1:8000

2. Frontend

cd frontend
npm install
echo "VITE_API_BASE_URL=http://127.0.0.1:8000/api/v1" > .env.local
npm run dev                  # http://localhost:5173

3. Mobile app

Covered in full from Setting up Expo onward.

Testing on a real phone localhost means the phone itself, so it will never reach your computer. Use your machine's LAN address (http://192.168.1.20:8000/api/v1) in both the frontend and mobile config, and start Laravel with php artisan serve --host=0.0.0.0 so it accepts connections from the network.

07 · Install first

First login and setup checklist

Default credentials

A fresh install creates one account — the Super Admin who sets up everything else.

RoleEmailPassword
Super Adminsuperadmin@sms.compassword123

The four demo logins

If you want to look around as each kind of user before setting up your own school, the package can create one account per role. They are linked to each other on purpose — the teacher takes the class the student sits in, and the parent is that student's father — so signing in as each shows the same school from four sides rather than four empty dashboards.

RoleEmailPasswordWhat they see
Branch Adminadmin@demo.comDemo@2026Full control of one campus.
Teacherteacher@demo.comDemo@2026Their own classes, attendance and marks entry.
Parentparent@demo.comDemo@2026Their child's attendance, fees and results.
Studentstudent@demo.comDemo@2026Their own records, timetable and lectures.

They are created only when you ask for them. Add this to .env and migrate:

ALLOW_DEMO_SEED=true
php artisan migrate

Never set ALLOW_DEMO_SEED on a real school These four passwords are published in this documentation, so anyone who reads it can sign in. The flag exists so that demo accounts can never appear by accident on a live installation — leave it out of .env entirely, and they are never created.

If you switched it on to explore and now want to go live, remove the line and run php artisan migrate:rollback --step=1 to delete the four accounts, or delete them from the Users screen.

Change this before you go live Every copy of this system sold ships with the same seeded password, and it is published in this documentation. Sign in, open Profile → Change Password, and set a real one as your very first action.

Then work through this order

The system is hierarchical — classes need a branch, sections need a class, students need a section. Setting things up out of order means going back and redoing them.

  1. Branches — even a single-campus school needs one branch record. Everything else hangs off it.
  2. System Settings — school name, logo, currency symbol, address. These appear on vouchers, certificates and the sign-in page.
  3. Academic Session — create the current year (e.g. 2026–2027) and mark it active.
  4. Education Levels — Primary, Middle, Secondary. Classes are grouped under these.
  5. Classes and Sections — Grade 1, then its sections A and B.
  6. Subjects, then assign them to classes.
  7. Grading Policy — the grade boundaries used to convert marks into A/B/C. Result cards will not render without one.
  8. Fee Heads and Fee Policies — tuition, transport, admission fee, and which classes pay what.
  9. Teachers and Staff — the system generates their login passwords automatically.
  10. Students — added individually, or in bulk from a spreadsheet.
  11. Roles — review what each role can reach before handing out logins.

Want to explore with sample data first? php artisan db:seed --class=SchoolDataSeeder fills the system with a realistic demo school — classes, students, fee vouchers, exam results — so you can click through every screen before entering anything real. Run php artisan migrate:fresh --seed to wipe it and start clean.

08 · Configuration

Environment reference

Settings split into two places. Anything that must exist before the database does lives in .env; everything a school administrator might change themselves lives in System Settings inside the app.

Backend — apis/.env

KeyExampleWhat it does
APP_NAMEGreenfield SchoolUsed in emails and page titles.
APP_ENVproductionSet to local only while developing.
APP_KEYbase64:...Encryption key. Generated by php artisan key:generate. Never change it after go-live — it invalidates every session.
APP_DEBUGfalseMust be false in production.
APP_URLhttps://yourschool.com/apis/publicThe API's own address. Signed file links and password-reset links are built from it.
FRONTEND_URLhttps://yourschool.comWhere password-reset emails send people back to.
DB_DATABASEyouruser_smsDatabase name.
DB_USERNAMEyouruser_smsuserDatabase user.
DB_PASSWORD—Database password.
SANCTUM_TOKEN_EXPIRATION43200How long a login lasts, in minutes. Default is 30 days. Lower it for stricter security.
QUEUE_CONNECTIONdatabaseLeave as database unless you run Redis.
FILESYSTEM_DISKpublicWhere uploads go. Leave as is.

Frontend — .env.production

A single line, read at build time only:

VITE_API_BASE_URL=https://yourschool.com/apis/public/api/v1

Changing this needs a rebuild Vite bakes the value into the compiled JavaScript. Editing .env.production on the server does nothing — you must run npm run build again and re-upload dist/.

Settings that live in the app, not in .env

Sign in as Super Admin and open System Settings. These take effect immediately, with no rebuild or file edit:

School identity

Name, logo, address, phone, currency symbol. Used across vouchers, certificates, result cards and the sign-in page.

Theme colour

The primary colour of the whole portal and the mobile app's login screen.

WhatsApp credentials

Phone number ID, permanent token, webhook verify token. See WhatsApp Cloud API.

Twilio credentials

Account SID, auth token, from-number for automated voice calls.

Landing page

Hero style (banner, video or slider) and the order and visibility of each section on the public home page.

Late fee rules

Fixed amount or per-day charge applied automatically once a voucher passes its due date.

System Settings screen
System Settings — everything a school administrator can change without touching a file.

09 · Configuration

Cron and the queue

Several features are not triggered by anyone clicking a button — they happen on a schedule. All of them depend on one cron entry.

The one line you need

* * * * * cd /home/youruser/public_html/apis && php artisan schedule:run >> /dev/null 2>&1

It runs every minute, but that does not mean work happens every minute — Laravel checks whether anything is due and usually exits immediately.

What stops working without it

FeatureConsequence if cron is missing
Overdue fee remindersParents are never told their fee is late.
Late fee calculationLate charges never appear on vouchers.
WhatsApp queueMessages sit unsent forever. This is the most common support question.
Absence alertsParents are not notified when a child is marked absent.
Scheduled announcementsAnything scheduled for later never goes out.

Check it is actually running Open Settings → WhatsApp and look at the queue health readout. If it reports the scheduler as not alive, or the pending count climbs and never falls, your cron entry is wrong — usually the wrong path, or a PHP binary the host does not expose as plain php.

Many cPanel hosts need the full binary path instead: /usr/local/bin/ea-php83 artisan schedule:run.

Queue workers

Messages are queued rather than sent during the web request, so a slow WhatsApp API never makes the page hang. On shared hosting the cron entry above drains the queue for you and nothing further is needed.

On a VPS with heavy traffic, run a dedicated worker under supervisor instead:

php artisan queue:work --sleep=3 --tries=3 --max-time=3600

10 · Configuration

Email

Email is used for password resets and optional notifications. WhatsApp handles the day-to-day parent communication.

Using your host's mail server

MAIL_MAILER=smtp
MAIL_HOST=mail.yourschool.com
MAIL_PORT=587
MAIL_USERNAME=noreply@yourschool.com
MAIL_PASSWORD=your-mailbox-password
MAIL_SCHEME=tls
MAIL_FROM_ADDRESS=noreply@yourschool.com
MAIL_FROM_NAME="${APP_NAME}"

Using Gmail

Set MAIL_HOST=smtp.gmail.com and port 587. You must use a Google App Password, not your normal account password — regular passwords are rejected on accounts with 2-factor authentication.

Test before you rely on it Trigger a password reset from the sign-in page. If nothing arrives, check apis/storage/logs/laravel.log — SMTP failures are written there in full.

11 · Configuration

Files and permissions

The storage symlink

Laravel keeps uploads outside the public folder and exposes them through a symbolic link. php artisan storage:link creates it. If uploads save successfully but images show as broken, the link is missing or was lost during an FTP copy.

php artisan storage:link
# creates: apis/public/storage → apis/storage/app/public

Some shared hosts block symlink creation. If yours does, see the workaround in step 5 of the cPanel install.

Upload size limits

Lecture videos are the only large uploads. If they fail silently, PHP is refusing them before Laravel ever sees the request. In cPanel's Select PHP Version → Options:

SettingSuggested
upload_max_filesize64M
post_max_size64M
max_execution_time300
memory_limit256M

Backups

Two things need backing up, and both matter:

  • The database — every record in the system. Export from phpMyAdmin, or mysqldump -u user -p dbname > backup.sql.
  • apis/storage/app/public — every uploaded photograph, document and video. These are not in the database and cannot be recovered from it.

12 · Configuration

Security

The system ships hardened. This section explains what is already in place so you do not accidentally undo it, and what remains your responsibility.

What is already protected

ProtectionHow it works
Student photographs are not publicServed only through short-lived signed links that expire after 12 hours. Guessing a filename gets you nothing.
Teaching material is not publicSyllabuses, study material and lecture video use the same signed links.
Private documents are sealedDiaries, message attachments and accounting paperwork are unreachable from the web entirely.
Every action is permission-checkedBeing signed in is never enough on its own. Each endpoint requires the specific permission for that action.
Branch isolationA branch admin cannot see or touch another campus's data, even by editing a URL.
Login throttlingSix sign-in attempts per minute, then a lockout — password guessing is impractical.
Sessions expireLogins last 30 days by default, so a token copied from a shared computer does not work forever.
Passwords are hashedStored with bcrypt and never included in an API response except for the staff who administer that account type.

Do not delete the .htaccess in apis/public It carries the rules that keep student photographs and private documents off the open internet. Some migration tools strip dotfiles when copying between hosts — after any server move, confirm it is still there.

Running Nginx? Those rules do not apply at all, and you must copy the location block from the VPS section instead.

Your responsibilities

  • Change the seeded super admin password. It is public knowledge.
  • Install an SSL certificate. Without HTTPS, passwords cross the network in plain text. Most hosts offer free Let's Encrypt certificates in cPanel.
  • Set APP_DEBUG=false. Debug mode prints credentials onto error pages.
  • Keep the visible-password feature in mind. The system deliberately stores the generated login password in readable form, so the office can tell a parent what it is. Only staff who administer that account type can see it — but decide whether that policy suits your school.
  • Take backups. Nothing else on this list matters if the data is gone.

13 · Configuration

Branding and theme

Most rebranding needs no code at all.

Without touching code

In System Settings you can set the school name, upload a logo, and choose the primary colour. The colour flows through the entire web portal, the printed vouchers and certificates, and the mobile app's login screen.

Theme settings
Theme settings — the primary colour applies across web, print and mobile.

Per-branch branding

A school network can give each campus its own logo and details under Branch Settings, so a voucher printed at one campus carries that campus's identity.

Branch settings
Branch Settings — per-campus identity for multi-branch schools.

Deeper changes

  • Favicon and page title — frontend/index.html, then rebuild.
  • Fonts and base styles — frontend/src/index.css.
  • Landing page content — editable from System Settings; the layout lives in src/pages/SchoolLandingPage.jsx.
  • Printed documents — the certificate and voucher templates are in backend/.../resources/views/.

14 · Using the system

Roles and permissions

Five kinds of account, each seeing a portal built for what they actually do. What a person can reach is the combination of their account type and the permissions granted to their role.

RoleScopeTypical use
Super AdminEvery branch, every module, every settingThe owner or head of a school network.
Branch AdminTheir own campus only — full control within itA campus principal. Cannot create branches or edit roles.
TeacherTheir own classes and studentsMarking attendance, entering marks, setting assignments.
StudentTheir own records onlyAttendance, fees, results, timetable, lectures.
ParentAll of their children, in one accountFollowing every child without separate logins.

Custom roles

Under Roles you can build a role from individual permissions — an accounts clerk who sees fees but not exam results, or a receptionist limited to admissions and visitors. Permissions are grouped by module and split into view, create, update and delete.

Roles and permissions
Roles — permissions are granted per module and per action.

Branch scoping is automatic You never have to configure it. Once a user belongs to a branch, every list they open, every report they run and every record they can edit is filtered to that branch by the API itself — not merely hidden in the interface.

What each role sees

Admin portal
Admin
Teacher portal
Teacher
Student portal
Student
Parent portal
Parent

15 · Using the system

Dashboard

The first screen after signing in, and different for every role.

The admin dashboard shows live student, teacher and parent counts, fee collection against what was billed, today's attendance percentage, a fee breakdown chart, attendance by class, marks-entry progress across exams, upcoming exam tasks and recent admissions. Nothing here needs generating — it reflects the database as it stands.

Admin dashboard
The Super Admin dashboard.

16 · Using the system

Students and admissions

Students

Students

The full student directory. Search by name or registration number, filter by class, section or branch, and open any student for their complete history — attendance, fees, results and timetable in one profile. Adding a student creates their portal login automatically.

Students list

Admissions

Admissions

The intake pipeline for applicants who are not yet students. Applications arrive from the public form on your website or are entered by the office, move through review, and on approval convert into a fully enrolled student with a registration number — no re-typing.

Admissions

Bulk Enrollment

Students → Bulk Enrollment

Enroll a whole class from a spreadsheet instead of one at a time. Download the template, fill it in, upload it. Rows that fail validation are reported individually so you can fix just those.

Bulk enrollment

Parents

Parents

The parent directory. One parent account links to any number of children, so a family with three pupils has a single login showing all three. This link is also what decides who receives which WhatsApp alert.

Parents

Pre-Eleventh

Pre-Eleventh

A recruitment pipeline for students still at feeder schools who are being approached before 11th grade — school name, principal contact, and each prospect's status. When one enrolls, they convert into a student record in place.

Pre-Eleventh

Student Progress

Students → Progress

A running academic picture of one student: attendance trend, exam performance over time, and assignment completion — the screen to open before a parent meeting.

Student progress

17 · Using the system

Academics

Classes and Sections

Academics → Classes

The academic backbone. Classes sit under education levels; sections divide a class into teachable groups with their own class teacher and capacity.

Classes
Sections

Subjects

Academics → Subjects

Subjects are defined once, then assigned to the classes that study them, each with its own marks total. That assignment is what drives the timetable, marks entry and result cards.

Subjects

Academic Sessions

Academics → Academic Sessions

School years such as 2026–2027. Enrollments, fees and results are all scoped to a session, so last year's records stay intact and separate when you roll over.

Academic sessions

Timetable

Academics → Timetable

The weekly schedule, period by period. Build it per class and the system checks for teacher clashes as you go. Teachers, students and parents each see the same timetable filtered to themselves.

Timetable

Syllabus

Academics → Syllabus

The curriculum plan per subject and class, published to students and parents as a downloadable document.

Syllabus

Grading Policy

Academics → Grading Policy

The grade boundaries that turn a mark into a letter, plus the pass mark. Result cards and cumulative results read from here, so this must be set before publishing any result.

Grading policy

18 · Using the system

Attendance

Student Attendance

Attendance → Students

Daily marking, one section at a time, with present, absent, late and leave. The register loads with everyone marked present so a teacher only changes the exceptions. Marking a student absent can trigger an automatic WhatsApp message to the parent.

Student attendance

Attendance Overview

Attendance → Overview

The school-wide view of who has marked their register today and who has not. This is the screen that stops a class being quietly missed.

Attendance overview

Teacher Attendance

Attendance → Teachers

The same daily register for teaching staff, feeding leave records and payroll reporting.

Teacher attendance

19 · Using the system

Examinations

Exam Types and Exams

Exams

Exam types are the categories — Monthly Test, Mid Term, Final Term — and carry a weighting used when combining results. Individual exams are then created per class against a type.

Exam types
Exams

Exam Schedules

Exams → Schedules

The datesheet — which paper is on which date, at what time, in which room. Published to students and parents.

Exam schedules

Marks Entry

Exams → Marks Entry

Where teachers enter obtained marks. It opens on an upload-status board showing every class and subject and whether marks have been entered yet, so an administrator can see at a glance what is outstanding. Grades are applied automatically from the grading policy.

Marks entry

Results

Exams → Results

Position, percentage, grade and subject breakdown per student, printable as result cards for a whole class in one run. Cumulative results combine several exams using their type weightings into a term or annual result.

Results overview
Cumulative results

20 · Using the system

Fees and finance

The most involved module. Set it up in the order below and the rest follows automatically.

Fee Heads

Fees → Fee Heads

The individual charges a school levies — tuition, admission, transport, examination, laboratory. Each is defined once and marked as recurring monthly or one-off.

Fee heads

Fee Head Policies

Fees → Fee Head Policies

Which class pays how much for which head. This is what lets Grade 1 pay one tuition rate and Grade 10 another, without touching individual students.

Fee head policies

Fee Discounts

Fees → Discounts

Sibling discounts, staff-child concessions, scholarships and hardship waivers — as a percentage or a fixed amount, applied to a student and carried onto every voucher generated for them.

Fee discounts

Generating vouchers

Fees → Generate Vouchers

Produce a month's vouchers for a whole class or the whole school in one run. The system applies each student's policies and discounts, assigns a voucher number, sets the due date, and can notify parents on WhatsApp as they are issued.

Generate vouchers
Fee vouchers

Recording payments

Fees → Payments

Record full or partial payments against a voucher by cash, bank transfer or online gateway. The outstanding balance updates immediately and a receipt is available to print. Late fees are applied automatically once a voucher passes its due date, using the rule set in System Settings.

Fee payments

Fee Reports

Fees → Reports

Collection against billing by class, section or date range, plus a defaulters list. Everything exports to Excel.

Fee reports

Accounts

Finance → Accounts

General income and expenditure beyond student fees — salaries, utilities, maintenance, donations — so the school's overall financial position is in one place.

Accounts

21 · Using the system

E-Learning

Assignments

E-Learning → Assignments

Homework set by teachers with a due date and an optional attachment, visible to the class and to parents, with submission tracking.

Assignments

Study Materials

E-Learning → Study Materials

Notes, worksheets and reference documents shared with a class. Downloads are served through expiring signed links, so material does not leak to the open web.

Study materials

Lectures and Playlists

E-Learning → Lectures

Recorded video lessons, grouped into playlists by subject and class, with view tracking so a teacher can see who has actually watched.

Lectures
Lecture playlists

Online Classes

E-Learning → Online Classes

Scheduled live sessions with a join link, plus an in-class chat. Students see only their own class's sessions.

Online classes

22 · Using the system

Staff and HR

Teachers

Staff → Teachers

The teaching directory — qualifications, subjects taught, classes assigned and attendance record. Adding a teacher creates their portal login and generates a password the office can read out.

Teachers

Staff and Departments

Staff

Non-teaching staff — administration, accounts, transport, support — organised into departments, which also drive what each person can reach in the system.

Staff
Departments

Leaves

HR → Leaves

Leave applications from staff and teachers, with approval or rejection and a running balance per leave type.

Leaves

23 · Using the system

Communication

Messages

Communication → Messages

Internal messaging between staff, teachers and parents, with attachments. Attachments are stored privately and are not reachable by URL.

Messages

Diaries

Communication → Diaries

The daily homework diary, published per class and visible to students and parents — the digital replacement for the notebook that gets left at school.

Diaries

Events

Communication → Events

The school calendar — sports day, parent evenings, holidays — shown to the audience you choose and optionally announced over WhatsApp.

Events

Announcements and circulars

Announcements go to a chosen audience — a class, a whole branch, all parents — and can be delivered in the portal, over WhatsApp, or as an automated voice call. See WhatsApp and Twilio.

24 · Using the system

Administration

Branches

Administration → Branches

Campuses. Every record in the system belongs to one, and a Super Admin switches between them from the top bar to work on a single campus at a time.

Branches

Users

Administration → Users

Every login in the system, whatever its type, with role assignment and the ability to deactivate an account without deleting its history.

Users

Certificates

Administration → Certificates

Generate school leaving certificates, character certificates and ID cards, rendered with the student's photograph and the branch's own letterhead.

Certificates

Library

Administration → Library

Book catalogue with issue and return tracking against students and staff.

Library

Inventory and Visitors

Inventory tracks school assets and stock; the visitor log records who came to the school, who they met and when they left.

Inventory
Visitors

Activity Logs

Administration → Activity Logs

An audit trail of who changed what and when — the first place to look when a record does not say what someone expected.

Activity logs

25 · WhatsApp & calls

WhatsApp Cloud API

The system talks to parents on WhatsApp directly through Meta's official Cloud API — no third-party reseller, no per-message middleman, and no phone left plugged in at the office. Fee vouchers, payment confirmations, absence alerts and results all go out automatically.

Cost Meta gives every business a free allowance of service conversations each month, after which utility messages are charged per 24-hour conversation window at country rates. For a typical school this is a few dollars a month. You pay Meta directly; the system adds nothing.

What you need before starting

  • A Facebook Business Manager account.
  • A phone number that is not currently registered on WhatsApp — not WhatsApp Messenger, not WhatsApp Business. If the school's number is already in use on someone's phone, either delete that account first or use a fresh number.
  • Your backend already live on HTTPS. Meta refuses to send webhooks to a plain HTTP address.
  1. Create a Meta app

    Go to developers.facebook.com → My Apps → Create App. Choose type Business, give it your school's name, and link it to your Business Manager account.

  2. Add the WhatsApp product

    On the app dashboard find WhatsApp and click Set up. Meta creates a test number for you immediately, which is enough to try everything before you commit a real number.

    From the API Setup page, copy these three values:

    ValueWhere it appears
    Phone number IDUnder the "From" number. A long numeric string — not the phone number itself.
    WhatsApp Business Account IDJust below it on the same page.
    App IDTop of the app dashboard.
  3. Create a permanent access token

    The token shown on the API Setup page expires after 24 hours and is only useful for a quick test. For production you need a System User token, which never expires.

    In Business Settings → Users → System Users, create a system user with the Admin role. Click Add Assets and give it full control of your WhatsApp app and WhatsApp Business Account. Then Generate New Token, select your app, set expiry to Never, and tick these permissions:

    whatsapp_business_messaging   whatsapp_business_management

    Copy the token now Meta shows it exactly once. If you navigate away without copying it you must generate a new one.

  4. Enter the credentials in the system

    Sign in as Super Admin and open System Settings → WhatsApp. Fill in:

    FieldValue
    WhatsApp enabledOn
    Phone number IDfrom step 2
    Business account IDfrom step 2
    App IDfrom step 2
    Permanent tokenfrom step 3
    WhatsApp numberthe number in international format, e.g. 923001234567
    Webhook verify tokenany random string you invent — you will paste the same one into Meta next
    Default country codee.g. 92 for Pakistan. Local numbers stored as 03001234567 are converted using this.
    Messages per second1 is safe. Raising it risks Meta rate-limiting you.
  5. Point Meta's webhook at your server

    Back in the Meta app dashboard, open WhatsApp → Configuration and click Edit next to Webhook. Enter:

    Callback URLhttps://yourschool.com/apis/public/api/v1/whatsapp/webhook
    Verify tokenthe exact string you invented in step 4

    Click Verify and save. Meta calls your server straight away; if the tokens match it saves, and if they do not it shows an error immediately.

    Then click Manage and subscribe to the messages field. Without that subscription you can send messages but will never receive replies.

    Verification failing? Almost always one of three things: the URL is missing the /apis/public part, the site is not on HTTPS, or the verify token has a stray space. Check apis/storage/logs/laravel.log — the incoming attempt is recorded there.

  6. Add your real number

    Once testing works, go to WhatsApp → API Setup → Add phone number, enter the school's number and verify it by SMS or call. You will also need to complete Business Verification in Business Manager before you can message beyond a handful of test recipients — Meta usually takes a few days to review.

  7. Send a test

    Open Communication → WhatsApp in the portal and send a message to your own number. If it arrives, the integration is complete.

    Nothing arrives, no error shown? The message is queued, not lost — and the queue only moves when cron runs. Check Cron & queue first; a missing cron entry is the single most common cause.

26 · WhatsApp & calls

Message templates

WhatsApp does not let a business message someone out of the blue in free text. Anything you send first must use a template that Meta has approved in advance. The system ships with seven, already written to pass review.

TemplateSent when
fee_voucher_issuedA fee voucher is generated for a student.
fee_payment_receivedA payment is recorded against a voucher.
fee_overdue_reminderA voucher passes its due date.
attendance_absent_alertA student is marked absent or late.
exam_result_publishedAn exam result is published.
admission_status_updateAn admission application is approved or rejected.
school_noticeCirculars, events, diary entries and general announcements.

Getting them approved

Open Communication → WhatsApp Templates. Each template shows its exact wording and a filled-in sample. Create the matching template in Meta Business Manager → WhatsApp Manager → Message Templates, using the same name, the Utility category and language en_US, and paste the body exactly as shown.

WhatsApp templates
Each template shows the wording to submit and a sample of how it reads when filled in.

Why these get approved Meta rejects templates that read as marketing. All seven here are transactional — they tell a parent something factual about their own child — and are submitted under the Utility category, which is both more likely to be approved and cheaper to send. Approval usually takes minutes to a few hours.

The 24-hour window

Once a parent replies to you, a 24-hour window opens in which you may send ordinary free-text messages. After it closes you must use a template again. The inbox shows which conversations are still open.

27 · WhatsApp & calls

Inbox and the AI assistant

The inbox

When a parent replies, the message lands in Communication → WhatsApp Inbox. The system matches the sender's number against parent and student records and attaches the conversation to that family, so staff see who they are talking to and about which children. Numbers it cannot match are listed separately for someone to identify.

WhatsApp inbox
Incoming replies, matched to the family they came from.

The AI assistant

Optionally, an AI assistant answers parents automatically. It is deliberately narrow: it can only read the records of children belonging to the number that messaged, and it can only answer questions about attendance, fees, results, timetable and recent announcements.

Enable it under System Settings → AI, where you can also edit the prompt that sets its tone and rules.

It cannot leak another family's data Ownership is enforced in the API, not in the prompt. Before any record is fetched the system checks that the child is actually linked to the parent who sent the message. A cleverly worded question cannot talk its way past that.

Escalation

Anything the assistant cannot answer confidently — or any message where the parent asks for a person — is flagged for a human, and a staff member can take over the conversation from the inbox at any point.

28 · WhatsApp & calls

Twilio voice calls

For urgent announcements the system can telephone parents and read the notice aloud — useful when a school closes at short notice and a message might not be read in time.

  1. Get Twilio credentials

    Create an account at twilio.com and buy a phone number with voice capability. From the console dashboard copy your Account SID and Auth Token.

  2. Enter them in the system

    In System Settings → Twilio, switch it on and enter the Account SID, Auth Token, and the Twilio number to call from in international format.

  3. Send a voice announcement

    Create an announcement as usual and choose Voice call as the channel. The system places a call to each parent, and when it is answered a synthesised voice reads out the notice, naming the branch it came from.

What is tracked

OutcomeRecorded as
A person answers and hears the noticeDelivered
An answering machine picks upNot delivered — the notice is left as a message instead
No answer, busy, or failedFailed, with the reason

Both channels at once An announcement can go out as a WhatsApp message and a voice call together. The parent receives both, and each is tracked separately so you can see which actually reached them.

29 · Mobile app

Setting up Expo

The mobile app is built with Expo, which means you can run it on a real phone and produce an installable APK without installing Android Studio. The app covers every role — admin, teacher, student and parent — across more than 80 screens.

App login
Login
Admin app
Admin
Teacher app
Teacher
Student app
Student
Parent app
Parent

What you need

ToolVersionNotes
Node.js20 LTS or newerFrom nodejs.org.
Expo GolatestFree app from Play Store or App Store — for testing on your own phone.
Expo accountfreeSign up at expo.dev. Required to build an APK.
Android StudiooptionalOnly for an emulator or a fully offline build.

Install the dependencies

cd mobile
npm install

This takes a few minutes the first time. The project uses Expo SDK 54 with React Native 0.81.

If npm install fails Delete node_modules and package-lock.json and try again. If it still fails, run npx expo install --fix, which pins every package to the version that matches your Expo SDK.

30 · Mobile app

Pointing the app at your server

This is the one change every buyer must make. The app ships with a placeholder address, so until you set your own the app will fail to sign in with a network error — that is expected, not a fault.

Open mobile/src/services/api.js and replace the placeholder on line 5:

// mobile/src/services/api.js

// ships as:
const API_BASE_URL = 'https://your-domain.com/apis/public/api/v1';

// change to your own server:
const API_BASE_URL = 'https://yourschool.com/apis/public/api/v1';

The value must be the same API base URL you used for the web portal — everything up to and including /api/v1, with no trailing slash.

Which address to use while developing

Testing onUse
Your live serverhttps://yourschool.com/apis/public/api/v1
Android emulator, backend localhttp://10.0.2.2:8000/api/v1
iOS simulator, backend localhttp://localhost:8000/api/v1
A real phone, backend localhttp://192.168.1.20:8000/api/v1 — your computer's LAN address

A real phone cannot reach "localhost" On a phone, localhost means the phone. Find your computer's LAN address with ipconfig on Windows or ifconfig | grep inet on macOS, use that, and start Laravel with php artisan serve --host=0.0.0.0 so it accepts connections from other devices. Both devices must be on the same Wi-Fi.

Android blocks plain HTTP by default A release build will refuse http:// addresses silently — the app simply reports a network error. For production always use https://. This is another reason to install an SSL certificate before building your APK.

31 · Mobile app

Running the app

Start the development server

cd mobile
npx expo start

A QR code appears in the terminal along with a menu of shortcuts.

On your own phone — the quickest way

  1. Install Expo Go from the Play Store or App Store.
  2. Make sure the phone and the computer are on the same Wi-Fi network.
  3. Android: open Expo Go and scan the QR code. iPhone: scan it with the built-in Camera app.

The app loads on the phone in a few seconds, and edits you make on your computer appear instantly.

On an emulator or simulator

PressOpensRequires
aAndroid emulatorAndroid Studio with a virtual device created
iiOS simulatormacOS with Xcode
wWeb browsernothing — useful for a quick look
rReloads the app—
jOpens the debugger—

Different networks, or a stubborn firewall? Run npx expo start --tunnel. It routes through Expo's servers so the phone can reach your computer from anywhere. It is slower, so use it only when the normal mode will not connect.

When changes do not show up

Occasionally the bundler serves a stale copy. Clearing its cache fixes it:

npx expo start --clear

32 · Mobile app

App branding

Before building for a school, change the name, icon and package identifier. All of it lives in one file.

Open mobile/app.json:

{
  "expo": {
    "name": "Greenfield School",          // ships as "School Management System"
    "slug": "greenfield-school",
    "version": "1.0.0",
    "icon": "./assets/icon.png",
    "splash": {
      "image": "./assets/splash-icon.png",
      "backgroundColor": "#8d1682"        // your school colour
    },
    "android": {
      "package": "com.greenfield.school",  // ships as com.yourschool.sms — MUST be unique
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#8d1682"
      }
    },
    "ios": {
      "bundleIdentifier": "com.greenfield.school"
    }
  }
}

The package name must be yours before you publish

It ships as com.yourschool.sms, which is a placeholder. An app identifier can never be changed once published to Google Play, and two different schools cannot share one — so set it to your own reverse-domain form, such as com.greenfield.school, before your first build.

The extra.eas block has already been removed for you, so eas init will create a project under your own Expo account. If you ever see a project ID in app.json that you did not create, delete it — a build against someone else's account will fail.

Replacing the images

FileSizeUsed for
assets/icon.png1024 × 1024The main app icon.
assets/adaptive-icon.png1024 × 1024Android adaptive icon. Keep the logo in the middle 66% — the edges get cropped into a circle or squircle depending on the phone.
assets/splash-icon.png1284 × 2778The launch screen.

Keep the same filenames and PNG format, and the app picks them up on the next build.

33 · Mobile app

Building the APK

EAS Build compiles the app on Expo's servers, so you get a real installable APK without Android Studio, without a Mac, and without configuring signing keys yourself. The free tier is enough for a school.

  1. Install the EAS command line tool

    npm install -g eas-cli
  2. Sign in

    eas login

    Use the free account you created at expo.dev.

  3. Link the project to your account

    cd mobile
    eas init

    This writes a new project ID into app.json under your own account. Make sure you deleted the original author's extra.eas block first, as described in App branding.

  4. Tell EAS you want an APK, not an AAB

    This is the step people miss, so it is already configured for you — but it is worth understanding. By default EAS produces an .aab bundle, which is what Google Play wants but which cannot be installed directly on a phone. The preview profile in eas.json therefore asks for an APK. Check that it still reads like this:

    {
      "build": {
        "development": {
          "developmentClient": true,
          "distribution": "internal"
        },
        "preview": {
          "distribution": "internal",
          "android": {
            "buildType": "apk"        // ← this line gives you an APK
          }
        },
        "production": {
          "autoIncrement": true
        }
      }
    }
  5. Build it

    eas build --platform android --profile preview

    The first time, EAS offers to generate an Android keystore for you — answer yes and let it manage the signing key.

    Never lose that keystore Android identifies an app by its signing key. If you lose it you can never publish an update to the same app — users would have to uninstall and reinstall from scratch. EAS keeps it safe on your account; back it up with eas credentials if you prefer to hold your own copy.

    The build runs on Expo's servers and takes roughly 10–20 minutes. You can close the terminal — progress is at expo.dev under your project's Builds tab.

  6. Download and install

    When it finishes the terminal prints a download link, and the same link with a QR code is on the build page. Open that link on an Android phone to download the APK, then tap it to install.

    Android will warn about installing from an unknown source — that is normal for any app not delivered through the Play Store. Allow it for your browser and the install proceeds.

Building locally instead

If you would rather not use Expo's servers, and you have Android Studio with a JDK installed:

cd mobile
npx expo prebuild --platform android     # generates the native android/ folder
cd android
./gradlew assembleRelease

The APK appears at android/app/build/outputs/apk/release/app-release.apk. You must configure your own signing key for a release build — see the React Native documentation on signed APKs.

Rebuild whenever these change The API URL, the app name, the package name, the icon and the splash screen are all compiled into the binary. Change any of them and you need a new build — updating the server alone will not affect an APK already on a phone.

34 · Mobile app

Publishing to the stores

Google Play

  1. Create a developer account

    A one-off $25 fee at play.google.com/console.

  2. Build an AAB, not an APK

    Play requires the bundle format. The production profile already produces one:

    eas build --platform android --profile production
  3. Upload and fill in the listing

    Create the app in the Play Console, upload the .aab, and complete the store listing — description, screenshots, a 512×512 icon and a 1024×500 feature graphic. You must also complete the Data Safety form and provide a privacy policy URL; a school app collects personal data, so this is mandatory.

  4. Submit for review

    First reviews typically take a few days. Later updates are usually faster.

Submit straight from the terminal Once the Play Console listing exists, eas submit --platform android uploads each new build for you.

Apple App Store

Requires an Apple Developer account at $99 per year. Build with eas build --platform ios --profile production and submit with eas submit --platform ios. You do not need a Mac — EAS builds iOS on its own machines.

Many schools skip the stores entirely Distributing the APK directly — a download link on the school website, or sent to parents over WhatsApp — avoids the fees, the review delays and the privacy paperwork. It works perfectly well for an app only your own school community uses.

35 · Reference

API reference

A REST API under /api/v1, authenticated with Laravel Sanctum bearer tokens. The web portal and the mobile app both use it, so anything they can do, your own integration can do too.

Authenticating

POST /api/v1/login
Content-Type: application/json

{ "login": "superadmin@sms.com", "password": "your-password" }

The response carries a token. Send it on every subsequent request:

Authorization: Bearer 1|AbCdEf...

Tokens expire after 30 days by default — adjust with SANCTUM_TOKEN_EXPIRATION.

Response format

Every endpoint answers in the same shape, so a client only ever needs to handle one:

// success
{ "success": true,  "message": "Students retrieved successfully", "data": [ ... ] }

// failure
{ "success": false, "message": "Forbidden.", "errors": null }

// validation failure
{ "success": false, "message": "The given data was invalid.",
  "errors": { "email": ["This email is already registered."] } }

Status codes

CodeMeaning
200 / 201Success.
401Missing, invalid or expired token.
403Signed in, but lacking the permission for this action.
404Not found — or deliberately hidden from you.
422Validation failed. Details in errors.
429Rate limited. Applies to sign-in attempts.

Common endpoints

Method & pathPurpose
POST /loginSign in and receive a token.
POST /logoutRevoke the current token.
GET /meThe signed-in user, with roles and permissions.
GET /studentsStudents, paginated and branch-scoped.
GET /students/{id}One student's full profile.
GET /attendanceAttendance records, filterable by class, section and date.
POST /attendance/bulkMark a whole section at once.
GET /fee-vouchersVouchers with their payment status.
GET /examsExams and their schedules.
GET /dashboardThe aggregate figures behind the dashboard.
GET /public/settingsSchool name, logo and theme. No authentication — used by the sign-in page.

Branch scoping

A Super Admin may work on one campus at a time by sending a header. Users who already belong to a branch are scoped automatically and the header is ignored:

X-Branch-Id: 3

36 · Reference

Database and seeders

Migrations

159 migrations build the full schema. They are ordered, repeatable and safe to run on an existing installation — new versions of the system add migrations rather than asking you to edit tables by hand.

php artisan migrate              # apply anything new
php artisan migrate:status       # see what has run
php artisan migrate:fresh --seed # DESTROYS all data, rebuilds from scratch

migrate:fresh drops every table It is for development only. Running it on a live school erases every student, payment and result permanently.

The two SQL dumps

If you cannot run artisan at all, import one of these through phpMyAdmin instead of migrating. Both create all 98 tables.

FileContainsUse when
sms-clean-install.sqlSchema, all 135 permissions, roles, default settings, education levels, departments, grading policy, fee heads, and one super admin.You are setting up a real school and want to enter your own data.
sms-demo-data.sqlEverything above plus a full sample school — classes, sections, teachers, parents, several hundred students, exams and fee vouchers.You are evaluating the system and want to click through populated screens.

After importing either one Run php artisan key:generate, or set APP_KEY by hand. Without it every request fails. And if you imported the demo data, run php artisan migrate:fresh --seed before entering real records — otherwise your school will contain several hundred fictional students.

Seeders

SeederCreates
SuperAdminSeederThe first super admin account.
PermissionSeederEvery permission in the system.
RoleSeederDefault roles with sensible permission sets.
BranchSeederA starter branch.
EducationLevelSeeder, ClassSeeder, SectionSeeder, SubjectSeederA typical academic structure.
GradingPolicySeederStandard grade boundaries.
FeeHeadSeeder, FeeHeadPolicySeeder, FeeDiscountSeederA working fee structure.
SystemSettingSeederDefault settings, including the WhatsApp and Twilio placeholders.
SchoolDataSeederA full demo school for evaluation. Not run by default.
php artisan db:seed                                # the standard set
php artisan db:seed --class=SchoolDataSeeder       # add demo data

Going live after evaluating If you explored with demo data and now want to start clean, run php artisan migrate:fresh --seed before entering real records. That keeps the roles, permissions and settings while removing every demo student.

37 · Reference

Troubleshooting

Start with apis/storage/logs/laravel.log — the real reason is almost always written there in full, even when the browser shows something generic.

Installation

SymptomCause and fix
500 error on every API pageAlmost always permissions. Run chmod -R 775 storage bootstrap/cache. If that does not fix it, .env is missing or APP_KEY is empty — run php artisan key:generate.
Blank white web portalThe .htaccess did not upload. Enable "Show Hidden Files" in File Manager and check. Also confirm VITE_API_BASE_URL was set before you ran npm run build.
Portal loads, every page 404s on refreshSame missing .htaccess — the server is not routing unknown paths to index.html.
"Network Error" on the sign-in pageThe portal cannot reach the API. Open your API base URL directly in a browser: if it does not return JSON, the backend is the problem, not the portal.
CORS errors in the browser consoleFRONTEND_URL in .env does not match the address the portal is actually served from. They must match exactly, including https and any www.
Migrations fail part-wayUsually PHP below 8.2, or the database user lacking full privileges. Check both, then php artisan migrate:fresh --seed.

Files and images

SymptomCause and fix
Student photos show as brokenThe storage symlink is missing. Run php artisan storage:link. See Files and permissions for hosts that block symlinks.
Photos worked, then stopped after a server moveThe symlink did not survive the copy — recreate it. Some tools copy the link as an empty folder; delete it first, then relink.
Large uploads fail silentlyPHP's upload_max_filesize and post_max_size are too low. Raise both to 64M.
Downloads 404 after upgradingTeaching material is served through expiring signed links. Confirm APP_URL exactly matches the address the API is served from — a mismatch invalidates every signature.

WhatsApp

SymptomCause and fix
Messages queue but never sendCron is not running. This is the most common problem of all — see Cron & queue.
Webhook verification failsThe URL is missing /apis/public, the site is not on HTTPS, or the verify token does not match exactly.
Template message rejectedThe template is not approved yet, or its name in Meta does not match the name in the system character for character.
Replies never arrive in the inboxYou did not subscribe to the messages field under WhatsApp → Configuration → Manage.
Works for your number, nobody else'sYou are still on the test number, which can only message a few pre-approved recipients. Add a real number and complete Business Verification.

Mobile app

SymptomCause and fix
App shows the demo school's dataAPI_BASE_URL in src/services/api.js was never changed. See Pointing the app at your server.
Network error in the built APK, fine in Expo GoYou are using an http:// address. Android blocks cleartext traffic in release builds — use HTTPS.
Expo Go cannot connectPhone and computer are on different networks. Try npx expo start --tunnel.
Changes do not appearStale bundler cache. Restart with npx expo start --clear.
EAS build fails immediatelyUsually the package name still contains "anonymous", or extra.eas.projectId points at an account you do not own. Fix both per App branding.
The build produced an .aab you cannot installThat is the Play Store format. Add "android": { "buildType": "apk" } to your build profile — see Building the APK.

Useful commands

php artisan config:clear     # after editing .env
php artisan cache:clear
php artisan route:clear
php artisan optimize         # re-cache everything for production
php artisan migrate:status   # which migrations have run
tail -f storage/logs/laravel.log

Always clear config after editing .env In production Laravel caches configuration. Until you run php artisan config:clear, your edits are ignored — which makes it look as though a setting simply does not work.

38 · Reference

Support

Before getting in touch

Most problems are answered in Troubleshooting. If yours is not, having these ready will get it solved in one reply rather than five:

  • What you were doing when it happened, and what you expected instead.
  • The last 30 lines of apis/storage/logs/laravel.log.
  • Your PHP version, and whether you are on shared hosting, a VPS, or local.
  • A screenshot, including the browser console if it is a portal problem (F12 → Console).

What support covers

Included

Bug fixes, installation help, clarification of anything in this documentation, and help with the WhatsApp and Twilio setup.

Not included

Custom feature development, changes to your own modifications, server administration, and third-party costs such as Meta message fees or Play Store registration.

Keeping the system updated

  1. Back up the database and apis/storage/app/public.
  2. Replace the application files, keeping your .env.
  3. Run php artisan migrate — never migrate:fresh.
  4. Run php artisan config:clear and php artisan optimize.
  5. Rebuild and re-upload the web portal, and rebuild the mobile app if it changed.

School Management System — complete documentation, version 1.0.
Laravel 12 · React 19 · Expo SDK 54. Thank you for your purchase.