Part 1 of this series introduced SparkPost Signals for on-premises deployments. Part 2 walked through setting up PowerMTA step-by-step. In this part, we’re going to dive into the details of connecting your Momentum server to SparkPost Signals. You’re going to need:

  • A host running Momentum 4.x
  • The Signals Agent rpm file and User Guide
  • A SparkPost account with API key permission for “Incoming Events: Write” as per Part 1

We’ll set up Momentum to stream events up to your SparkPost account, then you’ll be able to use the following Signals Analytics reports: 

Unlike PowerMTA, which requires external engagement-tracking, we’ll use Momentum’s built-in engagement tracking to capture recipient opens and clicks. That way, the Health Score, Engagement Recency, and Engagement reports all work immediately.

Configuring Momentum for Signals

There’s a lot of flexibility when setting up Momentum, and each setup will be different. This section will cover adding Signals integration to an existing working Momentum setup, as that’s what I expect most folks are interested in, so you don’t have to wade through a lot of basics that you already know. For the truly motivated, the details of our demo setup are covered in the “Annex: Momentum Signals demo configuration” section at the end. 

Firstly, follow the steps in the “Signals Agent User Guide” that you’ll receive with your SparkPost Signals account. On completion, you’ll have your specific API key stored within the script  /etc/init.d/signals-agent

.. and your file /opt/msys/ecelerity/etc/conf/default/ecelerity.conf will have (near the end)

include "signals-agent.conf" 

.. and the file signals-agent.conf will be present in the same directory.

There is nothing special you need to do for clustered installations.  The Signals Agent must be installed on each node and each node reports events independently.

Momentum Engagement Tracking

If you’re already using Momentum Engagement Tracking, you can skip this section!

The setup of Engagement Tracking shown in some detail here, because it helps to get the most out of Momentum / Signals integration.

For this example, our Momentum demo server is on with a single elastic IP (and an A record pointing to it).

After following the Support instructions for enabling engagement tracking, I checked that mails delivered through our demo containing html have their links wrapped correctly, and an open-tracking pixel added. I chose to use port 81, and simple http (not https) tracking; your setup may vary. I saw the following, inside delivered mails.

As per the above instructions, I configured nginx, establishing the internal endpoints that will receive the clicks and opens on port 2081. Here’s my setup in /opt/msys/3rdParty/nginx/conf.d/click_proxy_upstream.conf:

This port is not exposed to the Internet. Instead, I use nginx to forward traffic on port 81 to the internal endpoint. I also set the headers to make Momentum “look like” SparkPost engagement tracking (so that my demo will follow the links). Here’s my setup in /opt/msys/3rdParty/nginx/conf.d/my_click_proxy.conf:

Controlling what information your server presents publicly, by using settings such as server_tokens off  is generally good security practice. Now we test the nginx configuration:

Once iptables/firewalld configuration was done, and (in our case) AWS EC2 inbound security rule configured, we can test that our open pixel can be fetched from outside, using curl  from another host:

That response beginning GIF89a is the server delivering the open pixel, as a GIF file, back to our client. We can see the contents more easily by piping through hexdump :

That’s all you need to have Engagement Tracking running on your server, and you should see Open and Click events appear in your linked SparkPost account:

Here’s what you’ve been waiting for …

After a day or two of running, you’ll see Health Score data building up:

Reporting facets

Here’s how Momentum attributes map onto SparkPost Signals Analytics reporting facets:

Momentum attribute Signals Analytics facet Comment
Sending domain Sending domain No config needed, this is the same concept.
binding Sending IP Your Momentum binding name reports in SparkPost as “Sending IP”.
binding_group IP pool Your Momentum binding_group name is reported in SparkPost as the “IP pool” name which is an equivalent concept.
Custom header
Campaign See “Setting Campaign” below – Momentum is flexible here.
Subaccount Create the subaccount in SparkPost account. Tag mail with the (numeric) subaccount ID in injected message header “X-SP-SUBACCOUNT-ID” and you’re good to go.
IP address of the remote host (logfile %H) ip_address Address that Momentum delivered the message to (recipient mail server).


