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. You can query the data and traverse through the result pages as explained below.
The Events endpoint is available at:
v3/<domain>/events
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 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, and 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: retrieve.py message_key"
sys.exit(1)
api_key = YOUR_API_KEY
# output filename
filename = "message.eml"
# url for retrieval
domain = "mailgun.com"
key = sys.argv[1]
url = "https://api.mailgun.net/v3/domains/%s/messages/%s"
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:
message.write(r.json()["body-mime"])
os.system("thunderbird -file %s" % filename)
else:
print "Oops! Something went wrong: %s" % r.content
Time Range¶
The request time range should be given by a beginning timestamp and either an end timestamp or a search direction. If an end timestamp is not given, a search direction must be provided.
If the range end timestamp is provided then the relation between the beginning and the end timestamps determines the direction - ascending or descending - in which events are going to be traversed. E.g. if the end timestamp is less (older) than the beginning timestamp, then result pages are returned from newer to older and events on the pages are sorted in the descending order of their timestamps.
If the end timestamp is not provided, the direction must be specified. Depending on the range direction, the 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 then events will continue to be recorded but will not show in the current request time range pages that are provided. So after the most recent events have been retrieved and an empty result page has been reached, then requesting next page URL returned with the last page some time later will return events that occurred since then. And this can go on indefinitely.
Warning
Even though it seems that real-time event polling can be implemented by traversing next URLs of an ascending time range that has no explicit end timestamp, it is not that simple! Please refer to Event Polling for the proper way to do it.
If both the end range date and the direction of the search are specified 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 the following way:
- Make a request to the events API specifying an ascending time range that begins some time in the past (e.g. half an hour ago);
- Retrieve a result page;
- Check the timestamp of the last event on the result page. If it is older than some threshold age (e.g. half an hour) then go to step (4), otherwise proceed with step (6);
- The result page is trustworthy, use events from the page as you please;
- Make a request using the next page URL retrieved with the result page, proceed with step (2);
- Discard the result page for it is not trustworthy;
- Pause for some time (at least 15 seconds);
- Repeat the previous request, and proceed 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) |
<field> | <field> 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 then once, then all its filter expressions are combined with AND operator. |
Filter Field¶
Log records can be filtered by the following fields:
Fields | 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 to MIME header. |
size | Message size. Mostly intended to be used with range filtering expressions (see below). |
recipient | An email address of a particular recipient. While messages are addressable to one or more recipients, each event (with one exception) tracks one recipient. See stored events for use of recipients. |
recipients | Specific to stored events, this field tracks all of 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 then 10000 . This filter can
be applied to numeric fields only. |
>10000 <20000 | Matches values that are greater then 10000 and less then
20000 . This filter can be applied to numeric fields only. |
Note that more then 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 mailgun.org. |
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 mailgun.org. |
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 | Event id. It is guaranteed to be 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.
Below you can find sample events of various types:
Accepted:
{
"event": "accepted",
"id": "jxVuhYlhReaK3QsggHfFRA",
"timestamp": 1529692198.641821,
"log-level": "info",
"method": "smtp",
"envelope": {
"targets": "team@example.org",
"transport": "smtp",
"sender": "sender@example.org"
},
"flags": {
"is-authenticated": false
},
"message": {
"headers": {
"to": "team@example.org",
"message-id": "20180622182958.1.48906CB188F1A454@exmple.org",
"from": "sender@example.org",
"subject": "Test Subject"
},
"attachments": [],
"recipients": [
"team@example.org"
],
"size": 586
},
"storage": {
"url": "https://se.api.mailgun.net/v3/domains/example.org/messages/eyJwI...",
"key": "eyJwI..."
},
"recipient": "team@example.org",
"recipient-domain": "example.org",
"campaigns": [],
"tags": [],
"user-variables": {}
}
Accepted (Routed):
{
"event": "accepted",
"id": "cWgTfzV6QQiXY4PqhlLClw",
"timestamp": 1529692198.719447,
"log-level": "info",
"method": "smtp",
"routes": [
{
"expression": "match_recipient(\"team@example.org\")",
"id": "5b295a4aa4764a000108508c",
"match": {
"recipient": "team@example.org"
}
}
],
"envelope": {
"sender": "sender@example.org",
"transport": "smtp",
"targets": "john@example.com"
},
"flags": {
"is-routed": true,
"is-authenticated": false,
"is-system-test": false,
"is-test-mode": false
},
"message": {
"headers": {
"to": "team@example.org",
"message-id": "20180622182958.1.48906CB188F1A454@exmple.org",
"from": "sender@exmple.org",
"subject": "Test Subject"
},
"attachments": [],
"recipients": [
"team@example.org"
],
"size": 586
},
"storage": {
"url": "https://se.api.mailgun.net/v3/domains/example.org/messages/eyJwI...",
"key": "eyJwI..."
},
"recipient": "john@example.com",
"recipient-domain": "example.com",
"campaigns": [],
"tags": [],
"user-variables": {}
}
Delivered:
{
"event": "delivered",
"id": "hK7mQVt1QtqRiOfQXta4sw",
"timestamp": 1529692199.626182,
"log-level": "info",
"envelope": {
"transport": "smtp",
"sender": "sender@example.org",
"sending-ip": "123.123.123.123",
"targets": "john@example.com"
},
"flags": {
"is-routed": false,
"is-authenticated": false,
"is-system-test": false,
"is-test-mode": false
},
"delivery-status": {
"tls": true,
"mx-host": "aspmx.l.example.com",
"code": 250,
"description": "",
"session-seconds": 0.4367079734802246,
"utf8": true,
"attempt-no": 1,
"message": "OK",
"certificate-verified": true
},
"message": {
"headers": {
"to": "team@example.org",
"message-id": "20180622182958.1.48906CB188F1A454@exmple.org",
"from": "sender@exmple.org",
"subject": "Test Subject"
},
"attachments": [],
"size": 586
},
"storage": {
"url": "https://storage-us-west1.api.mailgun.net/v3/domains/...",
"region": "us-west1",
"key": "AwABB...",
"env": "production"
},
"recipient": "john@example.com",
"recipient-domain": "example.com",
"campaigns": [],
"tags": [],
"user-variables": {}
}
Failed (Permanent):
{
"event": "failed",
"id": "pl271FzxTTmGRW8Uj3dUWw",
"timestamp": 1529701969.818328,
"log-level": "error",
"severity": "permanent",
"reason": "suppress-bounce",
"envelope": {
"sender": "john@example.org",
"transport": "smtp",
"targets": "joan@example.com"
},
"flags": {
"is-routed": false,
"is-authenticated": true,
"is-system-test": false,
"is-test-mode": false
},
"delivery-status": {
"attempt-no": 1,
"message": "",
"code": 605,
"description": "Not delivering to previously bounced address",
"session-seconds": 0.0
},
"message": {
"headers": {
"to": "joan@example.com",
"message-id": "20180622211249.1.2A6098970A380E12@example.org",
"from": "john@example.org",
"subject": "Test Subject"
},
"attachments": [],
"size": 867
},
"storage": {
"url": "https://se.api.mailgun.net/v3/domains/example.org/messages/eyJwI...",
"key": "eyJwI..."
},
"recipient": "slava@mailgun.com",
"recipient-domain": "mailgun.com",
"campaigns": [],
"tags": [],
"user-variables": {}
}
Failed (Permanent, Delayed Bounce):
{
"severity": "permanent",
"timestamp": 1666214138.4046676,
"storage": {
"url": "https://storage-us-east4.api.mailgun.net/v3/domains/...",
"region": "us-east4",
"key": "AwABB...",
"env": "production"
},
"delivery-status": {
"bounce-code": "5.1.1",
"message": "smtp; 550-5.1.1 The email account that you tried to reach does not exist.",
"code": 550,
"description": "I'm sorry to have to inform you that your message could not be delivered to one or more recipients.",
"enhanced-code": "5.1.1"
},
"log-level": "error",
"id": "ZDMqRgjLTDi6rQcy7K60aA",
"reason": "generic",
"user-variables": {},
"flags": {
"is-delayed-bounce": true
},
"envelope": {},
"message": {
"headers": {
"to": "test@test.com",
"message-id": "20221019211537.93011d31ea99c5a8@test.com",
"from": "Test Message <test@test.com>",
"subject": "Testing delayed bounce"
},
"size": 275
},
"recipient": "christest123@test.com",
"event": "failed"
},
Failed (Temporary):
{
"severity": "temporary",
"tags": [],
"timestamp": 1665066523.871657,
"storage": {
"url": "https://storage-us-east4.api.mailgun.net/v3/domains/...",
"region": "us-east4",
"key": "AwABB...",
"env": "production"
},
"delivery-status": {
"tls": true,
"mx-host": "hotmail-com.olc.protection.outlook.com",
"code": 451,
"description": "",
"session-seconds": 0.7517080307006836,
"utf8": true,
"retry-seconds": 600,
"enhanced-code": "4.7.652",
"attempt-no": 1,
"message": "4.7.652 The mail server [xxx.xxx.xxx.xxx] has exceeded the maximum number of connections.",
"certificate-verified": true
},
"batch": {
"id": "633ee6154618b2fed628ccb0"
},
"recipient-domain": "test.com",
"id": "xYrATi63Rke8EC_s7EoJeA",
"campaigns": [],
"reason": "generic",
"user-variables": {},
"flags": {
"is-routed": false,
"is-authenticated": true,
"is-system-test": false,
"is-test-mode": false
},
"log-level": "warn",
"template": {
"name": "test"
},
"envelope": {
"transport": "smtp",
"sender": "test@test.com",
"sending-ip": "xxx.xxx.xxx.xxx",
"targets": "test@test.com"
},
"message": {
"headers": {
"to": "test@test.net",
"message-id": "20221006142837.1618e01bf42da9fa@test.com",
"from": "test@test.com",
"subject": "Test send"
},
"attachments": [],
"size": 3499
},
"recipient": "test@test.com",
"event": "failed"
}
Opened:
{
"event": "opened",
"id": "-laxIqj9QWubsjY_3pTq_g",
"timestamp": 1377047343.042277,
"log-level": "info",
"recipient": "recipient@example.com",
"geolocation": {
"country": "US",
"region": "Texas",
"city": "Austin"
},
"tags": [],
"campaigns": [],
"user-variables": {},
"ip": "111.111.111.111",
"client-info": {
"client-type": "mobile browser",
"client-os": "iOS",
"device-type": "mobile",
"client-name": "Mobile Safari",
"user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 6_1 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Mobile/10B143",
"bot": ""
},
"message": {
"headers": {
"message-id": "20130821005614.19826.35976@samples.mailgun.org"
}
},
}
Clicked:
{
"event": "clicked",
"id": "G5zMz2ysS6OxZ2C8xb2Tqg",
"timestamp": 1377075564.094891,
"log-level": "info",
"recipient": "recipient@example.com",
"geolocation": {
"country": "US",
"region": "TX",
"city": "Austin"
},
"tags": [],
"url": "http://example.com/signup",
"ip": "123.123.123.321",
"campaigns": [],
"user-variables": {},
"client-info": {
"client-type": "browser",
"client-os": "Linux",
"device-type": "desktop",
"client-name": "Chromium",
"user-agent": "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/28.0.1500.71 Chrome/28.0.1500.71 Safari/537.36",
"bot": ""
},
"message": {
"headers": {
"message-id": "20130821085807.30688.67706@samples.mailgun.org"
}
},
}
Unsubscribed:
{
"event": "unsubscribed",
"id": "W3X4JOhFT-OZidZGKKr9iA",
"timestamp": 1377213791.421473,
"log-level": "info",
"recipient": "recipient@example.com",
"geolocation": {
"country": "US",
"region": "TX",
"city": "San Antonio"
},
"campaigns": [],
"tags": [],
"user-variables": {},
"ip": "23.23.23.345",
"client-info": {
"client-type": "browser",
"client-os": "OS X",
"device-type": "desktop",
"client-name": "Chrome",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36"
},
"message": {
"headers": {
"message-id": "20130822232216.13966.79700@samples.mailgun.org"
}
},
}
Complained:
{
"event": "complained",
"id": "ncV2XwymRUKbPek_MIM-Gw",
"timestamp": 1377214260.049634,
"log-level": "warn",
"recipient": "foo@example.com",
"tags": [],
"campaigns": [],
"user-variables": {},
"flags": {
"is-test-mode": false
},
"message": {
"headers": {
"to": "foo@example.com",
"message-id": "20130718032413.263EE2E0926@example.com",
"from": "John Doe <sender@example.com>",
"subject": "This is the subject."
},
"attachments": [],
"size": 18937
},
}
Stored:
{
"event": "stored",
"id": "WRVmVc47QYi4DHth_xpRUA",
"timestamp": 1529692198.691758,
"log-level": "info",
"flags": {
"is-test-mode": false
},
"message": {
"headers": {
"to": "team@example.org",
"message-id": "20180622182958.1.48906CB188F1A454@exmple.org",
"from": "sender@example.org",
"subject": "Test Subject"
},
"attachments": [],
"recipients": [
"team@example.org"
],
"size": 586
},
"storage": {
"url": "https://se.api.mailgun.net/v3/domains/example.org/messages/eyJwI...",
"key": "eyJwI..."
},
"campaigns": [],
"tags": [],
"user-variables": {}
}
Rejected:
{
"event": "rejected"
"id": "OMTXD3-sSmKIQa1gSKkYVA",
"timestamp": 1529704976.104692,
"log-level": "warn",
"flags": {
"is-test-mode": false
},
"reject": {
"reason": "Sandbox subdomains are for test purposes only. Please add your own domain or add the address to authorized recipients in Account Settings.",
"description": ""
},
"message": {
"headers": {
"to": "joan@example.org",
"message-id": "20180622220256.1.B31A451A2E5422BB@sandbox55887fac92de874df5ae0023b75fd62f1d.mailgun.org",
"from": "john@sandbox55887fac92de874df5ae0023b75fd62f1d.mailgun.org",
"subject": "Test Subject"
},
"attachments": [],
"size": 867
},
"campaigns": [],
"tags": [],
"user-variables": {}
}
List Member Uploaded:
List Member Upload Error
List Uploaded:
Examples¶
Fetches the first page of log records that are older than Fri, 3 May 2013 09:00:00 -0000
and correspond to delivery to joe@example.com
:
curl -s --user 'api:YOUR_API_KEY' -G \
https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/events \
--data-urlencode begin='Fri, 3 May 2013 09:00:00 -0000' \
--data-urlencode ascending=yes \
--data-urlencode limit=25 \
--data-urlencode pretty=yes \
--data-urlencode recipient=joe@example.com
import com.mailgun.api.v3.MailgunEventsApi;
import com.mailgun.model.events.EventsQueryOptions;
import com.mailgun.model.events.EventsResponse;
import java.time.ZoneId;
import java.time.ZonedDateTime;
// ...
public EventsResponse getEvents() {
MailgunEventsApi mailgunEventsApi = MailgunClient.config(API_KEY)
.createApi(MailgunEventsApi.class);
EventsQueryOptions eventsQueryOptions = EventsQueryOptions.builder()
.begin(ZonedDateTime.now().minusDays(5))
.ascending(true)
.limit(1)
.build();
return mailgunEventsApi.getEvents(YOUR_DOMAIN_NAME, eventsQueryOptions);
}
# Include the Autoloader (see "Libraries" for install instructions)
require 'vendor/autoload.php';
use Mailgun\Mailgun;
# Instantiate the client.
$mgClient = Mailgun::create('PRIVATE_API_KEY', 'https://API_HOSTNAME');
$domain = 'YOUR_DOMAIN_NAME';
$queryString = array(
'begin' => 'Wed, 1 Jan 2020 09:00:00 -0000',
'ascending' => 'yes',
'limit' => 25,
'pretty' => 'yes',
'recipient' => 'bob@example.com'
);
# Issue the call to the client.
$result = $mgClient->events()->get($domain, $queryString);
def get_logs():
return requests.get(
"https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/events",
auth=("api", "YOUR_API_KEY"),
params={"begin" : "Fri, 3 May 2013 09:00:00 -0000",
"ascending" : "yes",
"limit" : 25,
"pretty" : "yes",
"recipient" : "joe@example.com"})
def get_logs
RestClient.get "https://api:YOUR_API_KEY"\
"@api.mailgun.net/v3/YOUR_DOMAIN_NAME/events",
:params => {
:'begin' => 'Fri, 3 May 2013 09:00:00 -0000',
:'ascending' => 'yes',
:'limit' => 25,
:'pretty' => 'yes',
:'recipient' => 'joe@example.com'
}
end
using System;
using System.IO;
using RestSharp;
using RestSharp.Authenticators;
public class EventsDateTimeRecipientChunk
{
public static void Main (string[] args)
{
Console.WriteLine (EventsDateTimeRecipient ().Content.ToString ());
}
public static IRestResponse EventsDateTimeRecipient ()
{
RestClient client = new RestClient ();
client.BaseUrl = new Uri ("https://api.mailgun.net/v3");
client.Authenticator =
new HttpBasicAuthenticator ("api",
"YOUR_API_KEY");
RestRequest request = new RestRequest ();
request.AddParameter ("domain", "YOUR_DOMAIN_NAME", ParameterType.UrlSegment);
request.Resource = "{domain}/events";
request.AddParameter ("begin", "Fri, 3 May 2013 09:00:00 -0000");
request.AddParameter ("ascending", "yes");
request.AddParameter ("limit", 25);
request.AddParameter ("pretty", "yes");
request.AddParameter ("recipient", "joe@example.com");
return client.Execute (request);
}
}
import (
"context"
"github.com/mailgun/mailgun-go/v3"
"time"
)
func PrintEventLog(domain, apiKey string) error {
mg := mailgun.NewMailgun(domain, apiKey)
// Create an iterator
it := mg.ListEvents(&mailgun.ListEventOptions{
Begin: time.Now().Add(-50 * time.Minute),
Limit: 100,
Filter: map[string]string{
"recipient": "joe@example.com",
},
})
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel()
// Iterate through all the pages of events
var page []mailgun.Event
for it.Next(ctx, &page) {
for _, event := range page {
fmt.Printf("%+v\n", event)
}
}
// Did iteration end because of an error?
if it.Err() != nil {
return it.Err()
}
return nil
}
const DOMAIN = 'YOUR_DOMAIN_NAME';
import formData from 'form-data';
import Mailgun from 'mailgun.js';
const mailgun = new Mailgun(formData);
const client = mailgun.client({ username: 'api', key: 'YOUR_API_KEY' || '' });
(async () => {
try {
const date = new Date(2021, 10, 1, 0, 0, 0, 0); // Mon Nov 01 2021 00:00:00 GMT+0200
const events = await client.events.get(DOMAIN, {
begin: date.toGMTString(), // Sun, 31 Oct 2021 22:00:00 GMT
ascending: 'yes',
limit: 5
});
console.log('events', events)
} catch (error) {
console.error(error);
}
})();
Sample response:
{
"items": [
{
"tags": [],
"id": "czsjqFATSlC3QtAK-C80nw",
"timestamp": 1376325780.160809,
"envelope": {
"sender": "me@samples.mailgun.org",
"transport": ""
},
"event": "accepted",
"campaigns": [],
"user-variables": {},
"flags": {
"is-authenticated": true,
"is-test-mode": false
},
"message": {
"headers": {
"to": "foo@example.com",
"message-id": "20130812164300.28108.52546@samples.mailgun.org",
"from": "Excited User <me@samples.mailgun.org>",
"subject": "Hello"
},
"attachments": [],
"recipients": [
"foo@example.com",
"baz@example.com",
"bar@example.com"
],
"size": 69
},
"recipient": "baz@example.com",
"method": "http"
}
],
"paging": {
"next":
"https://api.mailgun.net/v3/samples.mailgun.org/events/W3siY...",
"previous":
"https://api.mailgun.net/v3/samples.mailgun.org/events/Lkawm..."
}
}
Fetches the first page of log records that contain different types of failure, starting from the most recent:
curl -s --user 'api:YOUR_API_KEY' -G \
https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/events \
--data-urlencode event='rejected OR failed'
import com.mailgun.api.v3.MailgunEventsApi;
import com.mailgun.enums.EventType;
import com.mailgun.model.events.EventsQueryOptions;
import com.mailgun.model.events.EventsResponse;
// ...
public EventsResponse getEvents() {
MailgunEventsApi mailgunEventsApi = MailgunClient.config(API_KEY)
.createApi(MailgunEventsApi.class);
EventsQueryOptions eventsQueryOptions = EventsQueryOptions.builder()
.event(EventType.FAILED)
.build();
return mailgunEventsApi.getEvents(YOUR_DOMAIN_NAME, eventsQueryOptions);
}
# Include the Autoloader (see "Libraries" for install instructions)
require 'vendor/autoload.php';
use Mailgun\Mailgun;
# Instantiate the client.
$mgClient = Mailgun::create('PRIVATE_API_KEY', 'https://API_HOSTNAME');
$domain = 'YOUR_DOMAIN_NAME';
$queryString = array(
'begin' => 'Wed, 1 Jan 2020 09:00:00 -0000',
'ascending' => 'yes',
'limit' => 25,
'pretty' => 'yes',
'event' => 'failed'
);
# Issue the call to the client.
$result = $mgClient->events()->get($domain, $queryString);
def get_logs():
return requests.get(
"https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/events",
auth=("api", "YOUR_API_KEY"),
params={"event" : "rejected OR failed"})
def get_logs
RestClient.get "https://api:YOUR_API_KEY"\
"@api.mailgun.net/v3/YOUR_DOMAIN_NAME/events",
:params => {
:"event" => 'rejected OR failed'
}
end
using System;
using System.IO;
using RestSharp;
using RestSharp.Authenticators;
public class EventsFailureChunk
{
public static void Main (string[] args)
{
Console.WriteLine (EventsFailure ().Content.ToString ());
}
public static IRestResponse EventsFailure ()
{
RestClient client = new RestClient ();
client.BaseUrl = new Uri ("https://api.mailgun.net/v3");
client.Authenticator =
new HttpBasicAuthenticator ("api",
"YOUR_API_KEY");
RestRequest request = new RestRequest ();
request.AddParameter ("domain", "YOUR_DOMAIN_NAME", ParameterType.UrlSegment);
request.Resource = "{domain}/events";
request.AddParameter ("event", "rejected OR failed");
return client.Execute (request);
}
}
import (
"context"
"fmt"
"github.com/mailgun/mailgun-go/v3"
"github.com/mailgun/mailgun-go/v3/events"
"time"
)
func PrintFailedEvents(domain, apiKey string) error {
mg := mailgun.NewMailgun(domain, apiKey)
// Create an iterator
it := mg.ListEvents(&mailgun.ListEventOptions{
Filter: map[string]string{
"event": "rejected OR failed",
},
})
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel()
// Iterate through all the pages of events
var page []mailgun.Event
for it.Next(ctx, &page) {
for _, event := range page {
switch e := event.(type){
case *events.Failed:
fmt.Printf("Failed Reason: %s", e.Reason)
case *events.Rejected:
fmt.Printf("Rejected Reason: %s", e.Reject.Reason)
}
}
}
// Did iteration end because of an error?
if it.Err() != nil {
return it.Err()
}
return nil
}
const DOMAIN = 'YOUR_DOMAIN_NAME';
import formData from 'form-data';
import Mailgun from 'mailgun.js';
const mailgun = new Mailgun(formData);
const client = mailgun.client({ username: 'api', key: 'YOUR_API_KEY' || '' });
(async () => {
try {
const events = await client.events.get(DOMAIN, {
event: 'failed'
});
console.log('events', events)
} catch (error) {
console.error(error);
}
})();
{
"items": [
{
"severity": "temporary",
"tags": [],
"id": "czsjqFATSlC3QtAK-C80nw",
"envelope": {
"sender": "me@samples.mailgun.org",
"transport": ""
},
"delivery-status": {
"code": 498,
"message": "No MX for [example.com]",
"retry-seconds": 900,
"description": "No MX for [example.com]"
},
"campaigns": [],
"reason": "generic",
"user-variables": {},
"flags": {
"is-authenticated": true,
"is-test-mode": false
},
"timestamp": 1376435471.10744,
"message": {
"headers": {
"to": "baz@example.com, bar@example.com",
"message-id": "20130813230036.10303.40433@samples.mailgun.org",
"from": "Excited User <me@samples.mailgun.org>",
"subject": "Hello"
},
"attachments": [],
"recipients": [
"baz@example.com",
"bar@example.com"
],
"size": 370
},
"recipient": "bar@example.com",
"event": "failed"
}
],
"paging": {
"next":
"https://api.mailgun.net/v3/samples.mailgun.org/events/W3siY...",
"previous":
"https://api.mailgun.net/v3/samples.mailgun.org/events/Lkawm..."
}
}
Fetches the first page of log records written so far. Traversal is performed starting from the most recent records to the oldest:
curl -s --user 'api:YOUR_API_KEY' -G \
https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/events
import com.mailgun.api.v3.MailgunEventsApi;
import com.mailgun.model.events.EventsResponse;
// ...
public EventsResponse getEvents() {
MailgunEventsApi mailgunEventsApi = MailgunClient.config(API_KEY)
.createApi(MailgunEventsApi.class);
return mailgunEventsApi.getAllEvents(YOUR_DOMAIN_NAME);
}
# Include the Autoloader (see "Libraries" for install instructions)
require 'vendor/autoload.php';
use Mailgun\Mailgun;
# Instantiate the client.
$mgClient = Mailgun::create('PRIVATE_API_KEY', 'https://API_HOSTNAME');
$domain = 'YOUR_DOMAIN_NAME';
# Issue the call to the client.
$result = $mgClient->events()->($domain);
def get_logs():
return requests.get(
"https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/events",
auth=("api", "YOUR_API_KEY"))
def get_logs
RestClient.get "https://api:YOUR_API_KEY"\
"@api.mailgun.net/v3/YOUR_DOMAIN_NAME/events"}
end
using System;
using System.IO;
using RestSharp;
using RestSharp.Authenticators;
public class EventsTraversalChunk
{
public static void Main (string[] args)
{
Console.WriteLine (EventsTraversal ().Content.ToString ());
}
public static IRestResponse EventsTraversal ()
{
RestClient client = new RestClient ();
client.BaseUrl = new Uri ("https://api.mailgun.net/v3");
client.Authenticator =
new HttpBasicAuthenticator ("api",
"YOUR_API_KEY");
RestRequest request = new RestRequest ();
request.AddParameter ("domain", "YOUR_DOMAIN_NAME", ParameterType.UrlSegment);
request.Resource = "{domain}/events";
return client.Execute (request);
}
}
import (
"context"
"fmt"
"github.com/mailgun/mailgun-go/v3"
"github.com/mailgun/mailgun-go/v3/events"
"time"
)
func PrintEvents(domain, apiKey string) error {
mg := mailgun.NewMailgun(domain, apiKey)
// Create an iterator
it := mg.ListEvents(nil)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel()
// Iterate through all the pages of events
var page []mailgun.Event
for it.Next(ctx, &page) {
for _, event := range page {
switch e := event.(type) {
case *events.Accepted:
fmt.Printf("Accepted ID: %s", e.Message.Headers.MessageID)
case *events.Rejected:
fmt.Printf("Rejected Reason: %s", e.Reject.Reason)
// Add other event types here
}
fmt.Printf("%+v\n", event.GetTimestamp())
}
}
// Did iteration end because of an error?
if it.Err() != nil {
return it.Err()
}
return nil
}
const DOMAIN = 'YOUR_DOMAIN_NAME';
import formData from 'form-data';
import Mailgun from 'mailgun.js';
const mailgun = new Mailgun(formData);
const client = mailgun.client({ username: 'api', key: 'YOUR_API_KEY' || '' });
(async () => {
try {
const events = await client.events.get(DOMAIN);
console.log('events', events);
} catch (error) {
console.error(error);
}
})();
Fetches the next page of log records, assuming that the URL was returned by the previous request:
curl -s --user 'api:YOUR_API_KEY' -G \
https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/events/W3siYSI6IGZhbHNlLC
import com.mailgun.api.v3.MailgunEventsApi;
import com.mailgun.model.events.EventsResponse;
// ...
public EventsResponse getLogsPagination() {
MailgunEventsApi mailgunEventsApi = MailgunClient.config(API_KEY)
.createApi(MailgunEventsApi.class);
return mailgunEventsApi.getEvents(YOUR_DOMAIN_NAME, PAGE_ID);
}
# Include the Autoloader (see "Libraries" for install instructions)
require 'vendor/autoload.php';
use Mailgun\Mailgun;
# Instantiate the client.
$mgClient = Mailgun::create('PRIVATE_API_KEY', 'https://API_HOSTNAME');
$domain = 'YOUR_DOMAIN_NAME';
$queryString = array(
'begin' => 'Wed, 1 Jan 2020 09:00:00 -0000',
'ascending' => 'yes',
'limit' => 25,
'pretty' => 'yes'
);
# Issue the call to the client.
$result = $mgClient->events()->get($domain, $queryString);
# Request the next page.
$nextPage = $mgClient->events()->nextPage($result);
def get_logs():
return requests.get(
"https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/events/W3siYSI6IGZhbHNlLC",
auth=("api", "YOUR_API_KEY"))
def get_logs
RestClient.get "https://api:YOUR_API_KEY"\
"@api.mailgun.net/v3/YOUR_DOMAIN_NAME/events/W3siYSI6IGZhbHNlLC"}
end
using System;
using System.IO;
using RestSharp;
using RestSharp.Authenticators;
public class EventsPaginationChunk
{
public static void Main (string[] args)
{
Console.WriteLine (EventsPagination ().Content.ToString ());
}
public static IRestResponse EventsPagination ()
{
RestClient client = new RestClient ();
client.BaseUrl = new Uri ("https://api.mailgun.net/v3");
client.Authenticator =
new HttpBasicAuthenticator ("api",
"YOUR_API_KEY");
RestRequest request = new RestRequest ();
request.AddParameter ("domain", "YOUR_DOMAIN_NAME", ParameterType.UrlSegment);
request.Resource = "{domain}/events/W3siYSI6IGZhbHNlLC";
return client.Execute (request);
}
}
import (
"context"
"fmt"
"github.com/mailgun/mailgun-go/v3"
"github.com/mailgun/mailgun-go/v3/events"
"time"
)
func PrintEvents(domain, apiKey string) error {
mg := mailgun.NewMailgun(domain, apiKey)
// Create an iterator
it := mg.ListEvents(nil)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel()
// Iterate through all the pages of events
var page []mailgun.Event
for it.Next(ctx, &page) {
for _, event := range page {
switch e := event.(type) {
case *events.Accepted:
fmt.Printf("Accepted ID: %s", e.Message.Headers.MessageID)
case *events.Rejected:
fmt.Printf("Rejected Reason: %s", e.Reject.Reason)
// Add other event types here
}
fmt.Printf("%+v\n", event.GetTimestamp())
}
}
// Did iteration end because of an error?
if it.Err() != nil {
return it.Err()
}
return nil
}
const DOMAIN = 'YOUR_DOMAIN_NAME';
import formData from 'form-data';
import Mailgun from 'mailgun.js';
const mailgun = new Mailgun(formData);
const client = mailgun.client({ username: 'api', key: 'YOUR_API_KEY' || '' });
(async () => {
try {
const events = await client.events.get(DOMAIN, {
page: 'WzMseyJiIjoiMjAyMS0xMi0wNlQwODo1ODowMi43MTIrMDA6MDAiLCJlIjoiMjAyMS0xMS0wNlQwODo1ODowMi43MTMrMDA6MDAifSx7ImIiOiIyMDIxLTExLTI2VDEwOjI4OjI0LjQ2OCswMDowMCIsImUiOiIyMDIxLTExLTA2VDA4OjU4OjAyLjcxMyswMDowMCJ9LCJfZG9jI1Z2X2hFcHlNUWdPbjRvcjF5cVFsd2ciLFsiZiJdLG51bGwsW1siYWNjb3VudC5pZCIsIjU5NDU5N2Y3ZDAzMDNhNGJkMWYzMzg5OCJdLFsiZG9tYWluLm5hbWUiLCIyMDQ4LnplZWZhcm1lci5jb20iXSxbImV2ZW50IiwiZmFpbGVkIl1dLDEwMF0='
});
console.log('events', events);
} catch (error) {
console.error(error);
}
})();