Skip to main content

Email Sending Guide

This comprehensive guide covers everything you need to know about sending emails through EDITH, including request structure, personalization, attachments, tracking, and best practices.


Overview

The Email Send API enables you to send transactional and marketing emails with:

  • HTML and plain text content
  • File attachments
  • Open and click tracking
  • Per-recipient personalization
  • Email validation
  • Priority queue management

Send an Email

Endpoint

POST /v1/email/send

Authentication

Requires Bearer token authentication.


Request Body Structure

Complete Request Schema

{
"from": {
"email": "sender@mail.yourcompany.com",
"name": "Your Company"
},
"to": [
{
"email": "recipient@example.com",
"name": "John Doe"
}
],
"cc": [],
"bcc": [],
"replyTo": "support@yourcompany.com",
"subject": "Your Order Confirmation",
"content": [
{
"type": "text/html",
"value": "<html>...</html>"
},
{
"type": "text/plain",
"value": "Plain text version..."
}
],
"attachments": [],
"headers": {},
"substitutions": {},
"custom_args": {},
"campaign_id": "order-confirmation-2024",
"mailer_id": "domain_01JC3BBW8S9YGX2VNKG5MD7BTA",
"template_id": "",
"settings": {
"open_tracking": { "enable": true },
"click_tracking": { "enable": true },
"validate": { "enable": true },
"phishing_protection": { "enable": false },
"unsubscribe": { "enable": true },
"ip_pool": ""
},
"process_email_inbulk": false,
"priority": 0
}

Field-by-Field Reference

from (Required)

The sender information for the email.

FieldTypeRequiredDescription
emailstring✅ YesThe sender's email address. Must be from a verified domain associated with your account.
namestringNoDisplay name shown to recipients (e.g., "Your Company Support"). If omitted, only the email address appears.

Example:

{
"from": {
"email": "notifications@mail.yourcompany.com",
"name": "YourCompany Notifications"
}
}

Important Notes:

  • The email domain must match a verified sending domain
  • The mailer_id must correspond to this domain
  • Using a recognizable sender name improves open rates

to (Required)

Array of primary recipients. At least one recipient is required.

FieldTypeRequiredDescription
emailstring✅ YesRecipient's email address. Must be a valid email format.
namestringNoRecipient's display name. Used for personalization and better deliverability.

Example:

{
"to": [
{ "email": "john.doe@example.com", "name": "John Doe" },
{ "email": "jane.smith@example.com", "name": "Jane Smith" }
]
}

Limits:

  • Maximum recipients when process_email_inbulk: false: No limit (emails sent individually)
  • Maximum recipients when process_email_inbulk: true: Combined to + cc + bcc ≤ 100

cc (Optional)

Carbon copy recipients. They see each other's email addresses.

FieldTypeRequiredDescription
emailstring✅ YesCC recipient's email address
namestringNoCC recipient's display name

⚠️ Important: CC recipients are only allowed when process_email_inbulk: true

{
"cc": [
{ "email": "manager@example.com", "name": "Team Manager" }
]
}

bcc (Optional)

Blind carbon copy recipients. Hidden from other recipients.

FieldTypeRequiredDescription
emailstring✅ YesBCC recipient's email address
namestringNoBCC recipient's display name

⚠️ Important: BCC recipients are only allowed when process_email_inbulk: true


replyTo (Optional)

Email address(es) for recipient replies.

TypeFormatExample
stringEmail only"support@yourcompany.com"
stringName + Email"Support Team <support@yourcompany.com>"
stringMultiple"support@yourcompany.com, sales@yourcompany.com"

Examples:

// Simple email
{ "replyTo": "support@yourcompany.com" }

// With display name
{ "replyTo": "Customer Support <support@yourcompany.com>" }

// Multiple addresses
{ "replyTo": "support@yourcompany.com, billing@yourcompany.com" }

subject (Conditionally Required)

The email subject line.

