Postback vs Email Messengers in Listmonk: HTTP Webhooks vs SMTP Delivery

The Postback messenger pushes campaign data as JSON to a remote HTTP endpoint, while the Email messenger delivers formatted messages through SMTP servers to subscriber inboxes.

Listmonk, the open-source newsletter and mailing list manager, provides two distinct messenger back-ends for delivering campaign messages. Understanding the difference between Postback and Email messengers in Listmonk is essential for choosing the right integration strategy for your delivery pipeline. While both implement the same Messenger interface found in internal/messenger/, they handle message transport through fundamentally different protocols—HTTP webhooks versus SMTP transmission.

Core Architectural Differences

The primary distinction lies in the transport mechanism and intended recipient.

Postback operates as a push-style HTTP client. It marshals the entire campaign payload—including subject, body, recipient data, and attachments—into a JSON document and transmits it via POST request to a configured remote URL. This makes it ideal for integrating Listmonk with external systems, custom APIs, or push-notification services that consume raw campaign data.

Email functions as a traditional SMTP client. It constructs RFC-compliant email messages with proper MIME headers, handles TLS encryption, and manages connection pooling to SMTP servers. This messenger is designed for final-mile delivery to actual subscriber inboxes.

The configuration structures reflect these different purposes. In internal/messenger/postback/postback.go, the Options struct defines RootURL, Username, Password, MaxConns, and Timeout for HTTP communication. Conversely, internal/messenger/email/email.go defines a Server struct containing SMTP-specific fields such as Host, Port, AuthProtocol, TLSType, and a managed smtppool.Pool.

How the Postback Messenger Works

The Postback implementation centers around HTTP request construction and JSON serialization.

When initialized via postback.New(), the messenger creates an http.Client with configurable connection limits and timeouts. The Push() method accepts a models.Message struct, transforms it into an internal postback struct mirroring the Message fields, and marshals it to JSON before sending.

Key implementation details from internal/messenger/postback/postback.go include:

  • Payload structure: The postback struct maps directly to models.Message fields including Subject, From, Body, Recipients, Campaign, and Attachments
  • Authentication: Optional basic-auth credentials are injected into the request headers when Username and Password are configured
  • Error handling: Returns an error if the HTTP response status is not 200 OK
import (
    "github.com/knadh/listmonk/internal/messenger/postback"
    "github.com/knadh/listmonk/models"
    "time"
)

func examplePostback() error {
    // Initialise the messenger
    p, _ := postback.New(postback.Options{
        Name:     "webhook",
        RootURL:  "https://example.com/webhook",
        Username: "api_user",
        Password: "secret",
        MaxConns: 5,
        Timeout:  10 * time.Second,
    })

    // Build a Listmonk message
    msg := models.Message{
        Subject:     "Welcome!",
        From:        "no-reply@example.com",
        ContentType: "html",
        Body:        []byte("<h1>Hello</h1>"),
        Subscriber: &models.Subscriber{Email: "john@doe.com"},
    }

    // Push the message to the remote endpoint
    return p.Push(msg)
}

The exec method handles the actual HTTP execution, creating a new POST request with JSON content type and delivering the payload to the configured RootURL.

How the Email Messenger Works

The Email messenger handles RFC-compliant message construction and SMTP delivery through connection pooling.

Initialized via email.New(), this messenger creates a smtppool.Pool for each configured SMTP server. The pool manages keep-alive connections, retry logic, and TLS handshakes. When sending, the Push() method constructs a smtppool.Email struct, resolves headers like Message-Id and Return-Path, handles Cc and Bcc fields, attaches files, and transmits via the pooled connection.

Key aspects from internal/messenger/email/email.go include:

  • Pool management: Each Server configuration maintains a persistent smtppool.Pool created once during initialization
  • Header resolution: Automatically generates Message-Id, handles Return-Path, and manages custom email headers
  • TLS handling: Supports STARTTLS and implicit TLS with configurable certificate verification via TLSSkipVerify
import (
    "github.com/knadh/listmonk/internal/messenger/email"
    "github.com/knadh/listmonk/models"
)

func exampleEmail() error {
    // Initialise the messenger with a single SMTP server
    e, _ := email.New("smtp",
        email.Server{
            Name:          "primary",
            Host:          "smtp.example.com",
            Port:          587,
            Username:      "user",
            Password:      "secret",
            AuthProtocol:  "login",
            TLSType:       "STARTTLS",
            TLSSkipVerify: false,
        })

    // Build a Listmonk message
    msg := models.Message{
        From:        "no-reply@example.com",
        To:          []string{"john@doe.com"},
        Subject:     "Welcome!",
        ContentType: "html",
        Body:        []byte("<h1>Hello</h1>"),
    }

    // Send the e-mail via SMTP
    return e.Push(msg)
}

Error handling returns SMTP-level failures directly from pool.Send(), including authentication errors, delivery failures, and connection issues.

Configuration Examples

Both messengers are configured through the Listmonk YAML configuration file, but with distinct parameter sets.

Postback configuration focuses on HTTP endpoint details:

messengers:
  postback:
    name: webhook
    root_url: https://example.com/listmonk-hook
    username: api_user
    password: secret
    max_conns: 10
    timeout: 5s

Email configuration specifies SMTP server parameters:

messengers:
  email:
    name: smtp
    servers:
      - name: primary
        host: smtp.example.com
        port: 587
        username: user
        password: secret
        auth_protocol: login
        tls_type: STARTTLS

Summary

  • Transport protocol: Postback uses HTTP/JSON webhooks; Email uses SMTP with TLS
  • Configuration: Postback requires RootURL and HTTP auth; Email requires Host, Port, and SMTP auth protocols
  • Connection handling: Postback uses http.Client with max connections; Email uses smtppool.Pool for persistent SMTP connections
  • Use cases: Postback integrates with external APIs and services; Email delivers to actual mailboxes
  • Source files: Core logic resides in internal/messenger/postback/postback.go and internal/messenger/email/email.go

Frequently Asked Questions

Can I use both Postback and Email messengers simultaneously in Listmonk?

Yes. Listmonk supports multiple messenger configurations simultaneously. You can configure one or more SMTP servers for Email delivery alongside one or more Postback endpoints for webhook integration. The system routes campaigns to the appropriate messenger based on your configuration and campaign settings.

When should I choose Postback over Email?

Choose Postback when you need to integrate Listmonk with external systems that process campaign data before final delivery—such as custom notification services, mobile push gateways, or internal CRM systems that require the raw JSON payload. Choose Email when delivering directly to subscriber inboxes through standard SMTP infrastructure.

How does error handling differ between the two messengers?

The Postback messenger returns errors for non-200 HTTP responses or connection failures to the remote endpoint. The Email messenger returns SMTP-specific error codes, including authentication failures, delivery rejections, and TLS handshake errors. Both propagate errors through the common Messenger interface, but the error context reflects the underlying transport protocol.

What authentication methods does each messenger support?

The Postback messenger supports HTTP Basic Authentication via the Username and Password fields in Options. The Email messenger supports multiple SMTP authentication protocols including login, plain, and cram-md5, configured through the AuthProtocol field in the Server struct. Both support TLS encryption for secure transport.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →