Notifications API
The Notifications API manages the full lifecycle of push notifications: registering browser push subscriptions via the Web Push (VAPID) protocol, configuring per-category delivery preferences, and querying notification history. Categories are split into global (location-independent) and location-specific groups, each with granular controls for lead times, quiet hours, and scheduling.
Notification Categories
Section titled “Notification Categories”All endpoints that accept a category parameter validate against the following set:
Global Categories
Section titled “Global Categories”These apply regardless of observer location.
| Category | Description |
|---|---|
launch | Upcoming rocket launches |
reentry | Tracked reentry events |
space_weather | General space weather alerts |
solar_flare | Solar flare detections |
noaa_scales | NOAA space weather scale changes (R/S/G) |
neo_approach | Near-Earth object close approaches |
conjunction | Satellite conjunction warnings |
hf_conditions | HF radio propagation condition changes |
daily_digest | Scheduled daily summary of upcoming events |
Location-Specific Categories
Section titled “Location-Specific Categories”These require a location_id and deliver notifications relevant to that observer position.
| Category | Description |
|---|---|
satellite_pass | Visible satellite passes overhead |
aurora | Aurora visibility forecasts for the location |
observing | Favorable observing conditions (clear skies, low wind) |
sonde_nearby | Radiosonde landings predicted near the location |
whats_up | Notable objects currently above the horizon |
Push Subscriptions
Section titled “Push Subscriptions”Get VAPID Public Key
Section titled “Get VAPID Public Key”Returns the server’s VAPID public key, which the browser needs to create a push subscription. This key is configured server-side via the VAPID_PUBLIC_KEY environment variable.
GET /api/notifications/vapid-keyResponse
Section titled “Response”{ "public_key": "BNz..."}Returns 503 if VAPID keys have not been configured on the server.
Subscribe
Section titled “Subscribe”Registers a push subscription endpoint. If the endpoint URL already exists in the database, the subscription is reactivated and its keys are updated (upsert behavior).
POST /api/notifications/subscribeRequest Body
Section titled “Request Body”{ "endpoint": "https://fcm.googleapis.com/fcm/send/...", "keys": { "p256dh": "BNz...", "auth": "abc..." }, "user_agent": "Mozilla/5.0 ..."}| Field | Type | Required | Description |
|---|---|---|---|
endpoint | string | Yes | The push service URL from the browser subscription |
keys.p256dh | string | Yes | Client public key for message encryption |
keys.auth | string | Yes | Authentication secret |
user_agent | string | No | Browser user agent string for diagnostics |
Response
Section titled “Response”{ "id": 1, "endpoint": "https://fcm.googleapis.com/fcm/send/...", "is_active": true, "created_at": "2026-02-14T18:00:00Z"}Unsubscribe
Section titled “Unsubscribe”Deactivates a push subscription by its database ID. The subscription record is retained but marked inactive, so it will not receive further push messages.
DELETE /api/notifications/subscribe/{sub_id}| Parameter | Type | Description |
|---|---|---|
sub_id | integer (path) | The subscription ID returned from the subscribe endpoint |
Response
Section titled “Response”{ "ok": true}Returns 404 if the subscription ID does not exist.
Preferences
Section titled “Preferences”Preferences control which notification categories are enabled and how they are delivered. Global preferences have no location_id; location-specific preferences are scoped to a saved observer location.
List Preferences
Section titled “List Preferences”Returns all configured notification preferences, ordered by location (global first, then per-location) and category.
GET /api/notifications/preferencesResponse
Section titled “Response”[ { "id": 1, "location_id": null, "category": "daily_digest", "is_enabled": true, "lead_times": null, "schedule_cron": "0 19 * * *", "min_altitude": 10.0, "timezone": "America/Denver", "config": null, "quiet_start": "22:00", "quiet_end": "07:00" }, { "id": 2, "location_id": 5, "category": "satellite_pass", "is_enabled": true, "lead_times": [1440, 60, 15], "schedule_cron": null, "min_altitude": 25.0, "timezone": "America/Denver", "config": null, "quiet_start": "23:00", "quiet_end": "06:00" }]Set Global Preference
Section titled “Set Global Preference”Creates or updates a preference for a global (location-independent) category. Only categories from the global set are accepted here; location-specific categories will be rejected with a 400 error.
PUT /api/notifications/preferences/global/{category}| Parameter | Type | Description |
|---|---|---|
category | string (path) | One of the global notification categories |
Request Body
Section titled “Request Body”{ "is_enabled": true, "lead_times": [1440, 60, 15], "schedule_cron": "0 19 * * *", "min_altitude": 10.0, "timezone": "America/Denver", "config": null, "quiet_start": "22:00", "quiet_end": "07:00"}Preference Fields
Section titled “Preference Fields”| Field | Type | Default | Description |
|---|---|---|---|
is_enabled | boolean | true | Whether this category is active |
lead_times | list of int or null | null | Advance notice in minutes (e.g. [1440, 60, 15] for 1 day, 1 hour, 15 min). Max 10 entries, each 1—10080. |
schedule_cron | string or null | null | Cron expression for scheduled delivery (e.g. "0 19 * * *" for 7 PM daily) |
min_altitude | float | 10.0 | Minimum altitude in degrees (0—90) for pass-type notifications |
timezone | string | "UTC" | IANA timezone name used for scheduling and quiet hours |
config | object or null | null | Category-specific settings (max 20 keys) |
quiet_start | string or null | null | Start of quiet hours in HH:MM format (e.g. "22:00") |
quiet_end | string or null | null | End of quiet hours in HH:MM format (e.g. "07:00") |
Response
Section titled “Response”Returns the saved PreferenceOut object (same shape as the list response items).
Set Location Preference
Section titled “Set Location Preference”Creates or updates a preference for any category scoped to a specific observer location. Both global and location-specific categories are accepted here, but location-specific categories (like satellite_pass) must use this endpoint rather than the global one.
PUT /api/notifications/preferences/{location_id}/{category}| Parameter | Type | Description |
|---|---|---|
location_id | integer (path) | The saved location ID |
category | string (path) | Any valid notification category |
The request body and response follow the same shape as the global preference endpoint above.
Delete Global Preference
Section titled “Delete Global Preference”Removes a global preference for the given category.
DELETE /api/notifications/preferences/global/{category}| Parameter | Type | Description |
|---|---|---|
category | string (path) | The global category to delete |
Response
Section titled “Response”{ "ok": true}Returns 404 if no global preference exists for that category.
Delete Location Preference
Section titled “Delete Location Preference”Removes a preference for a specific location and category.
DELETE /api/notifications/preferences/{location_id}/{category}| Parameter | Type | Description |
|---|---|---|
location_id | integer (path) | The saved location ID |
category | string (path) | The category to delete |
Response
Section titled “Response”{ "ok": true}Returns 404 if no matching preference exists.
Notification History
Section titled “Notification History”List Notifications
Section titled “List Notifications”Returns a paginated list of all notification log entries, newest first.
GET /api/notifications| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer (query) | 50 | Number of results to return (max 200) |
offset | integer (query) | 0 | Number of results to skip |
Response
Section titled “Response”[ { "id": 42, "category": "satellite_pass", "title": "ISS Pass in 15 minutes", "body": "Max elevation 72 deg, magnitude -3.8", "data_json": "{\"type\": \"satellite_pass\", \"url\": \"/passes/25544\"}", "location_id": 5, "sent_at": "2026-02-14T02:45:00Z", "read_at": null, "push_sent": true }]Unread Count
Section titled “Unread Count”Returns the number of notifications that have not yet been marked as read.
GET /api/notifications/unread-countResponse
Section titled “Response”{ "count": 7}Mark as Read
Section titled “Mark as Read”Marks a single notification as read by setting its read_at timestamp.
POST /api/notifications/{notification_id}/read| Parameter | Type | Description |
|---|---|---|
notification_id | integer (path) | The notification log entry ID |
Response
Section titled “Response”{ "ok": true}Returns 404 if the notification ID does not exist.
Mark All as Read
Section titled “Mark All as Read”Marks every unread notification as read in a single operation.
POST /api/notifications/read-allResponse
Section titled “Response”{ "ok": true}Test Notification
Section titled “Test Notification”Sends a test push notification to all active subscriptions. This is useful for verifying that the VAPID configuration and browser push subscription are working end to end. The test notification is also logged in notification history and broadcast over the live WebSocket feed.
POST /api/notifications/testResponse
Section titled “Response”{ "ok": true, "sent_count": 2}The sent_count reflects how many active push subscriptions received the message.