Official JavaScript/TypeScript SDK for SendMailOS email API
npm install @sendmailos/sdkOfficial JavaScript/TypeScript SDK for SendMailOS email API.
``bash`
npm install @sendmailos/sdkor
yarn add @sendmailos/sdkor
pnpm add @sendmailos/sdk
`typescript
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: '
Welcome to our platform.
'Features
- Type-safe: Full TypeScript support with exported types
- Lightweight: Zero dependencies for core SDK
- Attachments: Send files via Base64 or URL (auto-fetched)
- Email Automation: Build workflows with triggers, delays, conditions, and actions
- 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
- Webhooks: Real-time event notifications with signature verification
- React Components: Pre-built components and hooks
- Error Handling: Custom error classes for different scenarios
- External IDs & Metadata: Correlate records with your system and store custom data
External IDs & Metadata
All resources support
externalId and metadata fields for integrating with your existing systems.`typescript
// Store your system's ID and custom data on any resource
await client.subscribers.create({
email: 'user@example.com',
externalId: 'your_system_user_123', // Your internal ID
metadata: {
source: 'mobile_app',
signup_campaign: 'summer_2024',
customer_tier: 'gold'
}
});// Query by external ID
const { subscribers } = await client.subscribers.list({
externalId: 'your_system_user_123'
});
// Works on all resources: subscribers, domains, campaigns, emails, workflows, pixels
await client.domains.create({
domain: 'client.com',
externalId: 'client_domain_456',
metadata: { client_name: 'Acme Corp' }
});
await client.emails.send({
to: 'user@example.com',
subject: 'Order Confirmation',
html: '
Thanks!
',
fromEmail: 'orders@company.com',
externalId: 'order_email_789',
metadata: { order_id: 'ORD-123', amount: 99.99 }
});
`API Reference
$3
`typescript
// Send transactional email (Single Brand)
await client.emails.send({
to: 'user@example.com',
subject: 'Hello!',
html: 'Welcome!
',
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: '
Order Ready!
',
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' }
});
// Send with external ID and metadata (for tracking in your system)
await client.emails.send({
to: 'customer@example.com',
subject: 'Order Confirmation #12345',
html: '
Order Confirmed!
',
fromName: 'Acme Store',
fromEmail: 'orders@acme.com',
externalId: 'order_email_12345',
metadata: {
order_id: 'ORD-12345',
order_total: 149.99,
source: 'checkout_flow'
}
});
`$3
Send emails with file attachments (max 5 files, 7MB total).
`typescript
// With Base64 content
await client.emails.send({
to: 'user@example.com',
subject: 'Invoice Attached',
html: 'Please find your invoice attached.
',
fromEmail: 'billing@company.com',
fromName: 'Billing',
attachments: [
{
filename: 'invoice.pdf',
content: 'JVBERi0xLjQK...', // Base64 encoded
contentType: 'application/pdf'
}
]
});// With URL (fetched automatically)
await client.emails.send({
to: 'user@example.com',
subject: 'Your Report',
html: '
Here is your report.
',
fromEmail: 'reports@company.com',
attachments: [
{
filename: 'report.pdf',
url: 'https://cdn.example.com/reports/123.pdf',
contentType: 'application/pdf'
}
]
});// Inline images (using cid)
await client.emails.send({
to: 'user@example.com',
subject: 'Check out our logo',
html: '
Our logo: 
',
fromEmail: 'hello@company.com',
attachments: [
{
filename: 'logo.png',
url: 'https://cdn.example.com/logo.png',
contentType: 'image/png',
cid: 'logo-image' // Reference in HTML with cid:logo-image
}
]
});
`Attachment Limits:
| Limit | Value |
|-------|-------|
| Max attachments | 5 |
| Single file max | 5 MB |
| Total size | 7 MB |
$3
`typescript
// Create subscriber (Single Brand)
const { subscriber } = await client.subscribers.create({
email: 'user@example.com',
firstName: 'John',
tags: ['newsletter', 'premium'],
externalId: 'crm_contact_123', // Optional: your system's ID
metadata: { source: 'website' } // Optional: custom data
});// 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'],
externalId: 'rb_customer_456',
metadata: { restaurant_id: 'rest_789' }
});
// List subscribers
const { subscribers, total } = await client.subscribers.list({
limit: 50,
offset: 0
});
// List by external ID
const { subscribers } = await client.subscribers.list({
externalId: 'crm_contact_123'
});
// List subscribers for a specific client (Multi-Brand)
const { subscribers } = await platform.subscribers.list({
domain: 'pizzapalace.com',
limit: 50
});
`$3
`typescript
// 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: 'Hello {{first_name}}!
',
tags: ['newsletter'], // Filter by subscriber tags
externalId: 'campaign_jan_2024', // Optional: track in your system
metadata: { campaign_type: 'weekly' }
});// 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: '
Check out our new items!
',
sourceDomains: ['pizzapalace.com'], // Only send to subscribers from this domain
externalId: 'rb_promo_123',
metadata: { restaurant_id: 'rest_456' }
});
`$3
`typescript
// Add sending domain (Single Brand)
const { domain } = await client.domains.create({
domain: 'yourcompany.com',
externalId: 'domain_001', // Optional: your system's ID
metadata: { environment: 'production' }
});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',
externalId: 'rb_domain_456',
metadata: { client_name: 'Pizza Palace' }
});
// 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.
`typescript
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', ... }
`$3
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
$3
For companies sending emails from their own domain. Simple setup, no workspaces needed.
`typescript
const client = new SendMailOS('sk_live_your_api_key');await client.emails.send({
to: 'customer@example.com',
fromEmail: 'hello@yourcompany.com',
subject: 'Welcome!',
html: '
Hello!
'
});
`$3
For agencies or SaaS platforms managing multiple clients. Each client gets their own workspace with isolated data.
`typescript
// 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: '
Order Ready!
'
});// 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: '
Check out our new menu!
'
});
`$3
| 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.
$3
Master API keys can access all workspaces in your organization with a single key. Ideal for SaaS platforms that need to manage multiple client workspaces programmatically.
#### Recommended Flow for SaaS Platforms
`typescript
// Your platform backend - when a new client signs up
const platform = new SendMailOS('sk_live_master_key');// 1. Create workspace first (with your client's data)
const { workspace } = await platform.workspaces.create({
name: 'Pizza Palace', // From your user's profile
industry: 'restaurants', // Optional - can be null
website: 'https://pizzapalace.com'
});
// 2. Add domain TO that workspace
const { dns_records } = await platform.domains.create({
domain: 'pizzapalace.com',
workspaceId: workspace.id // Link to the workspace
});
// 3. Show DNS records to your user
// They verify DNS, domain becomes active
// 4. Now send emails using workspace_id or domain
await platform.emails.send({
to: 'customer@example.com',
fromEmail: 'hello@pizzapalace.com',
subject: 'Welcome!',
html: '
Welcome!
',
workspaceId: workspace.id
});
`Your end users only see: "Add domain → Verify DNS → Done"
Behind the scenes: Workspace created with proper settings → Domain linked → Ready to send.
Master Key vs Workspace Key:
| Feature | Master Key | Workspace Key |
|---------|-----------|---------------|
| Access | All workspaces | Single workspace |
| workspaceId param | Required | Not needed |
| Use case | Platform/SaaS backend | Per-client integrations |
Agency Workspaces (Advanced)
For more control, you can manually manage workspaces:
`typescript
// 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.
$3
`
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)
`$3
`typescript
// 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);
`$3
Update a workflow with nodes (steps) and edges (connections):
`typescript
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' }
]
});
`$3
| 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 | - |$3
`typescript
// Activate workflow (start accepting triggers)
await platform.workflows.activate(workflowId);// Pause workflow (stop new triggers, existing runs continue)
await platform.workflows.pause(workflowId);
`$3
`typescript
// 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'
}
});
`$3
Workflows can pause and wait for specific events:
`typescript
// 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
`$3
`typescript
// 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' }
});
`Webhooks
Receive real-time notifications when events occur (emails sent, opened, clicked, bounced, etc.).
$3
`typescript
// Create a webhook endpoint
const { webhook } = await client.webhooks.create({
url: 'https://yourserver.com/webhooks',
events: ['email.sent', 'email.delivered', 'email.opened', 'email.clicked', 'email.bounced'],
});console.log('Webhook secret:', webhook.secret); // Use this to verify signatures
// List all webhooks
const { webhooks } = await client.webhooks.list();
// Get webhook with delivery history
const { webhook, recentDeliveries, stats } = await client.webhooks.get('webhook-id');
// Update webhook
await client.webhooks.update('webhook-id', {
events: ['email.sent', 'email.bounced', 'email.complained']
});
// Test webhook endpoint
const result = await client.webhooks.test('webhook-id');
if (result.test.sent) {
console.log(
Test delivered in ${result.test.responseTimeMs}ms);
}// Disable/Enable webhook
await client.webhooks.disable('webhook-id');
await client.webhooks.enable('webhook-id');
// Delete webhook
await client.webhooks.delete('webhook-id');
`$3
| Event | Description |
|-------|-------------|
|
email.sent | Email was sent to the recipient |
| email.delivered | Email was delivered to recipient's server |
| email.opened | Recipient opened the email |
| email.clicked | Recipient clicked a link in the email |
| email.bounced | Email bounced (hard or soft) |
| email.complained | Recipient marked email as spam |
| subscriber.created | New subscriber was added |
| subscriber.updated | Subscriber was updated |
| subscriber.unsubscribed | Subscriber unsubscribed |
| campaign.sent | Campaign finished sending |
| workflow.started | Workflow execution started |
| workflow.completed | Workflow execution completed |Pixel Tracking
Track website visitors and connect their behavior to email campaigns.
$3
`typescript
import { SendmailPixel } from '@sendmailos/sdk';const pixel = new SendmailPixel({
token: 'pk_live_your_public_key',
debug: false // Set true for console logging
});
pixel.init();
`$3
`typescript
// 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'
});
`$3
`typescript
// 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();
`$3
For script tag usage, use
createGlobalPixel to set up a global smp() function:`typescript
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');
`$3
`tsx
import { usePixel, usePageTracking, useIdentifyOnLogin } from '@sendmailos/sdk/react';// Basic usage
function MyComponent() {
const { track, identify, optOut } = usePixel('pk_live_...');
return (
);
}
// 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 ;
}
`$3
Track clicks without JavaScript using HTML data attributes:
`html
data-smp-track="button_clicked"
data-smp-track-button-id="cta-main"
data-smp-track-variant="blue"
>
Get Started
`React Integration
`tsx
import { SendMailOSProvider, SubscribeForm } from '@sendmailos/sdk/react';function App() {
return (
tags={['newsletter']}
onSuccess={() => console.log('Subscribed!')}
buttonText="Join Newsletter"
/>
);
}
`$3
`tsx
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
Thanks for subscribing!
; return (
);
}
`Webhook Verification
`typescript
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
`typescript
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 componentsMIT