Skip to main content
Schoolyi
School librarian arranging books and planning cards in a bright library

Browse docs

Fees & finance

Fees cron and online pay ops

The daily operations job behind autopay reminders and the dunning ladder, the environment it needs, and what stops happening when nobody schedules it.

Fees & finance guide for day and boarding schools.

Last updated August 29, 2026

What this is for

Every time-based behaviour in the fees module is driven by one shell script, scripts/run-school-cron.sh, which the school schedules on its own server. The application does not run an internal scheduler and Next.js does not keep a background worker alive between requests, so an installation with a complete fee structure, a configured dunning ladder and active autopay mandates will still do nothing on a timer until that script is wired into cron. This is the single most commonly missed step in a go-live, and it fails silently: collections look fine, and nobody notices that no reminder has ever been sent.

Set it up once, at the end of fee configuration and before the first collection cycle opens. The script authenticates with a shared secret rather than a user session, so it can run unattended from crontab, a systemd timer, or any external scheduler that can make an HTTP POST. Run it manually once first and read the printed output - each call echoes ok or failed, and a failed line is the earliest warning you will get.

Prerequisites

  • FEES_CRON_SECRET set in the server environment. The script sends it as the x-fees-cron-secret header; without it (and without a SESSION_COOKIE fallback) the script exits immediately with a non-zero status.
  • BASE_URL pointing at the running instance. It defaults to http://localhost:3000, which is usually wrong for a scheduled job on a reverse-proxied host.
  • ADMISSIONS_CRON_SECRET, only if you want admission offers to expire automatically. That step is skipped entirely when the variable is empty.
  • SMTP configured and passing /settings/email-test, because the dunning ladder sends reminder email from the second step onwards.
  • A dunning ladder saved at /fees/settings/dunning, and fee instances generated at /fees/bulk-generate. The job has nothing to act on otherwise.

Exactly what the script calls

EndpointAuth it sendsEffect
POST /api/fees/autopay/run-duex-fees-cron-secretIn-app autopay reminders for due mandates
POST /api/fees/dunning/runx-fees-cron-secretAdvances overdue fee lines one ladder step and notifies
POST /api/homework-assignments/due-reminders/runx-fees-cron-secretRejected: this route requires a signed-in staff session
POST /api/notices/cron/publish-scheduledx-fees-cron-secretPublishes due scheduled notices and notifies the audience
POST /api/admissions/cron/expire-offersx-admissions-cron-secretExpires pending offers past their deadline; skipped when unset
POST /api/transport/retention/purgex-fees-cron-secretPurges transport records past the retention window
POST /api/conversations/retention/purgex-fees-cron-secretTombstones chat messages past the retention window
POST /api/library/cron/expire-reservationsx-fees-cron-secretReleases library reservations that were never collected
POST /api/activity-log/retention/purgex-fees-cron-secretPurges audit rows past retention; heartbeat activity.retention-purge
POST /api/ops/cron/verify-activity-chainx-fees-cron-secretVerifies the activity-log hash chain over the last 2000 rows
POST /api/ops/cron/evaluate-alertsx-fees-cron-secretEvaluates the SLO alert rules, including stale cron heartbeats

What autopay and dunning actually do

Autopay does not move money. The runner picks up mandates with status ACTIVE whose nextRunAt is null or already past, takes up to five pending, partial or overdue fee instances per mandate, skips any balance above the mandate maxAmount, and writes an in-app FEE_AUTOPAY reminder to the linked parent portal user (or the student when no parent is linked). It then sets lastRunAt and moves nextRunAt to the mandate day of month in the following month, capped at day 28. Recurring provider charges are not implemented; externalMandateId is stored for future gateway work only. Treat autopay as a scheduled nudge to pay, not as a direct debit.

Dunning is the escalation ladder. Each run compares days overdue against the saved steps, and only advances a fee instance when the matching step is further along than its stored dunningStepIndex, so running the job twice in a day does not double-notify. Advancing writes dunningStepIndex and lastDunningAt and flips the instance status to OVERDUE. The in-app notification is addressed to the student account; the reminder email goes to the primary active guardian, falling back to the student email address.

Default stepChannelsWhat the family actually receives
1 day overdueIN_APPIn-app notification only
7 days overdueIN_APP, EMAILIn-app notification plus a fee reminder email
15 days overdueIN_APP, EMAIL, SMSAs above; the SMS step only writes a server log line
30 days overdueIN_APP, EMAIL, SMS, WHATSAPPAs above; WHATSAPP writes an in-app note holding a wa.me link for a human to open

