Notifications
How MisarBlog notifies authors of follows, reactions, comments, shares and new posts — in-app and by email — and the preference keys that control them.
MisarBlog keeps authors informed of engagement through a single in-app + email notification system. Every social event routes through one choke-point — createNotification() — which independently gates the in-app row and the email leg against the recipient's preferences.
These are first-party endpoints of the MisarBlog web app — session-authenticated and CSRF-protected, consumed by the dashboard UI. They are not part of the public API-key surface (api.misar.io/blog/v1/*). Paths below are relative to the app origin (www.misar.blog).
Notification types
| Type | Fires when | Emailed |
|---|---|---|
new_follower | Someone follows the author | Yes |
article_reaction | A reader likes / claps / bookmarks an article | Yes |
article_save | A reader saves (bookmarks) an article | Yes |
article_share | A reader shares an article | Yes |
content_reported | Content is reported (moderators + the owner) | Yes |
new_comment | A reader comments on an article | Yes |
comment_reply | Someone replies to a comment | Yes |
article_published | A followed author (or followed tag/topic) publishes | Yes |
new_subscriber | A new newsletter subscriber | Yes |
payment_received · first_revenue · payout_paid · payout_failed · payment_failed | Monetization events | Partial |
team_invitation · team_member_joined | Team events | Partial |
ai_citation | The article is cited by an AI answer engine | In-app |
content_quality_alert | Automated content-quality flag | In-app |
The article_save, article_share, and content_reported types were added in the latest release, alongside per-type rich emails for follow, reaction, save, share, comment, and reply built through the shared createNotification() path.
New-post fan-out
When an article is published, MisarBlog fans out an article_published notification to two deduplicated audiences:
- Tag/topic followers — users following any of the article's tags/topics.
- Author followers — everyone following the author (paginated past the 500 cap).
Each recipient's email leg is gated by their email_article_published preference; the in-app leg by inapp_article_published.
Preference keys
Preferences live on profiles.notification_preferences. A missing key means enabled (defaults are all on); an explicit false opts out. The in-app and email legs are governed independently.
| Key | Gates the email for |
|---|---|
email_new_follower | new_follower |
email_new_comment | new_comment, comment_reply |
email_article_reaction | article_reaction |
email_article_save | article_save |
email_article_share | article_share |
email_content_reported | content_reported |
email_new_subscriber | new_subscriber |
email_new_tip | payment_received |
email_article_published | article_published |
| Key | Gates the in-app row for |
|---|---|
inapp_new_follower | new_follower |
inapp_new_comment | new_comment, comment_reply |
inapp_article_reaction | article_reaction |
inapp_article_save | article_save |
inapp_article_share | article_share |
inapp_content_reported | content_reported |
Reading notifications
/api/notificationsReturns the signed-in user's notifications, newest first, with unread count and a resolved in-app deep link per row.
Query parameters
limitnumberquerydefault: 20Page size.
offsetnumberquerydefault: 0Pagination offset.
unread_onlybooleanqueryWhen true, return only unread notifications.
Response fields
notificationsNotification[]Rows with id, type, title, body, data, read_at, created_at, and a resolved url (null when not navigable).
unreadCountnumberCount of unread notifications.
hasMorebooleanWhether more pages exist beyond this range.
curl "https://www.misar.blog/api/notifications?limit=20&unread_only=true" \
-H "Cookie: <session>"{
"notifications": [
{
"id": "…",
"type": "article_share",
"title": "New share",
"body": "alice shared your article \"Ship faster\"",
"url": "/@you/articles/ship-faster",
"read_at": null,
"created_at": "2026-07-26T10:00:00Z"
}
],
"unreadCount": 1,
"hasMore": false
}PATCH /api/notifications marks a single row read ({ notification_id }) or all rows read ({ mark_all: true }); DELETE /api/notifications removes one ({ notification_id }). Real-time delivery is available client-side via a Supabase postgres_changes subscription on the recipient's notifications rows.
Recording a share
/api/articles/[id]/shareRecords a share of an article: atomically increments the article's share_count and — when a signed-in reader other than the author shares — notifies the author with an article_share notification. CSRF-protected; rate-limited to 30 requests / minute per IP.
Path parameters
idstring (UUID)pathrequiredThe article being shared.
Request body (optional)
platformstringbodyWhere it was shared: one of twitter, linkedin, facebook, reddit, whatsapp, telegram, email, copy_link, other.
Response fields
successbooleanAlways true on a recorded share.
share_countnumberThe article's new total share count.
curl -X POST "https://www.misar.blog/api/articles/550e8400-e29b-41d4-a716-446655440000/share" \
-H "Content-Type: application/json" \
-H "x-csrf-token: <token>" \
-H "Cookie: <session>" \
-d '{"platform":"linkedin"}'{ "success": true, "share_count": 42 }Status codes
- 400 — invalid article id or
platformvalue. - 403 — missing/invalid CSRF token.
- 429 — share rate limit exceeded (
Retry-Afterheader included). - 500 — failed to record the share.