FieldTypeRequiredDescription
subjectstring✅ When no template_idThe subject line displayed to recipients. Required unless using a template.

Best Practices:

  • Keep subjects under 50 characters for mobile compatibility
  • Avoid spam trigger words (FREE, URGENT, !!!)
  • Personalize with recipient's name when possible

content (Conditionally Required)

Email body content in HTML and/or plain text format.

FieldTypeRequiredDescription
typestring✅ YesMIME type: "text/html" or "text/plain"
valuestring✅ YesThe actual content. HTML or plain text depending on type.

Example with both formats:

{
"content": [
{
"type": "text/html",
"value": "<!DOCTYPE html><html><body><h1>Welcome!</h1><p>Thank you for joining us.</p></body></html>"
},
{
"type": "text/plain",
"value": "Welcome!\n\nThank you for joining us."
}
]
}

Content Requirements:

  • Required when template_id is not provided
  • Include both HTML and plain text for best deliverability
  • HTML should be valid and well-formed
  • Keep total content under 25MB

attachments (Optional)

Files to attach to the email.

FieldTypeRequiredDescription
contentstring✅ YesBase64-encoded file content
filenamestring✅ YesFilename with extension (e.g., "invoice.pdf")
typestring✅ YesMIME type of the file (see supported types below)
dispositionstringNo"attachment" (default) or "inline" for embedded images
contentIdstringNoContent-ID for inline images (used in HTML <img src="cid:...">)
contentLocationstringNoOriginal URL of the content (informational)

Supported MIME Types:

CategoryTypes
Documentsapplication/pdf, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document
Spreadsheetsapplication/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Presentationsapplication/vnd.ms-powerpoint, application/vnd.openxmlformats-officedocument.presentationml.presentation
Texttext/plain, text/csv, text/html, text/xml, text/calendar, text/css
Imagesimage/jpeg, image/png, image/gif, image/svg+xml, image/webp, image/bmp, image/tiff
Audioaudio/mpeg, audio/wav, audio/ogg
Videovideo/mp4, video/x-msvideo, video/quicktime, video/webm
Archivesapplication/zip, application/x-tar, application/gzip, application/vnd.rar, application/x-7z-compressed
Otherapplication/json, application/xml, application/rtf, application/octet-stream, message/rfc822

Example - Regular Attachment:

{
"attachments": [
{
"content": "JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9U...",
"filename": "invoice_12345.pdf",
"type": "application/pdf",
"disposition": "attachment"
}
]
}

Example - Inline Image:

{
"attachments": [
{
"content": "iVBORw0KGgoAAAANSUhEUgAAAAUA...",
"filename": "logo.png",
"type": "image/png",
"disposition": "inline",
"contentId": "company-logo"
}
]
}

Then reference in HTML:

<img src="cid:company-logo" alt="Company Logo">

headers (Optional)

Custom email headers.

TypeDescription
objectKey-value pairs of custom headers

Example:

{
"headers": {
"X-Custom-Header": "custom-value",
"X-Priority": "1",
"Message-ID": "<unique-id@yourcompany.com>"
}
}

Common Use Cases:

  • X-Priority: Email priority (1=High, 3=Normal, 5=Low)
  • Message-ID: Custom message identifier
  • References: For email threading
  • In-Reply-To: Reference to a previous message

substitutions (Optional)

Per-recipient personalization variables.

StructureDescription
{ "recipient@email.com": { "var": "value" } }Map of recipient email to their variable values

Example:

{
"substitutions": {
"john@example.com": {
"first_name": "John",
"order_id": "ORD-12345",
"discount": "20%"
},
"jane@example.com": {
"first_name": "Jane",
"order_id": "ORD-12346",
"discount": "15%"
}
}
}

In your content, use double curly braces:

<p>Hello {{first_name}},</p>
<p>Your order {{order_id}} qualifies for {{discount}} off!</p>

⚠️ Important: Substitutions are NOT allowed when process_email_inbulk: true