Who owns it, and running it by hand

  • Scheduling the script is a server administrator task, not an in-app one. It needs shell access and the environment file.
  • Both fee endpoints also accept a staff session with fee operations access: Platform Admin, Principal, Vice Principal, Support Staff - Finance, or Admin. Students and parents are refused.
  • Dry run first: GET /api/fees/dunning/preview, or Preview affected at /fees/settings/dunning, reports how many fee lines and students would advance without changing anything.
  • Run dunning now on /fees/settings/dunning posts the real run. Run due mandates on /fees/autopay does the same for autopay.
  • Every run writes a CRON entry to the activity log with the actor recorded as null when the cron secret was used.

What breaks if it is never scheduled

  • No overdue escalation, ever. Fee instances stay at dunningStepIndex 0, lastDunningAt is never set, and no reminder email is sent. /fees/defaulters still lists unpaid lines past their due date, but the dunning step and last-contacted columns stay empty, so nobody can tell which families have been chased.
  • No autopay reminders. Mandates sit at their original nextRunAt and guardians who set up autopay hear nothing.
  • Scheduled notices are late rather than lost: loading the notices list also publishes anything due, so a scheduled notice goes out the next time somebody opens /notices rather than at the time it was scheduled for.
  • Admission offers never expire on their own, so seats stay reserved against a deadline that has passed.
  • Retention purges and the activity-chain verification never run, which quietly weakens both the data-retention position and the audit guarantee.

Online pay mode and receipt email

  • FEES_ONLINE_PAYMENT_MODE selects the checkout: simulated, razorpay, stripe, telr, or disabled. Mirror it in NEXT_PUBLIC_FEES_ONLINE_PAYMENT_MODE so the parent-facing page agrees with the server.
  • Simulated mode needs no keys and is the right choice for staging and for schools collecting at the counter. A live gateway additionally needs its own keys - RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET and NEXT_PUBLIC_RAZORPAY_KEY_ID for Razorpay, with RAZORPAY_WEBHOOK_SECRET for signature verification.
  • Review the resolved mode, which keys are present, and which gateways suit the school region at /fees/settings/payment-gateway. No secret values are shown.
  • Receipt email uses the same SMTP transport as everything else. A payment whose receipt email failed carries [RECEIPT_EMAIL:FAILED]; resend it with POST /api/fees/receipts/[receiptNumber]/email, or from the Receipts tab at /fees/payment-history.

Limits

  • There is no in-app scheduler, no job queue, and no retry: a failed call is simply reported and skipped until the next run.
  • The runs are installation-wide. Dunning and autopay scan every matching fee instance in the database rather than one school, which matters on a multi-school install.
  • The script has no locking. Do not overlap runs; pick one time of day, for example 05:30 local.
  • Digest or quiet-hours batching does not exist, so a step that fires sends immediately.

Common questions

Quick answers in plain language.

Why has no fee reminder been sent even though the dunning ladder is configured?+

Almost always because scripts/run-school-cron.sh is not scheduled. Saving a ladder at /fees/settings/dunning stores configuration only; POST /api/fees/dunning/run has to be called for anything to advance. Confirm the fees.dunning heartbeat on /ops, and use Preview affected to check there are overdue lines to act on.

Does fee autopay charge a card or bank mandate automatically?+

No. POST /api/fees/autopay/run-due only creates in-app FEE_AUTOPAY reminders for due mandates and moves nextRunAt forward one month. No provider is charged, and externalMandateId is stored for future gateway work. Families still pay at /fees/pay-online or at the counter.

Is it safe to run the daily cron job more than once a day?+

Yes for fees. Dunning only advances a fee instance when the matching step is beyond its stored dunningStepIndex, and autopay pushes nextRunAt a month ahead after each pass, so a second run in the same day is mostly a no-op. Avoid overlapping runs though, because the script takes no lock.

Which environment variables does the daily job need?+

FEES_CRON_SECRET for the fee, notice, retention and observability calls, BASE_URL for the target host, and optionally ADMISSIONS_CRON_SECRET for offer expiry. SESSION_COOKIE can replace the fee secret for a manual run. Missing both the secret and the cookie makes the script exit before calling anything.

How do I run fee autopay and dunning on a schedule?+

Set FEES_CRON_SECRET and run bash scripts/run-school-cron.sh daily (e.g. 05:30 IST). The script calls autopay run-due, dunning, homework reminders, and optional admissions offer expiry.

Related searches

School leaders and IT teams often search for: how to fees cron workflow in school management software, best fee management software for K-12 schools, Schoolyi fees cron workflow guide, fees cron workflow step by step, online fee collection software, school billing software, and fee receipt software schools.