MisarMisar Docs
MisarMailMisarBlogMisarReachMisarPostMisarDevMisarCoderMisarSEOMisar PlatformMisar SSO
SDKs

TypeScript SDK

Manage MisarBlog content and embed posts with the @misar/blog TypeScript SDK

Installation

npm install @misar/blog
# or
pnpm add @misar/blog

API Client

new MisarBlog({ apiKey }) is the programmatic client. It authenticates with an mbk_ API key and defaults to https://api.misar.io/blog.

import { MisarBlog } from '@misar/blog';

const blog = new MisarBlog({ apiKey: process.env.MISARBLOG_API_KEY! });

// Override the base URL if needed (defaults to https://api.misar.io/blog)
const staging = new MisarBlog({
  apiKey: 'mbk_...',
  baseUrl: 'https://api.misar.io/blog',
});

Articles

// List your articles
const { articles, total } = await blog.articles.list({ status: 'published', limit: 20 });

// Get one by slug
const article = await blog.articles.get('my-first-article');

// Publish an article
const created = await blog.articles.create({
  title: 'Getting Started with the MisarBlog API',
  body_markdown: '## Introduction\n\nThis guide covers…',
  tags: ['typescript', 'api'],
  cover_image_url: 'https://example.com/cover.jpg',
  visibility: 'public',
});

// Save a draft
const draft = await blog.articles.createDraft({
  title: 'Work in Progress',
  body_markdown: '## Coming soon…',
  tags: ['draft'],
});

// Search
const results = await blog.articles.search({ q: 'react', type: 'articles', sort: 'newest' });

// Related-article recommendations
const { recommendations } = await blog.articles.recommendations(article.id, 5);

Series

const { series } = await blog.series.list();
const detail = await blog.series.get('my-series');       // { series, articles }
const newSeries = await blog.series.create({ title: 'A Series', description: 'Optional' });

AI titles

// action: "seo" needs a prompt; action: "suggest" needs context
const { titles } = await blog.ai.titles({
  action: 'seo',
  prompt: 'best AI writing tools for beginner bloggers 2025',
});

Analytics

const summary = await blog.analytics.summary(30); // days
// { period_days, views, revenue_cents, revenue_net_cents, active_subscribers }

Reactions

const state = await blog.reactions.get(article.id);          // counts + user_reactions
await blog.reactions.add(article.id, 'like');                // "like" | "clap" | "bookmark"
await blog.reactions.remove(article.id, 'like');

Comments, follows, profile

const { comments } = await blog.comments.list(article.id, { limit: 20 });
const follow = await blog.follows.status(userId);            // { is_following, follower_count }
const me = await blog.profiles.me();                          // your profile

Newsletter

// Session-scoped endpoints — call from a server context with a valid session
const { subscribers } = await blog.newsletter.subscribers({ limit: 50 });
const { issues } = await blog.newsletter.issues({ limit: 20 });

API key management

blog.apiKeys.generate() and blog.apiKeys.revoke() intentionally throw — key creation and revocation require a dashboard session and cannot be performed with a Bearer key. Manage keys in Dashboard → Settings → API Keys.

Resources

ResourceMethods
blog.articleslist(opts?) · get(slug) · create(opts) · createDraft(opts) · search(opts?) · recommendations(articleId, limit?)
blog.serieslist() · get(slug) · create(opts)
blog.aititles({ action, prompt?, context? })
blog.analyticssummary(days?)
blog.reactionsget(articleId) · add(articleId, type) · remove(articleId, type)
blog.commentslist(articleId, opts?)
blog.followsstatus(userId)
blog.profilesme()
blog.newslettersubscribers(opts?) · issues(opts?)
blog.apiKeysgenerate() · revoke() — dashboard only, both throw

Error handling

import { BlogApiError } from '@misar/blog';

try {
  await blog.articles.get('missing-slug');
} catch (err) {
  if (err instanceof BlogApiError) {
    console.error(err.status, err.message);
  }
}

Embed Helpers

The package also exports browser embed helpers (no API key required).

import { embed, embedUrl } from '@misar/blog';

// Just the URL
const url = embedUrl({ username: 'johndoe', theme: 'dark' });

// Inject an iframe into a container
const { iframe, destroy } = embed(document.getElementById('blog-container')!, {
  username: 'johndoe',
  width: '100%',
  height: '600px',
  theme: 'dark',
});

destroy(); // remove the embed later

Embed a specific post

const url = embedUrl({ username: 'johndoe', slug: 'my-first-post', theme: 'light' });

embedUrl(options) / embed(container, options)

OptionTypeDescription
usernamestringRequired. Blog author username.
slugstring?Post slug for a single-post embed.
theme'light' | 'dark' | 'auto'Color theme (default: auto).
widthstring?CSS width for the iframe (embed only).
heightstring?CSS height for the iframe (embed only).
classNamestring?Class applied to the iframe (embed only).

embedUrl returns a URL string. embed appends an <iframe> to container and returns { iframe, destroy }.

Token refresh

For gated (paywalled) embeds, refresh the session token:

import { refreshToken, getToken, clearToken } from '@misar/blog';

const { token, expiresAt } = await refreshToken({ token: 'current-session-token' });

const stored = getToken(); // string | null, from localStorage key "misar_blog_token"
clearToken();              // clear on logout