npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

migma

v1.3.4

Published

Official Node.js SDK for the Migma email platform API

Readme

migma

The official Node.js SDK for the Migma email platform API.

Generate pixel-perfect, on-brand emails with AI in 30 seconds. Manage contacts, domains, webhooks, and more — all from code.

npm version License: MIT

Install

npm install migma

Quick Start

import { Migma } from 'migma';

const migma = new Migma('mgma_sk_live_...');

// A project holds your brand info (colors, fonts, logos, tone of voice).
// Import one from your website, or grab the ID of an existing project
// from the Migma dashboard.
const { data: project } = await migma.projects.importAndWait({
  urls: ['https://yourcompany.com'],
});

// Generate an email design with AI — returns the finished HTML directly.
const { data: email } = await migma.emails.generateAndWait({
  projectId: project.projectId,
  prompt: 'Create a welcome email for new subscribers',
});

if (email?.status === 'completed') {
  console.log('Subject:', email.result.subject);
  console.log('HTML:', email.result.html); // primary email HTML
  console.log('Email ID:', email.result.emails[0].emailId);
}

Prefer async? Use migma.emails.generate() to kick off generation without waiting, then set up a webhook to get notified when the design is ready via the email.generation.completed event — no polling needed.

Get your API key from the Migma Dashboard under Settings > API Integration > API Keys.

Quick Start with OpenClaw

OpenClaw (formerly ClawdBot) is an open-source AI agent for WhatsApp, Telegram, and Discord. Pair it with Migma to generate and send professional emails from a single chat command.

import { Migma } from 'migma';

const migma = new Migma(process.env.MIGMA_API_KEY);

// 1. Set up an instant sending domain (no DNS needed)
await migma.domains.createManaged({ prefix: 'yourcompany' });

// 2. Generate an on-brand email with AI
const { data: email } = await migma.emails.generateAndWait({
  projectId: process.env.MIGMA_PROJECT_ID, // your brand project
  prompt: 'Create a summer sale email with 30% off everything',
});

if (!email?.result) throw new Error('Email generation failed');

// 3. Send it — emailId works for single emails and series slots
const selectedEmail = email.result.emails[0];
if (!selectedEmail.emailId) throw new Error('No emailId returned');
await migma.sending.send({
  recipientType: 'email',
  recipientEmail: '[email protected]',
  from: '[email protected]',
  fromName: 'Your Company',
  subject: selectedEmail.subject,
  emailId: selectedEmail.emailId,
});

// conversationId also works for single-email conversations.
// For multi-slot emails or series, use result.emails[].emailId.

Full OpenClaw tutorial

Documentation

| Resource | Link | |----------|------| | API Reference | docs.migma.ai/api-reference | | Quickstart Guide | docs.migma.ai/quickstart | | Authentication | docs.migma.ai/authentication | | Webhooks Guide | docs.migma.ai/webhooks | | Platform | migma.ai | | Community | Discord |

Features

  • Full coverage of all Migma API v1 endpoints (80+ methods across 15 resources)
  • TypeScript-first with complete type definitions
  • { data, error } return pattern — methods never throw
  • Automatic retries with exponential backoff on 5xx/429
  • Polling helpers for async operations (email generation, project import, previews)
  • Zero runtime dependencies (uses native fetch)
  • Dual ESM + CommonJS support

Usage

Configuration

import { Migma } from 'migma';

const migma = new Migma('mgma_sk_live_...', {
  baseUrl: 'https://api.migma.ai/v1', // default
  maxRetries: 2,                           // retries on 5xx/429
  retryDelay: 1000,                        // base delay between retries
});

Error Handling

Every method returns { data, error } instead of throwing. TypeScript narrows the types automatically:

const { data, error } = await migma.contacts.create({
  email: '[email protected]',
  status: 'subscribed', // only for contacts with marketing consent
  projectId: 'proj_abc123',
});

if (error) {
  console.error(error.statusCode, error.code, error.message);
  // 400 validation_error "Invalid email address"
  return;
}

// TypeScript knows data is non-null here
console.log(data.id, data.email);

