Skip to main content
listmonk’s transactional email API allows you to send one-off emails to subscribers or arbitrary email addresses, perfect for password resets, order confirmations, and notifications.

Transactional Email Overview

Transactional emails are different from campaigns:
  • Sent immediately to individual recipients
  • Use templates for consistent formatting
  • Support custom data variables
  • Can be sent to non-subscribers (external mode)
  • Support file attachments

API Endpoint

The transactional email endpoint is defined in cmd/handlers.go:195:
POST /api/tx
Requires the tx:send permission.

Message Format and Parameters

The transactional message structure is defined in models/messages.go:47-71.

Basic Request

{
  "subscriber_emails": ["[email protected]"],
  "template_id": 1,
  "from_email": "[email protected]",
  "data": {
    "name": "John",
    "reset_url": "https://yoursite.com/reset/token123"
  }
}

Complete API Parameters

ParameterTypeRequiredDescription
subscriber_emailsarrayConditionalList of recipient email addresses
subscriber_idsarrayConditionalList of subscriber IDs from database
subscriber_modestringNoHow to handle recipients: default, fallback, external
template_idintegerYesID of template to use
from_emailstringNoSender email (defaults to system from_email)
subjectstringNoEmail subject (can override template subject)
dataobjectNoCustom template variables
headersarrayNoCustom email headers
content_typestringNoMessage content type
messengerstringNoMessenger to use (defaults to email)
You must provide either subscriber_emails OR subscriber_ids, but not both.

Subscriber Modes

Transactional emails support three subscriber modes (defined in models/messages.go:41-45):

Default Mode

{
  "subscriber_mode": "default",
  "subscriber_emails": ["[email protected]"],
  "template_id": 1
}
  • Looks up subscriber in database
  • Fails if subscriber doesn’t exist
  • Uses subscriber’s profile data in template
  • Can use subscriber_ids or subscriber_emails

Fallback Mode

{
  "subscriber_mode": "fallback",
  "subscriber_emails": ["[email protected]"],
  "template_id": 1,
  "data": {"name": "User"}
}
  • Tries to find subscriber in database
  • If not found, creates ephemeral subscriber for this email only
  • Does NOT add to database
  • Must use subscriber_emails (IDs not allowed)

External Mode

{
  "subscriber_mode": "external",
  "subscriber_emails": ["[email protected]"],
  "template_id": 1,
  "data": {"name": "Guest"}
}
  • Always creates ephemeral subscriber
  • Never checks database
  • Perfect for sending to non-subscribers
  • Must use subscriber_emails (IDs not allowed)
The mode validation is handled in cmd/tx.go:196-221.

Template Usage

Transactional emails use templates for consistent formatting.

Creating a Transactional Template

  1. Go to Settings → Templates
  2. Create a new template with type “Transactional”
  3. Use template variables:
<h1>Hello {{ .Subscriber.Name }}!</h1>

<p>Your custom data: {{ .Tx.Data.custom_field }}</p>

<p>Click here to reset your password:</p>
<a href="{{ .Tx.Data.reset_url }}">Reset Password</a>

Available Template Variables

  • {{ .Subscriber.Email }} - Recipient email
  • {{ .Subscriber.Name }} - Subscriber name
  • {{ .Subscriber.UUID }} - Subscriber UUID
  • {{ .Subscriber.Attribs }} - Custom subscriber attributes
  • {{ .Tx.Data.* }} - Any custom data passed in request
  • {{ .Tx.Subject }} - Email subject
Template rendering is handled in models/messages.go:73-120.

Dynamic Subject Lines

