You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/api-endpoints.md
+18-6Lines changed: 18 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -267,7 +267,7 @@ Processes due plan reminders from the database.
267
267
268
268
Returns a **preview** of users who would receive routine notifications at the current UTC time, without sending anything.
269
269
270
-
Proxies to the backend `GET /internal/routine-notification-targets` endpoint, which matches routine time blocks whose local time equals the current minute in each user's timezone (`Routine.timezone`, with a fallback to the time block creation offset).
270
+
Proxies to the backend `GET /internal/routine-notification-targets` endpoint, which matches routine time blocks whose stored UTC time (`routine_time_blocks.time_utc`) equals the current UTC minute. Only **`PLAN`** and **`SERIES`** sessions are included.
271
271
272
272
**Response (200):**
273
273
@@ -284,9 +284,9 @@ Proxies to the backend `GET /internal/routine-notification-targets` endpoint, wh
284
284
{
285
285
"user_id": "uuid",
286
286
"notification": {
287
-
"title": "Day 3",
288
-
"body": "Time for your daily practice.",
289
-
"custom_image_url": null
287
+
"title": "Day 3 — Breath Awareness",
288
+
"body": "Take 10 minutes for today's practice.",
289
+
"image_url": "https://..."
290
290
},
291
291
"push_devices": [
292
292
{ "token": "...", "platform": "android" }
@@ -298,13 +298,25 @@ Proxies to the backend `GET /internal/routine-notification-targets` endpoint, wh
|`title`| string | Notification title shown to the user |
34
-
|`body`| string | Notification body shown to the user |
38
+
|`body`| string | Notification body shown to the user |
39
+
|`image`| string | Optional image URL for rich notifications (Android and supported iOS setups). Omitted when no image is resolved. |
35
40
36
41
### Data payload
37
42
@@ -40,18 +45,23 @@ All data values are **strings** (FCM requirement).
40
45
| Field | Type | Description |
41
46
|----------------|--------|-------------|
42
47
|`session_type`| string | Type of routine session (see [Session types](#session-types)) |
43
-
|`source_id`| string | UUID of the related entity (plan, series, collection, etc.). Empty string (`""`) when there is no linked entity |
48
+
|`source_id`| string | UUID of the related entity (plan or series). Empty string (`""`) when there is no linked entity |
49
+
|`title`| string | Resolved notification title (same value as the display notification) |
50
+
|`body`| string | Resolved notification body (same value as the display notification) |
51
+
|`image_url`| string | Presigned HTTPS URL for the notification image. Empty string (`""`) when no image is resolved |
52
+
53
+
The app can read routing fields (`session_type`, `source_id`) and content fields (`title`, `body`, `image_url`) from `message.data` in both foreground and background handlers.
Content is resolved by the **backend** when building routine notification targets. The worker forwards the resolved values into the FCM notification and data payload.
66
81
67
82
### Routine notifications (time-block based)
68
83
69
-
Triggered when a user's routine time block matches the current local time.
84
+
Triggered when a user's routine time block matches the current UTC minute (`routine_time_blocks.time_utc`).
|`PLAN`| Day notification title from `day_notifications`, or plan title if no day copy exists | Day notification body from `day_notifications`, or default body | See [Plan image rules](#plan-image-rules)|
89
+
|`SERIES`| Series metadata title, or default title | Default body | Series cover image (presigned), or empty string |
79
90
80
-
For `PLAN`, the worker calculates the user's current day from plan progress and loads that day's notification copy from the database.
91
+
For `PLAN`, the backend calculates the user's current day from plan progress and loads that day's notification copy from `day_notifications` (linked to the plan item / day).
92
+
93
+
#### Plan image rules
94
+
95
+
| Condition |`image_url`|
96
+
|-----------|-------------|
97
+
| Day notification exists with `image_type = CUSTOM`| Presigned URL for `day_notifications.image_url`|
98
+
| Day notification exists with `image_type = PLAN`, or no day notification | Presigned plan cover image (`plans.image_url`) |
99
+
| No plan cover available | Empty string |
100
+
101
+
#### Series image rules
102
+
103
+
| Condition |`image_url`|
104
+
|-----------|-------------|
105
+
| Series has a cover image | Presigned series cover image (`series.image`) |
106
+
| No series cover available | Empty string |
81
107
82
108
### Plan reminders (enrollment API)
83
109
84
110
Used when the app enrolls reminders via `POST /api/v1/notifications/reminders`.
85
111
86
-
Body resolution priority:
112
+
**Title/body resolution:**
87
113
88
114
1.`routine.message_template` (if set)
89
115
2.`"Time for {plan_name}"` (if `plan_name` is set)
90
116
3.`"Day {current_day_number}: time for your practice."` (if `current_day_number` is set)
91
-
4. Default body
117
+
4. Default title/body
92
118
93
-
Data payload for plan reminders:
119
+
**Data payload** includes `title` and `body` but not `image_url` (empty string):
94
120
95
121
```json
96
122
{
97
123
"session_type": "PLAN",
98
-
"source_id": "<plan_id>"
124
+
"source_id": "<plan_id>",
125
+
"title": "Time for Morning Practice",
126
+
"body": "Day 3: time for your practice.",
127
+
"image_url": ""
99
128
}
100
129
```
101
130
@@ -105,13 +134,14 @@ Data payload for plan reminders:
0 commit comments