Misar IO Docs
Misar.BlogApi Reference

WebSocket

Subscribe to real-time MisarBlog events over a persistent WebSocket connection.

Overview

MisarBlog exposes a WebSocket server at wss://api.misar.io/blog/ws/* for real-time event delivery. Use WebSocket channels to receive live notifications and article engagement updates without polling.

Authentication

Connect with your API key in the Authorization header or as a query parameter: ?token=mbk_.... WebSocket connections cannot set Authorization headers from browsers, so the ?token= query parameter is supported for browser clients.


Channels

ChannelPathDescription
User notificationswss://api.misar.io/blog/ws/notificationsReceive notification events for the authenticated user
Article live updateswss://api.misar.io/blog/ws/articles/:slugReceive live view count and reaction updates for an article

Connect

const token = "mbk_your_api_key";
const ws = new WebSocket(`wss://api.misar.io/blog/ws/notifications?token=${token}`);

ws.onopen = () => console.log("Connected");

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log("Event:", data);
};

ws.onclose = (event) => {
  console.log("Disconnected:", event.code, event.reason);
};

ws.onerror = (err) => console.error("WebSocket error:", err);
import WebSocket from "ws";

const ws = new WebSocket("wss://api.misar.io/blog/ws/notifications", {
  headers: { Authorization: "Bearer mbk_your_api_key" },
});

Notifications channel

Path

wss://api.misar.io/blog/ws/notifications

Event types

typeDescription
new_subscriberSomeone subscribed to your newsletter
new_commentA new comment was posted on your article
article_reactionA reader reacted to your article
milestoneView count milestone reached

Example events

{
  "type": "new_subscriber",
  "subscriber": {
    "email": "[email protected]",
    "subscribed_at": "2026-05-27T10:30:00Z"
  }
}
{
  "type": "new_comment",
  "articleSlug": "my-first-post",
  "comment": {
    "id": "comment-uuid",
    "body": "Great article!",
    "author": "reader123",
    "createdAt": "2026-05-27T10:31:00Z"
  }
}

Article live updates channel

Path

wss://api.misar.io/blog/ws/articles/:slug

Substitute :slug with the article's URL slug. Article channels are open to any authenticated user — they do not require ownership of the article.

Event types

typeDescription
view_updateView count changed
reaction_updateReaction counts changed

Example event

{
  "type": "view_update",
  "slug": "my-first-post",
  "viewCount": 1042,
  "timestamp": "2026-05-27T10:35:00Z"
}
const ws = new WebSocket(
  `wss://api.misar.io/blog/ws/articles/my-first-post?token=${token}`
);

ws.onmessage = (event) => {
  const { type, viewCount } = JSON.parse(event.data);
  if (type === "view_update") {
    document.getElementById("view-count")!.textContent = String(viewCount);
  }
};

Heartbeat

The server sends a ping frame every 30 seconds. Respond with a pong frame to keep the connection alive. Most WebSocket libraries handle this automatically.

If no pong is received within 30 seconds of a ping, the server closes the connection with code 1001 (Going Away).


Close codes

CodeMeaning
1000Normal closure
1001Server restart or timeout
1008Policy violation (auth failed)
4401Unauthorized — API key missing or invalid
4403Forbidden — access denied

Reconnection

Implement exponential backoff when reconnecting:

function connect(token: string, slug?: string) {
  const path = slug
    ? `wss://api.misar.io/blog/ws/articles/${slug}`
    : "wss://api.misar.io/blog/ws/notifications";
  const ws = new WebSocket(`${path}?token=${token}`);
  let retryDelay = 1000;

  ws.onclose = () => {
    console.log(`Reconnecting in ${retryDelay / 1000}s...`);
    setTimeout(() => connect(token, slug), retryDelay);
    retryDelay = Math.min(retryDelay * 2, 30_000);
  };

  ws.onopen = () => {
    retryDelay = 1000; // reset on successful connection
  };

  return ws;
}