Error codes: validation_error | not_found | unauthorized | forbidden | rate_limit_exceeded | conflict | timeout | network_error | internal_error


Projects

Import your brand and manage projects.

// List all projects
const { data } = await migma.projects.list({ limit: 20 });

// Import a brand from a URL and wait for it to finish
const { data: project } = await migma.projects.importAndWait(
  { urls: ['https://example.com'] },
  {
    interval: 3000,
    onPoll: (status) => console.log(`${status.progress.percentage}%`),
  }
);

Projects API Reference

AI Email Generation

Generate emails with AI — fire-and-forget or wait for the result.

// Generate and wait for the result
const { data } = await migma.emails.generateAndWait({
  projectId: 'proj_abc123',
  prompt: 'Black Friday promotional email with 30% discount',
  images: [{ source: { type: 'url', url: 'https://example.com/hero.jpg' } }],
  languages: ['en'],
});

if (data?.status === 'completed') {
  console.log('Subject:', data.result.subject);
  console.log('HTML:', data.result.html);
}

// Or fire-and-forget, check status later
const { data: gen } = await migma.emails.generate({
  projectId: 'proj_abc123',
  prompt: 'Weekly newsletter',
});
const { data: status } = await migma.emails.getGenerationStatus(gen.conversationId);

Email Generation API Reference

Contacts

// Create
const { data: contact } = await migma.contacts.create({
  email: '[email protected]',
  firstName: 'John',
  lastName: 'Doe',
  tags: ['newsletter', 'vip'],
  projectId: 'proj_abc123',
});

// List with filters
const { data: contacts } = await migma.contacts.list({
  projectId: 'proj_abc123',
  status: 'subscribed',
  tags: 'newsletter',
  limit: 50,
});

// Bulk import (sync for <=5000 contacts, async for more)
const { data: result } = await migma.contacts.bulkImport({
  subscribers: [
    { email: '[email protected]', firstName: 'Alice' },
    { email: '[email protected]', firstName: 'Bob' },
  ],
  projectId: 'proj_abc123',
});

// Batch delete by email (up to 1000; unmatched emails come back in notFound)
const { data: deletion } = await migma.contacts.bulkDeleteByEmail({
  emails: ['[email protected]', '[email protected]'],
  projectId: 'proj_abc123',
});

// Safe retries: pass an Idempotency-Key so a retried write never duplicates.
// Same key + same body within 24h replays the original response.
await migma.contacts.create(
  { email: '[email protected]', projectId: 'proj_abc123' },
  { idempotencyKey: '[email protected]' },
);

Contacts API Reference

Sending Emails

Send to a single recipient, segment, tag, or full audience.

// Send a generated email by emailId
await migma.sending.send({
  recipientType: 'email',
  recipientEmail: '[email protected]',
  from: '[email protected]',
  fromName: 'Your Company',
  subject: 'Welcome!',
  emailId: 'email_abc123',
});

// Single-email conversations can also be sent by conversationId.
await migma.sending.send({
  recipientType: 'email',
  recipientEmail: '[email protected]',
  from: '[email protected]',
  fromName: 'Your Company',
  subject: 'Welcome!',
  conversationId: 'conv_abc123',
});

// Send to a segment
await migma.sending.send({
  recipientType: 'segment',
  recipientId: 'seg_abc123',
  from: '[email protected]',
  fromName: 'Your Company',
  subject: 'Big Announcement',
  emailId: 'email_abc123',
});

// Single sends are automatically transactional — no flag needed
// Use transactional: true on batch sends to bypass subscription status
await migma.sending.send({
  recipientType: 'tag',
  recipientId: 'tag_active_users',
  from: '[email protected]',
  fromName: 'Your Company',
  subject: 'Your subscription renews tomorrow',
  emailId: 'email_abc123',
  transactional: true,
});

Sending API Reference

Generated Series and Editing

Generate a series with count, then use each email's emailId to fetch, prompt-edit, or send one email.

const { data: series } = await migma.emails.generateAndWait({
  projectId: 'proj_abc123',
  prompt: 'Create a three-email onboarding series',
  count: 3,
});

