JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 19
  • Score
    100M100P100Q53292F
  • License MIT

Official JavaScript/TypeScript SDK for SendMailOS email API

Package Exports

  • @sendmailos/sdk
  • @sendmailos/sdk/react

Readme

@sendmailos/sdk

Official JavaScript/TypeScript SDK for SendMailOS email API.

Installation

npm install @sendmailos/sdk
# or
yarn add @sendmailos/sdk
# or
pnpm add @sendmailos/sdk

Quick Start

import { SendMailOS } from '@sendmailos/sdk';

const client = new SendMailOS('sk_live_your_api_key');

// Send a transactional email
await client.emails.send({
  to: 'user@example.com',
  fromName: 'Your Company',
  fromEmail: 'hello@yourcompany.com',
  subject: 'Welcome!',
  html: '<h1>Hello!</h1><p>Welcome to our platform.</p>'
});

Features

  • Type-safe: Full TypeScript support with exported types
  • Lightweight: Zero dependencies for core SDK
  • Industry Templates: 50+ pre-built templates for different industries
  • Agency Workspaces: Manage multiple clients from one account
  • Pixel Tracking: Track website visitors and link to email campaigns
  • React Components: Pre-built components and hooks
  • Webhook Verification: Secure signature verification utilities
  • Error Handling: Custom error classes for different scenarios

API Reference

Emails

// Send transactional email (Single Brand)
await client.emails.send({
  to: 'user@example.com',
  subject: 'Hello!',
  html: '<h1>Welcome!</h1>',
  fromName: 'Acme',
  fromEmail: 'hello@acme.com',
});

// Send email (Multi-Brand / SaaS Platform)
// Workspace auto-detected from fromEmail domain - no extra params needed!
await platform.emails.send({
  to: 'customer@example.com',
  subject: 'Your order is ready!',
  html: '<h1>Order Ready!</h1>',
  fromName: 'Pizza Palace',
  fromEmail: 'orders@pizzapalace.com',  // ← Workspace auto-detected
});

// Send with template
await client.emails.sendTemplate({
  to: 'user@example.com',
  templateId: 'tmpl_welcome',
  variables: { firstName: 'John' }
});

Subscribers

// Create subscriber (Single Brand)
const { subscriber } = await client.subscribers.create({
  email: 'user@example.com',
  firstName: 'John',
  tags: ['newsletter', 'premium']
});

// Create subscriber (Multi-Brand / SaaS Platform)
const { subscriber } = await platform.subscribers.create({
  email: 'customer@example.com',
  firstName: 'Jane',
  domain: 'pizzapalace.com',  // Required for multi-brand
  tags: ['loyalty-member']
});

// List subscribers
const { subscribers, total } = await client.subscribers.list({
  limit: 50,
  offset: 0
});

// List subscribers for a specific client (Multi-Brand)
const { subscribers } = await platform.subscribers.list({
  domain: 'pizzapalace.com',
  limit: 50
});

Campaigns

// Send campaign (Single Brand)
await client.campaigns.send({
  name: 'Weekly Newsletter',
  subject: 'What\'s new this week',
  fromName: 'Company Newsletter',
  fromEmail: 'news@company.com',
  html: '<h1>Hello {{first_name}}!</h1>',
  tags: ['newsletter'] // Filter by subscriber tags
});

// Send campaign (Multi-Brand / SaaS Platform)
// Workspace auto-detected from fromEmail domain
await platform.campaigns.send({
  name: 'Pizza Promo',
  subject: 'New menu items!',
  fromName: 'Pizza Palace',
  fromEmail: 'promo@pizzapalace.com',  // ← Workspace auto-detected
  html: '<h1>Check out our new items!</h1>',
  sourceDomains: ['pizzapalace.com']   // Only send to subscribers from this domain
});

Domains

// Add sending domain (Single Brand)
const { domain } = await client.domains.create({
  domain: 'yourcompany.com'
});

console.log('DNS Records to add:', domain.dnsRecords);

// Add client domain (Multi-Brand / SaaS Platform)
// Show these DNS records to your client in your UI
const { dns_records, domain_id } = await platform.domains.create({
  domain: 'pizzapalace.com'
});

// After client verifies DNS:
// ✓ Domain status becomes "verified"
// ✓ Workspace is AUTO-CREATED for this domain
// ✓ You can now send emails from @pizzapalace.com

Organization & Industries

Set your industry during onboarding to get pre-built templates and workflows.

