Misar IO Docs
MisarMailApi Reference

WebSocket

Subscribe to real-time inbox events and campaign status updates via WebSocket.

Overview

MisarMail exposes a WebSocket server at wss://api.misar.io/mail/ws/* for real-time event delivery. Two channels are available: inbox (user events) and campaign status (per-campaign progress).

Authentication

Authenticate with your API key (msk_...) via the Authorization: Bearer header (Node.js) or ?token= query parameter (browser). WebSocket connections from browsers cannot set Authorization headers.


Channels

ChannelPathDescription
Inboxwss://api.misar.io/mail/ws/inboxReal-time inbox events for the authenticated user
Campaign statuswss://api.misar.io/mail/ws/campaigns/:idLive campaign delivery progress

Inbox channel

wss://api.misar.io/mail/ws/inbox?token=msk_your_api_key

Event types

typeDescription
email_receivedNew inbound email received
bounceAn outbound email bounced
complaintSpam complaint received
unsubscribeA contact unsubscribed
campaign_startedCampaign send began
campaign_completedCampaign send finished

Example events

{
  "type": "email_received",
  "from": "[email protected]",
  "subject": "Re: Your email",
  "receivedAt": "2026-05-27T10:15:00Z"
}
{
  "type": "campaign_completed",
  "campaignId": "uuid",
  "campaignName": "May Newsletter",
  "sent": 10000,
  "delivered": 9855,
  "opened": 2340,
  "completedAt": "2026-05-27T11:00:00Z"
}

Campaign status channel

wss://api.misar.io/mail/ws/campaigns/:id?token=msk_your_api_key

Ownership verified

The server checks that the campaign belongs to the authenticated user before upgrading the WebSocket connection. An unauthenticated or unauthorized request receives HTTP 403 before the WebSocket handshake completes.

Event types

typeDescription
progressPeriodic send progress update
completedCampaign send finished
failedCampaign send failed
cancelledCampaign was cancelled

Example events

{
  "type": "progress",
  "campaignId": "uuid",
  "sent": 2500,
  "total": 10000,
  "delivered": 2480,
  "bounced": 20,
  "opened": 450
}
{
  "type": "completed",
  "campaignId": "uuid",
  "sent": 10000,
  "delivered": 9855,
  "bounced": 145,
  "opened": 2340
}

TypeScript example

import WebSocket from "ws";

const ws = new WebSocket(
  `wss://api.misar.io/mail/ws/campaigns/${campaignId}`,
  { headers: { Authorization: `Bearer msk_your_api_key` } }
);

ws.on("message", (raw) => {
  const event = JSON.parse(raw.toString());

  if (event.type === "progress") {
    const pct = Math.round((event.sent / event.total) * 100);
    console.log(`Progress: ${pct}% (${event.sent}/${event.total})`);
  }

  if (["completed", "failed", "cancelled"].includes(event.type)) {
    ws.close();
  }
});
const ws = new WebSocket(
  `wss://api.misar.io/mail/ws/inbox?token=${apiKey}`
);

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  if (data.type === "email_received") {
    showNotification(`New email from ${data.from}`);
  }
};

Heartbeat

The server sends a ping frame every 30 seconds. The client must respond with a pong. Unresponsive connections are closed after 30 seconds with code 1001.


Close codes

CodeMeaning
1000Normal closure
1001Server restart or heartbeat timeout
4401Unauthorized — missing or invalid API key
4403Forbidden — campaign does not belong to this account