Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/J0S3-LEON/pf_pruebasAseguramientoCalid_SDD/llms.txt

Use this file to discover all available pages before exploring further.

MindFlow’s notification system runs entirely as a server-side background process. The NotificationService executes an hourly cron job that scans for upcoming task deadlines and dispatches reminders to eligible students. There is no public HTTP endpoint for notifications — the NotificationModule registers only a service provider, with no controller. Frontend dashboards learn about notification activity through the fatigueHistory and task fields already available on GET /api/v1/dashboard.
No public endpoint exists for notifications. The NotificationModule contains only a NotificationService (cron job) and no controller. There is no GET /api/v1/notifications or any other HTTP route under /api/v1/notifications. Notification dispatch is triggered exclusively by the backend scheduler.

How the cron job works

The @Cron(CronExpression.EVERY_HOUR) decorator on dispatchReminders() causes NestJS’s built-in scheduler (registered via ScheduleModule.forRoot() in AppModule) to invoke the method once per hour. Each run follows this flow:
  1. Find eligible tasks — queries all tasks where isDeleted = false, deadline falls within the next 24 hours, and at least one micro-objective has isCompleted = false and isAuditOnly = false.
  2. Per student–task pair — calls processTaskNotification(studentId, taskId, now) for each eligible task.
  3. Session suppression check — if the student has an active EMA session (isActive = true), the notification is suppressed and a NotificationLog entry is persisted with status = 'suppressed' and attemptCount = 0.
  4. Frequency cap check — counts sent notifications for the student within the last 24-hour rolling window. If the count is already 3 or more, the run is skipped without creating a new log entry.
  5. Retry resolution — looks for an existing NotificationLog entry for the same student–task pair with status IN ('pending', 'failed') and attemptCount < 3. If one exists, it is updated rather than a new entry being created.
  6. Dispatch attempt — calls deliver(studentId, taskId). On success the log entry is updated to status = 'sent'. On failure, attemptCount is incremented; once attemptCount reaches 3 the entry is set to status = 'failed' and no further retries occur.

NotificationLog database record

Every notification event — whether sent, suppressed, pending, or failed — is persisted to the notification_logs table via PrismaService. The schema for each record is:
FieldTypeDescription
idstring (UUID)Primary key, server-generated.
studentIdstring (UUID)The student this notification targets.
taskIdstring (UUID)The task that triggered the reminder.
statusstringOne of pending, sent, failed, or suppressed. See the status reference below.
attemptCountintegerNumber of dispatch attempts made. Range: 0 (suppressed) to 3 (max attempts exhausted).
dispatchedAtUtcDateTimeUTC timestamp of the most recent dispatch attempt or suppression event.
updatedAtDateTimeAuto-updated by Prisma on every write.
Example NotificationLog record (internal)
{
  "id": "e5f6a7b8-c9d0-1234-efab-567890123456",
  "studentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "taskId": "3f7e1b2c-4a5d-4e8f-9c0b-1a2b3c4d5e6f",
  "status": "sent",
  "attemptCount": 1,
  "dispatchedAtUtc": "2025-07-25T08:00:00.000Z",
  "updatedAt": "2025-07-25T08:00:00.000Z"
}

Status values

StatusMeaning
pendingA dispatch was attempted but has not yet succeeded. The cron job will retry on its next run, provided attemptCount < 3.
sentThe notification was delivered successfully.
failedAll 3 consecutive delivery attempts failed. No further retries will be made for this log entry.
suppressedDispatch was skipped because the student had an active EMA session at the time the cron ran. The attempt does not count toward the frequency cap.
A failed status is final. Once a NotificationLog entry reaches attemptCount = 3 with no successful delivery, the cron job will not retry it. A new log entry may be created in a future cron run if the task is still within the 24-hour deadline window and the student has not exceeded the daily frequency cap.

Dispatch rules reference

RuleDetail
Cron frequencyEvery hour (CronExpression.EVERY_HOUR).
Deadline windowTasks with deadline within the next 24 hours from the time of the cron run.
Eligibility filterTask must have isDeleted = false and at least one micro-objective with isCompleted = false and isAuditOnly = false.
Session suppressionIf the student has isActive = true on any session, the notification is suppressed and logged — no delivery is attempted.
Frequency capMaximum 3 sent notifications per student per 24-hour rolling window. Suppressed notifications do not count toward this cap.
Max retry attempts3 per student–task pair per frequency window. After 3 failed attempts the entry is permanently marked failed.
Persistence SLAEvery notification event is written to notification_logs in < 3 seconds.

Extending the delivery mechanism

The deliver(studentId, taskId) private method is the integration point for adding real push, email, or WebSocket delivery. In the current implementation it logs the event and returns true. To add a real channel:
  1. Inject the external notification provider (e.g. a mailer or push service) into NotificationService.
  2. Replace the log-and-return-true body of deliver() with a call to that provider.
  3. Let exceptions propagate — the caller already catches failures and handles the attemptCount increment.
Example — adding an email channel
// In NotificationService.deliver()
private async deliver(studentId: string, taskId: string): Promise<boolean> {
  try {
    await this.mailerService.sendDeadlineReminder(studentId, taskId);
    return true;
  } catch {
    return false;
  }
}

Build docs developers (and LLMs) love