DocsAdministration

Scheduled Jobs

Cron endpoints for background reminders and maintenance

OptiLearn exposes HTTP-triggered cron endpoints that run scheduled work (reminders, cleanup, etc). You wire them to any external scheduler — cron-job.org, GitHub Actions, a Coolify cron task, or a plain curl in a crontab.

Security

Every cron endpoint requires a bearer token that matches the CRON_SECRET environment variable:

Authorization: Bearer <CRON_SECRET>

If CRON_SECRET is unset, the endpoint will accept unauthenticated requests (useful in dev). Always set it in production.

Available Endpoints

GET /api/cron/assignment-reminders

Intended schedule: Every 1 hour

For every assignment with a dueDate in the next 24 hours, and for every active enrollment in that course that hasn't submitted, create an in-app reminder notification. Idempotent — each (assignment, student) pair is reminded at most once regardless of how often the cron runs.

Response:

{
  "data": {
    "processedAssignments": 12,
    "remindersQueued": 47,
    "studentsSkipped": 103
  }
}
  • processedAssignments — assignments inspected in this run
  • remindersQueued — in-app notifications created
  • studentsSkipped — students who already submitted, already reminded, or opted out
Note

This endpoint only creates in-app notifications. Email reminders require institution-slug resolution that isn't available in a cron context; use the live enrollment/progress handlers for transactional emails.

Setting It Up

Option 1 — Coolify cron task

In your Coolify app settings, add a scheduled task:

  • Schedule: 0 * * * * (every hour)
  • Command: curl -H "Authorization: Bearer $CRON_SECRET" https://learn.opticrm.app/api/cron/assignment-reminders

Option 2 — GitHub Actions

name: OptiLearn cron
on:
  schedule:
    - cron: '0 * * * *'
jobs:
  reminders:
    runs-on: ubuntu-latest
    steps:
      - run: |
          curl -f -H "Authorization: Bearer ${{ secrets.CRON_SECRET }}" \
            https://learn.opticrm.app/api/cron/assignment-reminders

Option 3 — cron-job.org

  1. Create a new cron job at cron-job.org
  2. URL: https://learn.opticrm.app/api/cron/assignment-reminders
  3. Schedule: Every 1 hour
  4. Request headers: Authorization: Bearer <your secret>

Monitoring

All cron endpoints return JSON. Log the output of your scheduler so you can see how many reminders were created per run. Zeros are normal for quiet periods — a sudden drop from a busy institution is the signal to investigate.

Adding New Cron Endpoints

Cron endpoints follow a simple pattern:

// src/app/api/cron/my-job/route.ts
export async function GET(request: NextRequest) {
  const auth = request.headers.get("authorization");
  const secret = process.env.CRON_SECRET;
  if (secret && auth !== `Bearer ${secret}`) {
    return NextResponse.json(errorResponse(API_ERRORS.AUTH_REQUIRED, "..."), { status: 401 });
  }
  // ... do the work
  return NextResponse.json(successResponse({ ... }));
}

Key rules:

  • Idempotent — running the cron twice in a row must be safe
  • Bounded — cap the work per run so long queues don't time out
  • No session — use prisma directly, never requireAuth()
  • Log counters — return concrete numbers in the response so schedulers can alert on anomalies