Learn what events Lettermint can send and how to handle them. Subscribe to the events you need when creating a webhook.
Common envelope
All webhook events share a consistent envelope:
{
"id": "54d7e8c9-1195-4ba0-9d3f-b9af92305add",
"event": "message.delivered",
"timestamp": "2025-08-08T20:14:00.000Z",
"data": { /* event-specific object */ }
}
Fields:
- id: Unique identifier (UUIDv4) for the specific delivery event. Useful for idempotency.
- event: Event type.
- timestamp: ISO timestamp when the event occurred.
- data: Event-specific fields (documented per event below).
Common data fields:
- message_id: The unique identifier for the message.
- subject: The email subject line.
- metadata: Custom metadata attached to the message (if any).
- tag: The tag assigned to categorize the message (e.g., “newsletter”, “order-confirmation”). Will be
null if no tag was assigned.
Event payloads
message.created
Message accepted for processing.
Example payload:
{
"id": "54d7e8c9-1195-4ba0-9d3f-b9af92305add",
"event": "message.created",
"timestamp": "2025-08-08T20:14:00.000Z",
"data": {
"message_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"from": {
"email": "updates@acme.com",
"name": "Acme Updates"
},
"to": ["user@example.com"],
"cc": ["cc@example.com"],
"bcc": ["bcc@example.com"],
"reply_to": "help@acme.com",
"subject": "Welcome to Acme",
"metadata": {
"X-Campaign-ID": "welcome-2025"
},
"tag": "welcome"
}
}
message.sent
Message sent to recipient server.
{
"id": "7f9c8e2a-1b3d-4f6e-b7d2-5c9f3a7e8b0c",
"event": "message.sent",
"timestamp": "2025-08-08T20:15:00.000Z",
"data": {
"message_id": "123e4567-e89b-12d3-a456-426614174000",
"subject": "Your weekly newsletter",
"recipient": "user@example.com",
"metadata": {
"X-User-ID": "user-123"
},
"tag": "newsletter"
}
}
message.delivered
Message successfully delivered.
{
"id": "9b0c4a4e-4e29-4d8b-8b3a-3f0f3e6d2f9b",
"event": "message.delivered",
"timestamp": "2025-08-08T20:15:12.000Z",
"data": {
"message_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"subject": "Your order has shipped",
"recipient": "user@example.com",
"response": {
"status_code": 250,
"enhanced_status_code": "2.0.0",
"content": "OK 1640705112 qp1355551phe.1 - gsmtp"
},
"metadata": {
"X-Transaction-ID": "txn-456"
},
"tag": "order-confirmation"
}
}
message.hard_bounced
Permanent delivery failure (e.g., user does not exist).
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"event": "message.hard_bounced",
"timestamp": "2025-08-08T20:15:30.000Z",
"data": {
"message_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"subject": "Summer sale - 50% off!",
"recipient": "user@example.com",
"response": {
"status_code": 250,
"enhanced_status_code": "2.0.0",
"content": "OK 1640705112 qp1355551phe.1 - gsmtp"
},
"metadata": {
"X-Campaign-ID": "summer-sale"
},
"tag": "marketing"
}
}
message.soft_bounced
Temporary delivery failure (e.g., mailbox full, transient error).
{
"id": "9b0c4a4e-4e29-4d8b-8b3a-3f0f3e6d2f9b",
"event": "message.soft_bounced",
"timestamp": "2025-08-08T20:15:30.000Z",
"data": {
"message_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"subject": "Your order #789 update",
"recipient": "user@example.com",
"response": {
"status_code": 250,
"enhanced_status_code": "2.0.0",
"content": "OK 1640705112 qp1355551phe.1 - gsmtp"
},
"metadata": {
"X-Order-ID": "order-789"
},
"tag": "order-notification"
}
}
message.spam_complaint
Recipient reported the message as spam.
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"event": "message.spam_complaint",
"timestamp": "2025-08-08T20:16:00.000Z",
"data": {
"message_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"subject": "Weekly digest - August edition",
"recipient": "user@example.com",
"metadata": {
"X-Newsletter-ID": "weekly-digest"
},
"tag": "newsletter"
}
}
message.failed
Processing failure within Lettermint.
{
"id": "9b0c4a4e-4e29-4d8b-8b3a-3f0f3e6d2f9b",
"event": "message.failed",
"timestamp": "2025-08-08T20:14:12.000Z",
"data": {
"message_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"subject": "Password reset request",
"recipient": "user@example.com",
"reason": "A network error occurred.",
"response": {
"status_code": 250
},
"metadata": {
"X-Session-ID": "sess-abc123"
},
"tag": null
}
}
message.suppressed
Message was suppressed due to previous bounce or complaint.
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"event": "message.suppressed",
"timestamp": "2025-08-08T20:14:05.000Z",
"data": {
"message_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"subject": "Your account statement",
"recipient": "user@example.com",
"reason": "hard_bounce",
"metadata": {
"X-Account-ID": "acc-xyz789"
},
"tag": "transactional"
}
}
message.policy_rejected
Message rejected by policy (spam or content filter). This occurs when a message’s spam score exceeds the configured threshold before delivery.
{
"id": "c4d5e6f7-a8b9-0123-cdef-456789abcdef",
"event": "message.policy_rejected",
"timestamp": "2025-08-08T20:14:03.000Z",
"data": {
"message_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"subject": "Limited time offer!!!",
"reason": "Spam score threshold exceeded",
"score": 7.5,
"spam_symbols": [
{
"name": "BAYES_SPAM",
"score": 3.5,
"options": [],
"description": "Bayes spam probability is very high"
},
{
"name": "SUBJ_ALL_CAPS",
"score": 1.5,
"options": [],
"description": "Subject is all capitals"
}
],
"metadata": {
"X-Campaign-ID": "promo-123"
},
"tag": "marketing"
}
}
Fields:
reason: Human-readable rejection reason (e.g., “spam content”, “policy violation”)
score: The spam score that triggered the rejection
spam_symbols: Array of spam rule matches with name, score, options, and description
message.unsubscribed
Recipient unsubscribed from the mailing list.
{
"id": "b2c3d4e5-f6a7-8901-bcde-f01234567891",
"event": "message.unsubscribed",
"timestamp": "2025-08-08T20:16:30.000Z",
"data": {
"message_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"subject": "August newsletter highlights",
"recipient": "user@example.com",
"unsubscribed_at": "2025-08-08T20:16:30.000Z",
"metadata": {
"X-Campaign-ID": "newsletter-august"
},
"tag": "newsletter"
}
}
message.opened
Recipient opened the email. Requires open tracking to be enabled on the route.
This event is only available for routes with open tracking enabled. Open tracking is a beta feature available on paid plans.
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"event": "message.opened",
"timestamp": "2025-08-08T20:17:00.000Z",
"data": {
"message_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"subject": "Your order has shipped",
"metadata": {
"X-Order-ID": "order-12345"
},
"tag": "shipping",
"recipient": "user@example.com",
"opened_at": "2025-08-08T20:17:00+00:00",
"first_open": true,
"device_type": "desktop",
"client_type": "browser",
"client_name": "Chrome",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
"bot": {
"detected": false,
"probability": 5
}
}
}
Fields:
opened_at: Timestamp when the open was detected
first_open: Whether this is the first time this recipient opened the email
device_type: Device category: desktop, mobile, or tablet
client_type: Client category: browser, email_client, etc.
client_name: Specific client name: Chrome, Safari, Outlook, etc.
user_agent: The user agent string from the request
bot.detected: Whether the open appears to be from an automated source (bot, proxy, scanner)
bot.probability: Confidence score (0-100) that this is a bot interaction
message.clicked
Recipient clicked a link in the email. Requires click tracking to be enabled on the route.
This event is only available for routes with click tracking enabled. Click tracking is a beta feature available on paid plans.
{
"id": "b2c3d4e5-f6a7-8901-bcde-f01234567892",
"event": "message.clicked",
"timestamp": "2025-08-08T20:18:30.000Z",
"data": {
"message_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"subject": "Your weekly recommendations",
"metadata": {
"X-Campaign-ID": "weekly-recs"
},
"tag": "recommendations",
"recipient": "user@example.com",
"clicked_at": "2025-08-08T20:18:30+00:00",
"destination_url": "https://example.com/product/123",
"link_index": 0,
"anchor_text": "View Product",
"first_click": true,
"device_type": "mobile",
"client_type": "browser",
"client_name": "Safari",
"user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15",
"bot": {
"detected": false,
"probability": 2
}
}
}
Fields:
clicked_at: Timestamp when the click occurred
destination_url: The original destination URL that was clicked
link_index: Zero-based index of the link in the email (order of appearance)
anchor_text: The visible text of the clicked link
first_click: Whether this is the first time this recipient clicked any link in this email
device_type: Device category: desktop, mobile, or tablet
client_type: Client category: browser, email_client, etc.
client_name: Specific client name: Chrome, Safari, Outlook, etc.
user_agent: The user agent string from the request
bot.detected: Whether the click appears to be from an automated source (security scanner, link preview bot)
bot.probability: Confidence score (0-100) that this is a bot interaction
message.inbound
Inbound email received on an inbound route. Only available for inbound route types.
This event includes the complete parsed email with headers, body (text and HTML), attachments, and spam filtering results.
This event is only available for webhooks attached to inbound routes. See the Inbound Mail guide for setup instructions.
Example payload:
{
"id": "e3d4f5a6-b7c8-9012-d3e4-f5a6b7c89012",
"event": "message.inbound",
"timestamp": "2025-10-02T14:30:00.000Z",
"data": {
"route": "support-inbox",
"message_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"envelope": {
"remote_ip": "203.0.113.42",
"remote_hostname": "mail.example.com",
"helo": "smtp.example.com",
"mail_from": "customer@example.com"
},
"from": {
"email": "customer@example.com",
"name": "John Doe",
"subaddress": null
},
"to": [
{
"email": "support@acme.com",
"name": null,
"subaddress": null
}
],
"cc": [],
"recipient": "support@acme.com",
"subaddress": null,
"reply_to": "customer@example.com",
"subject": "Question about my order",
"date": "2025-10-02T14:30:00.000Z",
"body": {
"text": "Hi, I have a question about my recent order...",
"html": "<p>Hi, I have a question about my recent order...</p>"
},
"tag": null,
"headers": [
{
"name": "Message-ID",
"value": "<abc123@mail.example.com>"
},
{
"name": "X-Mailer",
"value": "Apple Mail (2.3445.104.11)"
}
],
"attachments": [
{
"filename": "receipt.pdf",
"content": "JVBERi0xLjQKJeLjz9MK...",
"url": null,
"content_type": "application/pdf",
"size": 45678,
"content_id": null
}
],
"is_spam": false,
"spam_score": 1.2,
"spam_symbols": [
{
"name": "DKIM_VALID",
"score": -0.1,
"options": [],
"description": "Message has valid DKIM signature"
},
{
"name": "SPF_PASS",
"score": -0.1,
"options": [],
"description": "SPF check passed"
},
{
"name": "BAYES_HAM",
"score": -3.0,
"options": [],
"description": "Bayesian classifier identified message as non-spam"
}
]
}
}
Payload fields:
route: Route identifier/slug
message_id: Unique message ID
envelope: SMTP envelope data from the sending server
remote_ip: IP address of the sending mail server
remote_hostname: Reverse DNS (PTR) hostname of the sending server
helo: HELO/EHLO domain the sender identified as
mail_from: Envelope sender address (MAIL FROM command, may differ from the From header)
from: Sender information with optional subaddress
to: Array of TO recipients with optional subaddresses
cc: Array of CC recipients with optional subaddresses
recipient: Primary envelope recipient
subaddress: Parsed subaddress from recipient (e.g., “tag” from “user+tag@example.com”)
reply_to: Reply-To address if specified
subject: Email subject line
date: Email received timestamp (ISO 8601)
body.text: Plain text body content
body.html: HTML body content
tag: Custom tag from X-LM-Tag or X-Tag header
headers: All email headers excluding standard ones (From, To, Subject, etc.)
attachments: File attachments (see attachment delivery modes below)
is_spam: Whether message exceeded spam threshold
spam_score: Calculated spam score from Rspamd
spam_symbols: Array of spam rule objects, each containing name, score, options, and description fields
Attachment delivery modes: By default, attachments include base64-encoded content. You can configure your inbound route to use URL delivery mode instead, where attachments include a signed url for downloading. URLs are valid for 28 days. See the Inbound Mail guide for configuration.
Security: Always validate webhook signatures and sanitize email content before processing. Email bodies may contain malicious HTML or scripts.
webhook.test
Special event that can be triggered from the Dashboard for connectivity testing.
{
"id": "test-7f9c8e2a-1b3d-4f6e-b7d2-5c9f3a7e8b0c",
"event": "webhook.test",
"timestamp": "2025-08-08T20:14:12.000Z",
"data": {
"message": "This is a test webhook from Lettermint",
"webhook_id": "9f9bf19c-4a2c-45f3-a6c7-bc937224ec5a",
"timestamp": 1754921294
}
}
Next Steps