mailer_id (Required)

The identifier for your email configuration.

TypeDescription
stringUnique ID returned when you added a domain or configured SMTP/OAuth

This ties your email to the correct:

  • Verified sending domain
  • DKIM/SPF authentication
  • IP pool (if configured)
  • Tracking domain

template_id (Optional)

Reference a pre-created email template instead of providing content inline.

TypeDescription
stringTemplate ID returned when creating a template

When using a template:

  • subject becomes optional (uses template subject if not provided)
  • content becomes optional (uses template content)
  • You can still override with inline values

settings (Required)

Email sending configuration and tracking options.

{
"settings": {
"open_tracking": { "enable": true },
"click_tracking": { "enable": true },
"validate": { "enable": true },
"phishing_protection": { "enable": false },
"unsubscribe": { "enable": true },
"ip_pool": "transactional-pool"
}
}
SettingTypeDefaultDescription
open_tracking.enablebooleanfalseTrack when recipients open the email. Inserts an invisible tracking pixel.
click_tracking.enablebooleanfalseTrack link clicks. Rewrites URLs to pass through tracking servers.
validate.enablebooleanfalseValidate recipient email addresses before sending. Invalid addresses are rejected.
phishing_protection.enablebooleanfalseScan email content for potential phishing indicators.
unsubscribe.enablebooleanfalseInclude an unsubscribe link in the email. Required for marketing emails.
ip_poolstring""Specific IP pool to use for sending. Leave empty for default routing.

Note: Unsubscribe is NOT allowed when process_email_inbulk: true


process_email_inbulk (Optional)

Controls how multi-recipient emails are processed.

ValueBehavior
false (default)Each recipient receives an individual email. Allows substitutions.
trueAll recipients receive the same email. CC/BCC allowed. No substitutions. Maximum 100 total recipients.

Use Cases:

  • false: Personalized transactional emails, newsletters with personalization
  • true: Team notifications, internal announcements where all recipients should see each other

priority (Optional)

Email queue priority.

ValueDescription
0 (default)Normal priority - processed in order
1High priority - processed before normal priority emails

Use high priority sparingly for time-sensitive emails like:

  • Password reset emails
  • Two-factor authentication codes
  • Critical security alerts

campaign_id (Optional)

Identifier for grouping emails by campaign.

TypeDefaultDescription
string"NIL"Your custom campaign identifier for analytics and tracking

Example:

{ "campaign_id": "welcome-series-2024-q1" }

Use campaign IDs to:

  • Group email logs for reporting
  • Filter webhook events
  • Track performance by campaign

custom_args (Optional)

Custom data passed through webhooks.

TypeDescription
objectAny JSON object with your custom data

This data is included in webhook payloads, allowing you to:

  • Associate emails with internal records
  • Pass user IDs, order IDs, or other identifiers
  • Add context for event processing

Example:

{
"custom_args": {
"user_id": "usr_123456",
"order_id": "ord_789012",
"source": "checkout-flow"
}
}

Email Threading and Replies

Understanding email threading is crucial for maintaining conversation context and ensuring replies are properly associated with original messages.

Threading Headers Overview

HeaderPurposeUsed ByWhen to Include
Message-IDUnique identifier for THIS emailAll providersAuto-generated by EDITH
In-Reply-ToReferences the Message-ID being replied toAll providersWhen replying to an email
ReferencesChain of all Message-IDs in the threadAll providersWhen replying (cumulative)
Thread-IdGmail's conversation thread identifierGmail onlyWhen replying in Gmail

Message-ID: Auto-Generated by EDITH

You DON'T need to provide Message-ID - EDITH automatically generates it for every email.

EDITH Message-ID Format

<edith_mailer{unique_id}@sparrowmailer.com>

Components:

  • Prefix: edith_mailer (identifies EDITH-sent emails)
  • Unique ID: UUID or custom identifier from your headers
  • Domain: @sparrowmailer.com

