Skip to content

SMASHSEND's Official Node.js SDK

License

smashsend/smashsend-node

Repository files navigation

SMASHSEND

Official TypeScript / Node.js SDK for SMASHSEND
Easily integrate email marketing, transactional emails, automations, and contact management into your app.

NPM Version License

SMASHSEND Node.js SDK

What is SMASHSEND?

SMASHSEND is a bold, modern email platform built for business owners, creators, and startups.

  • ⚡️ Drag-and-drop email builder
  • 🪄 AI-powered personalization
  • 🤖 Automations & event triggers
  • 🚀 Scalable, high-deliverability transactional email API
  • 🗂️ Lightweight CRM & contact management
  • 📈 Deep analytics & link tracking

Explore more

Installation

npm install @smashsend/node       # or yarn add @smashsend/node / pnpm add @smashsend/node

Getting an API Key

  1. Log in to your SMASHSEND Dashboard
  2. Navigate to SettingsAPI Keys
  3. Click Create API Key
  4. Give your key a descriptive name (e.g., "Production Server", "Development")
  5. 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.

Create or update a contact

import { SmashSend, SmashsendContactStatus, SmashsendCountryCode } from '@smashsend/node';

const smashsend = new SmashSend(process.env.SMASHSEND_API_KEY!);

const contact = await smashsend.contacts.create({
  email: 'newcontact@example.com', // 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

Batch Contact Creation

Create multiple contacts efficiently in a single API call (takes less than 300ms to add 500 contacts. crazy fast!):

const result = await smashsend.contacts.createBatch(
  [
    { email: 'john@example.com', firstName: 'John', lastName: 'Doe' },
    { email: 'jane@example.com', firstName: 'Jane', lastName: 'Smith' },
    { email: 'bob@example.com', firstName: 'Bob', lastName: 'Johnson' },
  ],
  {
    allowPartialSuccess: true, // Create valid contacts even if some fail
    includeFailedContacts: true, // Include failed contacts in response
  }
);

console.log(`Created: ${result.summary.created}, Failed: ${result.summary.failed}`);

// Handle failures and retry if needed
if (result.failedContacts?.length > 0) {
  const retryableContacts = result.failedContacts
    .filter((fc) => fc.errors.some((e) => e.retryable))
    .map((fc) => fc.contact);

  if (retryableContacts.length > 0) {
    await smashsend.contacts.createBatch(retryableContacts);
  }
}

Send email (basic example)

The simplest way to send a transactional email is with raw HTML:

const response = await smashsend.emails.send({
  from: 'notifications@yourdomain.com',
  to: 'recipient@example.com',
  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', // (Optional) 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.

Send email with template (recommended)

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:

  1. Go to Emails => Transactional in your dashboard
  2. Click Create Transactional
  3. Design your email using the drag-and-drop editor
  4. Add variables (both template variables and contact variables)
  5. Save with a memorable template ID (e.g., welcome-email, order-confirmation)

Sending with a template:

const response = await smashsend.emails.sendWithTemplate({
  to: 'user@example.com',
  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.

Reply-To Addresses

You can specify custom reply-to addresses for both raw and templated emails. This allows recipients to reply to different addresses than the sender.

Single reply-to address:

await smashsend.emails.send({
  from: 'noreply@yourdomain.com',
  to: 'customer@example.com',
  subject: 'Support Request Received',
  html: '<p>We received your support request and will respond soon.</p>',
  replyTo: 'support@yourdomain.com', // Single address
});

Multiple reply-to addresses (max 5):

await smashsend.emails.send({
  from: 'noreply@yourdomain.com',
  to: 'customer@example.com',
  subject: 'Welcome to our platform',
  html: '<p>Welcome! Contact us if you need help.</p>',
  replyTo: ['support@yourdomain.com', 'sales@yourdomain.com'], // Multiple addresses
});

With templates:

await smashsend.emails.sendWithTemplate({
  template: 'welcome-email',
  to: 'user@example.com',
  variables: { firstName: 'John' },
  replyTo: ['support@yourdomain.com', 'welcome@yourdomain.com'], // Overrides template default
});

💡 Note: Dynamic reply-to addresses override any reply-to settings configured in the template. If no dynamic reply-to is provided, the template's reply-to setting is used. Duplicate addresses are automatically removed.

Send email with React

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: 'orders@yourdomain.com',
  to: 'customer@example.com',
  subject: 'Order Confirmation',
  react: OrderConfirmation({
    customerName: 'John',
    orderNumber: '12345',
    items: [
      { name: 'T-Shirt', price: 29.99 },
      { name: 'Shipping', price: 5.0 },
    ],
  }),
  groupBy: 'order-confirmation', // (Optional) Group analytics by email type
});

Events API

Send events to trigger automations, track user behavior, or sync data with your SMASHSEND workspace.

Send a single event:

const response = await smashsend.events.send({
  event: 'user.signup',
  properties: {
    source: 'website',
    campaign: 'summer-sale',
  },
  identify: {
    email: 'user@example.com',
    traits: {
      firstName: 'John',
      lastName: 'Doe',
    },
  },
});

console.log(`Event sent with ID: ${response.messageId}`);

Send multiple events in a batch:

const events = [
  {
    event: 'page.view',
    identify: { email: 'user1@example.com' },
    properties: { page: '/home' },
  },
  {
    event: 'button.click',
    identify: { email: 'user2@example.com' },
    properties: { button: 'signup' },
  },
];

const result = await smashsend.events.sendBatch(events);
console.log(`Accepted: ${result.accepted}, Failed: ${result.failed}`);

// Handle failed events
if (result.errors?.length > 0) {
  result.errors.forEach((error) => {
    console.log(`Event ${error.index} failed:`, error.errors);
  });
}

Event structure:

  • event: Event name (e.g., 'user.signup', 'purchase.completed')
  • identify.email: User email (required)
  • identify.traits: User attributes to sync with contact record (optional)
  • properties: Event-specific data (optional)
  • timestamp: Event timestamp (optional, defaults to current time)
  • messageId: Custom ID for deduplication (optional, auto-generated if not provided)

Advanced Configuration

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,
});

Using with Next.js (Server-Side Only!)

⚠️ 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 }),
    });
  };
}

Error Handling

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);
  }
}

Contact Properties

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 text
  • NUMBER for both integers and decimals

Multi-Select Properties (Tags)

For multi-select properties, you can use a simple array to replace all existing values:

// Simple array replaces all values
await smashsend.contacts.update(contactId, {
  customProperties: {
    tags: ['customer', 'enterprise', 'priority'], // Replaces all existing tags
    interests: ['email-marketing', 'automation'], // Replaces all interests
  },
});

// Empty array removes all values
await smashsend.contacts.update(contactId, {
  customProperties: {
    tags: [], // Removes all tags
  },
});

Advanced: Granular Control

For precise control without fetching current values, use the add/remove structure:

await smashsend.contacts.update(contactId, {
  customProperties: {
    tags: {
      add: ['vip', 'enterprise'], // Add these tags
      remove: ['trial', 'free'], // Remove these tags
    },
  },
});

This is useful for webhooks or event-driven updates where you only know what changed.

TypeScript Support

  • Built in TypeScript
  • Complete type definitions for all resources & enums
  • Works with strictNullChecks, moduleResolution=node, etc.

Automated Publishing Workflow

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

Documentation

Full API reference → https://smashsend.com/docs/api

Contributing

We ❤️ PRs!

  1. Forkgit checkout -b feat/awesome
  2. Add tests & docs
  3. PR against beta or main

License

MIT

About

SMASHSEND's Official Node.js SDK

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published