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: '[email protected]',
  to: '[email protected]',
  subject: 'Welcome!',
  html: '<h1>Welcome aboard!</h1>'
})

Send to Multiple Recipients

await client.emails.send({
  from: '[email protected]',
  to: ['[email protected]', '[email protected]'],
  cc: '[email protected]',
  bcc: ['[email protected]'],
  subject: 'Team Update',
  html: '<p>Important announcement...</p>'
})

Batch Sending

Send personalized emails to multiple recipients efficiently:

await client.emails.sendBatch([
  {
    from: '[email protected]',
    to: '[email protected]',
    subject: 'Welcome, John!',
    html: '<h1>Hi John, welcome aboard!</h1>'
  },
  {
    from: '[email protected]',
    to: '[email protected]',
    subject: 'Welcome, Sarah!',
    html: '<h1>Hi Sarah, welcome aboard!</h1>'
  }
])

Response

{
  "id": "email_abc123",
  "message_id": "<[email protected]>",
  "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: '[email protected]',
  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: '[email protected]', first_name: 'User', last_name: 'One' },
    { email: '[email protected]', 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.

⚠️ Volume-Based Pricing

Event tracking has volume limits based on your plan tier.

  • Free Plan: 2,000 events/month (hard limit)
  • Hobby Plan: 5,000 events/month ($0.02/1k overage)
  • Starter Plan: 50,000 events/month ($0.01/1k overage)
  • Pro Plan: 250,000 events/month ($0.005/1k overage)
  • Rate Limiting: Exceeding free tier limits returns 429 errors

Track an Event

await client.events.track({
  event: 'product_viewed',
  user: '[email protected]',
  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 10,000 times/month = 10k events consumed. On the Free plan (2k events), this would exceed your limit. Consider upgrading to Starter (50k events) or Pro (250k 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": "[email protected]",
        "subject": "You left something in your cart!",
        "template": "cart_reminder"
      }
    }
  ]
}

MCP / AI Assistants

Use MailJunky directly from AI assistants like Claude Code and ChatGPT via the Model Context Protocol (MCP).

What is MCP?

The Model Context Protocol allows AI assistants to use external tools. With MailJunky's MCP server, you can send emails, track events, and manage contacts through natural language.

MCP Server URL

https://mcp.mailjunky.ai/sse

Claude Code Setup

Run this command to add MailJunky as an MCP server:

claude mcp add --transport sse mailjunky https://mcp.mailjunky.ai/sse \
  --header "Authorization: Bearer YOUR_API_KEY"

Claude Desktop Setup

Add to your Claude Desktop config file:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "mailjunky": {
      "url": "https://mcp.mailjunky.ai/sse",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}

ChatGPT Setup

In ChatGPT, go to Settings → Connections → MCP Servers and add:

  • Name:MailJunky
  • URL:https://mcp.mailjunky.ai/sse
  • Authorization:Bearer token with your MailJunky API key

Available MCP Tools

Once connected, your AI assistant can use these tools:

ToolDescription
send_emailSend emails to one or more recipients
track_eventTrack user events for workflows
list_contactsList and search contacts
get_contactGet a specific contact by ID
create_contactCreate or update a contact (upsert)
update_contactUpdate an existing contact
delete_contactDelete a contact

Example Prompts

Once configured, just ask your AI assistant naturally:

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. These limits protect our infrastructure and ensure fair usage.

📧 Need Higher Limits?

All rate limits are negotiable. If your application requires higher throughput, contact us to discuss your needs. We're happy to work with high-volume senders.

Email Sending Limits

Daily and monthly limits ensure reliable delivery:

PlanPer DayPer MonthOverage
Free50200
Hobby50500$1.00/1k (max $4)
Starter50010,000$0.50/1k
Pro5,00050,000$0.40/1k
EnterpriseCustomCustomNegotiated

API Request Limits

Per API key, per minute:

EndpointLimit
POST /emails/send60 requests/min
POST /emails/batch10 requests/min
POST /events/trackQuota-based*
/contacts/*100 requests/min

Event Tracking Quotas

*Volume-Based Pricing - Event tracking is a premium feature with monthly volume limits based on your plan.

Exceeding your quota on the Free plan returns 429 errors. Paid plans allow overages.

PlanEvents/MonthOverage
Free2,000
Hobby5,000$0.02/1k events
Starter50,000$0.01/1k events
Pro250,000$0.005/1k events
EnterpriseCustomNegotiated

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.