Example:

Message-ID: <edith_mailer550e8400-e29b-41d4-a716-446655440000@sparrowmailer.com>

Custom Message-ID (Non-Gmail Only)

For SMTP, Outlook, and other providers (NOT Gmail), you can provide a custom unique ID in headers:

{
"headers": {
"Message-ID": "order-12345-confirmation"
}
}

EDITH wraps it with the prefix and domain:

Result: <edith_mailer_order-12345-confirmation@sparrowmailer.com>

⚠️ Gmail Exception: Gmail API does NOT allow custom Message-IDs. EDITH always generates a random UUID for Gmail.


When EDITH Sends Message-ID

EDITH includes the message_id in webhook payloads for ALL email events:

Outgoing Email Webhooks

{
"event": "MAIL_DELIVERED",
"details": {
"ref_id": "01JC3BBW8S9YGX2VNKG5MD7BTA",
"email": ["recipient@example.com"],
"message_id": "<edith_mailer550e8400@sparrowmailer.com>",
"thread_id": "18c5a1b2d3e4f5g6",
"custom_args": { "order_id": "12345" }
}
}

Events that include message_id:

  • MAIL_DELIVERED - Email successfully delivered
  • MAIL_OPENED - Recipient opened the email
  • MAIL_CLICKED - Link clicked
  • MAIL_BOUNCED - Email bounced
  • MAIL_SPAM - Marked as spam
  • GENERATION_FAILURE - Failed to generate email

Store the message_id in your database to correlate replies!

Incoming Email Webhooks

{
"event": "INCOMING_EMAIL",
"details": {
"message-id": "<CABc123def@mail.example.com>",
"in-reply-to": "<edith_mailer550e8400@sparrowmailer.com>",
"thread-id": "18c5a1b2d3e4f5g6",
"references": "<edith_mailer550e8400@sparrowmailer.com> <CABc123def@mail.example.com>",
"from": "customer@example.com",
"subject": "Re: Your order #12345"
}
}

In-Reply-To: For Standard Email Replies

Use In-Reply-To when replying to ANY email (Gmail, Outlook, SMTP).

Purpose: References the exact Message-ID of the email being replied to.

When to use:

  • ✅ Replying to a customer email
  • ✅ Continuing a conversation
  • ✅ Following up on a previous message

Example: Reply to Customer

Original email sent:

{
"from": { "email": "support@company.com" },
"to": [{ "email": "customer@example.com" }],
"subject": "Thank you for contacting us",
"mailer_id": "your-mailer-id"
}

EDITH webhook response:

{
"event": "MAIL_DELIVERED",
"details": {
"message_id": "<edith_mailerabc123@sparrowmailer.com>"
}
}

Store this message_id in your database!


Customer replies, you receive via incoming webhook:

{
"event": "INCOMING_EMAIL",
"details": {
"in-reply-to": "<edith_mailerabc123@sparrowmailer.com>",
"message-id": "<CABxyz@mail.example.com>",
"from": "customer@example.com",
"subject": "Re: Thank you for contacting us"
}
}

Your reply back to customer:

{
"from": { "email": "support@company.com" },
"to": [{ "email": "customer@example.com" }],
"subject": "Re: Thank you for contacting us",
"headers": {
"In-Reply-To": "<CABxyz@mail.example.com>",
"References": "<edith_mailerabc123@sparrowmailer.com> <CABxyz@mail.example.com>"
},
"mailer_id": "your-mailer-id"
}

Result: Email clients group this as a threaded conversation!


References: Full Thread History

References contains the complete chain of Message-IDs in the conversation.

Purpose: Helps email clients understand the entire conversation flow.

Format: Space-separated list of Message-IDs, oldest first.

Example Threading:

1. Initial Email (You → Customer)

{
"subject": "Welcome to our service"
// EDITH generates: <edith_mailermsg1@sparrowmailer.com>
}

2. Customer Reply (Customer → You)