for (const generated of series?.result?.emails ?? []) {
  console.log(generated.slot, generated.emailId, generated.html);
  console.log(generated.screenshotUrl);
}

const { data: email } = await migma.emails.get('email_abc123');
console.log(email?.html);

await migma.emails.edit('email_abc123', {
  prompt: 'Change the CTA from "Start now" to "Start your trial"',
});

Campaigns

Create a campaign from a generated email, send or schedule it, then read its engagement stats and per-recipient logs.

// Create from a generated email
const { data: campaign } = await migma.campaigns.create({
  projectId: 'proj_abc123',
  name: 'Spring Launch',
  conversationId: 'conv_abc123',
  emailId: 'email_abc123',
  subject: 'Spring is here',
  from: '[email protected]',
  fromName: 'Your Brand',
  recipientType: 'audience',
  recipientId: 'aud_abc123',
});

// Send now (idempotency key recommended — see below)
await migma.campaigns.send(campaign.id, {
  idempotencyKey: `campaign-send-${campaign.id}`,
});

// Or schedule for later
await migma.campaigns.schedule(
  campaign.id,
  { scheduledAt: '2026-07-01T15:00:00Z', scheduledTimezone: 'America/New_York' },
  { idempotencyKey: `campaign-send-${campaign.id}` },
);

// Aggregated engagement metrics (cached, may be slightly stale)
const { data: stats } = await migma.campaigns.stats(campaign.id);
console.log(stats?.openRate, stats?.clickRate, stats?.lastUpdated);

// Per-recipient logs, cursor-paginated and optionally filtered by status
const { data: logs } = await migma.campaigns.logs(campaign.id, {
  status: 'opened',
  limit: 50,
});
for (const row of logs?.emails ?? []) {
  console.log(row.to_email, row.status, row.opened_at);
}
if (logs?.hasMore) {
  await migma.campaigns.logs(campaign.id, { cursor: logs.nextCursor ?? undefined });
}

Campaign stats are sourced from the email tracking worker and cached on the campaign, so they can lag live activity. botOpens, botClicks, and mppOpens reflect Apple Mail Privacy Protection and bot detection when that data is available. Log rows come straight from the tracking store, so their field names are snake_case (to_email, opened_at, bounce_type) unlike the rest of the API.

Campaigns Guide

Idempotency

Write methods accept an optional trailing CallOptions argument carrying an idempotencyKey. The key is sent as the Idempotency-Key header (max 100 characters) and is scoped to your API key for 24 hours:

  • Same key + same body within 24h replays the original response (the server marks the replay with an Idempotent-Replayed: true header).
  • Same key + different body returns 409 IDEMPOTENCY_CONFLICT.
  • 429 and 5xx responses are never cached, so they stay safe to retry — and the SDK reuses your key across its own automatic retries, so a retried call never duplicates.
// Sending — key on the recipient + send time
await migma.sending.send(
  {
    recipientType: 'email',
    recipientEmail: '[email protected]',
    from: '[email protected]',
    fromName: 'Your Brand',
    subject: 'Welcome!',
    emailId: 'email_abc123',
  },
  { idempotencyKey: '[email protected]' },
);

// Campaign send — one key per campaign send
await migma.campaigns.send('camp_abc123', {
  idempotencyKey: 'campaign-send-camp_abc123',
});

// Email generation — key on project + the turn/prompt
await migma.emails.generate(
  { projectId: 'proj_abc123', prompt: 'Welcome email' },
  { idempotencyKey: 'email-gen-proj_abc123-welcome' },
);

// Contact add — key on the email
await migma.contacts.create(
  { email: '[email protected]', projectId: 'proj_abc123' },
  { idempotencyKey: '[email protected]' },
);

Choose a deterministic key so the same logical operation produces the same key on every attempt:

  • send: send-<emailId>-<recipientId|to>-<scheduledAt|"now">
  • campaign send: campaign-send-<campaignId>
  • email generate: email-gen-<projectId>-<turnOrPromptHash>
  • contact add: contact-<email>

