Skip to content

Send bulk emails (mail merge)

Request

Send the same email template to multiple recipients with personalization.

Mail merge functionality for:

  • Newsletters with personalized greetings
  • Marketing campaigns with custom variables
  • Bulk transactional emails with recipient-specific data

Features:

  • Template variables: Use {{variable}} syntax in subject, HTML, and text
  • Built-in placeholders: {{unsubscribe}} for unsubscribe links
  • Per-recipient personalization via vars object
  • Automatic variable substitution for each recipient
  • Single template, many recipients
  • File attachments (same for all recipients)
  • Calendar invites (same for all recipients)

Limits:

  • Recommended: Up to 10,000 recipients per call
  • Each recipient gets their own personalized copy

Use cases:

  • Newsletter campaigns: "Hi {{name}}, check out our new {{product}}..."
  • Promotional emails with unique discount codes
  • Event invitations with personalized details

Built-in Placeholders:

  • {{unsubscribe}} - Secure unsubscribe link (unique per recipient)

Not for: Different emails to each recipient → Use /api/batch instead

Security
ApiKeyAuth
Bodyapplication/jsonrequired
campaign_idstring, [ 1 .. 255 ] characters^(?=.*\S)[^/\x00-\x1F\x7F]+$

Constraints: 1-255 characters; must not contain / (the id becomes a path segment of the report URL) or control characters. Other characters are allowed but must be percent-encoded when building report URLs.

Optional campaign identifier for this send. Supply your own id (for example your application-side campaign id) to correlate reports with your system; if you omit it, a UUID is minted for this send and returned as campaign_id.

Whatever value is used is stamped on every recipient of this send, and is the key the /api/v1/campaigns/{campaignId}/... report endpoints scope on.

Maximum 255 characters.

Auto-pause protection: If bounce rate exceeds thresholds, the campaign will be automatically paused to protect IP reputation.

How Campaign Grouping Works:

  • Every recipient of this call carries this campaign_id
  • Campaign reports return exactly the recipients of the sends carrying this id
  • Reusing an id across calls deliberately merges those calls into one report
  • Auto-pause is applied per reputation session (bulk_session_id in the response), which groups sends by subject over 24 hours and is NOT the campaign id

⚠️ Splitting one campaign across several calls: if you chunk a large recipient list into multiple /bulk-send calls, you MUST pass the SAME campaign_id on every chunk. Omitting it mints a fresh id per call, so each chunk becomes its own campaign and a report covers only the chunk whose id you kept.

⚠️ CRITICAL: Each campaign MUST use a UNIQUE campaign_id

Best Practices:

  • ✅ DO: Use unique IDs per campaign send
    • Good: "monthly-newsletter-2026-03-05"
    • Good: "spring-sale-2026-week1"
    • Good: "product-launch-v2-2026-03"
  • ❌ DON'T: Reuse campaign_id across different campaigns
    • Bad: "newsletter" (reused every month)
    • Bad: "promo" (reused for all promos)
    • Bad: "bulk-send" (too generic)

Why This Matters: If you reuse a campaign_id:

  • Bounce rates from OLD campaigns affect NEW campaigns
  • New campaign might be immediately paused due to old bounces
  • Campaign reports mix data from different time periods
  • Cannot track individual campaign performance

Recommended Format: {campaign-name}-{date} or {campaign-name}-{version}-{date}

Examples:

  • Monthly newsletters: "newsletter-2026-03", "newsletter-2026-04"
  • Sales campaigns: "spring-sale-2026-w1", "spring-sale-2026-w2"
  • Product launches: "product-x-launch-2026-03-05"

If not provided, system auto-generates: bulk-{timestamp}-{customer_id}

Example:"spring-sale-2026-march"
subjectstringrequired

Email subject with optional variables like {{name}} or {{code}}.

Important: If using personalization, also provide subject_template for proper campaign tracking.

Example:"Hello {{name}}! Your code is {{code}}"
subject_templatestring

Recommended when using personalized subjects. The template before variable substitution.

Why needed: Helps system track campaigns correctly when subjects are personalized.

Example:

  • subject_template: "Hello {{name}}! Your code is {{code}}"
  • Actual subject for each recipient: "Hello John! Your code is SAVE20"

If not provided, system normalizes the subject automatically (less accurate).

Example:"Hello {{name}}! Your code is {{code}}"
preview_textstring

Preview/preheader text shown in email clients next to subject (40-130 characters recommended). Appears in inbox list view before opening the email.

Example:"Exclusive offer just for you - save 20% today!"
htmlstring

HTML email body with variables

