MisarMisar Docs
MisarMailMisar.BlogMisarReachMisarPostMisar.DevMisarSEOMisar PlatformMisar SSO
API Reference

WebSocket

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

Overview

MisarBlog runs a WebSocket server (a custom Next.js server) that accepts connections at wss://www.misar.blog/ws/*. Connect to a channel to receive real-time messages pushed by the platform without polling.

Host

WebSocket upgrades are served directly by the MisarBlog application server at www.misar.blog, not through the api.misar.io/blog REST gateway.

Authentication

Authenticate with your API key. In Node.js, send an Authorization: Bearer mbk_... header on the upgrade request. Browsers cannot set headers on a WebSocket handshake, so pass the key as a query parameter instead: ?token=mbk_.... A missing or invalid key causes the upgrade to be rejected and the socket to close immediately.


Channels

ChannelPathDescription
User notifications (default)wss://www.misar.blog/ws/notificationsReal-time messages for the authenticated user
Article updateswss://www.misar.blog/ws/articles/:slugLive updates for a specific article; substitute :slug with the article's URL slug

The path segment after /ws/ selects the channel and defaults to notifications. For the articles channel, the next segment is the article slug.


Connect

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

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

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log("Message:", 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://www.misar.blog/ws/notifications", {
  headers: { Authorization: "Bearer mbk_your_api_key" },
});
const ws = new WebSocket(
  `wss://www.misar.blog/ws/articles/my-first-post?token=${token}`
);

Messages

All messages are JSON. Two message types are part of the connection protocol:

connected{ type: "connected", channel, userId }

Sent by the server immediately after a successful connection. channel is notifications or articles; userId is the authenticated user's id.

pong{ type: "pong" }

Sent by the server in reply to a client {"type":"ping"} message (see Heartbeat).

{ "type": "connected", "channel": "notifications", "userId": "..." }

Beyond the connection protocol, the server pushes notification and article-update messages to subscribed clients as JSON objects carrying a type field. Route on type in your onmessage handler and ignore message types you don't recognize.


Heartbeat

The connection is kept alive at two levels:

  • Protocol ping/pong — the server sends a WebSocket protocol ping frame every 30 seconds and terminates the connection if the client hasn't answered by the next interval. Standard WebSocket clients answer protocol pings automatically.
  • Application ping/pong — you may also send {"type":"ping"} as a text message at any time; the server replies with {"type":"pong"}.
setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ type: "ping" }));
  }
}, 25_000);

Reconnection

The server does not send a custom close code on auth failure — the upgrade is simply rejected and the socket closes. Implement exponential backoff when reconnecting:

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

  ws.onclose = () => {
    setTimeout(() => connect(token, slug), retryDelay);
    retryDelay = Math.min(retryDelay * 2, 30_000);
  };

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

  return ws;
}