Anti-patterns to avoid:

  • Don't generate the key with uuid() or Date.now() per call — a fresh key every attempt defeats deduplication across retries and process restarts.
  • Don't hash the request body to build the key — identical retries you want deduplicated would each get a different key, and you lose the conflict signal.

Email Validation

Run preflight checks — compatibility, links, spelling, and deliverability.

// Run all checks at once
const { data } = await migma.validation.all({
  html: '<html>...</html>',
  subject: 'My Email',
  options: {
    checkCompatibility: true,
    checkLinks: true,
    checkSpelling: true,
    checkDeliverability: true,
  },
});

console.log('Score:', data.overallScore);
console.log('Spam score:', data.deliverability?.spamScore.score);

// Or run individual checks
const { data: compat } = await migma.validation.compatibility({ html: '...' });
const { data: links } = await migma.validation.links({ html: '...' });
const { data: spam } = await migma.validation.deliverability({ html: '...' });

Validation API Reference

Email Previews

Preview emails across 20+ email clients and devices.

const { data } = await migma.previews.createAndWait({
  html: '<html>...</html>',
  subject: 'Test Email',
  devices: ['apple-mail.ios', 'gmail.desktop-webmail'],
});

data.devices.forEach((d) => {
  console.log(`${d.deviceName}: ${d.screenshotUrl}`);
});

// List all supported devices
const { data: devices } = await migma.previews.getSupportedDevices();

Previews API Reference

Export

Export generated emails to HTML, MJML, PDF, or directly to ESP platforms.

const { data: html } = await migma.export.html('conversation_id');
const { data: mjml } = await migma.export.mjml('conversation_id');
const { data: pdf } = await migma.export.pdf('conversation_id');
const { data: klaviyo } = await migma.export.klaviyo('conversation_id', 'hybrid');
const { data: mailchimp } = await migma.export.mailchimp('conversation_id');

Export API Reference

Domains

Add custom sending domains or use managed quick-start domains.

// Add and verify a custom domain
await migma.domains.create({ domain: 'mail.example.com', region: 'us-east-1' });
await migma.domains.verify('mail.example.com');

// Or use a managed domain (quick start)
const { data: avail } = await migma.domains.checkAvailability('mycompany');
if (avail.available) {
  await migma.domains.createManaged({ prefix: 'mycompany' });
}

// Provision a transactional stream (notify.example.com) to isolate
// transactional reputation from marketing, then publish the returned DNS records
await migma.domains.provisionStream({ rootDomain: 'example.com', stream: 'transactional' });

// Or provision both streams (send. marketing + notify. transactional) in one call
const { data } = await migma.domains.setup({ rootDomain: 'example.com' });
// data.marketing, data.transactional

Domains API Reference

Tags, Segments & Topics

// Tags
const { data: tag } = await migma.tags.create({
  name: 'VIP Customers',
  color: '#FF5733',
  projectId: 'proj_abc123',
});

// Segments
const { data: segment } = await migma.segments.create({
  name: 'Active US Customers',
  filters: {
    status: 'subscribed',
    fields: [{ key: 'country', mode: 'is', values: ['US'] }],
    activity: [{ action: 'opened', channel: 'email', mode: 'within', unit: 'days', amount: 30 }],
  },
  projectId: 'proj_abc123',
});

const { data: campaignSegment } = await migma.segments.create({
  name: 'Campaign openers',
  filters: {
    activity: [{ action: 'opened', channel: 'email', mode: 'within', unit: 'days', amount: 14, campaignId: 'cmp_123' }],
  },
  projectId: 'proj_abc123',
});

// Topics with subscribe/unsubscribe
const { data: topic } = await migma.topics.create({
  name: 'Weekly Newsletter',
  defaultSubscription: 'opt_out',
  projectId: 'proj_abc123',
});
await migma.topics.subscribe('topic_id', {
  subscriberId: 'sub_id',
  projectId: 'proj_abc123',
});

Tags | Segments | Topics

Webhooks

const { data: webhook } = await migma.webhooks.create({
  url: 'https://example.com/webhooks',
  events: ['email.generation.completed', 'subscriber.added'],
});

