MisarMisar Docs

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-pointcreateNotification() — 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

TypeFires whenEmailed
new_followerSomeone follows the authorYes
article_reactionA reader likes / claps / bookmarks an articleYes
article_saveA reader saves (bookmarks) an articleYes
article_shareA reader shares an articleYes
content_reportedContent is reported (moderators + the owner)Yes
new_commentA reader comments on an articleYes
comment_replySomeone replies to a commentYes
article_publishedA followed author (or followed tag/topic) publishesYes
new_subscriberA new newsletter subscriberYes
payment_received · first_revenue · payout_paid · payout_failed · payment_failedMonetization eventsPartial
team_invitation · team_member_joinedTeam eventsPartial
ai_citationThe article is cited by an AI answer engineIn-app
content_quality_alertAutomated content-quality flagIn-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:

  1. Tag/topic followers — users following any of the article's tags/topics.
  2. 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.

KeyGates the email for
email_new_followernew_follower
email_new_commentnew_comment, comment_reply
email_article_reactionarticle_reaction
email_article_savearticle_save
email_article_sharearticle_share
email_content_reportedcontent_reported
email_new_subscribernew_subscriber
email_new_tippayment_received
email_article_publishedarticle_published
KeyGates the in-app row for
inapp_new_followernew_follower
inapp_new_commentnew_comment, comment_reply
inapp_article_reactionarticle_reaction
inapp_article_savearticle_save
inapp_article_sharearticle_share
inapp_content_reportedcontent_reported

Reading notifications

GET/api/notifications

Returns the signed-in user's notifications, newest first, with unread count and a resolved in-app deep link per row.

Query parameters

limitnumberquerydefault: 20

Page size.

offsetnumberquerydefault: 0

Pagination offset.

unread_onlybooleanquery

When 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).

unreadCountnumber

Count of unread notifications.

hasMoreboolean

Whether more pages exist beyond this range.

Request
curl "https://www.misar.blog/api/notifications?limit=20&unread_only=true" \
  -H "Cookie: <session>"
200 — OK
{
  "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

POST/api/articles/[id]/share

Records 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)pathrequired

The article being shared.

Request body (optional)

platformstringbody

Where it was shared: one of twitter, linkedin, facebook, reddit, whatsapp, telegram, email, copy_link, other.

Response fields

successboolean

Always true on a recorded share.

share_countnumber

The article's new total share count.

Request
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"}'
200 — Recorded
{ "success": true, "share_count": 42 }

Status codes

  • 400 — invalid article id or platform value.
  • 403 — missing/invalid CSRF token.
  • 429 — share rate limit exceeded (Retry-After header included).
  • 500 — failed to record the share.