import { SendMailOS, INDUSTRIES } from '@sendmailos/sdk';

const client = new SendMailOS('sk_live_...');

// Get current organization
const org = await client.organization.get();

// Set industries (multi-select)
await client.organization.setIndustries(['ecommerce', 'saas']);

// Convert to agency account (enables workspaces)
await client.organization.convertToAgency();

// Available industries (50+)
console.log(INDUSTRIES);
// { hotels: 'Hotels & Hospitality', restaurants: 'Restaurants & Food Service', ... }

Supported Industries

Hotels, Restaurants, Gyms, Travel Agencies, Real Estate, Schools, E-commerce, Dentists, Car Dealerships, Events, Beauty Salons, Law Firms, Non-Profits, SaaS, Recruitment, Insurance, Online Courses, Municipalities, Personal Trainers, Influencers, Doctors/Clinics, Nightclubs, Coworking, Wedding Planners, Art Galleries, Car Rentals, Podcasters, Consultants, Bakeries, Veterinarians, Airbnb Hosts, Accountants, Developers, Musicians, Spas, Bookstores, Cleaning Services, Churches, Graphic Designers, Coffee Shops, Florists, Political Campaigns, Libraries, Taxis/Limos, Home Inspectors, Photographers, Universities, Fashion, Nutritionists, Startups

Account Types

Single Brand (Business)

For companies sending emails from their own domain. Simple setup, no workspaces needed.

const client = new SendMailOS('sk_live_your_api_key');

await client.emails.send({
  to: 'customer@example.com',
  fromEmail: 'hello@yourcompany.com',
  subject: 'Welcome!',
  html: '<h1>Hello!</h1>'
});

Multi-Brand / SaaS Platform

For agencies or SaaS platforms managing multiple clients. Each client gets their own workspace with isolated data.

// Your platform's master API key
const platform = new SendMailOS('sk_live_platform_key');

// 1. Client signs up → Register their domain
const { dns_records } = await platform.domains.create({
  domain: 'pizzapalace.com'
});
// Show dns_records to client in your UI

// 2. Domain verified → Workspace auto-created!
//    (No manual workspace creation needed)

// 3. Send emails → Workspace auto-detected from fromEmail
await platform.emails.send({
  to: 'customer@example.com',
  fromEmail: 'orders@pizzapalace.com',  // ← Workspace detected automatically!
  fromName: 'Pizza Palace',
  subject: 'Your order is ready!',
  html: '<h1>Order Ready!</h1>'
});

// 4. Add subscribers → Use domain param
await platform.subscribers.create({
  email: 'customer@example.com',
  domain: 'pizzapalace.com'  // Required for multi-brand
});

// 5. List subscribers for a specific client
const { subscribers } = await platform.subscribers.list({
  domain: 'pizzapalace.com'
});

// 6. Send campaign → Workspace auto-detected
await platform.campaigns.send({
  subject: 'New Menu Items!',
  fromName: 'Pizza Palace',
  fromEmail: 'promo@pizzapalace.com',  // ← Auto-detected
  html: '<h1>Check out our new menu!</h1>'
});

How Workspace Detection Works

Action How workspace is identified
Register domain Creates domain record (pending)
Domain verified Workspace auto-created
Send email Auto-detect from fromEmail domain
Send campaign Auto-detect from fromEmail domain
Add subscriber From domain parameter
List subscribers From domain parameter

One verified domain = One workspace. Simple.

Agency Workspaces (Advanced)

For more control, you can manually manage workspaces:

// Convert to agency account first
await client.organization.convertToAgency();

// Create client workspaces manually
const pizzaPlace = await client.workspaces.create({
  name: 'Pizza Palace',
  industry: 'restaurants',
  import_templates: true  // Auto-import restaurant templates
});

// List all clients
const { workspaces } = await client.workspaces.list();
for (const ws of workspaces) {
  console.log(`${ws.name}: ${ws.stats?.subscribers} subscribers`);
}

// Get detailed stats
const details = await client.workspaces.get(pizzaPlace.id);
console.log(details.stats);
// { total_subscribers: 1200, active_subscribers: 1150, total_campaigns: 24, ... }

// Invite client to view reports
await client.workspaces.inviteClient(pizzaPlace.id, 'owner@pizzapalace.com');

Workflows

Automate email sequences with workflows. Create them via API, build the steps in your UI, then trigger them programmatically.

Workflow Lifecycle

