Send altbody so campaigns actually go out multipart/alternative
CI / test (push) Successful in 9s

listmonk never derives a plaintext part from HTML/Markdown on its own
(models/campaigns.go only compiles AltBodyTpl, and
internal/manager/message.go only emits text/plain, when altbody is
explicitly non-null) -- confirmed against the deployed v6.2.0 source.
Every campaign this tool created was HTML-only as a result, contrary
to the README's now-corrected claim.

Reusing the same raw Markdown+template source as altbody personalizes
it identically to body, since AltBodyTpl compiles whenever the text
contains {{ }} expressions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 22:05:49 -04:00
parent 43c0604ac6
commit e238bde036
2 changed files with 16 additions and 3 deletions
+11 -1
View File
@@ -335,7 +335,17 @@ func (in CampaignInput) payload() map[string]any {
"lists": in.ListIDs,
"content_type": "markdown",
"body": in.Body,
"type": in.Type,
// listmonk never derives a plaintext alternative from body/HTML on its
// own (models/campaigns.go only compiles AltBodyTpl, and
// internal/manager/message.go only emits a text/plain part, when
// altbody is explicitly non-null) — confirmed against the deployed
// v6.2.0 source. Reusing the same raw Markdown+template source as the
// altbody gets it personalized identically to body (AltBodyTpl compiles
// whenever the text contains {{ }} expressions), at the cost of
// Markdown syntax like *emphasis* showing up literally in plaintext
// clients — an acceptable, well-established tradeoff for Markdown.
"altbody": in.Body,
"type": in.Type,
// listmonk's create/update handlers default an omitted messenger to
// "email" before validating, but the test-send handler validates the
// raw request body as-is — an omitted messenger there fails with