Register the alias
The server targets a student by the OneSignalexternal_id alias. That alias must be the Supabase
auth.users.id, which is the same value as id in the GET /api/me response.
OneSignal.login after every sign-in. Call OneSignal.logout on sign-out. A student with no
alias gets no proactive message. The chat still works.
What arrives
Every notification has the titleLala. The body is the message text, in Turkish. The data
object identifies the source:
Route every one of these to the chat screen. They are chat messages, not system alerts.
The two jobs
1
Scheduled messages, every minute
A job drains the due rows of
scheduled_messages. Each row is claimed with
FOR UPDATE SKIP LOCKED, so two server replicas cannot send the same message twice.2
Silence check-in, 07:00 Europe/Istanbul
A daily job finds students who have been silent for 48 hours. It generates one short message
for each, and sends it once.
The history is the source of truth
The server appends every delivered message to the conversation history with the roleassistant.
The push is only the announcement. A push that fails to deliver does not remove the message.
Call GET /api/chat/history when the student opens the notification, and when the application
returns to the foreground. Do not build the chat list from notification payloads.
Push delivery failures never break chat. The server logs a failed push and continues.
Times
Lala schedules in Turkey time (UTC+3). Timestamps in the API are UTC, ISO 8601. Convert toEurope/Istanbul for display.