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 runremindersQueued— in-app notifications createdstudentsSkipped— students who already submitted, already reminded, or opted out
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
- Create a new cron job at cron-job.org
- URL:
https://learn.opticrm.app/api/cron/assignment-reminders - Schedule: Every 1 hour
- 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
prismadirectly, neverrequireAuth() - Log counters — return concrete numbers in the response so schedulers can alert on anomalies