Setting Campaign

Being able to report with Campaign as a facet is really useful. There are two ways to do this:

  1. Set up the X-MSYS-API header as described here. This special header provides various features as well, such as control of open and click tracking and metadata on your Momentum traffic stream. This is the method we used in this demo setup.
  2. Create your own custom X-header to carry a campaign identifier, and map this in the signals-agent-config.lua file. For example, this makes Momentum accept an X-Job header carrying campaign ID, just like PowerMTA:

That’s everything you need for Momentum / SparkPost Signals integration. If you want to know more about our demo configuration, read on.

Annex: Momentum Signals demo configuration

The config file structure can be found in /opt/msys/ecelerity/etc/conf/default/. A reference copy of selected files from our demo server config is on Github here.

File Description
ecelerity.conf Top level config file. Notably includes signals-agent.conf (supplied for you, not given here).
msg_gen.conf Declares the open & click tracker scriptlets and engagement_tracking_host
lua/policy.lua Policy that permits relaying for selected “safe domains” only
conf.d/bindings.conf Declares minimal binding_group and bindings for our demo
conf.d/dkim.conf Declares our signing domain
conf.d/ecelerity_mods.conf Declares our port 587 listener, TLS cert/key, auth login, engagement tracking, FBL handling, OOB bounce handling
dkim/  Signing domain keys live here ..
private key has been redacted

Momentum offers Auth login & STARTTLS on injection

Our demo has user/password protected message injection on Port 587, as we did with the PowerMTA demo and SparkPost itself. Following the inbound TLS setup instructions, we have:

A fresh reference CA bundle was fetched from Legacy TLS versions (prior to v1.1) are disabled here for safety. You can prove that by comparing the output you see (from another host) between -tls1 , -tls1_1  and  -tls1_2  from another host using

Momentum out-of-band bounce processing

Firstly, we set up DNS MX records for our Return-Path:  (which will be ). Check using:

The FBL and OOB listener on port 25 is separate to the injection port 587, and defined in ecelerity_mods.conf :

Now we check (from an external host) that this listener is NOT open-relay:

That “relaying denied” message tells us we’re safe. Next, we check it does accept messages destined for the bounce processor. This is not a true bounce message, but is enough to check the routing is correct.

Momentum FBL processing

The file ecelerity_mods.conf contains:

FBLs on a subdomain (in fact any address on a subdomain) is taken care of with a wildcard CNAME record:


This resolves via the return-path MX:

We check basic connectivity with swaks (not actually generating an FBL here, as such):

Putting test traffic through Momentum

Firstly we check with a single message:

Then we use this Traffic Generator to inject periodic message batches through Momentum, which delivers onwards to the Bouncy Sink. The Bouncy Sink accepts, opens, clicks, and in-band-bounces messages, and occasionally generates Out-of-Band bounces and FBLs.

We can check this is working as expected by looking in Momentum logs;  shows lots of deliveries such as

Momentum  shows these are processed, then “blackholed” as expected, i.e. does not try to forward them anywhere.

You can deliberately cause FBLs and OOBs, by sending to specific bouncy sink subdomains (as per this table).

Checking results

Looking in our SparkPost Events Search report, we can see Spam Complaint and Out of Band events showing up:

Showing the reporting facets

Our demo has a set of example subaccounts. Messages are assigned to specific bindings, via injected message headers. The campaign ID is set using the X-MSYS-API  header, for example:

Name Binding
0 (Master account) trusted
1 Alice’s Adventure Travels new
2 Bob’s Brewhouse trusted
3 Charlie’s Creative Advertising medium
4 Diana’s Dog Grooming medium

We see message streams are flowing through these subaccounts on the Summary report:

We see the individual campaign names:

“Sending IP” (aka Binding) can be used as a reporting facet:

We can also use “IP Pool” (aka Binding Group) as a reporting facet:

~ Steve