In-Reply-To: <edith_mailermsg1@sparrowmailer.com>
References: <edith_mailermsg1@sparrowmailer.com>
Message-ID: <customer_msg2@gmail.com>

3. Your Reply (You → Customer)

{
"headers": {
"In-Reply-To": "<customer_msg2@gmail.com>",
"References": "<edith_mailermsg1@sparrowmailer.com> <customer_msg2@gmail.com>"
}
}

4. Customer Reply Again (Customer → You)

In-Reply-To: <edith_mailermsg3@sparrowmailer.com>
References: <edith_mailermsg1@sparrowmailer.com> <customer_msg2@gmail.com> <edith_mailermsg3@sparrowmailer.com>

Pattern: Keep adding Message-IDs to References, always include In-Reply-To for immediate parent.


Thread-Id: Gmail OAuth-Specific Threading

Gmail OAuth Only: Gmail uses its own Thread-Id system for grouping conversations when using OAuth authentication.

When to use:

  • ✅ ONLY when sending/receiving via Gmail OAuth (not basic SMTP)
  • ✅ When replying to emails received via Gmail OAuth
  • ❌ NOT needed for Gmail SMTP (basic auth), Outlook, or other providers

How Gmail Thread-Id Works:

Gmail assigns a unique Thread-Id to each conversation. All emails in that conversation share the same Thread-Id.

Example:

Initial email sent via Gmail OAuth:

{
"from": { "email": "you@gmail.com" },
"to": [{ "email": "customer@example.com" }],
"subject": "Hello",
"mailer_id": "gmail-oauth-mailer-id"
}

EDITH webhook:

{
"event": "MAIL_DELIVERED",
"details": {
"message_id": "<edith_mailerxyz@sparrowmailer.com>",
"thread_id": "18c5a1b2d3e4f5g6" // Gmail's Thread-Id
}
}

Customer replies (you receive):

{
"event": "INCOMING_EMAIL",
"details": {
"thread-id": "18c5a1b2d3e4f5g6", // Same Thread-Id
"in-reply-to": "<edith_mailerxyz@sparrowmailer.com>",
"message-id": "<new-message-id>"
}
}

Your reply (include Thread-Id for Gmail OAuth):

{
"from": { "email": "you@gmail.com" },
"to": [{ "email": "customer@example.com" }],
"subject": "Re: Hello",
"headers": {
"Thread-Id": "18c5a1b2d3e4f5g6",
"In-Reply-To": "<new-message-id>",
"References": "<edith_mailerxyz@sparrowmailer.com> <new-message-id>"
},
"mailer_id": "gmail-oauth-mailer-id"
}

⚠️ Important: Gmail OAuth requires thread_id (not Message-ID) for threading replies! Note: Gmail SMTP (basic auth) does NOT use Thread-Id, only standard In-Reply-To and References.


Provider-Specific Threading Summary

ProviderMessage-IDIn-Reply-ToReferencesThread-Id
Gmail OAuth✅ Auto (EDITH)✅ Yes✅ YesRequired
Gmail SMTP✅ Auto (EDITH)✅ Yes✅ Yes❌ No
Outlook OAuth✅ Auto (EDITH)✅ Yes✅ Yes❌ No
Outlook SMTP✅ Auto (EDITH)✅ Yes✅ Yes❌ No
SMTP (Other)✅ Auto (EDITH)✅ Yes✅ Yes❌ No
SparkPost✅ Auto (EDITH)✅ Yes✅ Yes❌ No
Mailgun✅ Auto (EDITH)✅ Yes✅ Yes❌ No

Complete Threading Example

Scenario: Support ticket conversation

1. Customer sends initial inquiry (inbound):

{
"event": "INCOMING_EMAIL",
"details": {
"from": "customer@example.com",
"subject": "Need help with order #12345",
"message-id": "<customer-initial@gmail.com>",
"thread-id": "thread_abc123",
"mailer-service": "gmail"
}
}