1. Create workflow (draft)
2. Add nodes & edges (build the flow)
3. Activate workflow (start accepting triggers)
4. Trigger for subscribers (via API or events)

Create & Manage Workflows

// Create a workflow for a client (Multi-Brand)
const { data } = await platform.workflows.create({
  name: 'Welcome Sequence',
  domain: 'pizzapalace.com',  // Auto-detect workspace
  trigger: { type: 'api' }    // Triggered via API
});

const workflowId = data.workflow.id;

// List workflows
const { data: list } = await platform.workflows.list({ domain: 'pizzapalace.com' });

// Get workflow with nodes/edges
const { data: workflow } = await platform.workflows.get(workflowId);

// Delete workflow
await platform.workflows.delete(workflowId);

Build Workflow Steps

Update a workflow with nodes (steps) and edges (connections):

await platform.workflows.update(workflowId, {
  nodes: [
    {
      id: 'trigger-1',
      type: 'trigger',
      data: { triggerType: 'api' }
    },
    {
      id: 'email-1',
      type: 'email',
      data: {
        subject: 'Welcome to {{restaurantName}}!',
        templateId: 'tmpl_welcome'
      }
    },
    {
      id: 'delay-1',
      type: 'delay',
      data: { duration: 3, unit: 'days' }
    },
    {
      id: 'email-2',
      type: 'email',
      data: {
        subject: 'Your first order discount',
        templateId: 'tmpl_promo'
      }
    }
  ],
  edges: [
    { id: 'e1', source: 'trigger-1', target: 'email-1' },
    { id: 'e2', source: 'email-1', target: 'delay-1' },
    { id: 'e3', source: 'delay-1', target: 'email-2' }
  ]
});

Node Types

Type Description Data Fields
trigger Entry point triggerType
email Send email subject, templateId, fromName, fromEmail
delay Wait period duration, unit (minutes/hours/days)
condition Branch logic field, operator, value
tag Add/remove tags action (add/remove), tags
http Call webhook url, method, headers, body
wait_for_event Wait for event eventName, timeout
unsubscribe Unsubscribe user -
exit End workflow -

Activate & Pause

// Activate workflow (start accepting triggers)
await platform.workflows.activate(workflowId);

// Pause workflow (stop new triggers, existing runs continue)
await platform.workflows.pause(workflowId);

Trigger Workflows

// When a customer signs up at the restaurant
await platform.workflows.trigger(workflowId, {
  email: 'customer@example.com',
  data: {
    firstName: 'John',
    restaurantName: 'Pizza Palace',
    signupSource: 'website'
  }
});

Wait for Events

Workflows can pause and wait for specific events:

// Workflow has a "wait_for_event: purchase_completed" node
// When customer makes a purchase, send the event:
await platform.workflows.sendEvent({
  event: 'purchase_completed',
  email: 'customer@example.com',
  data: { orderTotal: 45.99 }
});
// The workflow resumes from where it was waiting

Example: Restaurant Welcome Flow

// 1. Create workflow
const { data } = await platform.workflows.create({
  name: 'New Customer Welcome',
  domain: 'pizzapalace.com'
});

// 2. Build the flow
await platform.workflows.update(data.workflow.id, {
  nodes: [
    { id: '1', type: 'trigger', data: { triggerType: 'api' } },
    { id: '2', type: 'email', data: { subject: 'Welcome!', templateId: 'tmpl_welcome' } },
    { id: '3', type: 'delay', data: { duration: 2, unit: 'days' } },
    { id: '4', type: 'condition', data: { field: 'has_ordered', operator: 'eq', value: true } },
    { id: '5', type: 'email', data: { subject: 'Thanks for ordering!', templateId: 'tmpl_thanks' } },
    { id: '6', type: 'email', data: { subject: '10% off your first order', templateId: 'tmpl_promo' } }
  ],
  edges: [
    { id: 'e1', source: '1', target: '2' },
    { id: 'e2', source: '2', target: '3' },
    { id: 'e3', source: '3', target: '4' },
    { id: 'e4', source: '4', target: '5', sourceHandle: 'yes' },
    { id: 'e5', source: '4', target: '6', sourceHandle: 'no' }
  ]
});

// 3. Activate
await platform.workflows.activate(data.workflow.id);

// 4. Trigger when new customer signs up
await platform.workflows.trigger(data.workflow.id, {
  email: 'newcustomer@gmail.com',
  data: { firstName: 'Jane', restaurantName: 'Pizza Palace' }
});

