Notifications API
Planned
This module is not yet shipped. The shape below describes the intended endpoints on the new Hono + Supabase + Drizzle stack.
In-app notifications. Persisted as rows in notifications and
fanned-out in real time via Supabase Realtime.
All endpoints require Authorization: Bearer <supabase-access-token>.
Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /notifications | List notifications (paginated) |
| GET | /notifications/count | Unread count |
| POST | /notifications/read | Mark a list of IDs as read |
| POST | /notifications/read-all | Mark all as read |
| DELETE | /notifications/:id | Delete a notification |
List
GET /notifications?unreadOnly=false&cursor=xxx&limit=20
Authorization: Bearer <token>
| Query | Type | Description |
|---|---|---|
unreadOnly | boolean | Default false |
cursor, limit | Pagination |
{
"data": [
{
"id": "uuid",
"type": "group_approved",
"userId": "uuid",
"payload": { "eventId": "uuid", "groupId": "uuid" },
"readAt": null,
"createdAt": "2026-04-01T12:00:00Z"
}
],
"nextCursor": null
}
Notification types
| Type | Trigger |
|---|---|
friend_request | Someone sent you a friend request |
friend_accepted | A friend request you sent was accepted |
author_invite | You were invited as event author |
group_like | A user liked your event |
group_approved | Your like group was approved |
group_rejected | Your like group was rejected |
message | New chat message (suppressed if chat is open) |
event_reminder | Event starting soon |
The payload shape is type-specific and validated server-side.
Unread count
GET /notifications/count
{ "count": 5 }
Mark as read
POST /notifications/read
{ "ids": ["uuid1", "uuid2"] }
POST /notifications/read-all
Both set read_at = now() for the matching rows owned by the caller.
Delete
DELETE /notifications/:id
Hard-deletes the row (small table, no audit need).
Realtime
Clients can subscribe to their own notification stream via Supabase Realtime:
supabase
.channel(`user:${userId}:notifications`)
.on(
"postgres_changes",
{ event: "INSERT", schema: "public", table: "notifications", filter: `user_id=eq.${userId}` },
(payload) => addToTray(payload.new),
)
.subscribe();
RLS ensures a user only sees their own rows on the stream.
Implementation notes
- Notifications are written by the originating service (e.g.
groups.service.tswritesgroup_approvedinside the same Drizzle transaction that flips group status). - For higher-volume types (e.g.
message), the API may batch or collapse notifications server-side rather than emit one per row. - Push (APNs / FCM) and email are out of scope for v1; only in-app notifications are tracked here.