Store in your database:

{
ticketId: "TKT-001",
messages: [
{
messageId: "<customer-initial@gmail.com>",
threadId: "thread_abc123",
from: "customer",
timestamp: "2024-01-15T10:00:00Z"
}
]
}

2. You reply to customer:

curl -X POST https://api.edith.example.com/v1/email/send \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"from": { "email": "support@company.com", "name": "Support Team" },
"to": [{ "email": "customer@example.com", "name": "Customer" }],
"subject": "Re: Need help with order #12345",
"content": [
{ "type": "text/html", "value": "<p>We are looking into this...</p>" }
],
"headers": {
"In-Reply-To": "<customer-initial@gmail.com>",
"References": "<customer-initial@gmail.com>",
"Thread-Id": "thread_abc123"
},
"mailer_id": "your-mailer-id",
"custom_args": { "ticket_id": "TKT-001" }
}'

EDITH webhook response:

{
"event": "MAIL_DELIVERED",
"details": {
"ref_id": "01JC3BBW8S9YGX2VNKG5MD7BTA",
"message_id": "<edith_mailersupport1@sparrowmailer.com>",
"thread_id": "thread_abc123",
"custom_args": { "ticket_id": "TKT-001" }
}
}

Update database:

{
ticketId: "TKT-001",
messages: [
{ messageId: "<customer-initial@gmail.com>", from: "customer" },
{ messageId: "<edith_mailersupport1@sparrowmailer.com>", from: "support" }
]
}

3. Customer replies again:

{
"event": "INCOMING_EMAIL",
"details": {
"from": "customer@example.com",
"subject": "Re: Need help with order #12345",
"message-id": "<customer-reply2@gmail.com>",
"in-reply-to": "<edith_mailersupport1@sparrowmailer.com>",
"references": "<customer-initial@gmail.com> <edith_mailersupport1@sparrowmailer.com>",
"thread-id": "thread_abc123"
}
}

4. You reply again (building the chain):

{
"headers": {
"In-Reply-To": "<customer-reply2@gmail.com>",
"References": "<customer-initial@gmail.com> <edith_mailersupport1@sparrowmailer.com> <customer-reply2@gmail.com>",
"Thread-Id": "thread_abc123"
}
}

Result: Perfect threaded conversation in Gmail, Outlook, and all email clients!


Best Practices for Threading

1. Always store message_id from EDITH webhooks

// When you send an email
const sentEmail = await sendEmailViaEdith({...});

// Store the ref_id
await db.emails.create({
refId: sentEmail.ref_id,
// Other data
});

// When webhook arrives
app.post('/webhooks/email-events', (req, res) => {
const { ref_id, message_id, thread_id } = req.body.details;

// Update with message_id and thread_id
await db.emails.update(
{ refId: ref_id },
{ messageId: message_id, threadId: thread_id }
);
});

2. Build References correctly

// Maintain conversation history
const conversation = await db.conversations.findOne({ threadId });
const messageIds = conversation.messages.map(m => m.messageId);

// Build References header
const references = messageIds.join(' ');

3. Handle provider differences

function buildThreadingHeaders(email, conversation) {
const headers = {
'In-Reply-To': conversation.lastMessageId,
'References': conversation.allMessageIds.join(' ')
};

// Add Thread-Id ONLY for Gmail
if (email.provider === 'gmail') {
headers['Thread-Id'] = conversation.threadId;
}

return headers;
}

4. Test threading

  • Send test emails
  • Reply in different email clients
  • Verify messages appear grouped
  • Check both Gmail and Outlook

5. Monitor threading webhooks

// Log threading info
console.log('Threading info:', {
messageId: details['message-id'],
inReplyTo: details['in-reply-to'],
threadId: details['thread-id'],
references: details.references
});

Complete Example Request

