Custom Headers
Custom headers let you embed metadata, tracking signals, and unsubscribe mechanisms into every outgoing message. PostMTA handles RFC 7230 formatting automatically and provides built-in protection against header injection attacks.
Adding Custom Headers via API
Pass custom headers in the headers object of the JSON payload. PostMTA serializes them into the message MIME tree before delivery.
curl -X POST https://api.postmta.com/v1/messages/send \
-H "Authorization: Bearer pmk_live_abc123xyz789..." \
-H "Content-Type: application/json" \
-d '{
"from": { "address": "hello@mail.example.com" },
"to": [ { "address": "alice@example.com" } ],
"subject": "Your order confirmation",
"html": "<p>Order confirmed.</p>",
"headers": {
"X-Order-ID": "12345",
"X-Campaign-Code": "SUMMER24",
"X-Mailer": "PostMTA/1.0"
}
}'The resulting MIME headers in the delivered message look like:
From: hello@mail.example.com
To: alice@example.com
Subject: Your order confirmation
X-Order-ID: 12345
X-Campaign-Code: SUMMER24
X-Mailer: PostMTA/1.0
Content-Type: text/html; charset="UTF-8"
[message body]Adding headers via SMTP
For SMTP submissions, add headers directly in the message body before the blank line that separates headers from the body:
DATA
From: hello@mail.example.com
To: alice@example.com
Subject: Your order confirmation
X-Order-ID: 12345
X-Campaign-Code: SUMMER24
Content-Type: text/html; charset="UTF-8"
<html>
<body>
<p>Order confirmed.</p>
</body>
</html>
.Required Headers (Auto-Added)
The following headers are always set by PostMTA and must not be duplicated in your payload. Attempting to override them via headers results in a 400 Bad Request validation error.
| Header | Source | Notes |
|---|---|---|
From | Envelope MAIL FROM | Set from the from.address JSON field or SMTP envelope. |
To | Envelope RCPT TO | Set from the to[].address JSON field or SMTP envelope. |
Subject | JSON subject field | Validated for UTF-8 encoding. |
Message-ID | PostMTA generated | Unique per message. Format: <msg_id@postmta.com> |
Date | PostMTA generated | UTC timestamp of message acceptance. |
DKIM-Signature | PostMTA generated | Present if domain DKIM is verified. Do not set manually. |
List-Unsubscribe and List-Unsubscribe-Post
List-Unsubscribe is required for bulk commercial email in many jurisdictions (CAN-SPAM, CASL). It provides recipients with a one-click unsubscribe mechanism that automatically processes opt-out requests.
List-Unsubscribe header
The List-Unsubscribe header specifies the URL a recipient clicks to unsubscribe. For mail clients that render an unsubscribe button, use the mailto: form as a fallback:
"headers": {
"List-Unsubscribe": "<https://example.com/unsubscribe/alice@example.com>, <mailto:unsubscribe@mail.example.com?subject=unsubscribe:alice@example.com>"
}When rendered in an email client, the first URL in the angle brackets (the <https://...> URL) is typically used as the unsubscribe link.
List-Unsubscribe-Post header
List-Unsubscribe-Post: List-Unsubscribe=One-Click tells the mail client that the unsubscribe URL can be triggered with a single POST request (no browser redirect needed). PostMTA processes these automatically and adds the recipient to the suppression list.
"headers": {
"List-Unsubscribe": "<https://example.com/unsubscribe/alice@example.com>",
"List-Unsubscribe-Post": "List-Unsubscribe=One-Click"
}List-Unsubscribe for any commercial or marketing mailing. Bulk mail without a working unsubscribe mechanism risks CAN-SPAM and CASL violations.X-Priority and X-Mailer Headers
X-Priority
X-Priority signals the importance of a message to the receiving mail server and client. The value is a number from 1 to 5:
| Value | Label | Use case |
|---|---|---|
| 1 | Highest | Urgent system alerts, security notifications. |
| 2 | High | Important business communications. |
| 3 | Normal (default) | Standard messages. |
| 4 | Low | Non-urgent informational messages. |
| 5 | Lowest | Newsletter or marketing content. |
"headers": {
"X-Priority": "1"
}X-Priority. For truly urgent delivery, use a dedicated high-priority sending infrastructure and warm your sending IPs accordingly.X-Mailer
X-Mailer identifies the software that generated the message. It is informational only and not required, but it helps with debugging and can be used for tracking purposes:
"headers": {
"X-Mailer": "MyApp/2.1.0 (PostMTA SMTP)"
}RFC 7230 Header Formatting Rules
PostMTA enforces RFC 7230 header field formatting. Headers that violate the specification are rejected at submission time with a400 Bad Request error.
Field name rules
- Header names are US-ASCII, case-insensitive.
- Use only characters
A-Z,a-z,0-9,-, and_in header field names. - Custom headers must not duplicate RFC-defined header names (e.g., do not set
Content-Typemanually).
Field value rules
- Header field values may contain UTF-8 characters (PostMTA handles encoding).
- Fold (wrap) long header values across multiple lines using a CRLF followed by at least one whitespace.
- Header values must not contain bare
CRorLFcharacters — this is a header injection attack vector. - Angle brackets around values (e.g.,
<url>) are used foraddr-spec and URL contexts — do not use them generically.
Header length limits
- Maximum 100 headers per message.
- Maximum 8,192 characters per header field line (after unfolding).
- Maximum 256 characters per header field name.
Header Injection Protection
Header injection (also called CRLF injection) occurs when an attacker injects unauthorized CRLF sequences into header values to smuggle additional headers or control the message body. PostMTA neutralizes this attack vector at the input validation layer.
Injection attack examples
Suppose an attacker controls the X-Custom header value and submits:
X-Custom: legitimate-value
X-Injected: attacker-controlled-header
From: attacker@evil.comPostMTA detects any bare CR, LF, orCRLF sequence appearing within a header value that is not part of a valid encoded word or URL and rejects the submission:
{
"error": "invalid_header",
"message": "Header field value contains an illegal CRLF sequence. Header injection attempt detected."
}Safely encoding special characters
For URL values that naturally contain &, ?, or =, use percent-encoding or place the URL inside angle brackets (which is the correct format for addr-spec and URL contexts):
"List-Unsubscribe": "<https://example.com/unsubscribe?token=abc123&user=alice@example.com>"The & inside a URL in an angle-bracketed context is permitted. A bare & in an unbracketed header value is also permitted since it is not a CRLF sequence.
CR, LF, or CRLF sequences that would allow header folding are blocked.Next Steps
- Sending Email — send messages with custom headers via REST or SMTP
- Sending Domains — DKIM and SPF alignment for authentication
- Bounce Classification — how bounces are classified and suppressed
- Webhooks — receive delivery events with header context