Official TypeScript / Node.js SDK for SMASHSEND
Easily integrate email marketing, transactional emails, automations, and contact management into your app.
SMASHSEND is a bold, modern email platform built for business owners, creators, and startups — not just marketers.
- ⚡️ Drag-and-drop email builder
- 🪄 AI-powered personalization ("Magic Boxes")
- 🤖 Automations & event triggers
- 🚀 High-deliverability transactional email API
- 🗂️ Lightweight CRM & contact management
- 📈 Deep analytics & link tracking
npm install @smashsend/node # or yarn add @smashsend/node / pnpm add @smashsend/node
- Log in to your SMASHSEND Dashboard
- Navigate to Settings → API Keys
- Click Create API Key
- Give your key a descriptive name (e.g., "Production Server", "Development")
- Copy the key immediately — it won't be shown again!
import { SmashSend } from '@smashsend/node';
const smashsend = new SmashSend(process.env.SMASHSEND_API_KEY!);
Security tip: Never commit API keys to version control. Use environment variables or a secrets manager.
import { SmashSend, SmashsendContactStatus, SmashsendCountryCode } from '@smashsend/node';
const smashsend = new SmashSend(process.env.SMASHSEND_API_KEY!);
const contact = await smashsend.contacts.create({
email: '[email protected]', // required
firstName: 'John',
lastName: 'Doe',
phone: '+1234567890',
status: SmashsendContactStatus.SUBSCRIBED, // defaults to SUBSCRIBED
countryCode: SmashsendCountryCode.US,
customProperties: {}, // define in dashboard first
});
console.log(contact.id); // contact UUID
console.log(contact.properties.email); // newcontact@example.com
The simplest way to send a transactional email is with raw HTML:
const response = await smashsend.emails.send({
from: '[email protected]',
to: '[email protected]',
subject: 'Your order has shipped!',
html: `
<h1>Great news!</h1>
<p>Your order #12345 has shipped and is on its way.</p>
<a href="https://track.example.com/12345">Track your package</a>
`,
text: 'Your order #12345 has shipped. Track at: https://track.example.com/12345',
groupBy: 'order-shipped', // Group analytics by email type
settings: {
trackClicks: true,
trackOpens: true,
},
});
📊 Analytics tip: Use the
groupBy
parameter to group similar emails together in your analytics dashboard. This helps you track performance across all "order shipped" emails, regardless of individual recipients.
For better maintainability and design flexibility, use templates — the recommended approach for transactional emails.
Why use templates?
- 🎨 Design beautiful emails in the SMASHSEND visual editor
- 🔄 Update email content without deploying code
- 📊 Built-in analytics and tracking
- 🧪 A/B test different versions
- 👥 Non-technical team members can modify content
- 🌐 Automatic responsive design
Creating a template:
- Go to Emails => Transactional in your dashboard
- Click Create Transactional
- Design your email using the drag-and-drop editor
- Add variables (both template variables and contact variables)
- Save with a memorable template ID (e.g.,
welcome-email
,order-confirmation
)
Sending with a template:
const response = await smashsend.emails.sendWithTemplate({
to: '[email protected]',
template: 'welcome-email', // Template ID from dashboard
variables: {
firstName: 'Sarah',
companyName: 'Acme Corp',
signupDate: new Date().toLocaleDateString(),
// Any variables used in your template
},
settings: {
trackClicks: true,
trackOpens: true,
},
});
console.log(response.messageId); // Unique ID for tracking
console.log(response.status); // SCHEDULED, SENT, etc.
For developers using React, you can write emails as React components:
First, install the React email renderer:
npm install @react-email/render
Create your email component:
// emails/OrderConfirmation.tsx
import * as React from 'react';
interface OrderConfirmationProps {
customerName: string;
orderNumber: string;
items: Array<{ name: string; price: number }>;
}
export default function OrderConfirmation({
customerName,
orderNumber,
items,
}: OrderConfirmationProps) {
const total = items.reduce((sum, item) => sum + item.price, 0);
return (
<div style={{ fontFamily: 'Arial, sans-serif', maxWidth: '600px', margin: '0 auto' }}>
<h1 style={{ color: '#333' }}>Thanks for your order, {customerName}!</h1>
<p>Order #{orderNumber} has been confirmed.</p>
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<tbody>
{items.map((item, i) => (
<tr key={i}>
<td style={{ padding: '10px 0' }}>{item.name}</td>
<td style={{ textAlign: 'right' }}>${item.price.toFixed(2)}</td>
</tr>
))}
<tr style={{ borderTop: '2px solid #333', fontWeight: 'bold' }}>
<td style={{ padding: '10px 0' }}>Total</td>
<td style={{ textAlign: 'right' }}>${total.toFixed(2)}</td>
</tr>
</tbody>
</table>
</div>
);
}
Send the React email:
import OrderConfirmation from './emails/OrderConfirmation';
const response = await smashsend.emails.send({
from: '[email protected]',
to: '[email protected]',
subject: 'Order Confirmation',
react: OrderConfirmation({
customerName: 'John',
orderNumber: '12345',
items: [
{ name: 'T-Shirt', price: 29.99 },
{ name: 'Shipping', price: 5.0 },
],
}),
groupBy: 'order-confirmation',
});
Custom Headers
// Add multiple headers
smashsend.setHeaders({
'X-Custom-Header': 'value',
'X-Tracking-ID': 'campaign-123',
});
// Or add an individual header
smashsend.setHeader('X-Source', 'website');
Debug Mode
smashsend.setDebugMode(true); // logs all requests & responses
Retry Configuration
const smashsend = new SmashSend(process.env.SMASHSEND_API_KEY!, {
maxRetries: 5, // default 3
timeout: 60000,
});
⚠️ SECURITY NOTE: This SDK contains your secret API key and must NEVER be used in client-side code. Only use it in:
- Server Components
- API Routes
- Server Actions
- Middleware
Never import this SDK in client components or expose your API key to the browser!
Helper (Server-Side Only)
// lib/smashsend.ts
// ⚠️ This file should only be imported in server-side code!
import { SmashSend } from '@smashsend/node';
let client: SmashSend;
export function getSmashSendClient(apiKey?: string) {
if (!client) {
client = new SmashSend(apiKey ?? process.env.SMASHSEND_API_KEY!);
}
return client;
}
Server Component Example
// app/contacts/page.tsx
// ✅ This is a Server Component - API key is safe here
import { getSmashSendClient } from '@/lib/smashsend';
export default async function ContactsPage() {
const smashsend = getSmashSendClient();
const { contacts } = await smashsend.contacts.list();
return (
<ul>
{contacts.map((c) => (
<li key={c.id}>
{c.properties.firstName} ({c.properties.email})
</li>
))}
</ul>
);
}
API Route Example
// app/api/contact/route.ts
// ✅ API routes run on the server - API key is safe here
import { getSmashSendClient, SmashsendContactStatus, SmashsendCountryCode } from '@/lib/smashsend';
import { NextResponse } from 'next/server';
export async function POST(req: Request) {
const data = await req.json();
try {
const smashsend = getSmashSendClient();
const contact = await smashsend.contacts.create({
email: data.email,
status: SmashsendContactStatus.SUBSCRIBED,
countryCode: SmashsendCountryCode.US,
customProperties: data.customFields,
});
return NextResponse.json({ success: true, contact });
} catch (err: any) {
return NextResponse.json({ success: false, error: err.message }, { status: 400 });
}
}
❌ What NOT to do
// components/BadExample.tsx
'use client' // ❌ Client component
import { SmashSend } from '@smashsend/node';
export function BadExample() {
// ❌ NEVER DO THIS! This exposes your API key to the browser!
const smashsend = new SmashSend('your-api-key');
// ❌ This will leak your API key in the browser's network tab
const handleClick = async () => {
await smashsend.emails.send({ ... });
};
}
✅ Correct approach for client-side interactions
// components/GoodExample.tsx
'use client';
export function GoodExample() {
const handleSubmit = async (email: string) => {
// ✅ Call your API route instead
await fetch('/api/contact', {
method: 'POST',
body: JSON.stringify({ email }),
});
};
}
import { SmashSend, SmashSendError } from '@smashsend/node';
try {
await smashsend.emails.send({
/* … */
});
} catch (err) {
if (err instanceof SmashSendError) {
console.error(err.statusCode, err.requestId, err.message);
} else {
console.error('Unexpected error', err);
}
}
SMASHSEND supports custom contact properties with the following types:
import { SmashsendPropertyType } from '@smashsend/node';
// Available property types:
SmashsendPropertyType.SELECT; // Single choice dropdown
SmashsendPropertyType.MULTI_SELECT; // Multiple choice selection
SmashsendPropertyType.STRING; // Text (max 255 characters)
SmashsendPropertyType.NUMBER; // Decimal numbers
SmashsendPropertyType.DATE; // Date values
SmashsendPropertyType.BOOLEAN; // True/False values
Creating a custom property:
const property = await smashsend.contacts.createProperty({
displayName: 'Industry',
type: SmashsendPropertyType.SELECT,
description: 'The industry sector',
typeConfig: {
multiple: false,
options: ['Technology', 'Healthcare', 'Finance', 'Other'],
},
});
Important: There are no separate EMAIL, URL, PHONE, TEXT, or INTEGER types. Use:
STRING
for email addresses, URLs, phone numbers, and any textNUMBER
for both integers and decimals
- Built in TypeScript
- Complete type definitions for all resources & enums
- Works with
strictNullChecks
,moduleResolution=node
, etc.
GitHub Actions publishes to npm automatically.
Branch | Release type |
---|---|
beta |
Prereleases x.y.z-beta.n |
main |
Stable releases x.y.z |
Version bumps & Git tags (v1.2.3
/ v1.2.3-beta.4
) are handled for you.
Required secret
NPM_TOKEN → Settings ▸ Secrets ▸ Actions
Full API reference → https://smashsend.com/docs/api
We ❤️ PRs!
- Fork →
git checkout -b feat/awesome
- Add tests & docs
- PR against
beta
ormain