Example:"<h1>Hi {{name}}</h1><p>Your discount code: {{code}}</p>"
textstring

Plain text email body with variables. Supports the same {{variable}} merge fields as html.

Auto-generation: If omitted, a plain-text version is automatically generated from html before sending. Supplying your own text is recommended for best plain-text client rendering.

Example:"Hi {{name}}, Your discount code: {{code}}"
fromstring, (email)

From email (must use verified domain, optional)

Example:"hello@mail.yourdomain.com"
from_namestring

Display name for the sender (appears as "From Name ")

Example:"Your Company"
domainstring

Sending domain (optional - auto-extracted from from email if not provided). If provided, must match the domain in from email address.

Example:"mail.yourdomain.com"
reply_tostring, (email)

Reply-to address

Example:"support@yourdomain.com"
track_opensboolean

Enable open tracking (default: true)

Default:true
Example:true
track_clicksboolean

Enable click tracking (default: true)

Default:true
Example:true
tagstring

Tag for organizing/filtering emails

Example:"newsletter-2025-12"
stream_typestring

Stream type is locked to broadcast for bulk sending. Bulk/mail merge emails are marketing/newsletter content and must use the broadcast stream to protect transactional IP reputation.

Default:"broadcast"
Value:"broadcast"
Example:"broadcast"
attachmentsArray of objects

File attachments sent with every email (sent directly, not stored). Same attachments sent to all recipients.

icalEventstring

iCal calendar event in RFC 5545 format (sent directly, not stored). Same calendar invite sent to all recipients.

recipientsArray of objectsrequired

Array of recipients with personalization variables

Example:
[ { "email": "john@example.com", "vars": { … } }, { "email": "jane@example.com", "vars": { … } } ]
curl -i -X POST \
  https://api.mailerlogic.net/api/v1/bulk-send \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: YOUR_API_KEY_HERE' \
  -d '{
    "campaign_id": "spring-sale-2026-discount-20",
    "subject": "Hello {{name}}! Special offer inside",
    "subject_template": "Hello {{name}}! Special offer inside",
    "preview_text": "Exclusive discount code just for you - save 20% today!",
    "html": "<h1>Hi {{name}}</h1><p>Use code <strong>{{code}}</strong> for 20% off!</p><p><a href=\"{{unsubscribe}}\">Unsubscribe</a></p>",
    "text": "Hi {{name}}, Use code {{code}} for 20% off!\n\nUnsubscribe: {{unsubscribe}}",
    "from": "hello@mail.yourdomain.com",
    "from_name": "Your Company",
    "track_opens": true,
    "track_clicks": true,
    "tag": "holiday-sale-2025",
    "stream_type": "broadcast",
    "recipients": [
      {
        "email": "john@example.com",
        "vars": {
          "name": "John",
          "code": "JOHN20"
        }
      },
      {
        "email": "jane@example.com",
        "vars": {
          "name": "Jane",
          "code": "JANE20"
        }
      }
    ]
  }'

Responses

Bulk send request accepted and queued for delivery

Bodyapplication/json
messagestring
Example:"Emails queued for delivery"
queuedinteger

Number of emails successfully queued

Example:2
errorsinteger

Number of emails that failed to queue

Example:0
campaign_idstring

Campaign identifier for this send. Pass this to the /api/v1/campaigns/{campaignId}/... report endpoints.

Echoes the campaign_id you supplied, or a UUID minted for this call if you did not supply one. Reports cover exactly the recipients of the calls carrying this id — so when you chunk one campaign across several calls, supply the SAME campaign_id on every chunk; only an omitted id is minted fresh per call.

IMPORTANT: Save this ID to track campaign performance, bounce rates, opens, and clicks.

Example:"f1e2d3c4-b5a6-7890-cdef-123456789abc"
bulk_session_idstring or null, (uuid)

Not a campaign id — do not pass it to the report endpoints.

The reputation/auto-pause session this send joined. Sessions group every send that shares a subject within a 24-hour window, so one session can span several distinct campaigns. It is exposed only to correlate auto-pause events.

Previously this field was an alias of campaign_id. It is now a separate value, and reports keyed on it return only sends made before per-send campaign ids existed.

Example:"a7b8c9d0-1234-5678-9abc-def012345678"
jobsArray of objects

Array of queued email jobs with tracking information

error_detailsArray of objects

Array of errors for failed recipients (only included if errors > 0)

Response
{ "message": "Emails queued for delivery", "queued": 2, "errors": 0, "campaign_id": "spring-sale-2026-week1", "bulk_session_id": "a7b8c9d0-1234-5678-9abc-def012345678", "jobs": [ { … }, { … } ] }