Table of Contents

Viewing Stored Messages

Time Range

Event Polling

Query Options

Filter Field

Filter Expression

Event Types

Event Structure

Event Samples

Introduction to Events

Mailgun tracks every event that happens to your emails and makes this data available to you through the Events API. Mailgun retains this detailed data for one day for free accounts and up to 30 days for paid accounts based on subscription plan.

The Events endpoint is available at:

GET /<domain>/events

A request should define a time range and can specify a set of filters to apply. In response, a page of events is returned along with URLs that can be used to retrieve the next and previous result pages. To traverse the entire range, you should keep requesting the next page URLs returned along with result pages until an empty result page is reached.

Both the Next and Previous page URLs are always returned, even when retrieving one of them makes no sense. There are two such cases:

  • Previous page URL for the first result page
  • Next page URL for the last result page Requesting these URLs always returns an empty result page.

Viewing Stored Messages

To access the contents of the stored messages, copy the API URL of the message into a browser. The API URL can be found in the expanded log entry under the "storage" section. For the username, enter "api" and provide an API key for the password in order to view the parsed message.

To view the raw MIME, the message's Mailgun storage key will be needed. Run the following python script with the storage key as a parameter. The script will retrieve the message from Mailgun. In the script the message is saved to "message.eml", which can then be opened in Mozilla Thunderbird for analysis.

"""View a message using its Mailgun storage key."""
import os
import sys

import requests

if len(sys.argv) != 2:
  print "Usage: message_key"

api_key = YOUR_API_KEY

# output filename
filename = "message.eml"

# url for retrieval
domain = ""
key = sys.argv[1]
url = ""
url = url % (domain, key)

headers = {"Accept": "message/rfc2822"}

# request to API
r = requests.get(url, auth=("api", api_key), headers=headers)

if r.status_code == 200:
  with open(filename, "w") as message:
  os.system("thunderbird -file %s" % filename)
  print "Oops! Something went wrong: %s" % r.content

Time Range

When making a request, you need to specify a time range, which consists of a starting timestamp. Additionally, you must include either an ending timestamp or indicate a search direction. If you don't provide an ending timestamp, you must specify a search direction.

When you provide a range end timestamp, the relationship between the beginning and end timestamps determines the traversal direction of events—either ascending or descending. For instance, if the end timestamp is less (older) than the beginning timestamp, the result pages are returned from newer to older, and events on these pages are sorted in descending order based on their timestamps.

If the end timestamp is not provided, the direction must be specified, and based on this direction, the behavior of result page traversal behaves differently:

  • If the range is descending, then the end timestamp is determined by the user tariff plan retention period.
  • If the range is ascending, events will continue to be recorded, however, the request time range won't be displayed on the provided pages. After retrieving the latest events and reaching an empty result page, requesting the next page URL later will show events that happened afterward. This process can continue indefinitely.

Although it may appear that real-time event polling can be achieved by continuously traversing the next URLs of an ascending time range without an explicit end timestamp, the process is not as straightforward as it seems. Please refer to the guidelines outlined in the Event Polling section for the correct approach.

If both the end range dates and the direction of the search are specfied, then they should agree with each other, otherwise the request will return an error.

Event Polling

In our system, events are generated by physical hosts and follow different routes to the event storage. Therefore, the order in which they appear in the storage and become retrievable - via the events API - does not always correspond to the order in which they occur. Consequently, this system behavior makes straight forward implementation of event polling miss some events. The page of most recent events returned by the events API may not contain all the events that occurred at that time because some of them could still be on their way to the storage engine. When the events arrive and are eventually indexed, they are inserted into the already retrieved pages which could result in the event being missed if the pages are accessed too early (i.e. before all events for the page are available).

To ensure that all your events are retrieved and accounted for, please implement polling in the following way:

  1. Make a request to the events API specifying an ascending time range that begins sometime in the past (e.g. half an hour ago)
  2. Retrieve a result page
  3. Check the timestamp of the last event on the result page. If it is older than the threshold age (e.g. half an hour), then go to step (4), otherwise proceed to step (6).
  4. The result page is trustworthy, use events from the page as you please.
  5. Make a request using the next page URL retrieved with the result safe, proceed with step (2).
  6. Discard the result page for it is not trustworthy.
  7. Pause for some time (at least 15 seconds)
  8. Repeat the previous request and processed with step (2).