const { data: test } = await migma.webhooks.test(webhook.id);
console.log('Success:', test.success);

Webhooks Guide

Knowledge Base & Images

// Add context for AI email generation
await migma.knowledgeBase.add('proj_abc123', {
  title: 'Company FAQ',
  content: 'We are a SaaS company that...',
});

// Manage project images
await migma.images.add('proj_abc123', {
  url: 'https://example.com/hero.jpg',
  description: 'Hero banner',
});

await migma.images.updateLogos('proj_abc123', {
  primary: 'https://example.com/logo.png',
});

All Resources

| Resource | Methods | Docs | |----------|---------|------| | migma.contacts | create list get update remove bulkImport getBulkImportStatus changeStatus | Contacts | | migma.tags | create list get update remove | Tags | | migma.segments | create list get update remove | Segments | | migma.topics | create list get update remove subscribe unsubscribe | Topics | | migma.sending | send getBatchStatus | Sending | | migma.projects | list get import getImportStatus retryImport fieldCatalog importAndWait | Projects | | migma.emails | generate getGenerationStatus generateAndWait sendTest get edit | Email Generation | | migma.campaigns | list create get send schedule cancel stats logs archive unarchive | Campaigns | | migma.validation | all compatibility links spelling deliverability | Validation | | migma.previews | create get getStatus getDevice getSupportedDevices createAndWait | Previews | | migma.export | getFormats getStatus html mjml pdf klaviyo mailchimp hubspot | Export | | migma.domains | create list get verify update remove checkAvailability listManaged createManaged removeManaged provisionStream setup | Domains | | migma.webhooks | create list get update remove test getDeliveries getEvents getStats | Webhooks | | migma.knowledgeBase | list add update remove | API Ref | | migma.images | add update remove updateLogos | API Ref |

Async Polling Helpers

Three resources support async operations with built-in polling:

// All polling methods accept optional config
const options = {
  interval: 2000,      // poll every 2s (default)
  maxAttempts: 150,     // max polls before timeout (default)
  onPoll: (status, attempt) => console.log(`Attempt ${attempt}:`, status),
  signal: abortController.signal,  // cancel polling
};

await migma.emails.generateAndWait(params, options);
await migma.projects.importAndWait(params, options);
await migma.previews.createAndWait(params, options);

Use Cases

Send emails from WhatsApp/Telegram via OpenClaw

See Quick Start with OpenClaw above, or read the full tutorial.

Generate + validate + send in one pipeline

const { data: email } = await migma.emails.generateAndWait({
  projectId: 'proj_abc123',
  prompt: 'Summer sale — 30% off everything this weekend',
});

const { data: checks } = await migma.validation.all({
  html: email.result.html,
  subject: email.result.subject,
});

if (checks.overallScore > 80) {
  await migma.sending.send({
    recipientType: 'tag',
    recipientId: 'newsletter-subscribers',
    from: '[email protected]',
    fromName: 'Your Brand',
    subject: email.result.subject,
    conversationId: email.conversationId,
  });
}

Pull live data from Shopify, Notion, Stripe via MCP

Migma connects to 26+ tools via MCP. Generate emails with live product data, content calendars, or revenue metrics.

await migma.emails.generateAndWait({
  projectId: 'proj_abc123',
  prompt: 'Product launch email for items tagged "New Arrival" from Shopify',
});

Email preview testing in CI/CD

const { data: preview } = await migma.previews.createAndWait({
  html: emailHtml,
  devices: ['apple-mail.ios', 'gmail.desktop-webmail', 'outlook.desktop-windows'],
});

const failed = preview.devices.filter((d) => d.status === 'FAILED');
if (failed.length) throw new Error(`Broken on: ${failed.map((d) => d.deviceName)}`);

One-click export to Klaviyo, Mailchimp, HubSpot

const { data } = await migma.export.klaviyo(conversationId, 'hybrid');
console.log('Download:', data.files[0].url);

Requirements

  • Node.js 18 or later (uses native fetch)
  • Migma API key (Settings > API Integration)

Support

License

MIT