Pixel Tracking

Track website visitors and connect their behavior to email campaigns.

Basic Setup

import { SendmailPixel } from '@sendmailos/sdk';

const pixel = new SendmailPixel({
  token: 'pk_live_your_public_key',
  debug: false // Set true for console logging
});

pixel.init();

Track Events

// Track custom events
pixel.track('button_clicked', { button_id: 'cta-signup' });

// Track page views (called automatically on init)
pixel.pageView();

// Identify a visitor by email
pixel.identify('user@example.com', {
  first_name: 'John',
  plan: 'premium'
});

Privacy Controls

// Opt out of tracking (e.g., user declines cookies)
pixel.optOut();

// Opt back in
pixel.optIn();

// Check opt-out status
if (pixel.isOptedOut()) {
  console.log('Tracking disabled');
}

// Reset identity (e.g., on logout)
pixel.reset();

Global smp() Function

For script tag usage, use createGlobalPixel to set up a global smp() function:

import { createGlobalPixel } from '@sendmailos/sdk';

createGlobalPixel({ token: 'pk_live_...' });

// Now use anywhere:
smp('track', 'event_name', { property: 'value' });
smp('identify', 'user@example.com');
smp('optOut');
smp('optIn');

React Pixel Hooks

import { usePixel, usePageTracking, useIdentifyOnLogin } from '@sendmailos/sdk/react';

// Basic usage
function MyComponent() {
  const { track, identify, optOut } = usePixel('pk_live_...');

  return (
    <button onClick={() => track('button_clicked', { id: 'cta' })}>
      Click Me
    </button>
  );
}

// Auto page tracking (Next.js App Router)
function Layout({ children }) {
  const pathname = usePathname();
  usePageTracking('pk_live_...', pathname);
  return <>{children}</>;
}

// Auto identify on login
function App() {
  const { user } = useAuth();
  useIdentifyOnLogin('pk_live_...', user?.email, {
    first_name: user?.firstName,
    plan: user?.plan
  });
  return <Main />;
}

Data Attributes

Track clicks without JavaScript using HTML data attributes:

<button data-smp-track="signup_clicked">Sign Up</button>

<button
  data-smp-track="button_clicked"
  data-smp-track-button-id="cta-main"
  data-smp-track-variant="blue"
>
  Get Started
</button>

React Integration

import { SendMailOSProvider, SubscribeForm } from '@sendmailos/sdk/react';

function App() {
  return (
    <SendMailOSProvider publicKey="pk_live_...">
      <SubscribeForm
        tags={['newsletter']}
        onSuccess={() => console.log('Subscribed!')}
        buttonText="Join Newsletter"
      />
    </SendMailOSProvider>
  );
}

Custom Form with Hook

import { useSubscribe } from '@sendmailos/sdk/react';

function CustomForm() {
  const { subscribe, isLoading, error, isSuccess } = useSubscribe();
  const [email, setEmail] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    await subscribe({ email, tags: ['newsletter'] });
  };

  if (isSuccess) return <p>Thanks for subscribing!</p>;

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
      />
      <button disabled={isLoading}>
        {isLoading ? 'Subscribing...' : 'Subscribe'}
      </button>
      {error && <p>{error.message}</p>}
    </form>
  );
}

Webhook Verification

import { verifyWebhookSignature } from '@sendmailos/sdk';

app.post('/webhooks', (req, res) => {
  const isValid = verifyWebhookSignature({
    payload: JSON.stringify(req.body),
    signature: req.headers['x-sendmailos-signature'],
    timestamp: req.headers['x-sendmailos-timestamp'],
    secret: process.env.WEBHOOK_SECRET
  });

  if (!isValid) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Process webhook event
  const event = req.body;
  console.log('Event:', event.type);

  res.status(200).json({ received: true });
});

Error Handling

import { SendMailOS, SendMailOSError, RateLimitError } from '@sendmailos/sdk';

try {
  await client.emails.send({ ... });
} catch (error) {
  if (error instanceof RateLimitError) {
    console.log(`Rate limited. Retry after ${error.retryAfter}s`);
  } else if (error instanceof SendMailOSError) {
    console.log(`Error: ${error.message} (${error.code})`);
  }
}

Security

  • Never expose secret keys (sk_*) in client-side code
  • Use public keys (pk_*) for React components
  • Verify webhook signatures to prevent spoofing
  • Use environment variables for API keys

License

MIT