This module provides:
- User-managed reminder tasks (
/api/v1/tasks) - In-app notifications inbox (
/api/v1/notifications) - Realtime notifications stream (
/api/v1/notifications/stream)
Reminder execution flow:
- Create/update a task
- Reminders scheduler enqueues due jobs into Jobs
- Reminder jobs worker creates
reminder_duenotifications - Jobs notifications bridge maps terminal job events into
job_completed/job_failednotifications
Routes require AuthNZ permissions:
tasks.readtasks.controlnotifications.readnotifications.control
Rate-limit keys:
tasks.read,tasks.controlnotifications.read,notifications.control
Base: /api/v1/tasks
POST /api/v1/tasks- Permission:
tasks.control - Response:
201
Example request:
{
"title": "Review export output",
"body": "Validate citations and rerun if needed",
"schedule_kind": "one_time",
"run_at": "2026-03-01T10:00:00+00:00",
"enabled": true,
"link_type": "item",
"link_id": "item-42"
}Recurring example:
{
"title": "Weekly follow-up",
"schedule_kind": "recurring",
"cron": "0 9 * * MON",
"timezone": "America/New_York",
"enabled": true
}GET /api/v1/tasks- Permission:
tasks.read
GET /api/v1/tasks/{task_id}- Permission:
tasks.read
PATCH /api/v1/tasks/{task_id}- Permission:
tasks.control
Example patch:
{
"enabled": false,
"title": "Updated title"
}DELETE /api/v1/tasks/{task_id}- Permission:
tasks.control
Base: /api/v1/notifications
GET /api/v1/notifications?limit=100&offset=0&include_archived=false- Permission:
notifications.read - Archived notification items include
snooze_untilwhen the notification has an active snooze reminder backing it.
GET /api/v1/notifications/unread-count- Permission:
notifications.read
POST /api/v1/notifications/mark-read- Permission:
notifications.control
Request:
{
"ids": [101, 102]
}POST /api/v1/notifications/{notification_id}/dismiss- Permission:
notifications.control
POST /api/v1/notifications/{notification_id}/snooze- Permission:
notifications.control - Creates the reminder task and dismisses the source notification from the active inbox.
Request:
{
"minutes": 30
}Response:
{
"task_id": "task_abc123",
"run_at": "2026-03-01T10:30:00+00:00"
}DELETE /api/v1/notifications/{notification_id}/snooze- Permission:
notifications.control
Response:
{
"cancelled": true,
"deleted_tasks": 1
}GET /api/v1/notifications/preferencesPATCH /api/v1/notifications/preferences- Permissions:
notifications.read(GET),notifications.control(PATCH)
Patch body fields:
reminder_enabledjob_completed_enabledjob_failed_enabled
Endpoint:
GET /api/v1/notifications/stream- Permission:
notifications.read - Content type:
text/event-stream
Cursoring:
- Preferred:
Last-Event-IDheader - Alternate:
afterquery param - Server emits
id:per event for replay/resume
Event types:
notificationnotifications_coalescedreset_requiredheartbeat
Notification frame example:
id: 1205
event: notification
data: {"event_id":1205,"notification_id":1205,"kind":"job_failed","created_at":"2026-03-01T10:00:00+00:00","title":"Job failed","message":"chatbooks/export failed.","severity":"error"}
reset_required example:
event: reset_required
data: {"reason":"cursor_too_old","min_event_id":700,"latest_event_id":1205}
REMINDERS_SCHEDULER_ENABLED(default: disabled)REMINDERS_SCHEDULER_TZ(default:UTC)REMINDERS_SCHEDULER_RESCAN_SEC(default:300, minimum30)- Task create/update/delete requests attempt immediate in-process scheduler reconciliation when a scheduler instance is running; periodic rescan remains as safety sync.
REMINDER_JOBS_WORKER_ENABLED(default: disabled)REMINDER_JOBS_QUEUE(default:default)
JOBS_NOTIFICATIONS_BRIDGE_ENABLED(default: disabled)JOBS_NOTIFICATIONS_CONSUMER_NAME(default:jobs_notifications_bridge)JOBS_NOTIFICATIONS_LEASE_OWNER_ID(default: auto-generated from pid)JOBS_NOTIFICATIONS_LEASE_SECONDS(default:30, minimum5)JOBS_NOTIFICATIONS_BATCH_SIZE(default:200, clamped1..500)JOBS_NOTIFICATIONS_POLL_INTERVAL_SEC(default:1.0, minimum0.01)JOBS_NOTIFICATIONS_BRIDGE_STATE_USER_ID(default: single-user ID, fallback1)
NOTIFICATIONS_STREAM_REPLAY_WINDOW(default:500, clamped1..5000)NOTIFICATIONS_STREAM_BATCH_SIZE(default:200, clamped1..1000)NOTIFICATIONS_STREAM_BURST_THRESHOLD(default:50, clamped1..1000)NOTIFICATIONS_STREAM_POLL_SEC(default:1.0, minimum0.01)NOTIFICATIONS_STREAM_HEARTBEAT_SEC(default:10.0, minimum0.05)NOTIFICATIONS_STREAM_MAX_DURATION_SEC(default: disabled when unset/<=0)NOTIFICATIONS_STREAM_FLOOR_CHECK_EVERY_POLLS(default:15, clamped1..3600)NOTIFICATIONS_STREAM_SEND_TIMEOUT_SEC(default:1.0, minimum0.05)
NOTIFICATIONS_PRUNE_ENABLED(default: disabled)NOTIFICATIONS_PRUNE_INTERVAL_SEC(default:3600, minimum60)NOTIFICATIONS_PRUNE_READ_DISMISSED_DAYS(default:30)NOTIFICATIONS_PRUNE_ARCHIVE_GRACE_DAYS(default:7)NOTIFICATIONS_RETENTION_DAYS_REMINDER_DUE(default:90)NOTIFICATIONS_RETENTION_DAYS_REMINDER_FAILED(default:90)NOTIFICATIONS_RETENTION_DAYS_JOB_COMPLETED(default:30)NOTIFICATIONS_RETENTION_DAYS_JOB_FAILED(default:60)
- Notifications are deduped via
dedupe_key(jobs bridge usesjobs-event:{event_id}). - Current implementation does not enforce an active reminder task cap at API level.