Skip to main content
Lettr provides a powerful and flexible API for sending transactional and marketing emails. Whether you’re sending password resets, order confirmations, or newsletters, the sending API handles delivery, tracking, and analytics so you can focus on building your application.

Prerequisites

Before sending your first email, make sure you have:
  1. A verified sending domain with DKIM and SPF records configured
  2. An API key with sending permissions
  3. At least one of html or text content prepared for your email
New to Lettr? Start with a quickstart guide to get set up in minutes.

Basic Email

Send a simple email with minimal configuration:
curl -X POST https://app.lettr.com/api/emails \
  -H "Authorization: Bearer lttr_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "you@yourdomain.com",
    "to": ["recipient@example.com"],
    "subject": "Hello World",
    "html": "<p>This is my first email!</p>"
  }'

Email Options

ParameterTypeRequiredDescription
fromstringYesSender email address (must be from verified domain)
from_namestringNoFriendly sender name
tostring[]YesRecipient address(es), max 50
subjectstringYesEmail subject line (max 998 chars)
htmlstringNo*HTML content
textstringNo*Plain text content
amp_htmlstringNoAMP HTML content
ccstring[]NoCC recipients
bccstring[]NoBCC recipients
reply_tostringNoReply-to address
reply_to_namestringNoReply-to display name
attachmentsarrayNoFile attachments
optionsobjectNoTracking and sending options
tagstringNoTag for analytics grouping (max 64 chars)
metadataobjectNoCustom metadata (available in webhooks)
substitution_dataobjectNoVariables for template substitution
template_slugstringNoTemplate slug to use for email content
template_versionintegerNoSpecific template version to use
project_idintegerNoProject to associate with this email
*At least one of html, text, or template_slug is required.

Response

{
  "message": "Email queued for delivery.",
  "data": {
    "request_id": "7582751837467401763",
    "accepted": 1,
    "rejected": 0
  }
}
The request_id uniquely identifies this send request. Store it to track delivery status, correlate with webhook events, and debug issues. See Idempotency for patterns to prevent duplicate sends.

Transactional vs. Marketing Emails

Lettr handles both transactional and marketing emails through the same API, but the two have different characteristics and best practices. Transactional emails are triggered by a user action — password resets, order confirmations, shipping notifications, account alerts. They are expected by the recipient and typically have high open rates. Marketing emails are sent proactively — newsletters, promotions, product announcements. They require clear unsubscribe mechanisms and careful list management. Use the transactional option to signal the email type:
// Transactional: password reset (transactional is true by default)
await lettr.emails.send({
  from: 'security@example.com',
  to: ['user@example.com'],
  subject: 'Reset your password',
  html: '<p>Click the link below to reset your password.</p>',
  options: {
    transactional: true
  }
});

// Marketing: newsletter
await lettr.emails.send({
  from: 'updates@example.com',
  from_name: 'Acme Newsletter',
  to: ['subscriber@example.com'],
  subject: 'This month at Acme',
  html: newsletterHtml,
  options: {
    transactional: false,
    open_tracking: true,
    click_tracking: true
  }
});
Transactional emails typically have higher deliverability. Separating transactional and marketing email on different sending domains or subdomains is a common practice to protect your transactional sender reputation. See Best Practices for domain warm-up and reputation guidance.

Common Sending Patterns

Personalized Emails with Merge Tags

Use substitution_data to personalize content for each recipient. Lettr’s template language supports variables, conditionals, and loops:
await lettr.emails.send({
  from: 'orders@example.com',
  from_name: 'Acme Store',
  to: ['customer@example.com'],
  subject: 'Your order #{{order_id}} has shipped',
  html: `
    <p>Hi {{name}},</p>
    <p>Your order <strong>#{{order_id}}</strong> has shipped via {{carrier}}.</p>
    <p>Track your package: <a href="{{tracking_url}}">{{tracking_number}}</a></p>
  `,
  substitution_data: {
    name: 'Alex',
    order_id: '12345',
    carrier: 'FedEx',
    tracking_number: '9400111899223456789012',
    tracking_url: 'https://example.com/track/9400111899223456789012'
  }
});

Sending with Templates

Instead of inline HTML, reference a template created in the Lettr dashboard or via the API:
await lettr.emails.send({
  from: 'welcome@example.com',
  from_name: 'Acme',
  to: ['newuser@example.com'],
  subject: 'Welcome to Acme, {{first_name}}!',
  template_slug: 'welcome-email',
  substitution_data: {
    first_name: 'Jordan',
    login_url: 'https://app.example.com/login'
  }
});
See Templates for creating and managing email templates with the visual editor.

Tracking and Metadata

Attach metadata for analytics and debugging, and control tracking per email:
await lettr.emails.send({
  from: 'notifications@example.com',
  to: ['user@example.com'],
  subject: 'Your invoice is ready',
  html: '<p>Your invoice for January is ready. <a href="https://example.com/invoices/123">View invoice</a></p>',
  metadata: {
    invoice_id: 'inv_123',
    customer_id: 'cust_456',
    source: 'billing-service'
  },
  options: {
    open_tracking: true,
    click_tracking: true
  }
});
Metadata is included in webhook payloads, allowing you to correlate delivery events with your application data.

Sending to Multiple Recipients

Send to up to 50 recipients per API call. Use substitution_data to personalize content with merge tags that apply to all recipients:
await lettr.emails.send({
  from: 'team@example.com',
  to: ['alice@example.com', 'bob@example.com', 'carol@example.com'],
  subject: 'Team Update',
  html: '<p>Here is the latest update for the team.</p>'
});
For larger lists, see Batch Sending for patterns to send to hundreds or thousands of recipients efficiently.

Error Handling

The API returns structured errors to help you diagnose issues:
{
  "error_code": "invalid_domain",
  "message": "The sending domain is not verified."
}
Common errors include unverified domains (invalid_domain), unconfigured domains (unconfigured_domain), validation failures (validation_error), and template errors (template_not_found). Server errors (5xx) are safe to retry with exponential backoff, while client errors (4xx) should not be retried. See Errors & Retries for a complete error code reference and retry patterns.

Next Steps

Once you’re sending emails, there are three systems you should configure to complete your integration:
  • Webhooks — Push real-time delivery, bounce, open, and click notifications to your server so you can react to events as they happen
  • Analytics — Aggregate dashboards for monitoring delivery rates and engagement trends over time
  • Suppressions — Automatically manage bounced and unsubscribed addresses to protect your sender reputation

Learn More