- Pricing & Plans|
- Documentation|
- The Blog|
- Login
User Manual¶
Introduction¶
This document is meant to be an overview of all of the capabilities of Mailgun and how you can best leverage those capabilities. It is organized around the three major features that Mailgun provides:
At the heart of Mailgun is the API. Most of the Mailgun service can be accessed through the RESTful HTTP API without the need to install any libraries.
You can also access many Mailgun features through your Mailgun Control Panel using your browser and logging in at https://mailgun.net/cp.
In addition to the API, Mailgun supports standard email protocols (SMTP, POP3 and IMAP). We have included some instructions on how to use Mailgun with these protocols at the end of the User Manual.
If you are anxious to get started right away, feel free to check out the Quickstart Guide or API Reference. There are also FAQ and Email Best Practices that you can reference.
Finally, always feel free to contact us: support@mailgunhq.com.
Getting Started¶
We’ve tried to make the sign-up and on-boarding process as intuitive as possible. However, there are a few things to mention.
Pricing & Features Overview¶
Pricing
Pricing is a usage-based, monthly subscription with a minimum cost per month. Usage is based on messages sent and received and per GB of storage (if you are using Mailboxes). You can think of the minimum as a credit towards your usage costs. If you go over the minimum amount, just the additional usage cost is charged on top of the minimum.
We have High Volume plans that are more economical as you approach 1 million emails per month. Please contact sales@mailgunhq.com for more details.
Features
Most of Mailgun’s features are exposed for all of our plans. There are a few exceptions:
- The Free Plan does not allow for custom domains / white labeling, it is limited to 200 messages per day (sent and received) and storage for mailboxes is temporary.
- Only the Express, Priority and First-Class Plans have dedicated IP addresses assigned to the domain(s). Shared IP addresses may affect deliverability and timing of delivery to some extent, as you are sharing your traffic with other customers.
Configuring a Domain¶
When you sign up for an account, you will need to pick a domain name. You have the ability to either use your own top level domain or use a subdomain of mailgun.org, like myapp.mailgun.org.
You can also add multiple domain names for your account. Each domain can be thought of as its own mail server. The mail queues for each domain are separate.
The Standard Plan uses a mailgun domain as the DKIM signing domain. This results in some email clients showing a message that the message was sent “via mailgun”. For the Express, Priority and First-Class Plans, the first custom domain you set up will be used as the DKIM signing domain for all of the other domains added to the account. If you need each domain’s DKIM signing domain to match its sending domain, just let us know and we can configure that for you.
DNS Records
If you are using your own domain, there are records that need to be set for sending (TXT and CNAME) and records that need to be set for receiving (MX).
If you do not set your sending records, you will still be able to send messages but the delivery to inbox will be sub-optimal. If you do not set your MX records, you will not be able to receive messages.
You can verify the correctness of your DNS configuration using the control panel: invalid DNS records will be highlighted in red.
Warning
You can not receive email properly using the same domain at two different mail servers, so if you are receiving mail at the domain elsewhere, do not also add Mailgun MX records.
Sending Messages¶
There are two ways to send messages using Mailgun:
- HTTP API
- SMTP
Both methods work great and support the same feature set, so choose one based on your preferences and requirements.
Sending via API¶
When sending via HTTP API, Mailgun offers two options:
- You can send emails in MIME format, but this would require you to use a MIME building library for your programming language.
- You can submit the individual parts of your messages to Mailgun, such as text and html parts, attachments, and so on. This doesn’t require any MIME knowledge on your part.
Note
Mailgun supports maximum messages size of 25MB.
See sending messages section in our API Reference for a full list of message sending options.
Examples: sending messages via HTTP¶
Sending mails using Mailgun API is extremely simple: as simple as performing an HTTP POST request to an API URL.
Sending a plain text message:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/samples.mailgun.org/messages \
-F from='Excited User <me@samples.mailgun.org>' \
-F to=serobnic@mail.ru\
-F to=sergeyo@profista.com \
-F subject='Hello' \
-F text='Testing some Mailgun awesomness!'
public static ClientResponse SendSimpleMessage() {
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org" +
"/messages");
MultivaluedMapImpl formData = new MultivaluedMapImpl();
formData.add("from", "Excited User <me@samples.mailgun.org>");
formData.add("to", "sergeyo@profista.com");
formData.add("to", "serobnic@mail.ru");
formData.add("subject", "Hello");
formData.add("text", "Testing some Mailgun awesomness!");
return webResource.type(MediaType.APPLICATION_FORM_URLENCODED).
post(ClientResponse.class, formData);
}
function send_simple_message() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v2/samples.mailgun.org/messages');
curl_setopt($ch, CURLOPT_POSTFIELDS, array('from' => 'Excited User <me@samples.mailgun.org>',
'to' => 'obukhov.sergey.nickolayevich@yandex.ru',
'subject' => 'Hello',
'text' => 'Testing some Mailgun awesomness!'));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def send_simple_message():
return requests.post(
"https://api.mailgun.net/v2/samples.mailgun.org/messages",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
data={"from": "Excited User <me@samples.mailgun.org>",
"to": ["sergeyo@profista.com", "serobnic@mail.ru"],
"subject": "Hello",
"text": "Testing some Mailgun awesomness!"})
def send_simple_message
RestClient.post "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/messages",
:from => "Excited User <me@samples.mailgun.org>",
:to => "sergeyo@profista.com, serobnic@mail.ru",
:subject => "Hello",
:text => "Testing some Mailgun awesomness!"
end
public static RestResponse SendSimpleMessage() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.AddParameter("domain",
"samples.mailgun.org", ParameterType.UrlSegment);
request.Resource = "{domain}/messages";
request.AddParameter("from", "Excited User <me@samples.mailgun.org>");
request.AddParameter("to", "sergeyo@profista.com");
request.AddParameter("to", "serobnic@mail.ru");
request.AddParameter("subject", "Hello");
request.AddParameter("text", "Testing some Mailgun awesomness!");
request.Method = Method.POST;
return client.Execute(request);
}
Sample response:
{
"message": "Queued. Thank you.",
"id": "<20111114174239.25659.5817@samples.mailgun.org>"
}
Sending a message with HTML and text parts. This example also attaches two files to the message:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/samples.mailgun.org/messages \
-F from='Excited User <me@samples.mailgun.org>' \
-F to='obukhov.sergey.nickolayevich@yandex.ru' \
-F cc='sergeyo@profista.com' \
-F bcc='serobnic@mail.ru' \
-F subject='Hello' \
-F text='Testing some Mailgun awesomness!' \
--form-string html='<html>HTML version of the body</html>' \
-F attachment=@files/cartman.jpg \
-F attachment=@files/cartman.png
public static ClientResponse SendComplexMessage() {
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org/" +
"messages");
FormDataMultiPart form = new FormDataMultiPart();
form.field("from", "Excited User <me@samples.mailgun.org>");
form.field("to", "obukhov.sergey.nickolayevich@yandex.ru");
form.field("bcc", "sergeyo@profista.com");
form.field("cc", "serobnic@mail.ru");
form.field("subject", "Hello");
form.field("text", "Testing some Mailgun awesomness!");
String file_separator = System.getProperty("file.separator");
File txtFile = new File("." + file_separator +
"files" + file_separator + "test.txt");
form.bodyPart(new FileDataBodyPart("attachment",txtFile,
MediaType.TEXT_PLAIN_TYPE));
File jpgFile = new File("." + file_separator +
"files" + file_separator + "test.jpg");
form.bodyPart(new FileDataBodyPart("attachment",jpgFile,
MediaType.APPLICATION_OCTET_STREAM_TYPE));
return webResource.type(MediaType.MULTIPART_FORM_DATA_TYPE).
post(ClientResponse.class, form);
}
function send_complex_message() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v2/samples.mailgun.org/messages');
curl_setopt($ch, CURLOPT_POSTFIELDS, array('from' => 'Excited User <me@samples.mailgun.org>',
'to' => 'obukhov.sergey.nickolayevich@yandex.ru',
'cc' => 'serobnic@mail.ru',
'bcc' => 'sergeyo@profista.com',
'subject' => 'Hello',
'text' => 'Testing some Mailgun awesomness!',
'html' => '<html>HTML version of the body</html>',
'attachment[1]' => '@/path/to/file.txt',
'attachment[2]' => '@/path/to/pic.jpg'));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def send_complex_message():
return requests.post(
"https://api.mailgun.net/v2/samples.mailgun.org/messages",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
files=MultiDict([("attachment", open("files/test.jpg")),
("attachment", open("files/test.txt"))]),
data={"from": "Excited User <me@samples.mailgun.org>",
"to": "obukhov.sergey.nickolayevich@yandex.ru",
"cc": "serobnic@mail.ru",
"bcc": "sergeyo@profista.com",
"subject": "Hello",
"text": "Testing some Mailgun awesomness!",
"html": "<html>HTML version of the body</html>"})
def send_complex_message
data = Multimap.new
data[:from] = "Excited User <me@samples.mailgun.org>"
data[:to] = "obukhov.sergey.nickolayevich@yandex.ru"
data[:cc] = "serobnic@mail.ru"
data[:bcc] = "sergeyo@profista.com"
data[:subject] = "Hello"
data[:text] = "Testing some Mailgun awesomness!"
data[:html] = "<html>HTML version of the body</html>"
data[:attachment] = File.new(File.join("files", "test.jpg"))
data[:attachment] = File.new(File.join("files", "test.txt"))
RestClient.post "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/messages", data
end
public static RestResponse SendComplexMessage() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.AddParameter("domain",
"samples.mailgun.org", ParameterType.UrlSegment);
request.Resource = "{domain}/messages";
request.AddParameter("from", "Excited User <me@samples.mailgun.org>");
request.AddParameter("to", "obukhov.sergey.nickolayevich@yandex.ru");
request.AddParameter("cc", "serobnic@mail.ru");
request.AddParameter("bcc", "sergeyo@profista.com");
request.AddParameter("subject", "Hello");
request.AddParameter("text", "Testing some Mailgun awesomness!");
request.AddParameter("html", "<html>HTML version of the body</html>");
request.AddFile("attachment", Path.Combine("files", "test.jpg"));
request.AddFile("attachment", Path.Combine("files","test.txt"));
request.Method = Method.POST;
return client.Execute(request);
}
Sending a MIME message which you pre-build yourself using a MIME library of your choice:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/samples.mailgun.org/messages.mime \
-F to='Ev <ev@mailgun.net>' \
-F message=@files/message.mime
public static ClientResponse SendMimeMessage() {
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org" +
"/messages.mime");
FormDataMultiPart form = new FormDataMultiPart();
form.field("to", "sergeyo@profista.com");
String file_separator = System.getProperty("file.separator");
File mimeFile = new File("." + file_separator + "files" +
file_separator + "message.mime");
form.bodyPart(new FileDataBodyPart("message", mimeFile,
MediaType.APPLICATION_OCTET_STREAM_TYPE));
return webResource.type(MediaType.MULTIPART_FORM_DATA_TYPE).
post(ClientResponse.class, form);
}
function send_mime_message() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v2/samples.mailgun.org/messages');
curl_setopt($ch, CURLOPT_POSTFIELDS, array('from' => 'Excited User <me@samples.mailgun.org>',
'to' => 'obukhov.sergey.nickolayevich@yandex.ru',
'message' => '@files/message.mime'));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def send_mime_message():
return requests.post(
"https://api.mailgun.net/v2/samples.mailgun.org/messages.mime",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
data={"to": "sergeyo@profista.com"},
files={"message": open("files/message.mime")})
def send_mime_message
RestClient.post "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/messages.mime",
:to => "sergeyo@profista.com",
:message => File.new(File.join("files", "message.mime"))
end
public static RestResponse SendMimeMessage() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.AddParameter("domain",
"samples.mailgun.org", ParameterType.UrlSegment);
request.Resource = "{domain}/messages.mime";
request.AddParameter("to", "sergeyo@profista.com");
request.AddFile("message", Path.Combine("files", "message.mime"));
request.Method = Method.POST;
return client.Execute(request);
}
An example of how to toggle tracking on a per-message basis. Note the o:tracking option. This will disable link rewriting for this message:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/samples.mailgun.org/messages \
-F from='Sender Bob <sbob@samples.mailgun.org>' \
-F to='Sasha Klizhentas <alex@mailgun.net>' \
-F subject='Hello' \
-F text='Testing some Mailgun awesomness!' \
-F o:tracking=False
public static ClientResponse SendMessageNoTracking() {
Client client = new Client();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org" +
"/messages");
MultivaluedMapImpl formData = new MultivaluedMapImpl();
formData.add("from", "Excited User <me@samples.mailgun.org>");
formData.add("to", "sergeyo@profista.com");
formData.add("to", "serobnic@mail.ru");
formData.add("subject", "Hello");
formData.add("text", "Testing some Mailgun awesomness!");
formData.add("o:tracking", false);
return webResource.type(MediaType.APPLICATION_FORM_URLENCODED).
post(ClientResponse.class, formData);
}
function send_message_no_tracking() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v2/samples.mailgun.org/messages');
curl_setopt($ch, CURLOPT_POSTFIELDS, array('from' => 'Excited User <me@samples.mailgun.org>',
'to' => 'obukhov.sergey.nickolayevich@yandex.ru',
'subject' => 'Hello',
'text' => 'Testing some Mailgun awesomness!',
'o:tracking' => false));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def send_message_no_tracking():
return requests.post(
"https://api.mailgun.net/v2/samples.mailgun.org/messages",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
data={"from": "Excited User <me@samples.mailgun.org>",
"to": ["sergeyo@profista.com", "serobnic@mail.ru"],
"subject": "Hello",
"text": "Testing some Mailgun awesomness!",
"o:tracking": False})
def send_message_no_tracking
RestClient.post "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/messages",
:from => "Excited User <me@samples.mailgun.org>",
:to => "sergeyo@profista.com, serobnic@mail.ru",
:subject => "Hello",
:text => "Testing some Mailgun awesomness!",
"o:tracking" => false
end
public static RestResponse SendMessageNoTracking() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.AddParameter("domain",
"samples.mailgun.org", ParameterType.UrlSegment);
request.Resource = "{domain}/messages";
request.AddParameter("from", "Excited User <me@samples.mailgun.org>");
request.AddParameter("to", "sergeyo@profista.com");
request.AddParameter("to", "serobnic@mail.ru");
request.AddParameter("subject", "Hello");
request.AddParameter("text", "Testing some Mailgun awesomness!");
request.AddParameter("o:tracking", false);
request.Method = Method.POST;
return client.Execute(request);
}
An example of how to set message delivery time using the o:deliverytime option:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/samples.mailgun.org/messages \
-F from='Sender Bob <sbob@samples.mailgun.org>' \
-F to='Sasha Klizhentas <alex@mailgun.net>' \
-F subject='Hello' \
-F text='Testing some Mailgun awesomness!' \
-F o:deliverytime='Fri, 14 Oct 2011 23:10:10 -0000'
public static ClientResponse SendScheduledMessage() {
Client client = new Client();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org" +
"/messages");
MultivaluedMapImpl formData = new MultivaluedMapImpl();
formData.add("from", "Excited User <me@samples.mailgun.org>");
formData.add("to", "sergeyo@profista.com");
formData.add("subject", "Hello");
formData.add("text", "Testing some Mailgun awesomness!");
formData.add("o:deliverytime", "Fri, 14 Oct 2011 23:10:10 -0000");
return webResource.type(MediaType.APPLICATION_FORM_URLENCODED).
post(ClientResponse.class, formData);
}
function send_scheduled_message() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v2/samples.mailgun.org/messages');
curl_setopt($ch, CURLOPT_POSTFIELDS, array('from' => 'Excited User <me@samples.mailgun.org>',
'to' => 'obukhov.sergey.nickolayevich@yandex.ru',
'subject' => 'Hello',
'text' => 'Testing some Mailgun awesomness!',
'o:deliverytime' => 'Fri, 25 Oct 2011 23:10:10 -0000'))
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def send_scheduled_message():
return requests.post(
"https://api.mailgun.net/v2/samples.mailgun.org/messages",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
data={"from": "Excited User <me@samples.mailgun.org>",
"to": "sergeyo@profista.com",
"subject": "Hello",
"text": "Testing some Mailgun awesomness!",
"o:deliverytime": "Fri, 25 Oct 2011 23:10:10 -0000"})
def send_scheduled_message
RestClient.post "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/messages",
:from => "Excited User <me@samples.mailgun.org>",
:to => "sergeyo@profista.com",
:subject => "Hello",
:text => "Testing some Mailgun awesomeness!",
"o:deliverytime" => "Fri, 25 Oct 2011 23:10:10 -0000"
end
public static RestResponse SendScheduledMessage() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.AddParameter("domain",
"samples.mailgun.org", ParameterType.UrlSegment);
request.Resource = "{domain}/messages";
request.AddParameter("from", "Excited User <me@samples.mailgun.org>");
request.AddParameter("to", "sergeyo@profista.com");
request.AddParameter("subject", "Hello");
request.AddParameter("text", "Testing some Mailgun awesomness!");
request.AddParameter("o:deliverytime", "Fri, 14 Oct 2011 23:10:10 -0000");
request.Method = Method.POST;
return client.Execute(request);
}
An example of how to tag a message with the o:tag option:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/samples.mailgun.org/messages \
-F from='Sender Bob <sbob@samples.mailgun.org>' \
-F to='Sasha Klizhentas <alex@mailgun.net>' \
-F subject='Hello' \
-F text='Testing some Mailgun awesomness!' \
-F o:tag='September newsletter' \
-F o:tag='newsletters'
public static ClientResponse SendTaggedMessage() {
Client client = new Client();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org" +
"/messages");
MultivaluedMapImpl formData = new MultivaluedMapImpl();
formData.add("from", "Excited User <me@samples.mailgun.org>");
formData.add("to", "sergeyo@profista.com");
formData.add("subject", "Hello");
formData.add("text", "Testing some Mailgun awesomness!");
formData.add("o:tag", "September newsletter");
formData.add("o:tag", "newsletters");
return webResource.type(MediaType.APPLICATION_FORM_URLENCODED).
post(ClientResponse.class, formData);
}
function send_tagged_message() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v2/samples.mailgun.org/messages');
curl_setopt($ch, CURLOPT_POSTFIELDS, array('from' => 'Excited User <me@samples.mailgun.org>',
'to' => 'obukhov.sergey.nickolayevich@yandex.ru',
'subject' => 'Hello',
'text' => 'Testing some Mailgun awesomness!',
'o:tag[1]' => 'September newsletter',
'o:tag[2]' => 'newsletters'));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def send_tagged_message():
return requests.post(
"https://api.mailgun.net/v2/samples.mailgun.org/messages",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
data=MultiDict([("from", "Excited User <me@samples.mailgun.org>"),
("to", "sergeyo@profista.com"),
("subject", "Hello"),
("text", "Testing some Mailgun awesomness!"),
("o:tag", "September newsletter"),
("o:tag", "newsletters")]))
def send_tagged_message
data = Multimap.new
data[:from] = "Excited User <me@samples.mailgun.org>"
data[:to] = "sergeyo@profista.com"
data[:subject] = "Hello"
data[:text] = "Testing some Mailgun awesomness!"
data["o:tag"] = "September newsletter"
data["o:tag"] = "newsletters"
RestClient.post "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/messages", data
end
public static RestResponse SendTaggedMessage() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.AddParameter("domain",
"samples.mailgun.org", ParameterType.UrlSegment);
request.Resource = "{domain}/messages";
request.AddParameter("from", "Excited User <me@samples.mailgun.org>");
request.AddParameter("to", "sergeyo@profista.com");
request.AddParameter("subject", "Hello");
request.AddParameter("text", "Testing some Mailgun awesomness!");
request.AddParameter("o:tag", "September newsletter");
request.AddParameter("o:tag", "newsletters");
request.Method = Method.POST;
return client.Execute(request);
}
Sending Inline Images¶
Mailgun assigns content-id to each image passed via inline API parameter, so it can be referenced in HTML part.
Example of sending inline image. Note how image is referenced in HTML part simply by the filename:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/samples.mailgun.org/messages \
-F from='Excited User <me@samples.mailgun.org>' \
-F to='Sasha Klizhentas <alex@mailgun.net>' \
-F subject='Hello' \
-F text='Testing some Mailgun awesomness!' \
--form-string html='<html>Inline image here: <img src="cid:cartman.jpg"></html>' \
-F inline=@files/cartman.jpg
public static ClientResponse SendInlineImage() {
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org" +
"/messages");
FormDataMultiPart form = new FormDataMultiPart();
form.field("from", "Excited User <me@samples.mailgun.org>");
form.field("to", "serobnic@mail.ru");
form.field("subject", "Hello");
form.field("text", "Testing some Mailgun awesomness!");
form.field("html", "<html>Inline image here: <img src=\"cid:test.jpg\"></html>");
File jpgFile = new File("files/test.jpg");
form.bodyPart(new FileDataBodyPart("inline",jpgFile,
MediaType.APPLICATION_OCTET_STREAM_TYPE));
return webResource.type(MediaType.MULTIPART_FORM_DATA_TYPE).
post(ClientResponse.class, form);
}
function send_inline_image() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v2/samples.mailgun.org/messages');
curl_setopt($ch,
CURLOPT_POSTFIELDS,
array('from' => 'Excited User <me@samples.mailgun.org>',
'to' => 'sergeyo@profista.com',
'subject' => 'Hello',
'text' => 'Testing some Mailgun awesomness!',
'html' => '<html>Inline image: <img src="cid:test.jpg"></html>',
'inline' => '@files/test.jpg'))
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def send_inline_image():
return requests.post(
"https://api.mailgun.net/v2/samples.mailgun.org/messages",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
files=MultiDict([("inline", open("files/test.jpg"))]),
data={"from": "Excited User <me@samples.mailgun.org>",
"to": "sergeyo@profista.com",
"subject": "Hello",
"text": "Testing some Mailgun awesomness!",
"html": '<html>Inline image here: <img src="cid:test.jpg"></html>'})
def send_inline_image
data = Multimap.new
data[:from] = "Excited User <me@samples.mailgun.org>"
data[:to] = "sergeyo@profista.com"
data[:subject] = "Hello"
data[:text] = "Testing some Mailgun awesomness!"
data[:html] = '<html>Inline image here: <img src="cid:test.jpg"></html>'
data[:inline] = File.new(File.join("files", "test.jpg"))
RestClient.post "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/messages", data
end
public static RestResponse SendInlineImage() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.AddParameter("domain",
"samples.mailgun.org", ParameterType.UrlSegment);
request.Resource = "{domain}/messages";
request.AddParameter("from", "Excited User <me@samples.mailgun.org>");
request.AddParameter("to", "serobnic@mail.ru");
request.AddParameter("subject", "Hello");
request.AddParameter("text", "Testing some Mailgun awesomness!");
request.AddParameter("html", "<html>Inline image here: <img src=\"cid:test.jpg\"></html>");
request.AddFile("inline", "files/test.jpg");
request.Method = Method.POST;
return client.Execute(request);
}
Sending via SMTP¶
Mailgun supports sending via SMTP. Our servers listen on ports 25, 465 (SSL/TLS) and 587 (STARTTLS).
Note
Some ISPs are blocking or throttling SMTP port 25. We recommend using #587 instead.
Note
If you need to configure your firewall, our SMTP/API IPs are: 50.22.251.69 and 174.37.214.195
Use “plain text” SMTP authentication and the credentials from the domain details page in your Control Panel which can be found by clicking on a domain in the Domains Tab. Mailbox credentials will work as well. For enhanced security, use TLS encryption.
Note
See SMTP, POP3 and IMAP to learn how to configure the most popular SMTP software and email clients to work with Mailgun
Passing Sending Options
When sending a message via SMTP you can pass additional sending options via custom MIME headers listed in the table below.
| Header | Description |
|---|---|
| X-Mailgun-Tag | Tag string used for aggregating stats. See Tagging for more information. You can mark a message with several categories by setting multiple X-Mailgun-Tag headers. |
| X-Mailgun-Campaign-Id | Id of the campaign the message belongs to. See Campaign Analytics for details. You can assign a message to several campaigns by setting multiple different X-Mailgun-Campaign-Id headers. |
| X-Mailgun-Dkim | Enables/disables DKIM signatures on per-message basis. Use yes or no. |
| X-Mailgun-Deliver-By | Desired time of delivery. See Scheduling Delivery and Date Format. |
| X-Mailgun-Drop-Message | Enables sending in test mode. Pass yes if needed. See Sending in Test Mode. |
| X-Mailgun-Track | Toggles tracking on a per-message basis, see Tracking Messages for details. Pass yes or no. |
| X-Mailgun-Track-Clicks | Toggles clicks tracking on a per-message basis. Has higher priority than domain-level setting. Pass yes, no or htmlonly. |
| X-Mailgun-Track-Opens | Toggles opens tracking on a per-message basis. Has higher priority than domain-level setting. Pass yes or no. |
| X-Mailgun-Variables | Use this header to attach a custom JSON data to the message. See Attaching Data to Messages for more information. |
Message Queue¶
When you submit messages for delivery Mailgun places them in a message queue.
- You can submit a large amount of messages and Mailgun will automatically queue the delivery in compliance with the receiving domains’ guidelines and maximum allowed sending rate optimized for each ESP (email service provider) such as Yahoo, GMail, etc.
- The Queue is dynamic so as you send more messages, your sending rates will increase, assuming you are sending quality traffic. (See Email Best Practices about warming up IP addresses.) Do not get discouraged if your messages take longer to be delivered at the beginning. As your reputation grows, your sending rate will grow too.
The queueing algorithms are one of the most important features of Mailgun. If you try to send bulk mailings all at once, most ISPs will block you, or worse, just drop your messages without telling you. In addition, it is important to gradually increase your sending rates according to many factors, including consistency of traffic, IP address sending history, and domain reputation.
Batch Sending¶
Mailgun supports the ability send to a group of recipients through a single API call or SMTP session. This is achieved by either:
- Using Batch Sending by specifying multiple recipient email addresses as to parameters and using Recipient Variables.
- Using Mailing Lists with Template Variables.
Warning
It is important when using Batch Sending to also use Recipient Variables. This tells Mailgun to send each recipient an individual email with only their email in the to field. If they are not used, all recipients’ email addresses will show up in the to field for each recipient.
Recipient Variables
Recipient Variables are custom variables that you define, which you can then reference in the message body. They give you the ability to send a custom message to each recipient while still using a single API Call (or SMTP session).
Recipient Variables allow you to:
- Submit a message template;
- Include multiple recipients; and
- Include a set of key:value pairs with unique data for each recipient.
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/samples.mailgun.org/messages \
-F from='Excited User <me@samples.mailgun.org>' \
-F to=alex@mailgun.net \
-F to=ev@mailgun.net \
-F recipient-variables='{"ev@mailgun.net": {"first":"Ev", "id":1}, "alex@mailgun.net": {"first":"Sasha", "id": 2}}' \
-F subject='Hey, %recipient.first%' \
-F text='If you wish to unsubscribe, click http://mailgun/unsubscribe/%recipient.id%'
public static ClientResponse SendTemplateMessage() {
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org" +
"/messages");
MultivaluedMapImpl formData = new MultivaluedMapImpl();
formData.add("from", "Excited User <me@samples.mailgun.org>");
formData.add("to", "alex@mailgun.net");
formData.add("to", "ev@mailgun.net");
formData.add("subject", "Hey, %recipient.first%");
formData.add("text", "If you wish to unsubscribe, click http://mailgun/unsubscribe/%recipient.id%");
formData.add("recipient-variables", "{\"ev@mailgun.net\": {\"first\":\"Ev\", \"id\":1}, \"alex@mailgun.net\": {\"first\":\"Sasha\", \"id\": 2}}");
return webResource.type(MediaType.APPLICATION_FORM_URLENCODED).
post(ClientResponse.class, formData);
}
function send_template_message() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v2/samples.mailgun.org/messages');
curl_setopt($ch,
CURLOPT_POSTFIELDS,
array('from' => 'Excited User <me@samples.mailgun.org>',
'to' => 'obukhov.sergey.nickolayevich@yandex.ru',
'subject' => 'Hello',
'text' => 'If you wish to unsubscribe, click http://mailgun/unsubscribe/%recipient.id%',
'recipient-variables' => '{"ev@mailgun.net": {"first":"Ev", "id":1}, "alex@mailgun.net": {"first":"Sasha", "id": 2}}'));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def send_template_message():
return requests.post(
"https://api.mailgun.net/v2/samples.mailgun.org/messages",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
data={"from": "Excited User <me@samples.mailgun.org>",
"to": ["alex@mailgun.net, ev@mailgun.net"],
"subject": "Hey, %recipient.first%",
"text": "If you wish to unsubscribe, click http://mailgun/unsubscribe/%recipient.id%'",
"recipient-variables": ('{"ev@mailgun.net": {"first":"Ev", "id":1}, '
'"alex@mailgun.net": {"first":"Sasha", "id": 2}}')})
def send_template_message
RestClient.post "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/messages",
:from => "Excited User <me@samples.mailgun.org>",
:to => "alex@mailgun.net, ev@mailgun.net",
:subject => "Hey, %recipient.first%",
:text => "If you wish to unsubscribe, click http://mailgun/unsubscribe/%recipient.id%'",
:'recipient-variables' => '{"ev@mailgun.net": {"first":"Ev", "id":1}, "alex@mailgun.net": {"first":"Sasha", "id": 2}}'
end
public static RestResponse SendTemplateMessage() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.AddParameter("domain",
"samples.mailgun.org", ParameterType.UrlSegment);
request.Resource = "{domain}/messages";
request.AddParameter("from", "Excited User <me@samples.mailgun.org>");
request.AddParameter("to", "alex@mailgun.net");
request.AddParameter("to", "ev@mailgun.net");
request.AddParameter("subject", "Hey, %recipient.first%");
request.AddParameter("text", "If you wish to unsubscribe, click http://mailgun/unsubscribe/%recipient.id%'");
request.AddParameter("recipient-variables", "{\"ev@mailgun.net\": {\"first\":\"Ev\", \"id\":1}, \"alex@mailgun.net\": {\"first\":\"Sasha\", \"id\": 2}}");
request.Method = Method.POST;
return client.Execute(request);
}
Note
The maximum number of recipients allowed for Batch Sending is 1,000.
Note
Recipient variables should be set as a valid JSON-encoded dictionary, where key is a plain recipient address and value is a dictionary with variables.
In the example above, Ev and Sasha both will get personalized subject lines “Hey, Sasha” and “Hey, Ev” and unique unsubscribe links.
When sent via SMTP, recipient variables can be also supplied through a special construct, called a variables container.
To contain variables you create the following MIME construct:
multipart/mailgun-variables
--application/json (base64 encoded)
--message/rfc822
----original-message
In this construct, JSON will be Base64 encoded and will be stored inside the part body, which will handle recipient variables containing special characters.
Example:
Content-Type: multipart/mailgun-variables; boundary="8686cc907910484e9d21c54776cd791c"
Mime-Version: 1.0
From: alex@alex-mg
Date: Thu, 26 Jul 2012 15:43:07 +0000
Message-Id: <20120726154307.29852.44460@definebox.com>
Sender: alex=alex-mg@definebox.com
--8686cc907910484e9d21c54776cd791c
Mime-Version: 1.0
Content-Type: application/json
Content-Transfer-Encoding: base64
eyJkZXNjcmlwdGlvbiI6ICJrbGl6aGVudGFzIn0=
--8686cc907910484e9d21c54776cd791c
Content-Type: message/rfc822
Mime-Version: 1.0
Date: Thu, 26 Jul 2012 19:42:55 +0400
To: %recipient.description% <support@mailgunhq.com>
From: alex@alex-mg
Subject: (rackspace) Hello
MSK 2012 support@mailgunhq.com %recipient.description%
Message-Id: <20120726154302.29322.40670@definebox.com>
support@mailgunhq.com %recipient.description%
--8686cc907910484e9d21c54776cd791c--
Mailing Lists¶
Mailing Lists provide a convenient way to send to multiple recipients by using an alias email address. Mailgun sends a copy of the message sent to the alias address to each subscribed member of the Mailing List. You can create and maintain your subscriber lists using the API or Control Panel. In addition, you can use Template Variables to create a unique message for each member of the Mailing List.
Overview
To use Mailing Lists you create a Mailing List address, like devs@mailgun.net and add member addresses to it. Each time you send a message to devs@mailgun.net, a copy of it is delivered to each subscribed member.
Managing a list
You can create Mailing Lists using the Mailing List tab in the Control Panel or through the API. We support a couple of formats to make your life easier: you can upload a CSV file with members, use JSON or use form-like file upload.
Creating a mailing list through the API:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/lists \
-F address='dev@samples.mailgun.org' \
-F description='Mailgun developers list'
public static ClientResponse CreateMailingList() {
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/lists");
MultivaluedMapImpl formData = new MultivaluedMapImpl();
formData.add("address", "dev@samples.mailgun.org");
formData.add("description", "Mailgun developers list");
return webResource.type(MediaType.APPLICATION_FORM_URLENCODED).
post(ClientResponse.class, formData);
}
function create_mailing_list() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v2/lists');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POSTFIELDS, array('address' => 'dev@samples.mailgun.org',
'description' => 'Mailgun developers list'));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def create_mailing_list():
return requests.post(
"https://api.mailgun.net/v2/lists",
auth=('api', 'key-3ax6xnjp29jd6fds4gc373sgvjxteol0'),
data={'address': 'dev@samples.mailgun.org',
'description': "Mailgun developers list"})
def create_mailing_list
RestClient.post("https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0" \
"@api.mailgun.net/v2/lists",
:address => 'dev@samples.mailgun.org',
:description => "Mailgun developers list")
end
public static RestResponse CreateMailingList() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.Resource = "lists";
request.AddParameter("address", "dev@samples.mailgun.org");
request.AddParameter("description", "Mailgun developers list");
request.Method = Method.POST;
return client.Execute(request);
}
Adding a member through the API:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/lists/dev@samples.mailgun.org/members \
-F subscribed=True \
-F address='bob@gmail.com' \
-F name='Bob Bar' \
-F description='Developer' \
-F vars='{"age": 26}'
public static ClientResponse AddListMember() {
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/lists/" +
"dev@samples.mailgun.org/members");
MultivaluedMapImpl formData = new MultivaluedMapImpl();
formData.add("address", "bob@gmail.com");
formData.add("subscribed", true);
formData.add("name", "Bob Bar");
formData.add("description", "Developer");
formData.add("vars", "{\"age\": 26}");
return webResource.type(MediaType.APPLICATION_FORM_URLENCODED).
post(ClientResponse.class, formData);
}
function add_list_member() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt(
$ch, CURLOPT_URL, 'https://api.mailgun.net/v2/lists/dev@samples.mailgun.org/members');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POSTFIELDS, array('address' => 'bob@gmail.com',
'name' => 'Bob Bar',
'description' => 'Developer',
'subscribed' => true,
'vars' => '{"age": 26}'));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def add_list_member():
return requests.post(
"https://api.mailgun.net/v2/lists/dev@samples.mailgun.org/members",
auth=('api', 'key-3ax6xnjp29jd6fds4gc373sgvjxteol0'),
data={'subscribed': True,
'address': 'bob@gmail.com',
'name': 'Bob Bar',
'description': 'Developer',
'vars': '{"age": 26}'})
def add_list_member
RestClient.post("https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0" \
"@api.mailgun.net/v2/lists/dev@samples.mailgun.org/members",
:subscribed => true,
:address => 'bob@gmail.com',
:name => 'Bob Bar',
:description => 'Developer',
:vars => '{"age": 26}')
end
public static RestResponse AddListMember() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.Resource = "lists/{list}/members";
request.AddParameter("list", "dev@samples.mailgun.org", ParameterType.UrlSegment);
request.AddParameter("address", "bob@gmail.com");
request.AddParameter("subscribed", true);
request.AddParameter("name", "Bob Bar");
request.AddParameter("description", "Developer");
request.AddParameter("vars", "{\"age\": 26}");
request.Method = Method.POST;
return client.Execute(request);
}
Note
You can attach a JSON dictionary with the structured data to each member of the mailing list and reference that data in the message body using Template Variables (see vars parameter in the example above).
Note
There are two modes available when adding a new member: strict and upsert. Strict will raise an error in case if the member already exists, upsert will update an existing member if it’s here or insert a new one. Learn how to toggle between the the modes and skip malformed addresses in the Mailing Lists API section.
Sending to a list
You can set the access level of Mailing Lists to:
- Only allow the administrator to post to the list (limited to an API call or authenticated SMTP session);
- Allow Mailing List members to post to the list; or
- Allow anybody to post to the list.
Campaigns
Mailing lists are integrated with Campaign Analytics. Each message sent to a lists with a Campaign ID will be tracked and reported. In this case, the mailing list will have detailed analytics for all recipients that can be retrieved via API or seen in the Campaign tab of the Control Panel.
Template Variables
There are some pre-defined variables you can use to personalize your message to each recipient. When adding members to a Mailing List you can also define your own variables in addition to these pre-defined variables by using the vars parameter.
| Variable | Description |
|---|---|
| %recipient% | Full recipient spec, like “Alex Lee <alee@mailgun.net>” (for using as value for “To” MIME header). |
| %recipient_fname% | Recipient’s first name. |
| %recipient_lname% | Recipient’s last name. |
| %unsubscribe_url% | A generated URL which allows users to unsubscribe from messages. |
| %mailing_list_unsubscribe_url% | A generated URL which allows users to unsubscribe from mailing lists. |
| %unsubscribe_email% | An email address which can be used for automatic unsubscription by adding it to List-Unsubscribe MIME header. |
Unsubscribing
For managing unsubscribes in Mailing Lists, you can use %mailing_list_unsubscribe_url%. We will generate the unique link to unsubscribe from the mailing list. Once a recipient clicks on the unsubscribe link, we mark the recipient as “unsubscribed” from this list and they won’t get any further emails addressed to this list. Note, that you can still override the “unsubscribe” setting via the API or the Control Panel (in case of user error or accidental unsubscribe, for example). You can also manually unsubscribe the customer without using any links via the API or in the Control Panel. Read more in the Mailing Lists API section.
Mailing Lists, Routes and Mailboxes
Mailing Lists work independently from Routes and Mailboxes. If there is a Mailing List, Route or a Mailbox with the same address, incoming message will hit the Route, Mailing List and the Mailbox simultaneously. This can be pretty convenient for processing replies to the Mailing List and integrating into things like forums or commenting systems.
Scheduling Delivery¶
Mailgun also allows you to request a specific time for your message delivery by using the o:deliverytime parameter if sending via the API, or X-Mailgun-Deliver-By MIME header if sending via SMTP.
While messages are not guaranteed to arrive at exactly the requested time due to the dynamic nature of the queue, Mailgun will do it’s best.
Scheduling Delivery API Example
Supply RFC 2822 or Unix epoch time to schedule your message:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/samples.mailgun.org/messages \
-F from='Sender Bob <sbob@samples.mailgun.org>' \
-F to='Sasha Klizhentas <alex@mailgun.net>' \
-F subject='Hello' \
-F text='Testing some Mailgun awesomness!' \
-F o:deliverytime='Fri, 14 Oct 2011 23:10:10 -0000'
public static ClientResponse SendScheduledMessage() {
Client client = new Client();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org" +
"/messages");
MultivaluedMapImpl formData = new MultivaluedMapImpl();
formData.add("from", "Excited User <me@samples.mailgun.org>");
formData.add("to", "sergeyo@profista.com");
formData.add("subject", "Hello");
formData.add("text", "Testing some Mailgun awesomness!");
formData.add("o:deliverytime", "Fri, 14 Oct 2011 23:10:10 -0000");
return webResource.type(MediaType.APPLICATION_FORM_URLENCODED).
post(ClientResponse.class, formData);
}
function send_scheduled_message() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v2/samples.mailgun.org/messages');
curl_setopt($ch, CURLOPT_POSTFIELDS, array('from' => 'Excited User <me@samples.mailgun.org>',
'to' => 'obukhov.sergey.nickolayevich@yandex.ru',
'subject' => 'Hello',
'text' => 'Testing some Mailgun awesomness!',
'o:deliverytime' => 'Fri, 25 Oct 2011 23:10:10 -0000'))
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def send_scheduled_message():
return requests.post(
"https://api.mailgun.net/v2/samples.mailgun.org/messages",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
data={"from": "Excited User <me@samples.mailgun.org>",
"to": "sergeyo@profista.com",
"subject": "Hello",
"text": "Testing some Mailgun awesomness!",
"o:deliverytime": "Fri, 25 Oct 2011 23:10:10 -0000"})
def send_scheduled_message
RestClient.post "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/messages",
:from => "Excited User <me@samples.mailgun.org>",
:to => "sergeyo@profista.com",
:subject => "Hello",
:text => "Testing some Mailgun awesomeness!",
"o:deliverytime" => "Fri, 25 Oct 2011 23:10:10 -0000"
end
public static RestResponse SendScheduledMessage() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.AddParameter("domain",
"samples.mailgun.org", ParameterType.UrlSegment);
request.Resource = "{domain}/messages";
request.AddParameter("from", "Excited User <me@samples.mailgun.org>");
request.AddParameter("to", "sergeyo@profista.com");
request.AddParameter("subject", "Hello");
request.AddParameter("text", "Testing some Mailgun awesomness!");
request.AddParameter("o:deliverytime", "Fri, 14 Oct 2011 23:10:10 -0000");
request.Method = Method.POST;
return client.Execute(request);
}
Sending in Test Mode¶
You can send messages in test mode by setting o:testmode parameter to true. When you do this, Mailgun will accept the message but will not send it. This is useful for testing purposes. You are not charged for messages sent in test mode.
Tracking Messages¶
Once you start sending and receiving messages, it’s important to track what’s happening with them. We try to make tracking your messages as easy as possible.
Mailgun allows you to:
- Access real-time message logs through the Control Panel or the API to see every message that you have sent or received and what has happened with it.
- Access data on Events through the Control Panel or the API.
- Receive notifications of events through a webhook each time an Open, Link Click, Bounce, Unsubscribe or Spam Complaint occurs.
- Create Campaigns and access detailed analytics on those Campaigns through the Control Panel or the API.
There are three methods for accessing tracking data on your messages:
- Control Panel: There are summary tracking stats, tables with event details and campaign reports.
- HTTP API: You can programmatically access event data and tracking stats.
- Events: Mailgun can perform an HTTP request into your application when an event occurs.
Events/Webhooks¶
When you submit a message to Mailgun to delivery, a number of events may happen to it. Mailgun keeps track of eight different types of such events:
| Event | Notes | Webhook Available? |
|---|---|---|
| Message Sent | Always enabled | No |
| Deliveries | Always enabled | Yes |
| Bounces | Always enabled | Yes |
| Drops | Always enabled | Yes |
| Complaints | Always enabled | Yes |
| Unsubscribes | Available if enabled in control panel | Yes |
| Opens | Available only if enabled in the control panel or via X-Mailgun-Track header | Yes |
| Link Clicks | Available only if enabled in the control panel or via X-Mailgun-Track header | Yes |
The events are logged and you can query them using our HTTP API. Additionally, Mailgun can make an HTTP POST request into your app (webhook) to notify you when an event happens.
If you would like Mailgun to POST event notifications (webhooks), you need to provide a callback URL in the respective tab of the Control Panel. Webhooks are at the domain level so you can provide a unique URL for each domain by using the domain drop down selector. Mailgun will attempt to perform an HTTP POST to the URL when events occur. We recommend using http://postbin.ryanbigg.com for creating temporary URLs to test and debug your webhooks.
For Webhook POSTs, Mailgun listens for the following codes from your server and reacts accordingly:
- If Mailgun receives a 200 (Success) code it will determine the webhook POST is successful and not retry.
- If Mailgun receives a 406 (Not Acceptable) code, Mailgun will determine the POST is rejected and not retry.
- For any other code, Mailgun will retry POSTing according to the schedule below for Webhooks other than the delivery notification.
If your application is unable to process the webhook request but you do not return a 406 error code, Mailgun will retry (other than for delivery notification) during 8 hours at the following intervals before stop trying: 10 minutes, 10 minutes, 15 minutes, 30 minutes, 1 hour, 2 hour and 4 hours.
Securing Webhooks
To ensure the authenticity of event requests, Mailgun signs them and posts the signature along with other webhook parameters:
| Parameter | Type | Description |
|---|---|---|
| timestamp | int | Number of seconds passed since January 1, 1970. |
| token | string | Randomly generated string with length 50. |
| signature | string | String with hexadecimal digits generate by HMAC algorithm. |
To verify the webhook is originating from Mailgun you need to:
- Concatenate timestamp and token values.
- Encode the resulting string with the HMAC algorithm (using your API Key as a key and SHA256 digest mode).
- Compare the resulting hexdigest to the signature.
- Optionally, you can check if the timestamp is not too far from the current time.
Note
Due to potentially large size of posted data, Mailgun computes an authentication signature based on a limited set of HTTP headers.
Below is a Python code sample used to verify the signature:
import hashlib, hmac
def verify(api_key, token, timestamp, signature):
return signature == hmac.new(
key=api_key,
msg='{}{}'.format(timestamp, token),
digestmod=hashlib.sha256).hexdigest()
And here’s a sample in Ruby:
require 'openssl'
def verify(api_key, token, timestamp, signature):
return signature == OpenSSL::HMAC.hexdigest(
OpenSSL::Digest::Digest.new('sha256'),
api_key,
'%s%s' % [timestamp, token])
end
Enable Tracking
Tracking for Messages Sent, Bounces and Complaints are always enabled.
You can enable Unsubscribes tracking in the “Unsubscribes” tab of the Control Panel. You can also manage unsubscribes per message by using the %unsubscribe_url% variable (see Tracking Unsubscribes)
You can enable Opens & Clicks tracking on two levels: per sending domain and per message.
- You can enable Open & Click tracking on per domain basis in the “Tracking” tab of the control panel.
- Tracking can also be toggled by setting o:tracking option when sending your message. This will override the domain-level setting.
Attaching Data to Messages¶
When sending, you can add custom X-Mailgun-Variables header to your message with a JSON string and it will be posted into your webhook handler. Several such headers may be included and their values will be combined.
Example:
X-Mailgun-Variables: {"first_name": "John", "last_name": "Smith"}
X-Mailgun-Variables: {"my_message_id": 123}
Note
Value of X-Mailgun-Variables header should be valid JSON string, otherwise Mailgun won’t be able to parse it.
Tagging¶
Sometimes it’s helpful to categorize your outgoing email traffic based on some criteria, perhaps separate signup emails from password recovery emails or from user comments. Mailgun lets you tag each outgoing message with a custom value. When you access stats on you messages, they will be aggregated by these tags.
Tagging Code Samples
Supply one or more o:tag parameters to tag the message.
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/samples.mailgun.org/messages \
-F from='Sender Bob <sbob@samples.mailgun.org>' \
-F to='Sasha Klizhentas <alex@mailgun.net>' \
-F subject='Hello' \
-F text='Testing some Mailgun awesomness!' \
-F o:tag='September newsletter' \
-F o:tag='newsletters'
public static ClientResponse SendTaggedMessage() {
Client client = new Client();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org" +
"/messages");
MultivaluedMapImpl formData = new MultivaluedMapImpl();
formData.add("from", "Excited User <me@samples.mailgun.org>");
formData.add("to", "sergeyo@profista.com");
formData.add("subject", "Hello");
formData.add("text", "Testing some Mailgun awesomness!");
formData.add("o:tag", "September newsletter");
formData.add("o:tag", "newsletters");
return webResource.type(MediaType.APPLICATION_FORM_URLENCODED).
post(ClientResponse.class, formData);
}
function send_tagged_message() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v2/samples.mailgun.org/messages');
curl_setopt($ch, CURLOPT_POSTFIELDS, array('from' => 'Excited User <me@samples.mailgun.org>',
'to' => 'obukhov.sergey.nickolayevich@yandex.ru',
'subject' => 'Hello',
'text' => 'Testing some Mailgun awesomness!',
'o:tag[1]' => 'September newsletter',
'o:tag[2]' => 'newsletters'));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def send_tagged_message():
return requests.post(
"https://api.mailgun.net/v2/samples.mailgun.org/messages",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
data=MultiDict([("from", "Excited User <me@samples.mailgun.org>"),
("to", "sergeyo@profista.com"),
("subject", "Hello"),
("text", "Testing some Mailgun awesomness!"),
("o:tag", "September newsletter"),
("o:tag", "newsletters")]))
def send_tagged_message
data = Multimap.new
data[:from] = "Excited User <me@samples.mailgun.org>"
data[:to] = "sergeyo@profista.com"
data[:subject] = "Hello"
data[:text] = "Testing some Mailgun awesomness!"
data["o:tag"] = "September newsletter"
data["o:tag"] = "newsletters"
RestClient.post "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/messages", data
end
public static RestResponse SendTaggedMessage() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.AddParameter("domain",
"samples.mailgun.org", ParameterType.UrlSegment);
request.Resource = "{domain}/messages";
request.AddParameter("from", "Excited User <me@samples.mailgun.org>");
request.AddParameter("to", "sergeyo@profista.com");
request.AddParameter("subject", "Hello");
request.AddParameter("text", "Testing some Mailgun awesomness!");
request.AddParameter("o:tag", "September newsletter");
request.AddParameter("o:tag", "newsletters");
request.Method = Method.POST;
return client.Execute(request);
}
Note
A single message may be marked with up to 3 tags. Maximum tag name length is 128 characters.
Logs¶
Logs are real time records of what is happening with your messages. They are stored temporarily and are used for debugging any issues with your messages. You can see whether the message is sent or received or if there are bounces or errors.
However, you can programmatically download Mailgun logs via an API and keep them on your servers for as long as you need.
An example of how to fetch a single 51st log message skipping the first 50, using the API:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 -G \
https://api.mailgun.net/v2/samples.mailgun.org/log \
-d skip=50 \
-d limit=1
public static ClientResponse GetLogs() {
Client client = new Client();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org/log");
MultivaluedMapImpl queryParams = new MultivaluedMapImpl();
queryParams.add("skip", 50);
queryParams.add("limit", 1);
return webResource.queryParams(queryParams).get(ClientResponse.class);
}
function get_logs() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v2/samples.mailgun.org/log');
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def get_logs():
return requests.get(
"https://api.mailgun.net/v2/samples.mailgun.org/log",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
params={"skip": 50,
"limit": 1})
def get_logs
RestClient.get "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/log", :params => {
:skip => 50,
:limit => 1
}
end
public static RestResponse GetLogs() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.AddParameter("domain",
"samples.mailgun.org", ParameterType.UrlSegment);
request.Resource = "{domain}/log";
request.AddParameter("skip", 50);
request.AddParameter("limit", 1);
return client.Execute(request);
}
Sample response:
{
"total_count": 1244,
"items": [
{
"hap": "delivered",
"created_at": "Wed, 15 Feb 2012 12:35:39 GMT",
"type": "info",
"message": "Delivered: me@samples.mailgun.org \u2192 Sasha Klizhentas <alex@mailgun.net> 'Hello'",
"message_id": "20120215123539.7267.56179@samples.mailgun.org"
}
]
}
Tracking Opens¶
Mailgun can keep track of every time a recipient opens your messages. You can access this data through the Control Panel. In addition, you can be notified through a webhook.
Opens are tracked by including a transparent .png file, which will only work if there is an HTML component to the email (i.e., text only emails will not track opens). You should note that many email service providers disable images by default, so this data will only show up if the recipient clicks on display images button in his/her email.
Note
Return Path certification allows your images to be enabled by default at many ISPs. Please contact us if you would like to get your IP Address certified.
You can enable Open tracking by clicking on the checkbox in the ‘Tracking’ tab of your Control Panel and adding the appropriate CNAME records to your DNS as specified in the ‘Domain’ tab of your Control Panel.
Opens Webhook
You can specify a webhook URL in the ‘Tracking’ tab of your Control Panel. When a user opens one of your emails, your URL will be invoked with the following parameters.
| Parameter Name | Description |
|---|---|
| event | Event name (“opened”). |
| recipient | Recipient who opened. |
| domain | Domain that sent the original message. |
| ip | IP address the event originated from. |
| country | Two-letter country code (as specified by ISO3166) the event came from or ‘unknown’ if it couldn’t be determined. |
| region | Two-letter or two-digit region code or ‘unknown’ if it couldn’t be determined. |
| city | Name of the city the event came from or ‘unknown’ if it couldn’t be determined. |
| user-agent | User agent string of the client triggered the event. |
| device-type | Device type the email was opened on. Can be ‘desktop’, ‘mobile’, ‘tablet’, ‘other’ or ‘unknown’. |
| client-type | Type of software the email was opened in, e.g. ‘browser’, ‘mobile browser’, ‘email client’. |
| client-name | Name of the client sofware, e.g. ‘Thunderbird’, ‘Chrome’, ‘Firefox’. |
| client-os | OS family running the client sofware, e.g. ‘Linux’, ‘Windows’, ‘OSX’. |
| campaign-id | The id of campaign triggering the event. |
| campaign-name | The name of campaign triggering the event. |
| tag | Message tag, if message was tagged. See Tagging |
| mailing-list | The address of mailing list the original message was sent to. |
| “custom variables” | Your own custom JSON object included in the header (see Attaching Data to Messages). |
| timestamp | Number of second passed since January 1, 1970 (see securing webhooks). |
| token | Randomly generated string with length 50 (see securing webhooks). |
| signature | String with hexadecimal digits generate by HMAC algorithm (see securing webhooks). |
Tracking Clicks¶
Mailgun can keep track of every time a recipient clicks on a link in your messages. You can access this data through the Control Panel. In addition, you can be notified through a webhook.
All links will be overwritten and pointed to our servers so we can track clicks. This is only possible if you add the appropriate CNAME records to your DNS (as specified in the ‘Domain’ tab of your Control Panel).
You must enable Click tracking by clicking on the checkbox in the ‘Tracking’ tab of your Control Panel.
Clicks Webhook
You can specify a webhook URL in the ‘Tracking’ tab of your Control Panel. Every time a user clicks on a link inside of your messages, your URL will be called with the following parameters:
| Parameter Name | Description |
|---|---|
| event | Event name (“clicked”). |
| recipient | Recipient who clicked. |
| domain | Domain that sent the original message. |
| ip | IP address the event originated from. |
| country | Two-letter country code (as specified by ISO3166) the event came from or ‘unknown’ if it couldn’t be determined. |
| region | Two-letter or two-digit region code or ‘unknown’ if it couldn’t be determined. |
| city | Name of the city the event came from or ‘unknown’ if it couldn’t be determined. |
| user-agent | User agent string of the client triggered the event. |
| device-type | Device type the link was clicked on. Can be ‘desktop’, ‘mobile’, ‘tablet’, ‘other’ or ‘unknown’. |
| client-type | Type of software the link was opened in, e.g. ‘browser’, ‘mobile browser’, ‘email client’. |
| client-name | Name of the client sofware, e.g. ‘Thunderbird’, ‘Chrome’, ‘Firefox’. |
| client-os | OS family running the client sofware, e.g. ‘Linux’, ‘Windows’, ‘OSX’. |
| campaign-id | The id of campaign triggering the event. |
| campaign-name | The name of campaign triggering the event. |
| tag | Message tag, if it was tagged. See Tagging. |
| url | The URL that was clicked. |
| mailing-list | The address of mailing list the original message was sent to. |
| “custom variables” | Your own custom JSON object included in the header (see Attaching Data to Messages). |
| timestamp | Number of second passed since January 1, 1970 (see securing webhooks). |
| token | Randomly generated string with length 50 (see securing webhooks). |
| signature | String with hexadecimal digits generate by HMAC algorithm (see securing webhooks). |
Tracking Unsubscribes¶
Mailgun can keep track of every time a recipient requests to be unsubscribed from your mailings. Mailgun can also insert unsubscribe links and remove those recipients from your mailings automatically for you.
Mailgun supports three types of unsubscribes: domain, tag or Mailing Lists levels.
- Domain level: Once recipient selects to unsubscribe from domain, he will not receive any more messages from this sending domain.
- Tag level: Sometimes you need to separate traffic by types, for example provide newsletter mailings, security updates mailings and so on. Recipients may want to unsubscribe from your newsletters but still receive security updates. For this purpose you can use tags: mark your messages by setting approriate X-Mailgun-Tag header and use special %tag_unsubscribe_url% variable (see below).
- Mailing Lists level: If a recipient unsubscribes from a Mailing List, they will still be a member of the Mailing List but will be flagged as unsubscribed and Mailgun will no longer send messages from that Mailing List to the unsubscribed recipient.
Auto-Handling
You can enable Mailgun’s Unsubscribe functionality by clicking on the checkbox in the Unsubscribes tab in your Control Panel. We will automatically prevent future emails being sent to recipients that have unsubscribed. You can edit the unsubscribed address list from your Control Panel or through the API.
Mailgun provides you with several unsubscribe variables:
| Variable | Description |
|---|---|
| %unsubscribe_url% | link to unsubscribe recipient from all messages sent by given domain |
| %tag_unsubscribe_url% | link to unsubscribe from all tags provided in the message |
| %mailing_list_unsubscribe_url% | link to unsubscribe from future messages sent to a mailing list |
If you include these variables in your emails, any recipient that clicks on the url will be automatically unsubscribed and those email addresses will be blocked from receiving future emails from that domain or message tag as appropriate.
Mailgun can automatically provide an unsubscribe footer in each email you send. You can customize your unsubscribe footer by editing the settings in the control panel.
To enable/disable unsubscribes programmaticaly per message you can do the following:
- Enable unsubscription feature for your domain.
- Remove text in the html and text footer templates so they won’t be appended automatically.
- Insert a variable in the html and text bodies of your email when you need unsubscribe links.
- This variable will be replaced by the corresponding unsubscribe link.
In the “Unsubscribes” tab of the Control Panel or through the API you can also:
- View/get a list of unsubscribed addresses.
- Remove an unsubscribed address from the list.
Take a look at Unsubscribes section of the API refrence to learn how to programmatically manage lists of unsubscribed users.
Unsubscribes Webhook
You can specify a webhook URL in the ‘Unsubscribes’ tab of your Control Panel. When a user unsubscribes, Mailgun will invoke the webhook with the following parameters:
| Parameter Name | Description |
|---|---|
| event | Event name (“unsubscribed”). |
| recipient | Recipient who unsubscribed. |
| domain | Domain that sent the unsubscribe request. |
| ip | IP address the event originated from. |
| country | Two-letter country code (as specified by ISO3166) the event came from or ‘unknown’ if it couldn’t be determined. |
| region | Two-letter or two-digit region code or ‘unknown’ if it couldn’t be determined. |
| city | Name of the city the event came from or ‘unknown’ if it couldn’t be determined. |
| user-agent | User agent string of the client triggered the event. |
| device-type | Device type the person unsubscribed on. Can be ‘desktop’, ‘mobile’, ‘tablet’, ‘other’ or ‘unknown’. |
| client-type | Type of software the unsubscribe link was clicked in, e.g. ‘browser’, ‘mobile browser’, ‘email client’. |
| client-name | Name of the client sofware, e.g. ‘Thunderbird’, ‘Chrome’, ‘Firefox’. |
| client-os | OS family running the client sofware, e.g. ‘Linux’, ‘Windows’, ‘OSX’. |
| campaign-id | The id of the campaign that recipient has unsubscribed from. |
| campaign-name | The name of campaign triggering the event. |
| tag | Message tag, if it was tagged. See Tagging. |
| mailing-list | The address of mailing list the original message was sent to. |
| “custom variables” | Your own custom JSON object included in the header (see Attaching Data to Messages). |
| timestamp | Number of second passed since January 1, 1970 (see securing webhooks). |
| token | Randomly generated string with length 50 (see securing webhooks). |
| signature | String with hexadecimal digits generate by HMAC algorithm (see securing webhooks). |
Tracking Spam Complaints¶
Mailgun automatically keeps track of every time a recipient complains that a message is spam. You can access this data through the Control Panel or through the API. In addition, you can be notified through a webhook.
Email service providers (“ESPs”) are very sensitive to users clicking on spam complaint buttons and it’s important to monitor that activity to maintain a good sending reputation. While, not every ESP supports Feedback Loop (“FBL”) notifications, we make sure that you get data on all of the ones that do. We will remove recipients from future messages if a complaint has been filed by that recipient. This is necessary to maintain your reputation and not have your emails automatically sent to spam folders.
Spam Complaint tracking is always enabled.
Mailgun provides Spam complaints API to programmatically control the lists of users who have complainted.
Spam Complaints Webhook
You can specify a webhook URL in the ‘Complaints’ tab in the control panel. When a user reports one of your emails as spam, Mailgun will invoke the webhook with the following parameters:
| Parameter Name | Description |
|---|---|
| event | Event name (“complained”). |
| recipient | Recipient who clicked spam. |
| domain | The domain that sent the complaint. |
| message-headers | String list of all MIME headers of the original message dumped to a JSON string (order of headers preserved). |
| campaign-id | The id of campaign triggering the event. |
| campaign-name | The name of campaign triggering the event. |
| tag | Message tag, if it was tagged. See Tagging. |
| mailing-list | The address of mailing list the original message was sent to. |
| “custom variables” | Your own custom JSON object included in the header (see Attaching Data to Messages). |
| timestamp | Number of second passed since January 1, 1970 (see securing webhooks). |
| token | Randomly generated string with length 50 (see securing webhooks). |
| signature | String with hexadecimal digits generate by HMAC algorithm (see securing webhooks). |
| attachment-x | attached file (‘x’ stands for number of the attachment). Attachments are included if the recipient ESP includes them in the bounce message. They are handled as file uploads, encoded as multipart/form-data. |
Tracking Bounces¶
Mailgun automatically keeps track of every time a message bounces (is not accepted by the recipient). You can access this data through the Control Panel or through the API. In addition, you can be notified through a webhook.
Mailgun classifies bounces into two groups:
- Hard bounces: Recipient is not found either due to a bad domain name or the recipient is unknown. Mailgun stops attempting delivery to invalid recipients after one Hard Bounce.
- Soft bounces: Email is not delivered because the mailbox is full or for other reasons. Mailgun will attempt to deliver messages multiple times depending on feedback from the ESP but will eventually quit attempting delivery in order to maintain your sending reputation.
Bounce tracking is always enabled.
In the “Bounces” tab of the Control Panel or through the API you can:
- View/get a list of bounced addresses.
- Remove a bounced address from the list.
You can have programmatic control over list of bounced recipients. See Bounces section in the API Reference for more details.
Bounce Event Webhook
You can specify a webhook URL in the ‘Bounces’ tab of your Control Panel. If you do, every time a message experiences a hard bounce, your URL will be invoked with the following parameters:
| Parameter Name | Description |
|---|---|
| event | Event name (“bounced”). |
| recipient | Recipient who could not be reached. |
| domain | Domain of the recipient who could not be reached. |
| message-headers | String list of all MIME headers of the original message dumped to a JSON string (order of headers preserved). |
| code | SMTP bounce error code in form (X.X.X). |
| error | SMTP bounce error string. |
| notification | Detailed reason for bouncing (optional). |
| campaign-id | The id of campaign triggering the event. |
| campaign-name | The name of campaign triggering the event. |
| tag | Message tag, if it was tagged. See Tagging. |
| mailing-list | The address of mailing list the original message was sent to. |
| “custom variables” | Your own custom JSON object included in the header (see Attaching Data to Messages). |
| timestamp | Number of second passed since January 1, 1970 (see securing webhooks). |
| token | Randomly generated string with length 50 (see securing webhooks). |
| signature | String with hexadecimal digits generate by HMAC algorithm (see securing webhooks). |
| attachment-x | attached file (‘x’ stands for number of the attachment). Attachments are included if the recipient ESP includes them in the bounce message. They are handled as file uploads, encoded as multipart/form-data. |
Tracking Failures¶
If you want to get notified every time a message fails to be delivered, you need to handle the “drop” event in your application.
Drop Event Webhook
In the Logs tab, you can specify a webhook URL to be notified every time a messages is dropped. There are a few reasons why Mailgun needs to stop attempting to deliver messages and drop them. The most common reason is that Mailgun received a Hard bounce or repeatedly received Soft bounces and continuing attempting to deliver may hurt your reputation with the receiving ESP. Also, if the address is on one of the ‘do not send lists’ because that recipient had previously bounced, unsubscribed or complained of spam, we will not attempt delivery and drop the message. If one of these events occur we will POST the following parameters to your URL:
| Parameter Name | Description |
|---|---|
| event | Event name (“dropped”). |
| recipient | Intended recipient. |
| domain | Domain that sent the message. |
| message-headers | String list of all MIME headers of the original message dumped to a JSON string (order of headers preserved). |
| reason | Reason for failure. Can be one of [“hardfail”, “maxfails”, “old”]. See below. |
| code | ESP response code, e.g. if the message was blocked as a spam (optional). |
| description | Detailed explanation of why the messages was dropped |
| “custom variables” | Your own custom JSON object included in the header (see Attaching Data to Messages). |
| timestamp | Number of second passed since January 1, 1970 (see securing webhooks). |
| token | Randomly generated string with length 50 (see securing webhooks). |
| signature | String with hexadecimal digits generate by HMAC algorithm (see securing webhooks). |
| attachment-x | attached file (‘x’ stands for number of the attachment). Attachments are included if the recipient ESP includes them in the bounce message. They are handled as file uploads, encoded as multipart/form-data. |
- Hardfail indicates a hard bounce or recipient has previously bounced, unsubscribed or complained of spam.
- Maxfails indicates a soft bounce and the maximum number of attempts was reached.
- Old indicates that Mailgun tried to deliver the message unsuccessfully for more than 8 hours.
Tracking Deliveries¶
If you want to get notified every time a message is delivered, you need to handle the “delivered” event in your application.
Delivered Event Webhook
In the Logs tab, you can specify a webhook URL to be notified every time a message is delivered. If the message is successfully delivered to the intended recipient, we will POST the following parameters to your URL:
| Parameter Name | Description |
|---|---|
| event | Event name (“delivered”). |
| recipient | Intended recipient. |
| domain | Domain that sent the message. |
| message-headers | String list of all MIME headers dumped to a JSON string (order of headers preserved). |
| Messsage-Id | String id of the original message delivered to the recipient. |
| “custom variables” | Your own custom JSON object included in the header of the original message (see Attaching Data to Messages). |
| timestamp | Number of seconds passed since January 1, 1970 (see securing webhooks). |
| token | Randomly generated string with length 50 (see securing webhooks). |
| signature | String with hexadecimal digits generate by HMAC algorithm (see securing webhooks). |
Note
Unlike other event webhooks (due to frequency of delivered events), Delivered Event will only POST once, right after delivery, and won’t attempt again in case of failure to POST successfully.
Stats¶
Stats provide you with the summary of the events that occur with your messages. We keep track of events for 6 months and stats are aggregated by tag, see Tagging above.
You can see your current statistics in the control panel, or download them using the API
Get stats for ‘open’ and ‘sent’ events sorted by date:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 -G \
https://api.mailgun.net/v2/samples.mailgun.org/stats \
-d event='sent'\
-d event='opened' \
-d skip=1 \
-d limit=2
public static ClientResponse GetStats() {
Client client = new Client();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org" +
"/stats");
MultivaluedMapImpl queryParams = new MultivaluedMapImpl();
queryParams.add("event", "sent");
queryParams.add("event", "opened");
queryParams.add("skip", 1);
queryParams.add("limit", 2);
return webResource.queryParams(queryParams).get(ClientResponse.class);
}
function get_stats() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt(
$ch,
CURLOPT_URL,
'https://api.mailgun.net/v2/samples.mailgun.org/stats?event=sent&event=opened');
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def get_stats():
return requests.get(
"https://api.mailgun.net/v2/samples.mailgun.org/stats",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
params={"event": ["sent", "opened"],
"skip": 1,
"limit": 2})
def get_stats
url_params = Multimap.new
url_params[:skip] = 1
url_params[:limit] = 2
url_params[:event] = "sent"
url_params[:event] = "opened"
query_string = url_params.collect {|k, v| "#{k.to_s}=#{CGI::escape(v.to_s)}"}.
join("&")
RestClient.get "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/stats?#{query_string}"
end
public static RestResponse GetStats() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.AddParameter("domain",
"samples.mailgun.org", ParameterType.UrlSegment);
request.Resource = "{domain}/stats";
request.AddParameter("event", "sent");
request.AddParameter("event", "opened");
request.AddParameter("skip", 1);
request.AddParameter("limit", 2);
return client.Execute(request);
}
Sample response:
{
"total_count": 95,
"items": [
{
"event": "sent",
"total_count": 16,
"created_at": "Tue, 14 Feb 2012 00:00:00 GMT",
"id": "4f39f7c56addaa3e1966714d",
"tags": {}
},
{
"event": "sent",
"total_count": 1,
"created_at": "Mon, 13 Feb 2012 00:00:00 GMT",
"id": "4f38f9ac6addaa3e1966335e",
"tags": {}
}
]
}
Campaign Analytics¶
Mailgun allows you to easily create Campaigns either using the API or the Campaigns Tab in the Control Panel. Campaigns are a tool to segment your email traffic for analysis. When you include messages in a Campaign, Mailgun tracks those messages and creates detailed analytics on those messages.
Campaign Analytics allow you to do things like:
- Track events down to the individual recipient.
- Compare Campaigns for A/B testing.
- Optimize segmentation of Mailing Lists and content by geo location and recipient domain (Gmail, Hotmail, etc.).
- Optimize delivery times by seeing engagement levels (opens & clicks) throughout the day.
Warning
Due to the increased cost of storing analytics, the cost of sending a message with a Campaign ID is 50% over the normal Mailgun rate. For example, if you are using the Express Plan ($0.50 per 1,000 emails), sending a message with a Campaign ID will cost an additional $0.25 per 1,000 emails.
Creating a Campaign
You can create Campaigns using the Campaign Tab in the Control Panel or the API.
Each Campaign has name and id. You use the id when referring to the campaign via the API. You can supply your own id when creating campaign, otherwise Mailgun will generate one for you.
Creating a campaign with supplied id:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/samples.mailgun.org/campaigns \
-F name='Newsletter' \
-F id='my_campaign_id'
public static ClientResponse CreateCampaign() {
Client client = new Client();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org/campaigns");
MultivaluedMapImpl formData = new MultivaluedMapImpl();
formData.add("name", "Newsletter");
formData.add("id", "my_campaign_id");
return webResource.type(MediaType.APPLICATION_FORM_URLENCODED).
post(ClientResponse.class, formData);
}
function create_campaign() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v2/samples.mailgun.org/campaigns');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POSTFIELDS, array('name' => 'Newsletter',
'id' => 'my_campaign_id'));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def create_campaign():
return requests.post(
"https://api.mailgun.net/v2/samples.mailgun.org/campaigns",
auth=('api', 'key-3ax6xnjp29jd6fds4gc373sgvjxteol0'),
data={'name': 'Newsletter',
'id': 'my_campaign_id'})
def create_campaign
RestClient.post("https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0" \
"@api.mailgun.net/v2/samples.mailgun.org/campaigns",
:name => 'Newsletter',
:id => 'my_campaign_id') {
|response, request, result| response
}
end
public static RestResponse CreateCampaign() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.Resource = "{domain}/campaigns";
request.AddParameter("domain", "samples.mailgun.org",
ParameterType.UrlSegment);
request.AddParameter("name", "Newsletter");
request.AddParameter("id", "my_campaign_id");
request.Method = Method.POST;
return client.Execute(request);
}
Note
Supplied id must be pure ASCII and unique across all campaigns for a particular domain. Campaign name and campaign ID should not exceed the maximum length of 64 characters.
Note
There are limits on campaigns number for an account: 1 for Free Plans, 20 for Standard Plans and 1,000 for Express Plans. Once the limit is reached, you will need to delete campaigns in order to create new ones. All of the data associated with a campaign is deleted when you delete a campaign. High Volume Plans can have unlimited number of campaigns.
Sending a Campaign
Including a message in a campaign can be done by setting o:campaign option with your campaign’s id value:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/samples.mailgun.org/messages \
-F from='Excited User <me@samples.mailgun.org>' \
-F to=roman@mailgun.net \
-F subject='Hello' \
-F text='Testing some Mailgun awesomness!' \
-F o:campaign='my_campaign_id'
public static ClientResponse SendCampaignMessage() {
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org" +
"/messages");
MultivaluedMapImpl formData = new MultivaluedMapImpl();
formData.add("from", "Excited User <me@samples.mailgun.org>");
formData.add("to", "sergeyo@profista.com");
formData.add("to", "serobnic@mail.ru");
formData.add("subject", "Hello");
formData.add("text", "Testing some Mailgun awesomness!");
formData.add("o:campaign", "my_campaign_id");
return webResource.type(MediaType.APPLICATION_FORM_URLENCODED).
post(ClientResponse.class, formData);
}
function send_campaign_message() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v2/samples.mailgun.org/messages');
curl_setopt($ch, CURLOPT_POSTFIELDS, array('from' => 'Excited User <me@samples.mailgun.org>',
'to' => 'Roman <roman@mailgun.net>',
'subject' => 'Hello',
'text' => 'Testing some Mailgun awesomness!',
'o:campaign' => 'my_campaign_id'))
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def send_campaign_message():
return requests.post(
"https://api.mailgun.net/v2/samples.mailgun.org/messages",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
data={"from": "Excited User <me@samples.mailgun.org>",
"to": ["roman@mailgun.net"],
"subject": "Hello",
"text": "Testing some Mailgun awesomness!",
"o:campaign": 'my_campaign_id'})
def send_campaign_message
RestClient.post("https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0" \
"@api.mailgun.net/v2/samples.mailgun.org/messages",
:from => "Excited User <me@samples.mailgun.org>",
:to => "roman@mailgun.net",
:subject => "Hello",
:text => "Testing some Mailgun awesomness!",
'o:campaign' => 'my_campaign_id')
end
public static RestResponse SendCampaignMessage() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.AddParameter("domain",
"samples.mailgun.org", ParameterType.UrlSegment);
request.Resource = "{domain}/messages";
request.AddParameter("from", "Excited User <me@samples.mailgun.org>");
request.AddParameter("to", "sergeyo@profista.com");
request.AddParameter("to", "serobnic@mail.ru");
request.AddParameter("subject", "Hello");
request.AddParameter("text", "Testing some Mailgun awesomness!");
request.AddParameter("o:campaign", "my_campaign_id");
request.Method = Method.POST;
return client.Execute(request);
}
Alternatively, if you’re building MIME yourself, include X-Mailgun-Campaign-Id header to send a campaign message.
Note
A single message may belong to up to 3 campaigns at the same time - just use multiple o:campaign values or X-Mailgun-Campaign-Id headers.
Accessing Analytics
Once a message has been sent using a Campaign ID, Mailgun tracks the message and provides detailed analytics. Mailgun provides you with several reports that you can find in the Control Panel, including:
- Comparison of events across campaigns for A/B testing (including opens, clicks, bounces, unsubscribes and complaints).
- Performance of link clicks in a campaign.
- Comparison of events across Domains (Gmail, Yahoo, etc.).
- Timeline of events occurring throughout the day to measure optimal delivery time (which can be used in conjunction with Scheduling Delivery).
- Geographic performance of opens and clicks.
You can also create your own reports by accessing the data via the API.
You may want to see how’re recipients performing for your campaign during daily hours:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 -G \
-d "groupby=daily_hour&limit=2" \
https://api.mailgun.net/v2/samples.mailgun.org/campaigns/my_campaign_id/stats
public static ClientResponse GetCampaignStats() {
Client client = new Client();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org/campaigns/my_campaign_id/stats");
MultivaluedMapImpl queryParams = new MultivaluedMapImpl();
queryParams.add("groupby", "daily_hour");
queryParams.add("limit", 2);
return webResource.queryParams(queryParams).get(ClientResponse.class);
}
function get_campaign_stats() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt(
$ch,
CURLOPT_URL,
'https://api.mailgun.net/v2/samples.mailgun.org/campaigns/my_campaign_id/stats'
.'?groupby=daily_hour&limit=2');
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def get_campaign_stats():
return requests.get(
("https://api.mailgun.net/v2/samples.mailgun.org/campaigns"
"/my_campaign_id/stats?groupby=daily_hour&limit=2"),
auth=('api', 'key-3ax6xnjp29jd6fds4gc373sgvjxteol0'))
def get_campaign_stats
RestClient.get("https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/campaigns/"\
"my_campaign_id/stats?groupby=daily_hour&limit=2")
end
public static RestResponse GetCampaignStats() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.Resource = "{domain}/campaigns/my_campaign_id/stats";
request.AddParameter("domain",
"samples.mailgun.org", ParameterType.UrlSegment);
request.AddParameter("groupby", "daily_hour");
request.AddParameter("limit", 2);
return client.Execute(request);
}
Sample response:
[
{
"daily_hour": 8,
"total": {
"complained": 0,
"clicked": 6,
"opened": 7,
"unsubscribed": 5,
"bounced": 0
},
"unique": {
"clicked": {
"recipient": 3,
"link": 3
},
"opened": {
"recipient": 3
}
}
},
{
"daily_hour": 13,
"total": {
"complained": 0,
"clicked": 0,
"opened": 0,
"unsubscribed": 0,
"bounced": 0
},
"unique": {
"clicked": {
"recipient": 0,
"link": 0
},
"opened": {
"recipient": 0
}
}
}
]
Or retrieve the entire history for a particular recipient:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 -G \
-d "recipient=roman@mailgunhq.com&limit=2" \
https://api.mailgun.net/v2/samples.mailgun.org/campaigns/my_campaign_id/events
public static ClientResponse GetCampaignRecipientHistory() {
Client client = new Client();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org/campaigns/my_campaign_id/events");
MultivaluedMapImpl queryParams = new MultivaluedMapImpl();
queryParams.add("recipient", "roman@mailgunhq.com");
queryParams.add("limit", 2);
return webResource.queryParams(queryParams).get(ClientResponse.class);
}
function get_campaign_recipient_history() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt(
$ch,
CURLOPT_URL,
'https://api.mailgun.net/v2/samples.mailgun.org/campaigns/my_campaign_id/events'
.'?recipient=roman@mailgunhq.com&limit=2');
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def get_campaign_recipient_history():
return requests.get(
("https://api.mailgun.net/v2/samples.mailgun.org/campaigns"
"/my_campaign_id/events?recipient=roman@mailgunhq.com&limit=2"),
auth=('api', 'key-3ax6xnjp29jd6fds4gc373sgvjxteol0'))
def get_campaign_recipient_history
RestClient.get("https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/campaigns/"\
"my_campaign_id/events?recipient=roman@mailgunhq.com&limit=2")
end
public static RestResponse GetCampaignRecipientHistory() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.Resource = "{domain}/campaigns/my_campaign_id/events";
request.AddParameter("domain",
"samples.mailgun.org", ParameterType.UrlSegment);
request.AddParameter("recipient", "roman@mailgunhq.com");
request.AddParameter("limit", 2);
return client.Execute(request);
}
Sample response:
[
{
"domain": "mailgunhq.com",
"tags": [],
"timestamp": "Wed, 08 Feb 2012 13:44:02 GMT",
"recipient": "roman@mailgunhq.com",
"event": "delivered",
"user_vars": {}
},
{
"domain": "mailgunhq.com",
"tags": [],
"timestamp": "Wed, 08 Feb 2012 13:38:35 GMT",
"recipient": "roman@mailgunhq.com",
"event": "delivered",
"user_vars": {}
}
]
See our Analytics API reference for the full list of available methods.
Note
‘Delivered’ events are stored for 2 weeks only.
Receiving Messages¶
There are two ways to handle incoming messages using Mailgun:
- Forward incoming messages using Routes to a URL or to another email address.
- Store incoming messages in Mailboxes.
Routes¶
You can define a list of routes to handle incoming emails. This idea of routes is borrowed from MVC web frameworks like Django or Ruby on Rails: if a message matches a route expression, Mailgun can forward it to your application via HTTP or to another email address.
You can define routes visually in the Control Panel, or programmatically using the API.
Mailgun route is a pair of filter+action. Each incoming message is passed to a filter expression, and if it evaluates to true, the action is executed.
Each Route can be assigned a priority. Routes are evaluated in the order of priority, with lower numbers having a higher priority. The default is for all Routes to be evaluated (even if a higher priority Route is triggered). To avoid this you can use a stop() action (see below).
Here’s a more formal list of route properties:
| Field | Description |
|---|---|
| Priority | Integer indicating the priority of route execution. Lower numbers have higher priority. |
| Filter | Filters available in routes - match_recipient() match_header() catchall() (see below for description). |
| Actions | Type of action to take when a filter is triggered - forward() stop() (see below for description). |
| Description | Arbitrary string to describe the route (shown in the Control Panel UI) |
Route Filters¶
match_recipient(pattern)
Matches smtp recipient of the incoming message against the regular expression pattern. For example this filter will match messages going to foo@bar.com:
match_recipient("foo@bar.com")
You can use Python-style regular expression in your filter. For example this will match all messages coming to any recipient at @bar.com:
match_recipient(".*@bar.com")
Mailgun supports regexp captures in filters. This allows you to use captured values inside of your actions. The example below captures the local name (the part of email before @) and passes it as a mailbox parameter to an application URL:
route filter : match_recipient("(.*)@bar.com")
route action : forward("http://myhost.com/post/?mailbox=\1")
You can use named captures as well:
route filter : match_recipient("(?P<user>.*?)@(?P<domain>.*)")
route action : forward("http://mycallback.com/domains/\g<domain>/users/\g<user>")
match_header(header, pattern)
Similar to match_recipient but instead of looking at a message recipient, it applies the pattern to an arbitrary MIME header of the message. The example below matches any message with a word “support” in its subject:
match_header("Subject", ".*support")
catch_all()
Matches if no preceeding routes matched. Usually you need to use it in a route with a lowest priority, to make sure it evaluates last.
Route Actions¶
If a route expression evaluates to true, Mailgun executes the corresponding action. Currently you can use the folowing two actions in your routes: forward() and stop().
forward(destination)
Forwards the message to a specified destination, which can be another email address or a URL. A few examples:
forward("mailbox@myapp.com")
forward("http://myapp.com/messages")
You can combine multiple destinations separating them by a comma:
forward("http://myapp.com/messages, mailbox@myapp.com")
stop()
Simply stops the priority waterfall so the subsequent routes will not be evaluated. Without a stop() action executed, all lower priority Routes will also be evaluated.
Receiving Messages via HTTP¶
When you specify a URL of your application as a route destination, Mailgun can perform HTTP POST request into it using one of two following formats:
- Fully parsed: Mailgun will parse the message, process attachments, attempt to separate quoted parts from the actual message. This is the preferred option.
- Raw MIME: message is posted as-is. In this case you are responsible for parsing MIME. To receive raw MIME messages, the destination URL must end with mime.
Regardless of which delivery method you choose, Mailgun will transcode the original message into UTF-8 encoding, even for raw MIME. We do this because we love our users: most MIME parsers are not good at dealing with all of the various broken MIME messages that exist in the wild.
Below are two tables of HTTP parameters that you can expect to be posted into your application.
Note
In addition to these parameters Mailgun will post all MIME headers.
Parsed Messages Parameters¶
| Parameter | Type | Description |
|---|---|---|
| recipient | string | recipient of the message as reported by MAIL TO during SMTP chat. |
| sender | string | sender of the message as reported by MAIL FROM during SMTP chat. Note: this value may differ from From MIME header. |
| from | string | sender of the message as reported by From message header, for example “Bob Lee <blee@mailgun.net>”. |
| subject | string | subject string. |
| body-plain | string | text version of the email. This field is always present. If the incoming message only has HTML body, Mailgun will create a text representation for you. |
| stripped-text | string | text version of the message without quoted parts and signature block (if found). |
| stripped-signature | string | the signature block stripped from the plain text message (if found). |
| body-html | string | HTML version of the message, if message was multipart. Note that all parts of the message will be posted, not just text/html. For instance if a message arrives with “foo” part it will be posted as “body-foo”. |
| stripped-html | string | HTML version of the message, without quoted parts. |
| attachment-count | int | how many attachments the message has. |
| attachment-x | string | attached file (‘x’ stands for number of the attachment). Attachments are handled as file uploads, encoded as multipart/form-data. |
| timestamp | int | number of second passed since January 1, 1970 (see securing webhooks). |
| token | string | randomly generated string with length 50 (see securing webhooks). |
| signature | string | string with hexadecimal digits generate by HMAC algorithm (see securing webhooks). |
| message-headers | string | list of all MIME headers dumped to a json string (order of headers preserved). |
| content-id-map | string | JSON-encoded dictionary which maps Content-ID (CID) of each attachment to the corresponding attachment-x parameter. This allows you to map posted attachments to tags like <img src='cid'> in the message body. |
Note the message-headers parameter. It was added because not all web frameworks support multi-valued keys parameters. For example Ruby on Rails requires a special syntax to post params like that: you need to add [] to a key to collect it’s values on the server side as an array. Below is a Ruby on Rails example of obtaining MIME headers via message-headers parameter:
def mailgun_posted_params
message_headers = JSON.parse(params["message-headers"])
message_headers.each do |header|
key, value = header
puts "header key: #{key}, header value: #{value}"
end
end
MIME Messages Parameters¶
| Parameter | Type | Description |
|---|---|---|
| recipient | string | recipient of the message. |
| sender | string | sender of the message as reported by SMTP MAIL FROM. |
| from | string | sender of the message as reported by From message header, for example “Bob Lee <blee@mailgun.net>”. |
| subject | string | subject string. |
| body-mime | string | full MIME envelope. You will need a MIME parsing library to process this data. |
| timestamp | int | number of second passed since January 1, 1970 (see securing webhooks). |
| token | string | randomly generated string with length 50 (see securing webhooks). |
| signature | string | string with hexadecimal digits generate by HMAC algorithm(see securing webhooks). |
Note
To receive raw MIME messages and perform your own parsing you must configure a route with a URL ending with “mime”, like http://myhost/post_mime.
Note
Consider using http://postbin.ryanbigg.com to debug and play with your routes. This tool allows you to forward incoming messages to a temporary URL and inspecting the posted data. No programming required.
API Routing Samples¶
Most users prefer to define their routes using the Control Panel. However, you can define routes programmatically using our HTTP API like in these examples.
Create a route of the highest priority with multiple actions:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/routes \
-F priority=1 \
-F description='Sample route' \
-F expression='match_recipient(".*@samples.mailgun.org")' \
-F action='forward("http://myhost.com/messages/")'\
-F action='stop()'
public static ClientResponse CreateRoute() {
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/routes");
MultivaluedMapImpl formData = new MultivaluedMapImpl();
formData.add("priority", 1);
formData.add("description", "Sample route");
formData.add("expression", "match_recipient('.*@samples.mailgun.org')");
formData.add("action", "forward('http://myhost.com/messages/')");
formData.add("action", "stop()");
return webResource.type(MediaType.APPLICATION_FORM_URLENCODED).
post(ClientResponse.class, formData);
}
function create_route() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v2/routes');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch,
CURLOPT_POSTFIELDS,
array('priority' => 1,
'expression' => 'match_recipient(".*@samples.mailgun.org")',
'action[1]' => 'forward("http://host.com/messages")',
'action[2]' => 'stop()',
'description' => 'Sample route'));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def create_route():
return requests.post(
"https://api.mailgun.net/v2/routes",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
data=MultiDict([("priority", 1),
("description", "Sample route"),
("expression", "match_recipient('.*@samples.mailgun.org')"),
("action", "forward('http://myhost.com/messages/')"),
("action", "stop()")]))
def create_route
data = Multimap.new
data[:priority] = 1
data[:description] = "Sample route"
data[:expression] = "match_recipient('.*@samples.mailgun.org')"
data[:action] = "forward('http://myhost.com/messages/')"
data[:action] = "stop()"
RestClient.post "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/routes", data
end
public static RestResponse CreateRoute() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.Resource = "routes";
request.AddParameter("priority", 1);
request.AddParameter("description", "Sample route");
request.AddParameter("expression",
"match_recipient('.*@samples.mailgun.org')");
request.AddParameter("action",
"forward('http://myhost.com/messages/')");
request.AddParameter("action", "stop()");
request.Method = Method.POST;
return client.Execute(request);
}
Sample response:
{
"message": "Route has been created",
"route": {
"description": "Sample route",
"created_at": "Wed, 15 Feb 2012 13:03:31 GMT",
"actions": [
"forward(\"http://myhost.com/messages/\")",
"stop()"
],
"priority": 1,
"expression": "match_recipient(\".*@samples.mailgun.org\")",
"id": "4f3bad2335335426750048c6"
}
}
Listing routes:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 -G \
https://api.mailgun.net/v2/routes \
-d skip=1 \
-d limit=1
public static ClientResponse GetRoutes() {
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/routes");
MultivaluedMapImpl queryParams = new MultivaluedMapImpl();
queryParams.add("skip", 1);
queryParams.add("limit", 1);
return webResource.queryParams(queryParams).get(ClientResponse.class);
}
function get_routes() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v2/routes?skip=1&limit=1');
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def get_routes():
return requests.get(
"https://api.mailgun.net/v2/routes",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
params={"skip": 1,
"limit": 1})
def get_routes
RestClient.get "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/routes", :params => {
:skip => 1,
:limit => 1
}
end
public static RestResponse GetRoutes() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.Resource = "routes";
request.AddParameter("skip", 1);
request.AddParameter("limit", 1);
return client.Execute(request);
}
Sample response:
{
"total_count": 266,
"items": [
{
"description": "Sample route",
"created_at": "Wed, 15 Feb 2012 12:58:12 GMT",
"actions": [
"forward(\"http://myhost.com/messages/\")",
"stop()"
],
"priority": 1,
"expression": "match_recipient(\".*@samples.mailgun.org\")",
"id": "4f3babe4ba8a481c6400476a"
}
]
}
Access the route by id:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/routes/4f3bad2335335426750048c6
public static ClientResponse GetRoute() {
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/routes" +
"/4e97c1b2ba8a48567f007fb6");
return webResource.get(ClientResponse.class);
}
function get_route() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v2/routes/4e97c1b2ba8a48567f007fb6');
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def get_route():
return requests.get(
"https://api.mailgun.net/v2/routes/4e97c1b2ba8a48567f007fb6",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"))
def get_route
RestClient.
get("https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/routes/"\
"4e97c1b2ba8a48567f007fb6"){|response, request, result| response }
end
public static RestResponse GetRoute() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.Resource = "routes/{id}";
request.AddUrlSegment("id", "4e97c1b2ba8a48567f007fb6");
return client.Execute(request);
}
Sample response:
{
"route": {
"description": "Sample route",
"created_at": "Wed, 15 Feb 2012 13:03:31 GMT",
"actions": [
"forward(\"http://myhost.com/messages/\")",
"stop()"
],
"priority": 1,
"expression": "match_recipient(\".*@samples.mailgun.org\")",
"id": "4f3bad2335335426750048c6"
}
}
Mailboxes¶
Warning
Mailboxes is a legacy feature of Mailgun that is no longer offered to new customers. Contact support@mailgun.com with questions.
Mailgun gives you the ability to programmatically create mailboxes which can be used to receive and store mail. Mailboxes are accessible via POP3 or IMAP so you could connect to them with a typical email client. We charge per aggregate storage used, not per mailbox.
We don’t have an HTTP API to access data in Mailboxes, but context.io has some great APIs for this.
Mailbox API Examples¶
Listing all mailboxes:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 -G \
https://api.mailgun.net/v2/samples.mailgun.org/mailboxes
public static ClientResponse GetMailboxes() {
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org" +
"/mailboxes");
return webResource.get(ClientResponse.class);
}
function get_mailboxes() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v2/samples.mailgun.org/mailboxes');
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def get_mailboxes():
return requests.get(
"https://api.mailgun.net/v2/samples.mailgun.org/mailboxes",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"))
def get_mailboxes
RestClient.get "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/mailboxes"
end
public static RestResponse GetMailboxes() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.AddParameter("domain",
"samples.mailgun.org",
ParameterType.UrlSegment);
request.Resource = "{domain}/mailboxes";
return client.Execute(request);
}
Sample response:
{
"total_count": 2,
"items": [
{
"size_bytes": 0,
"created_at": "Tue, 27 Sep 2011 20:24:22 GMT",
"mailbox": "postmaster@samples.mailgun.org"
},
{
"size_bytes": 0,
"created_at": "Thu, 06 Oct 2011 10:22:36 GMT",
"mailbox": "user@samples.mailgun.org"
}
]
}
Creating a new mailbox:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/samples.mailgun.org/mailboxes \
-F mailbox='user@samples.mailgun.org' \
-F password='supasecret'
public static ClientResponse CreateMailbox() {
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org" +
"/mailboxes");
MultivaluedMapImpl formData = new MultivaluedMapImpl();
formData.add("mailbox", "sergeyo@samples.mailgun.org");
formData.add("password", "secret");
return webResource.type(MediaType.APPLICATION_FORM_URLENCODED).
post(ClientResponse.class, formData);
}
function create_mailbox() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v2/samples.mailgun.org/mailboxes');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POSTFIELDS, array('mailbox' => 'sergeyo@samples.mailgun.org',
'password' => 'secret'));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def create_mailbox():
return requests.post(
"https://api.mailgun.net/v2/samples.mailgun.org/mailboxes",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
data={"mailbox": "sergeyo@samples.mailgun.org",
"password": "secret"})
def create_mailbox
RestClient.post "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/mailboxes",
:mailbox => "sergeyo@samples.mailgun.org",
:password => "secret"
end
public static RestResponse CreateMailbox() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.AddParameter("domain",
"samples.mailgun.org", ParameterType.UrlSegment);
request.Resource = "{domain}/mailboxes";
request.AddParameter("mailbox", "sergeyo@samples.mailgun.org");
request.AddParameter("password", "secret");
request.Method = Method.POST;
return client.Execute(request);
}
Sample response:
{
"message": "Created 1 mailboxes"
}
Updating the password for a given mailbox:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 -X PUT \
https://api.mailgun.net/v2/samples.mailgun.org/mailboxes/user \
-F password='abc123'
public static ClientResponse ChangeMailboxPassword() {
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org" +
"/mailboxes/sergeyo");
MultivaluedMapImpl formData = new MultivaluedMapImpl();
formData.add("password", "supersecret");
return webResource.type(MediaType.APPLICATION_FORM_URLENCODED).
put(ClientResponse.class, formData);
}
function change_mailbox_password() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,
'https://api.mailgun.net/v2/samples.mailgun.org/mailboxes/'.
'sergeyo');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD,
'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,
http_build_query(array('password' => 'supersecret')));
$r = curl_exec($ch);
curl_close($ch);
return $r;
}
def change_mailbox_password():
return requests.put(
"https://api.mailgun.net/v2/samples.mailgun.org/mailboxes/sergeyo",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
data={"password": "supersecret"})
def change_mailbox_password
RestClient.put "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/mailboxes/sergeyo",
:password => "supersecret"
end
public static RestResponse ChangeMailboxPassword() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.AddParameter("domain",
"samples.mailgun.org", ParameterType.UrlSegment);
request.Resource = "{domain}/mailboxes/{username}";
request.AddUrlSegment("username", "sergeyo");
request.AddParameter("password", "supersecret");
request.Method = Method.PUT;
return client.Execute(request);
}
Sample response:
{
"message": "Password changed"
}
Deleting a given mailbox:
curl -s --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 -X DELETE \
https://api.mailgun.net/v2/samples.mailgun.org/mailboxes/sergeyo
public static ClientResponse DeleteMailbox() {
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0"));
WebResource webResource =
client.resource("https://api.mailgun.net/v2/samples.mailgun.org" +
"/mailboxes/sergeyo");
return webResource.delete(ClientResponse.class);
}
function delete_mailbox() {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, 'api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt(
$ch, CURLOPT_URL, 'https://api.mailgun.net/v2/samples.mailgun.org/mailboxes/sergeyo');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
def delete_mailbox():
return requests.delete(
"https://api.mailgun.net/v2/samples.mailgun.org/mailboxes/sergeyo",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"))
def delete_mailbox
RestClient.delete "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/mailboxes/sergeyo"
end
public static RestResponse DeleteMailbox() {
RestClient client = new RestClient();
client.BaseUrl = "https://api.mailgun.net/v2";
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-3ax6xnjp29jd6fds4gc373sgvjxteol0");
RestRequest request = new RestRequest();
request.AddParameter("domain",
"samples.mailgun.org", ParameterType.UrlSegment);
request.Resource = "{domain}/mailboxes/{username}";
request.AddUrlSegment("username", "sergeyo");
request.Method = Method.DELETE;
return client.Execute(request);
}
Sample response:
{
"message": "Mailbox has been deleted",
"spec": "sergeyo@samples.mailgun.org"
}
Spam Filter¶
If you are receiving email, you need spam filtering. Mailgun spam filtering is powered by an army of SpamAssassin machines. Mailgun gives you three ways to configure spam filtering. You can select the appropriate option in the Control Panel when you click on a domain name in the ‘Domains’ tab.
- Disabled (default)
- Delete spam (spam is removed and you won’t see it)
- Mark spam with MIME headers and you decide what to do with it
If you chose option 3, there are three headers we provide for you: X-Mailgun-Sflag, X-Mailgun-Sscore and X-Mailgun-Spf.
- X-Mailgun-Sflag
- Inserted with the value ‘Yes’ if the message was classified as a spam.
- X-Mailgun-Sscore
A ‘spamicity’ score that you can use to calibrate your own filter. Inserted for every message checked for a spam. The score ranges from low negative digits (very unlikely to be spam) to 20 and occasionally higher (very likely to be spam).
At the time of writing this, we are filtering spam at a score of around 5.0 but we are constantly calibrating this.
- X-Mailgun-Spf
- Mailgun will perform an SPF validation, and results will be stored in this header. Possible values are: ‘Pass’, ‘Neutral’, ‘Fail’ or ‘SoftFail’.
SMTP, POP3 and IMAP¶
In addition to our HTTP API, Mailgun servers support all standard email protocols:
- You can send using SMTP with or without TLS.
- You can receive mail using POP3 or IMAP.
Please consult a standard library documentation for language of your choice to learn how to use these protocols. Below are some helpful links for a few popular languages:
SMTP Relay¶
You can also configure your own mailserver to relay mail via Mailgun as shown below. All of them require these three variables which you can look up in the Control Panel:
- Your SMTP username
- Your SMTP password
- SMTP host name mailserver (these instructions will use smtp.mailgun.org as an example)
You have an SMTP username and password for each domain you have at Mailgun. To send mail from a particular domain, just use the appropriate credentials.
Postfix Instructions
You have to configure a relay host with SASL authentication like shown below:
# /etc/postfix/main.cf:
mydestination = localhost.localdomain, localhost
relayhost = [smtp.mailgun.org]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = static:postmaster@mydomain.com:password
smtp_sasl_security_options = noanonymous
# TLS support
smtp_tls_security_level = may
smtpd_tls_security_level = may
smtp_tls_note_starttls_offer = yes
When using TLS encryption, make sure Postfix knows where to locate the CA database for your Linux distribution:
smtpd_tls_key_file = /etc/ssl/private/smtpd.key
smtpd_tls_cert_file = /etc/ssl/certs/smtpd.crt
smtpd_tls_CApath = /etc/ssl/certs
Note
You can use SMTP username/password or credentials for any mailbox on your mailgun domain, but not your Control Panel password.
Exim Instructions
For more information see Exim’s documentation for authenticated outgoing SMTP. You need to configure “smarthost” for your Exim setup.
# In your exim.conf:
# In routes configuration:
mailgun:
driver = manualroute
domains = ! +local_domains
transport = mailgun_transport
route_list = * smtp.mailgun.org byname
# In transports configuration:
mailgun_transport:
driver=smtp
hosts_try_auth = smtp.mailgun.org
Also make sure to configure login credentials (in your /etc/exim/passwd.client):
*.mailgun.org:username:password
Sendmail Instructions
Define the smarthost in your sendmail.mc before mailer definitions:
## Mailgun
define(`SMART_HOST', `smtp.mailgun.org')dnl
FEATURE(`authinfo', `hash /etc/mail/authinfo')dnl
# optional, see http://www.sendmail.org/m4/features.html before enabling:
# FEATURE(`accept_unresolvable_domains')dnl
# FEATURE(`accept_unqualified_senders')dnl
# execute: make -C /etc/mail
## Mailgun
Specify login credentials in your authinfo:
AuthInfo:smtp.mailgun.org "U:<LOGIN>" "P:<PASSWORD>" "M:PLAIN"
Don’t forget to run the following command and then restart sendmail:
make -C /etc/mail
Using Standard Email Clients¶
Standard email clients like Thunderbird or Outlook can also be used to send and fetch mail.
Settings for fetching mail:
POP3 server: pop.mailgun.org
IMAP server: imap.mailgun.org
Settings for sending mail:
SMTP server: smtp.mailgun.org
Note
Use a full mailbox name like “user@mymailgundomain.com” as a login for POP, IMAP and SMTP. SSL or TLS are supported for all protocols.