Docs Guides

Sending Emails

Learn the different ways to send emails with mailshit.

Overview

mailshit offers multiple ways to send emails:

  1. API - Programmatic sending from your backend
  2. Flow Builder - Visual workflow with send on API call
  3. Export - Render in mailshit, send with your own infrastructure

Sending via API

The most common method for transactional emails.

Use /execute to run your configured flow:

curl -X POST https://api.mailsh.it/v1/execute \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "tmpl_abc123",
    "variables": {
      "firstName": "Alice"
    },
    "_to": "alice@example.com"
  }'

This:

  1. Renders the template with variables
  2. Runs any verification nodes
  3. Sends via your configured provider

Render Only

Use /render to get HTML without sending:

curl -X POST https://api.mailsh.it/v1/render \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "tmpl_abc123",
    "variables": {
      "firstName": "Alice"
    }
  }'

Response:

{
  "html": "<!DOCTYPE html>...",
  "subject": "Welcome, Alice!"
}

Then send with your own method.

Required Fields

When sending via provider output, _to is required:

{
  "templateId": "...",
  "variables": {...},
  "_to": "recipient@example.com"
}

Optional Fields

FieldDescription
_subjectOverride template subject
_replyToReply-to address
_ccCC recipients (comma-separated)
_bccBCC recipients (comma-separated)

Multiple Recipients

Send to multiple addresses by comma-separating:

{
  "_to": "alice@example.com, bob@example.com"
}

Each recipient receives the same email.

Handling Responses

Successful Send

{
  "success": true,
  "type": "sent",
  "messageId": "msg_abc123"
}

Store messageId for tracking delivery issues.

Validation Failed

{
  "success": false,
  "type": "validation_failed",
  "error": "Invalid email address",
  "field": "_to"
}

The field tells you what to fix.

Send Failed

{
  "success": false,
  "type": "send_failed",
  "error": "Provider error message"
}

Check your provider dashboard for details.

Sending Patterns

Welcome Email on Signup

// After user signs up
await fetch('https://api.mailsh.it/v1/execute', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    templateId: WELCOME_TEMPLATE_ID,
    variables: {
      firstName: user.firstName,
      loginUrl: 'https://app.example.com/login',
    },
    _to: user.email,
  }),
})

Order Confirmation

await fetch('https://api.mailsh.it/v1/execute', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    templateId: ORDER_CONFIRMATION_ID,
    variables: {
      firstName: order.customer.firstName,
      orderNumber: order.id,
      orderTotal: formatCurrency(order.total),
      orderItems: formatOrderItems(order.items),
    },
    _to: order.customer.email,
    _subject: `Order #${order.id} confirmed`,
  }),
})

Password Reset

await fetch('https://api.mailsh.it/v1/execute', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    templateId: PASSWORD_RESET_ID,
    variables: {
      resetLink: `https://app.example.com/reset?token=${token}`,
      expiry: '24 hours',
    },
    _to: user.email,
  }),
})

Error Handling

Always handle API errors:

try {
  const response = await fetch('...', {...})
  const result = await response.json()

  if (result.success) {
    console.log('Email sent:', result.messageId)
  } else {
    console.error('Failed:', result.error)
    // Maybe queue for retry, alert admin, etc.
  }
} catch (error) {
  console.error('Network error:', error)
  // Queue for retry
}

Retry Strategy

For failed sends:

  1. Don’t retry immediately - wait at least a few seconds
  2. Exponential backoff - double wait time each retry
  3. Max retries - give up after 3-5 attempts
  4. Alert on failures - monitor persistent failures
async function sendWithRetry(payload, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const result = await sendEmail(payload)
    if (result.success) return result

    // Don't retry validation errors
    if (result.type === 'validation_failed') throw new Error(result.error)

    await sleep(Math.pow(2, i) * 1000) // Exponential backoff
  }
  throw new Error('Max retries exceeded')
}