You can use template variables in the subject:
{
  "subject": "Hello {{ .Subscriber.Name }}, reset your password",
  "subscriber_emails": ["[email protected]"],
  "template_id": 1
}
Subjects with {{ are automatically compiled as templates (models/messages.go:94-101).

File Attachments

Send emails with file attachments using multipart form data.

Example with cURL

curl -X POST http://localhost:9000/api/tx \
  -H "Authorization: Bearer your-api-token" \
  -F 'data={
    "subscriber_emails": ["[email protected]"],
    "template_id": 1,
    "subject": "Your invoice",
    "data": {"invoice_number": "INV-001"}
  }' \
  -F 'file=@/path/to/invoice.pdf' \
  -F 'file=@/path/to/receipt.pdf'

Attachment Processing

Attachment handling is in cmd/tx.go:20-59:
  • Accepts multiple files in file field
  • Extracts filename and content type
  • Encodes as base64 for email transmission
  • Supports any file type
Attachment size is limited by your SMTP server configuration and max_file_size setting.

Custom Email Headers

Add custom headers to transactional emails:
{
  "subscriber_emails": ["[email protected]"],
  "template_id": 1,
  "headers": [
    {"X-Custom-Header": "value"},
    {"Reply-To": "[email protected]"}
  ]
}
Headers are processed in cmd/tx.go:154-162.

Use Cases

{
  "subscriber_mode": "fallback",
  "subscriber_emails": ["[email protected]"],
  "template_id": 2,
  "subject": "Reset your password",
  "data": {
    "reset_url": "https://yoursite.com/reset?token=abc123",
    "expiry": "1 hour"
  }
}
{
  "subscriber_mode": "external",
  "subscriber_emails": ["[email protected]"],
  "template_id": 3,
  "subject": "Order #{{.Tx.Data.order_id}} confirmed",
  "data": {
    "order_id": "12345",
    "items": ["Product A", "Product B"],
    "total": "$99.99"
  }
}
curl -X POST http://localhost:9000/api/tx \
  -F 'data={
    "subscriber_ids": [123],
    "template_id": 4,
    "subject": "Welcome to our service!",
    "data": {"guide_url": "https://yoursite.com/guide"}
  }' \
  -F '[email protected]'
{
  "subscriber_mode": "external",
  "subscriber_emails": ["[email protected]"],
  "template_id": 5,
  "subject": "System Alert: High CPU Usage",
  "data": {
    "server": "web-01",
    "cpu_usage": "95%",
    "timestamp": "2024-01-15 10:30:00"
  }
}

Rate Limiting

Transactional emails are subject to your SMTP server’s rate limits and listmonk’s configuration.

Relevant Settings

From schema.sql:236-237:
app.message_rate = 10          # Messages per second
app.concurrency = 10            # Concurrent connections

Rate Limiting Behavior

The message manager (cmd/tx.go:164) pushes messages to a queue:
  • Messages are processed by the manager’s worker pool
  • Respects message_rate throttling
  • Failed messages are not automatically retried
  • Check SMTP logs for delivery status
High-volume transactional sending may impact campaign delivery. Consider dedicated SMTP servers for transactional vs. bulk email.

Error Handling

The API returns detailed error responses for troubleshooting.

Common Errors

400 Bad Request - Invalid parameters
{
  "message": "one or more values in 'subscriber_emails' are invalid"
}
400 Bad Request - Subscriber not found (default mode)
{
  "message": "subscriber not found"
}
400 Bad Request - Template not found
{
  "message": "template 999 not found"
}
500 Internal Server Error - Rendering error
{
  "message": "error rendering template"
}

Handling Errors in Code

import requests

response = requests.post(
    'http://localhost:9000/api/tx',
    headers={'Authorization': 'Bearer token'},
    json={
        'subscriber_emails': ['[email protected]'],
        'template_id': 1,
        'data': {'name': 'John'}
    }
)

if response.status_code == 200:
    print('Email sent successfully')
else:
    print(f'Error: {response.json()["message"]}')

Multiple Recipients

Send the same email to multiple recipients:
{
  "subscriber_emails": [
    "[email protected]",
    "[email protected]",
    "[email protected]"
  ],
  "template_id": 1,
  "data": {"message": "Shared notification"}
}
Processing logic (cmd/tx.go:88-168):
  • Each recipient gets an individual email
  • Templates are rendered per-recipient
  • If one fails, others still send
  • Returns error if all recipients fail

Integration Examples

import requests

def send_password_reset(email, reset_token):
    response = requests.post(
        'http://localhost:9000/api/tx',
        headers={'Authorization': 'Bearer your-token'},
        json={
            'subscriber_mode': 'fallback',
            'subscriber_emails': [email],
            'template_id': 2,
            'data': {
                'reset_url': f'https://yoursite.com/reset?token={reset_token}'
            }
        }
    )
    return response.status_code == 200

Best Practices

1

Use appropriate subscriber modes

  • default for existing subscribers
  • fallback for user accounts that may or may not be subscribers
  • external for guest users or system emails
2

Create dedicated templates

Separate templates for different email types (password resets, notifications, etc.)
3

Validate email addresses

Always validate email addresses before sending to avoid bounce issues
4

Include meaningful data

Pass all necessary context in the data object to avoid missing information
5

Handle errors gracefully

Implement retry logic for transient failures and log errors for debugging
6

Monitor sending rate

Track transactional email volume to ensure it doesn’t exceed SMTP limits

Build docs developers (and LLMs) love