Core Concepts
Webhook delivery
When MailLaser receives and parses a valid email, it sends the extracted content to your configured webhook URL as an HTTP POST request with a JSON body.
HTTP request details
Every webhook delivery uses these HTTP settings:
| Property | Value |
|---|---|
| Method | POST |
| Content-Type | application/json |
| User-Agent | MailLaser/2.0.0 |
| URL | Value of MAIL_LASER_WEBHOOK_URL |
The User-Agent header reflects the application name and version from the Cargo package metadata.
In release builds, MailLaser enforces HTTPS-only connections to the webhook URL. In debug builds, HTTP is also permitted for local development.
JSON payload format
The payload contains the parsed email content:
{
"sender": "user@example.com",
"sender_name": "John Doe",
"recipient": "alerts@myapp.com",
"subject": "Monthly Report",
"body": "Please find the report attached.\n\nBest regards,\nJohn",
"html_body": "<html><body><p>Please find the report attached.</p><p>Best regards,<br>John</p></body></html>",
"headers": {
"X-Custom-Id": "12345",
"X-Priority": "high"
}
}
Required fields
These fields are always present in every payload:
| Field | Type | Description |
|---|---|---|
sender | string | The email address from the MAIL FROM command. |
recipient | string | The accepted email address from the RCPT TO command. |
subject | string | The Subject: header value. Empty string if no subject header exists. |
body | string | Plain text body content. If the email is HTML-only, this contains a text conversion generated by html2text. |
Optional fields
These fields are omitted entirely from the JSON when they have no value. They are not set to null; they are absent from the payload.
| Field | Type | Description |
|---|---|---|
sender_name | string | Display name from the From: header (e.g., "John Doe" from John Doe <john@example.com>). Omitted when the From: header contains only an email address or is absent. |
html_body | string | Raw HTML content from the text/html MIME part. Omitted when the email has no HTML content. |
headers | object | Key-value map of headers matching the configured MAIL_LASER_HEADER_PREFIX. Omitted when no prefixes are configured or no headers match. See Header passthrough. |
Body processing
MailLaser determines the body and html_body fields through this logic:
- If the email has a
text/plainMIME part and atext/htmlMIME part (typical for multipart/alternative), thebodyis generated from the HTML usinghtml2text, andhtml_bodycontains the raw HTML. - If the email has only a
text/htmlpart, thebodyis generated from the HTML usinghtml2text, andhtml_bodycontains the raw HTML. - If the email has only a
text/plainpart, thebodycontains the plain text directly, andhtml_bodyis omitted. - If neither part is found,
bodyis an empty string andhtml_bodyis omitted.
Text conversion
The html2text library converts HTML to readable plain text. It preserves paragraph structure, converts links to reference-style notation, and handles bold/italic formatting. The output width is set to 80 characters.
Delivery behavior
Webhook delivery is handled by the WebhookActor, which runs as a persistent actor in the acton-reactive framework. The actor processes emails sequentially, applying resilience patterns to each delivery attempt.
For each email:
- The circuit breaker is checked. If the circuit is open, the email is dropped (see Resilience).
- The initial delivery attempt is made with a configurable timeout (
MAIL_LASER_WEBHOOK_TIMEOUT, default 30 seconds). - If the attempt fails or times out, retries occur with exponential backoff up to
MAIL_LASER_WEBHOOK_MAX_RETRIES(default 3). - The circuit breaker state is updated based on the outcome.
Webhook delivery is fire-and-forget from the SMTP session's perspective. The SMTP session responds with 250 OK: Message accepted for delivery as soon as the email data is parsed and passed to the webhook actor. A webhook failure does not cause the SMTP transaction to fail.
Response handling
MailLaser checks the HTTP status code of the webhook response:
- 2xx: Logged as successful. The circuit breaker's failure counter resets.
- 4xx or 5xx: Logged as an error. Counts as a failure for retry and circuit breaker purposes.
- Timeout: Logged as a timeout. Counts as a failure.
The response body from the webhook endpoint is not read or logged. MailLaser only examines the status code.