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

> Understand the difference between Listmonk postback (HTTP webhooks) and email messengers (SMTP delivery). Push campaign data or send formatted messages to subscribers.

- Repository: [Kailash Nadh/listmonk](https://github.com/knadh/listmonk)
- Tags: deep-dive
- Published: 2026-05-19

---

**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`](https://github.com/knadh/listmonk/blob/main/internal/messenger/postback/postback.go), the `Options` struct defines `RootURL`, `Username`, `Password`, `MaxConns`, and `Timeout` for HTTP communication. Conversely, [`internal/messenger/email/email.go`](https://github.com/knadh/listmonk/blob/main/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`](https://github.com/knadh/listmonk/blob/main/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`

```go
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`](https://github.com/knadh/listmonk/blob/main/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`

```go
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:

```yaml
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:

```yaml
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`](https://github.com/knadh/listmonk/blob/main/internal/messenger/postback/postback.go) and [`internal/messenger/email/email.go`](https://github.com/knadh/listmonk/blob/main/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.