Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 105 additions & 69 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@

## What is SMASHSEND?

**SMASHSEND** is a bold, modern email platform built for **business owners, creators, and startups** — not just marketers.
**SMASHSEND** is a bold, modern email platform built for **business owners, creators, and startups**.

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

Expand Down Expand Up @@ -79,26 +79,29 @@ console.log(contact.properties.email); // newcontact@example.com

## Batch Contact Creation

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

```typescript
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
});
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);
.filter((fc) => fc.errors.some((e) => e.retryable))
.map((fc) => fc.contact);

if (retryableContacts.length > 0) {
await smashsend.contacts.createBatch(retryableContacts);
}
Expand Down Expand Up @@ -193,15 +196,11 @@ await smashsend.emails.send({

```typescript
await smashsend.emails.send({
from: 'noreply@yourdomain.com',
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',
'billing@yourdomain.com'
], // Multiple addresses
replyTo: ['support@yourdomain.com', 'sales@yourdomain.com'], // Multiple addresses
});
```

Expand Down Expand Up @@ -292,6 +291,67 @@ const response = await smashsend.emails.send({
});
```

## Events API

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

**Send a single event:**

```typescript
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:**

```typescript
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**
Expand Down Expand Up @@ -487,67 +547,43 @@ const property = await smashsend.contacts.createProperty({
- `STRING` for email addresses, URLs, phone numbers, and any text
- `NUMBER` for both integers and decimals

## Events API

Send events to trigger automations, track user behavior, or sync data with your SMASHSEND workspace.
### Multi-Select Properties (Tags)

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

```typescript
const response = await smashsend.events.send({
event: 'user.signup',
identify: {
email: 'user@example.com',
traits: {
firstName: 'John',
lastName: 'Doe',
plan: 'premium'
}
// 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
},
properties: {
source: 'website',
campaign: 'summer-sale'
}
});

console.log(`Event sent with ID: ${response.messageId}`);
// Empty array removes all values
await smashsend.contacts.update(contactId, {
customProperties: {
tags: [], // Removes all tags
},
});
```

**Send multiple events in a batch:**
**Advanced: Granular Control**

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

```typescript
const events = [
{
event: 'page.view',
identify: { email: 'user1@example.com' },
properties: { page: '/home' }
await smashsend.contacts.update(contactId, {
customProperties: {
tags: {
add: ['vip', 'enterprise'], // Add these tags
remove: ['trial', 'free'], // Remove these tags
},
},
{
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)
This is useful for webhooks or event-driven updates where you only know what changed.

## TypeScript Support

Expand Down
44 changes: 43 additions & 1 deletion examples/contact-properties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ async function createContactWithCustomProperties() {

// For select fields (if configured)
industry: 'Technology', // SELECT type
interests: ['Marketing', 'Sales'], // MULTI_SELECT type

// MULTI_SELECT type - simple array replaces all values
interests: ['Marketing', 'Sales'], // Replaces existing interests
tags: ['customer', 'enterprise'], // Replaces existing tags
},
});

Expand All @@ -109,6 +112,39 @@ async function createContactWithCustomProperties() {
}
}

async function updateContactWithMultiSelect() {
try {
const contact = await smashsend.contacts.update('ctc_xxxxx', {
customProperties: {
interests: ['Marketing', 'Sales', 'Product'],
},
});

console.log('Multi-select properties replaced:', contact.id);
} catch (error: any) {
console.error('Error updating contact:', error.message);
throw error;
}
}

async function granularMultiSelectControl() {
try {
const contact = await smashsend.contacts.update('ctc_xxxxx', {
customProperties: {
interests: {
add: ['Product Updates'],
remove: ['Blog Posts'],
},
},
});

console.log('Granular multi-select update completed:', contact.id);
} catch (error: any) {
console.error('Error with granular update:', error.message);
throw error;
}
}

// Important notes about property types:
console.log(`
IMPORTANT: Property Type Mapping
Expand All @@ -128,6 +164,12 @@ Common misconceptions:
- There is NO 'URL' type - use STRING
- There is NO 'PHONE' type - use STRING

Multi-Select Properties (NEW BEHAVIOR):
- Simple arrays REPLACE all existing values: ["tag1", "tag2"]
- For granular control, use: { add: ["tag1"], remove: ["tag2"] }
- Empty array removes all values: []
- Same values = no change (efficient!)

The backend will validate and store these as appropriate.
`);

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@smashsend/node",
"version": "1.18.0",
"version": "1.19.0",
"description": "SMASHSEND Node.js SDK - Official Node.js client for SMASHSEND API",
"main": "dist/index.js",
"module": "dist/index.mjs",
Expand Down