curl -X POST https://api.edith.example.com/v1/email/send \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"from": {
"email": "orders@mail.yourcompany.com",
"name": "YourCompany Orders"
},
"to": [
{
"email": "customer@example.com",
"name": "John Customer"
}
],
"replyTo": "support@yourcompany.com",
"subject": "Your Order #12345 Has Been Confirmed",
"content": [
{
"type": "text/html",
"value": "<!DOCTYPE html><html><head><style>body{font-family:Arial,sans-serif;}</style></head><body><h1>Thank you for your order!</h1><p>Hi {{first_name}},</p><p>Your order #{{order_id}} has been confirmed and will ship within 2-3 business days.</p><p>Track your order: <a href=\"{{tracking_url}}\">Click here</a></p></body></html>"
},
{
"type": "text/plain",
"value": "Thank you for your order!\n\nHi {{first_name}},\n\nYour order #{{order_id}} has been confirmed and will ship within 2-3 business days.\n\nTrack your order: {{tracking_url}}"
}
],
"substitutions": {
"customer@example.com": {
"first_name": "John",
"order_id": "12345",
"tracking_url": "https://track.yourcompany.com/12345"
}
},
"attachments": [
{
"content": "JVBERi0xLjQKJeLjz9M...",
"filename": "invoice_12345.pdf",
"type": "application/pdf"
}
],
"mailer_id": "domain_01JC3BBW8S9YGX2VNKG5MD7BTA",
"campaign_id": "order-confirmation",
"settings": {
"open_tracking": { "enable": true },
"click_tracking": { "enable": true },
"validate": { "enable": true },
"unsubscribe": { "enable": false }
},
"custom_args": {
"order_id": "12345",
"user_id": "usr_67890"
},
"priority": 1
}'

Response

Success Response

{
"success": true,
"ref_id": "01JC3BBW8S9YGX2VNKG5MD7BTA"
}
FieldDescription
successBoolean indicating the email was queued successfully
ref_idUnique reference ID for tracking this email. Use this to query logs and filter webhooks.

Error Response

{
"success": false,
"error": "from address does not exist with the provided mailer_id"
}

Common Validation Errors

ErrorCauseSolution
from address does not existThe from email doesn't match the mailer_id's domainUse an email address from the verified domain
subject is required when template_id is not providedNo subject and no templateAdd a subject or use a template_id
content is required when template_id is not providedNo content and no templateAdd content or use a template_id
cc recipients are not allowed when processing emails individuallyUsing CC with bulk mode offSet process_email_inbulk: true or remove CC
bcc recipients are not allowed when processing emails individuallyUsing BCC with bulk mode offSet process_email_inbulk: true or remove BCC
substitutions are not allowed when processing emails in bulkUsing substitutions with bulk modeSet process_email_inbulk: false or remove substitutions
total recipients cannot exceed 100Too many recipients in bulk modeReduce recipient count or send individually
unsubscribe is not allowed when processing emails in bulkUsing unsubscribe with bulk modeDisable unsubscribe or send individually
invalid email formatMalformed recipient emailVerify email address format

Best Practices

1. Always Include Plain Text

Include both HTML and plain text versions for:

  • Better deliverability
  • Accessibility
  • Mail clients that don't render HTML

2. Use Meaningful Campaign IDs

Group related emails for better analytics:

{ "campaign_id": "welcome-series-step-1" }
{ "campaign_id": "password-reset" }
{ "campaign_id": "order-shipped" }

3. Enable Tracking Thoughtfully

  • Enable open/click tracking for marketing emails
  • Consider privacy implications for transactional emails
  • Be transparent with recipients about tracking

4. Validate Before Sending

Enable validate to catch invalid email addresses before sending:

{ "settings": { "validate": { "enable": true } } }

5. Use Priority Wisely

Reserve high priority ("priority": 1) for:

  • Time-sensitive authentication emails
  • Critical security notifications
  • Password reset requests

6. Leverage Custom Args

Pass context for webhook processing:

{
"custom_args": {
"user_id": "123",
"trigger": "signup"
}
}