Misar.BlogStreaming
Newsletter Send Progress
Monitor newsletter issue send progress in real time using Server-Sent Events.
Overview
The newsletter send-stream endpoint delivers live progress updates while a newsletter issue is being sent to subscribers. Use it to display a real-time progress bar in your dashboard or integration.
Endpoint
GET api.misar.io/blog/newsletter/issues/:id/send-stream
Authorization: Bearer mbk_your_api_key
Returns text/event-stream — a Server-Sent Events stream.
Response headers
| Header | Value |
|---|---|
Content-Type | text/event-stream |
Cache-Control | no-cache, no-transform |
X-Accel-Buffering | no |
Event format
Each event is a JSON-encoded data line:
data: {"status":"sending","sent":250,"total":1000,"opened":42,"bounced":3}
data: {"status":"sent","sent":1000,"total":1000,"opened":380,"bounced":12}
data: [DONE]
Fields
status"sending" | "sent" | "failed" | "cancelled"Current send status. "sent", "failed", and "cancelled" are terminal states.
sentnumberNumber of emails sent so far.
totalnumberTotal recipients in the issue.
openednumberEmails opened (tracked via pixel).
bouncednumberEmails that bounced.
Stream behavior
- Polls the issue status every 3 seconds
- Terminates automatically when
statusreaches"sent","failed", or"cancelled" - Times out after 10 minutes and sends
data: [DONE] - Returns error event
{"error":"issue_not_found"}if the issue ID doesn't belong to your account
Error events
data: {"error":"issue_not_found"}
data: [DONE]
data: {"error":"poll_failed"}
data: [DONE]
Browser example
const issueId = "your-issue-uuid";
const token = "mbk_your_api_key";
const source = new EventSource(
`https://api.misar.io/blog/newsletter/issues/${issueId}/send-stream`,
{
// EventSource doesn't support Authorization headers — use a short-lived
// signed URL or proxy the request through your backend
}
);
// Alternatively use fetch + ReadableStream for header support:
const res = await fetch(
`https://api.misar.io/blog/newsletter/issues/${issueId}/send-stream`,
{ headers: { Authorization: `Bearer ${token}` } }
);
const reader = res.body!.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const lines = decoder.decode(value).split("\n\n");
for (const line of lines) {
if (line.startsWith("data: [DONE]")) return;
if (line.startsWith("data: ")) {
const event = JSON.parse(line.slice(6));
updateProgressBar(event.sent, event.total);
if (["sent", "failed", "cancelled"].includes(event.status)) return;
}
}
}Authentication
Requires an API key with at least read scope. The issue must belong to the authenticated user's account.