Query Options

URL parameters allow you to manipulate the results of your query.

Parameter Description
begin The beginning of the search time range. It can be specified as a string (see date-format) or linux epoch seconds. Refer to Time Range for details.
end The end of the search time range. It can be specified as a string (see date-format) or linux epoch seconds. Refer to Time Range for details.
ascending Defines the direction of the search time range and must be provided if the range end time is not specified. Can be either yes or no. Refer to Time Range for details.
limit Number of entries to return. (300 max)
is the name of the Filter Field. The value of the parameter should be a valid Filter Expression. Several field filters can be specified in one request. If the same field is mentioned, more than once, then all its filter expressions are combined with AND operator.

Filter Field

Log Records can be filtered by the following fields:

Filter Description
event An event type. For a complete list of all events written to the log see the Event Types table below.
list The email address of a mailing list the message was originally sent to.
attachment A name of an attached file
from An email address mentioned in the from MIME header.
message-id A Mailgun message id returned by the messages API
subject A subject line
to An email address mentioned in the MIME header
size Message size. Mostly intended to be used with a wide ranger filtering expressions (see below)
recipient Specific to stored events, this field tracks all the potential message recipients.
tags User defined tags
severity Temporary or Permanent. Used to filter events based on severity, if exists. (Currently failed events only)

Filter Expression

Possible filtering expressions are listed below:

Expression Description
foo bar Matches field values that contain both term foo and term bar.
foo AND bar Same as above.
foo OR bar Matches field values that contain either term foo or term bar.
"foo bar" Matches field values that literally contain foo bar.
NOT foo Matches field values that do not contain term foo.
>10000 Matches values that greater than 10000. This filter can be applied to numeric fields only.
>10000 <20000 Matches values that are greater than 10000 and less than 20000. This filter can be applied to numeric fields only.

Note that more than one expression can be used as a filter value and parentheses can be used to specify grouping. E.g.: (Hello AND NOT Rachel) OR (Farewell AND Monica).

Event Types

Mailgun tracks all of the events that occur throughout the system. Below are listed the events that you can retrieve using this API.

Event Type Description
accepted Mailgun accepted the request to send/forward the email and the message has been placed in queue.
rejected Mailgun rejected the request to send/forward the email.
delivered Mailgun sent the email and it was accepted by the recipient email server.
failed Mailgun could not deliver the email to the recipient email server. severity=permanent when a message is not delivered. There are several reasons why Mailgun stops attempting to deliver messages and drops them including: hard bounces, messages that reached their retry limit, previously unsubscribed/bounced/complained addresses, or addresses rejected by an ESP. severity=temporary when a message is temporary rejected by an ESP.
opened The email recipient opened the email and enabled image viewing. Open tracking must be enabled in the Mailgun control panel, and the CNAME record must be pointing to
clicked The email recipient clicked on a link in the email. Click tracking must be enabled in the Mailgun control panel, and the CNAME record must be pointing to
unsubscribed The email recipient clicked on the unsubscribe link. Unsubscribe tracking must be enabled in the Mailgun control panel.
complained The email recipient clicked on the spam complaint button within their email client. Feedback loops enable the notification to be received by Mailgun.
stored Mailgun has stored an incoming message

Event Structure

Events are represented as loosely structured JSON documents. The exact event structure depends on the event type. But there are three fields that every event has. They are as follows:

Field Description
event The type of the event. Events of a particular type have an identical structure, though some fields may be optional. For the list of event types please refer to Event Types.
timestamp The time when the event was generated in the system provided as Unix epoch seconds.
id The event ID, unique within a day. It can be used to distinguish events that have already been retrieved when requests with overlapping time ranges are made.

Events can change their structure as new features are added to Mailgun, but we only add new fields and never modify or remove existing ones.

Event Samples

Response Samples of various Event types can be found on the Events API Reference page.