Documentation

Everything you need to integrate MailJunky into your application.

Getting Started

  1. 1Sign up for a free account
  2. 2Verify your sending domain
  3. 3Generate an API key
  4. 4Start sending emails!

Installation

Install the official SDK for your language:

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

Initialize the Client

import { MailJunky } from '@mailjunky/sdk'

const client = new MailJunky({
  apiKey: 'mj_live_your_api_key'
})

Sending Emails

Send a Simple Email

await client.emails.send({
  from: 'hello@yourapp.com',
  to: 'user@example.com',
  subject: 'Welcome!',
  html: '<h1>Welcome aboard!</h1>'
})

Send to Multiple Recipients

await client.emails.send({
  from: 'hello@yourapp.com',
  to: ['user1@example.com', 'user2@example.com'],
  cc: 'manager@example.com',
  bcc: ['analytics@yourapp.com'],
  subject: 'Team Update',
  html: '<p>Important announcement...</p>'
})

Batch Sending

Send personalized emails to multiple recipients efficiently:

await client.emails.sendBatch([
  {
    from: 'hello@yourapp.com',
    to: 'user1@example.com',
    subject: 'Welcome, John!',
    html: '<h1>Hi John, welcome aboard!</h1>'
  },
  {
    from: 'hello@yourapp.com',
    to: 'user2@example.com',
    subject: 'Welcome, Sarah!',
    html: '<h1>Hi Sarah, welcome aboard!</h1>'
  }
])

Response

{
  "id": "email_abc123",
  "message_id": "<timestamp.random@yourdomain.com>",
  "status": "sent"
}

Contacts

Store and manage your recipient list with custom properties for personalization in emails and workflows.

Create or Update a Contact

The upsert endpoint creates a new contact or updates an existing one if the email already exists:

await client.contacts.upsert({
  email: 'user@example.com',
  first_name: 'John',
  last_name: 'Doe',
  phone: '+1 555 123 4567',
  properties: {
    company: 'Acme Inc',
    plan: 'pro',
    signup_source: 'website'
  },
  tags: ['customer', 'newsletter']
})

List Contacts

Retrieve contacts with optional filtering:

const { data, pagination } = await client.contacts.list({
  page: 1,
  limit: 25,
  status: 'active',
  tag: 'customer'
})

// data contains array of contacts
// pagination: { page, limit, total, has_more }

Update a Contact

await client.contacts.update('contact_id', {
  tags: ['customer', 'vip'],
  properties: { plan: 'enterprise' },
  status: 'active' // or 'unsubscribed'
})

Batch Import

Import up to 1,000 contacts at once:

const result = await client.contacts.batch({
  contacts: [
    { email: 'user1@example.com', first_name: 'User', last_name: 'One' },
    { email: 'user2@example.com', first_name: 'User', last_name: 'Two' },
    // ... up to 1000 contacts
  ]
})

// result: { created: 150, updated: 50, failed: [] }

Using Contact Properties in Templates

Contact properties can be used as variables in your email templates:

In your email template HTML:

Hi {{contact.first_name}},<br>
Your {{contact.properties.plan}} plan is active.

Event Tracking

Track user behavior to trigger smart email workflows.

⚠️ Paid Feature - Volume-Based Pricing

Event tracking is a premium feature with strict volume limits to ensure platform stability and cost control. Events are charged based on your plan tier and monthly volume.

  • Free Plan: 10,000 events/month included
  • Pro Plan: 500,000 events/month included
  • Overages: $0.10 per 1,000 additional events
  • Rate Limiting: Exceeding hourly limits returns 429 errors

Track an Event

await client.events.track({
  event: 'product_viewed',
  user: 'user@example.com',
  properties: {
    product_id: 'prod_123',
    price: 49.99
  }
})

Common Events

  • user_signed_upNew user registration
  • page_viewUser viewed a page
  • cart_addedItem added to cart
  • checkout_completedPurchase completed

AI-Powered Workflows

Create automated email sequences triggered by user behavior.

✨ AI Feature

Describe your workflow in plain English and our AI will build it for you!

💰 Workflows Use Event Tracking

Event-triggered workflows consume event quota each time they're triggered. High-volume workflows can quickly use your monthly event allocation.

Example: A "cart_abandoned" workflow triggered 100,000 times/month = 100k events consumed. On the Free plan (10k events), this would hit your limit in 3 days. Consider upgrading to Pro (500k events) for high-traffic workflows.

Example: Cart Abandonment

Send a reminder email 30 minutes after cart addition if no checkout occurs.

{
  "name": "Cart Abandonment - 30min",
  "trigger_type": "EVENT",
  "trigger_config": {
    "eventName": "cart_added",
    "delay": 30
  },
  "conditions": [
    {
      "type": "event_not_occurred",
      "event": "checkout_completed",
      "within_minutes": 30
    }
  ],
  "actions": [
    {
      "type": "send_email",
      "config": {
        "from": "hello@yourapp.com",
        "subject": "You left something in your cart!",
        "template": "cart_reminder"
      }
    }
  ]
}

Authentication

API Keys

All requests must include your API key:

Authorization: Bearer mj_live_your_api_key_here

Permissions

PermissionDescription
sendSend emails
trackTrack user events
contactsManage contacts (create, update, delete)
readRead analytics, logs, and contacts
*All permissions

Rate Limits

API requests are rate limited based on your plan tier and endpoint type:

Email Sending Limits

PlanEmails/HourEmails/Month
Free100 emails/hour1,000 emails
Pro1,000 emails/hour50,000 emails
EnterpriseCustomUnlimited

Event Tracking Limits & Pricing

⚠️ Cost Warning: Event tracking consumes significant server resources. We enforce strict limits to prevent runaway costs.

When you exceed your monthly event quota, additional events are charged at $0.10 per 1,000 events. Exceeding hourly rate limits will result in 429 errors until the limit resets.

PlanEvents/HourIncluded/MonthOverage Cost
Free1,000/hour10,000 eventsHard limit
Pro10,000/hour500,000 events$0.10/1k events
EnterpriseCustomCustom volumeNegotiated

Rate Limit Headers

Every API response includes rate limit information:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1640995200

Handling Rate Limits

When you exceed the rate limit, you'll receive a 429 status code:

try {
  await client.emails.send({ /* ... */ })
} catch (error) {
  if (error.status === 429) {
    // Rate limit exceeded
    const retryAfter = error.headers['retry-after']
    console.log(\`Rate limited. Retry after \${retryAfter} seconds\`)
  }
}

Ready to get started?

Start sending emails and building AI-powered workflows today.