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’s and Email Best Practices that you can reference.
Finally, always feel free to contact us: support@mailgunhq.com.
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
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:
When you sign up for an account, you will need to pick a domain name. You have the ability to either use a custom root domain or use a subdomain of mailgun.org, like myapp.mailgun.org
DNS Records
If you are using your own root domain, there are records that need to be set for sending (A, 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.
There are two ways to send messages using Mailgun:
Both methods work great and support the same feature set, so choose one based on your preferences and requirements.
When sending via HTTP API, Mailgun offers two options:
Note
Mailgun supports maximum messages size of 25MB.
See sending messages section in our API Reference for a full list of message sending options.
API 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:
def post_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
Response:
{
"message": "Queued. Thank you.",
"id": "<20120203170704.20654.39224@samples.mailgun.org>"
}
Sending a message with HTML and text parts. This example also attaches two files to the message:
def post_text_and_html_and_files
data = Multimap.new
data[:from] = "Excited User <me@samples.mailgun.org>"
data[:to] = "sergeyo@profista.com, serobnic@mail.ru"
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
Response:
{
"message": "Queued. Thank you.",
"id": "<20120203170705.20654.81656@samples.mailgun.org>"
}
Sending a MIME message which you pre-build yourself using a MIME library of your choice:
def post_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
Response:
{
"message": "Queued. Thank you.",
"id": "<20120203170706.26694.70704@samples.mailgun.org>"
}
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:
def post_message_tracking_off
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
Response:
{
"message": "Queued. Thank you.",
"id": "<20120203170707.20653.16901@samples.mailgun.org>"
}
Sending a message with HTML and text parts. This example also attaches two files to the message and sets emails for CC and BCC:
def post_text_and_html_and_files_with_cc_and_bcc
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
Response:
{
"message": "Queued. Thank you.",
"id": "<20120203170707.26695.20461@samples.mailgun.org>"
}
An example of how to set message delivery time using the o:deliverytime option:
def post_message_delivery_time
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
Response:
{
"message": "Queued. Thank you.",
"id": "<20120203170708.26695.29087@samples.mailgun.org>"
}
An example of how to tag a message with the o:tag option:
def post_message_with_tag
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
Response:
{
"message": "Queued. Thank you.",
"id": "<20120203170709.20654.73847@samples.mailgun.org>"
}
Sending a plain text message:
public static ClientResponse PostMessage() {
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);
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111114174238.6405.69989@samples.mailgun.org>"
}
Sending a message with HTML and text parts. This example also attaches two files to the message:
public static ClientResponse PostTextAndHtmlAndFiles() {
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", "sergeyo@profista.com");
form.field("to", "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);
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111114174238.6404.26938@samples.mailgun.org>"
}
Sending a MIME message which you pre-build yourself using a MIME library of your choice:
public static ClientResponse PostMimeMessage() {
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);
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111114174238.25659.9938@samples.mailgun.org>"
}
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:
public static ClientResponse PostMessageTrackingOff() {
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);
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111114174238.6404.96764@samples.mailgun.org>"
}
Sending a message with HTML and text parts. This example also attaches two files to the message and sets emails for CC and BCC:
public static ClientResponse PostTextAndHtmlAndFilesWithCcAndBcc() {
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);
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111114174239.25660.70079@samples.mailgun.org>"
}
An example of how to set message delivery time using the o:deliverytime option:
public static ClientResponse PostMessageDeliveryTime() {
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);
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111114174239.25659.5817@samples.mailgun.org>"
}
An example of how to tag a message with the o:tag option:
public static ClientResponse PostMessageWithTag() {
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);
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111114174239.6405.95896@samples.mailgun.org>"
}
Sending a plain text message:
function post_message() {
$request =
new HttpRequest('https://api.mailgun.net/v2/samples.mailgun.org/messages',
HttpRequest::METH_POST);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$request
->setPostFields(array('from' =>'Excited User'.
'<Excited User <me@samples.mailgun.org>>',
'to' => 'sergeyo@profista.com, serobnic@mail.ru',
'subject' => 'Hello',
'text' => 'Testing some Mailgun awesomness!'
));
$request->send();
return $request;
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111115080851.17787.43714@samples.mailgun.org>"
}
Sending a message with HTML and text parts. This example also attaches two files to the message:
function post_text_and_html_and_files() {
$request =
new HttpRequest('https://api.mailgun.net/v2/samples.mailgun.org/messages',
HttpRequest::METH_POST);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$request
->setPostFields(array('from' => 'Excited User <me@samples.mailgun.org>',
'to' => 'sergeyo@profista.com',
'subject' => 'Hello',
'text' => 'Testing some Mailgun awesomness!',
'html' => '<html>HTML version of the body</html>'
));
$request->addPostFile('attachment', 'files/test.txt');
$request->addPostFile('attachment', 'files/test.jpg');
$request->send();
return $request;
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111115080851.6405.60954@samples.mailgun.org>"
}
Sending a MIME message which you pre-build yourself using a MIME library of your choice:
function post_mime_message() {
$request =
new HttpRequest('https://api.mailgun.net/v2/samples.mailgun.org/'.
'messages.mime',
HttpRequest::METH_POST);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$request
->setPostFields(array('from' => 'Excited User <me@samples.mailgun.org>',
'to' => 'sergeyo@profista.com',
'subject' => 'Hello',
'text' => 'Testing some Mailgun awesomness!',
));
$request->addPostFile('message', 'files/message.mime');
$request->send();
return $request;
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111115080852.6404.7041@samples.mailgun.org>"
}
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:
function post_message_tracking_off() {
$request =
new HttpRequest('https://api.mailgun.net/v2/samples.mailgun.org/messages',
HttpRequest::METH_POST);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$request
->setPostFields(array('from' => 'Excited User <me@samples.mailgun.org>',
'to' => 'sergeyo@profista.com, serobnic@mail.ru',
'subject' => 'Hello',
'text' => 'Testing some Mailgun awesomness!',
'o:tracking' => false
));
$request->send();
return $request;
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111115080852.17787.24491@samples.mailgun.org>"
}
Sending a message with HTML and text parts. This example also attaches two files to the message and sets emails for CC and BCC:
function post_text_and_html_and_files_with_cc_and_bcc() {
$request =
new HttpRequest('https://api.mailgun.net/v2/samples.mailgun.org/messages',
HttpRequest::METH_POST);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$request
->setPostFields(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>'
));
$request->addPostFile('attachment', 'files/test.txt');
$request->addPostFile('attachment', 'files/test.jpg');
$request->send();
return $request;
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111115080852.6405.53274@samples.mailgun.org>"
}
An example of how to set message delivery time using the o:deliverytime option:
function post_message_delivery_time() {
$request =
new HttpRequest('https://api.mailgun.net/v2/samples.mailgun.org/messages',
HttpRequest::METH_POST);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$request
->setPostFields(array('from' => 'Excited User <me@samples.mailgun.org>',
'to' => 'sergeyo@profista.com, serobnic@mail.ru',
'subject' => 'Hello',
'text' => 'Testing some Mailgun awesomness!',
'o:deliverytime' => 'Fri, 25 Oct 2011 23:10:10 -0000'
));
$request->send();
return $request;
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111115080852.6404.39610@samples.mailgun.org>"
}
An example of how to tag a message with the o:tag option:
function post_message_with_tags() {
$request =
new HttpRequest('https://api.mailgun.net/v2/samples.mailgun.org/messages',
HttpRequest::METH_POST);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$data = array('from' => 'Excited User <me@samples.mailgun.org>',
'to' => array('sergeyo@profista.com', 'serobnic@mail.ru'),
'subject' => 'Hello',
'text' => 'Testing some Mailgun awesomness!',
'o:tag' => array('September newsletter', 'newsletters')
);
$dataString = '';
foreach ($data as $key => $val) {
if (is_array($val)) {
foreach ($val as $v) {
$dataString .= urlencode($key).'='.urlencode($v).'&';
}
} else {
$dataString .= urlencode($key).'='.urlencode($val).'&';
}
}
$request->setRawPostData(substr($dataString, 0, -1));
$request->send();
return $request;
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111115080853.17788.513@samples.mailgun.org>"
}
Sending a plain text message:
def post_message():
r = 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!"
}
)
return r
Response:
{
"message": "Queued. Thank you.",
"id": "<20120207071511.31733.9015@samples.mailgun.org>"
}
Sending a message with HTML and text parts. This example also attaches two files to the message:
def post_text_and_html_and_files():
r = requests.\
post(("https://api.mailgun.net/v2/samples.mailgun.org/"
"messages"),
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
files=MultiDict([
("attachment", open(os.path.join("files",
"test.jpg"))),
("attachment", open(os.path.join("files",
"test.txt"))),
]),
data={
"from": "Excited User <me@samples.mailgun.org>",
"to": ("sergeyo@profista.com,"
"serobnic@mail.ru"),
"subject": "Hello",
"text": "Testing some Mailgun awesomness!",
"html": "<html>HTML version of the body</html>"
}
)
return r
Response:
{
"message": "Queued. Thank you.",
"id": "<20120207071512.31733.84355@samples.mailgun.org>"
}
Sending a MIME message which you pre-build yourself using a MIME library of your choice:
def post_mime_message():
r = requests.\
post(("https://api.mailgun.net/v2/samples.mailgun.org/"
"messages.mime"),
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
data={
"to": "sergeyo@profista.com",
},
files={
"message": open(os.path.join("files",
"message.mime"))
}
)
return r
Response:
{
"message": "Queued. Thank you.",
"id": "<20120207071513.30916.43157@samples.mailgun.org>"
}
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:
def post_message_tracking_off():
r = 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
}
)
return r
Response:
{
"message": "Queued. Thank you.",
"id": "<20120207071513.31733.11043@samples.mailgun.org>"
}
Sending a message with HTML and text parts. This example also attaches two files to the message and sets emails for CC and BCC:
def post_text_and_html_and_files_with_cc_and_bcc():
r = requests.\
post(("https://api.mailgun.net/v2/samples.mailgun.org/"
"messages"),
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
files=MultiDict([
("attachment", open(os.path.join("files",
"test.jpg"))),
("attachment", open(os.path.join("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>"
}
)
return r
Response:
{
"message": "Queued. Thank you.",
"id": "<20120207071514.26600.87528@samples.mailgun.org>"
}
An example of how to set message delivery time using the o:deliverytime option:
def post_message_delivery_time():
r = 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"),
}
)
return r
Response:
{
"message": "Queued. Thank you.",
"id": "<20120207071515.31734.65362@samples.mailgun.org>"
}
An example of how to tag a message with the o:tag option:
def post_message_with_tag():
r = 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")
])
)
return r
Response:
{
"message": "Queued. Thank you.",
"id": "<20120207071516.30917.4734@samples.mailgun.org>"
}
Sending a plain text message:
public static RestResponse PostMessage() {
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);
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111115104146.6404.55913@samples.mailgun.org>"
}
Sending a message with HTML and text parts. This example also attaches two files to the message:
public static RestResponse PostTextAndHtmlAndFiles() {
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("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);
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111115104146.17788.96325@samples.mailgun.org>"
}
Sending a MIME message which you pre-build yourself using a MIME library of your choice:
public static RestResponse PostMimeMessage() {
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);
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111115104147.17787.30322@samples.mailgun.org>"
}
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:
public static RestResponse PostMessageTrackingOff() {
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);
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111115104147.17788.42361@samples.mailgun.org>"
}
Sending a message with HTML and text parts. This example also attaches two files to the message and sets emails for CC and BCC:
public static RestResponse PostTextAndHtmlAndFilesWithCcAndBcc() {
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);
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111115104147.17787.15582@samples.mailgun.org>"
}
An example of how to set message delivery time using the o:deliverytime option:
public static RestResponse PostMessageDeliveryTime() {
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);
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111115104147.6405.67461@samples.mailgun.org>"
}
An example of how to tag a message with the o:tag option:
public static RestResponse PostMessageWithTag() {
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);
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111115104147.17788.366@samples.mailgun.org>"
}
Sending a plain text message:
curl -s -k --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 to=ev@mailgun.net \
-F subject='Hello' \
-F text='Testing some Mailgun awesomness!'
Response:
{
"message": "Queued. Thank you.",
"id": "<20120215125214.26235.78102@samples.mailgun.org>"
}
Sending a message with HTML and text parts. This example also attaches two files to the message:
curl -s -k --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 to=ev@mailgun.net \
-F subject='Hello' \
-F text='Testing some Mailgun awesomness!' \
-F html='\<html\>HTML version of the body\<\html>' \
-F attachment=@files/cartman.jpg\
-F attachment=@files/cartman.png
Response:
{
"message": "Queued. Thank you.",
"id": "<20120215125215.4813.74280@samples.mailgun.org>"
}
Sending a MIME message which you pre-build yourself using a MIME library of your choice:
curl -s -k --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
Response:
{
"message": "Queued. Thank you.",
"id": "<20110701233001.E54C4F01F6B@mxa.mailgun.org>"
}
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 -k --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
Response:
{
"message": "Queued. Thank you.",
"id": "<20120215125217.26234.47234@samples.mailgun.org>"
}
Mailgun supports sending via SMTP. Our servers listen on ports 25, 587 and 465 (TLS).
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. |
When you submit messages for delivery Mailgun places them in a message queue.
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.
Mailing Lists provide an easy and convenient way to send to multiple recipients using single call. You can create and maintain your subscriber lists using the API or Control Panel.
Overview
To use Mailing Lists you create a Mailing List address, like developers@mailgun.net and add member addresses to it. Each time you send a message to developers@mailgun.net, the copy of it is delivered to each member until he or she unsubscribes from the list.
Managing a list
You can create Mailing Lists using the Mailing List tab in the Control Panel or the API. We support a couple of formats to make your life easier: you can upload the CSV file with members, use JSON or use form-like file upload.
Creating a mailing 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 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 new_mailing_list() {
$request =
new HttpRequest("https://api.mailgun.net/v2/lists",
HttpRequest::METH_POST);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$request->setPostFields(array('address' => 'dev@samples.mailgun.org',
'description' => "Mailgun developers list"));
$request->setOptions(array('connecttimeout' => 15));
$request->send();
return $request;
}
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"})
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);
}
curl -s -k --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/lists \
-F address='dev@samples.mailgun.org' \
-F description='Sample mailing list'
Adding a member:
def add_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 ClientResponse AddNewMember() {
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() {
$request =
new HttpRequest("https://api.mailgun.net/v2/lists/dev@samples.mailgun.org"
."/members",
HttpRequest::METH_POST);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$request->setPostFields(array('subscribed' => true,
'address' => 'bob@gmail.com',
'name' => 'Bob Bar',
'description' => "Developer",
'vars' => '{"age": 26}'));
$request->setOptions(array('connecttimeout' => 15));
$request->send();
return $request;
}
def add_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}'})
public static RestResponse AddNewMember() {
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);
}
curl -s -k --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}'
Note
You can attach a JSON dictionary with the structured data to each member of the mailing list to retrieve it later. We will add more cool features that work with the custom list data, so stay tuned.
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.
Note
There are limits on total number of all your mailing lists subscribers for an account: 200 for Free Plans, 50,000 for Basic Plans and 350,000 for Pro Plans (unlimited for High-Volume Plans). The number of mailing lists is not limited.
Sending to a list
We will only accept messages to your Mailing List if they arrive via API or an authenticated SMTP session, i.e. only from you. The outsiders (spammers) will not be allowed to submit messages to the mailing lists.
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 lisl 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
When sending messages to mailing lists you can use variables as usual to personalize your message for each recipient.
| 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.
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:
def schedule_message_delivery
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
Response:
{
"message": "Queued. Thank you.",
"id": "<20120203170725.26694.48614@samples.mailgun.org>"
}
Supply RFC 2822 or Unix epoch time to schedule your message:
public static ClientResponse PostMessageDeliveryTime() {
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);
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111114174248.25660.92496@samples.mailgun.org>"
}
Supply RFC 2822 or Unix epoch time to schedule your message:
function schedule_message_delivery() {
$request =
new HttpRequest('https://api.mailgun.net/v2/samples.mailgun.org/messages',
HttpRequest::METH_POST);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$request
->setPostFields(array('from' => 'Excited User <me@samples.mailgun.org>',
'to' => 'sergeyo@profista.com, serobnic@mail.ru',
'subject' => 'Hello',
'text' => 'Testing some Mailgun awesomness!',
'o:deliverytime' => 'Fri, 25 Oct 2011 23:10:10 -0000'
));
$request->send();
return $request;
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111115080857.6405.36569@samples.mailgun.org>"
}
Supply RFC 2822 or Unix epoch time to schedule your message:
def post_message_delivery_time():
r = 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"),
}
)
return r
Response:
{
"message": "Queued. Thank you.",
"id": "<20120207071521.31734.13701@samples.mailgun.org>"
}
Supply RFC 2822 or Unix epoch time to schedule your message:
public static RestResponse PostMessageDeliveryTime() {
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);
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111115104153.6405.3154@samples.mailgun.org>"
}
Supply RFC 2822 or Unix epoch time to schedule your message:
curl -s -k --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/samples.mailgun.org/messages \
-F from='Lovely Me <me@samples.mailgun.org>' \
-F to='Sasha Klizhentas <alex@mailgun.net>' \
-F subject='Hello' \
-F text='Testing some Mailgun awesomness!' \
-F o:deliverytime='Wed, 15 Feb 2012 12:52:17 -0000'
Response:
{
"message": "Queued. Thank you.",
"id": "<20120215125218.4813.70891@samples.mailgun.org>"
}
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.
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:
There are three methods for accessing tracking data on your messages:
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. Mailgun will attempt to perform an HTTP POST to the URL when events occur. We recommend using postbin.heroku.com for creating temporary URLs to test and debug your webhooks.
If your application is unable to process the webhook request, Mailgun will retry seven times before stop trying: three times with 10 minutes interval and then four times with 30 minutes interval.
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:
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.
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.
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.
def tag_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
Response:
{
"message": "Queued. Thank you.",
"id": "<20120203170731.26695.61583@samples.mailgun.org>"
}
Supply one or more o:tag parameters to tag the message.
public static ClientResponse PostMessageWithTag() {
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);
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111114174255.6404.11005@samples.mailgun.org>"
}
Supply one or more o:tag parameters to tag the message.
function tag_message() {
$request =
new HttpRequest('https://api.mailgun.net/v2/samples.mailgun.org/messages',
HttpRequest::METH_POST);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$data = array('from' => 'Excited User <me@samples.mailgun.org>',
'to' => array('sergeyo@profista.com', 'serobnic@mail.ru'),
'subject' => 'Hello',
'text' => 'Testing some Mailgun awesomness!',
'o:tag' => array('September newsletter', 'newsletters')
);
$dataString = '';
foreach ($data as $key => $val) {
if (is_array($val)) {
foreach ($val as $v) {
$dataString .= urlencode($key).'='.urlencode($v).'&';
}
} else {
$dataString .= urlencode($key).'='.urlencode($val).'&';
}
}
$request->setRawPostData(substr($dataString, 0, -1));
$request->send();
return $request;
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111115080859.6404.67027@samples.mailgun.org>"
}
Supply one or more o:tag parameters to tag the message.
def post_message_with_tag():
r = 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")
])
)
return r
Response:
{
"message": "Queued. Thank you.",
"id": "<20120207071524.26600.78697@samples.mailgun.org>"
}
Supply one or more o:tag parameters to tag the message.
public static RestResponse PostMessageWithTag() {
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);
}
Response:
{
"message": "Queued. Thank you.",
"id": "<20111115104158.17788.7846@samples.mailgun.org>"
}
Supply one or more o:tag parameters to tag the message.
curl -s -k --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/samples.mailgun.org/messages \
-F from='Lovely Me <me@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'
Response:
{
"message": "Queued. Thank you.",
"id": "<20120215125219.4814.6131@samples.mailgun.org>"
}
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:
def get_logs
RestClient.get "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/log", :params => {
:skip => 50,
:limit => 2
}
end
Response:
{
"total_count": 837,
"items": [
{
"hap": "delivered",
"created_at": "Fri, 03 Feb 2012 10:39:50 GMT",
"message": "Delivered: me@samples.mailgun.org \u2192 Sasha Klizhentas <alex@mailgun.net> 'Hello'",
"type": "info",
"message_id": "20120203103948.20654.16663@samples.mailgun.org"
},
{
"hap": "delivered",
"created_at": "Fri, 03 Feb 2012 10:39:50 GMT",
"message": "Delivered: me@samples.mailgun.org \u2192 ev@mailgun.net 'Hello'",
"type": "info",
"message_id": "20120203103948.20654.16663@samples.mailgun.org"
}
]
}
An example of how to fetch a single 51st log message skipping the first 50, using the API:
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();
Integer a = 50;
queryParams.add("skip", a);
queryParams.add("limit", 1);
return webResource.queryParams(queryParams).get(ClientResponse.class);
}
Response:
{
"total_count": 46,
"items": []
}
An example of how to fetch a single 51st log message skipping the first 50, using the API:
function get_logs() {
$request =
new HttpRequest('https://api.mailgun.net/v2/samples.mailgun.org/log',
HttpRequest::METH_GET);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$request->setQueryData(array('skip' => 1, 'limit' => 2));
$request->send();
return $request;
}
Response:
{
"total_count": 81,
"items": [
{
"message": "Sent: me@samples.mailgun.org \u2192 sergeyo@profista.com 'Hello' ",
"type": "info",
"created_at": "Mon, 14 Nov 2011 18:07:11 GMT"
},
{
"message": "Sent: me@samples.mailgun.org \u2192 sergeyo@profista.com 'Hello' ",
"type": "info",
"created_at": "Mon, 14 Nov 2011 18:07:01 GMT"
}
]
}
An example of how to fetch a single 51st log message skipping the first 50, using the API:
def get_logs():
r = requests.\
get(("https://api.mailgun.net/v2/samples.mailgun.org/"
"log"),
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
params={
"skip": 1,
"limit": 2
}
)
return r
Response:
{
"total_count": 1010,
"items": [
{
"hap": "delivered",
"created_at": "Tue, 07 Feb 2012 07:11:45 GMT",
"message": "Delivered: sbob@samples.mailgun.org \u2192 Sasha Klizhentas <alex@mailgun.net> 'Hello'",
"type": "info",
"message_id": "20120207071144.30917.84205@samples.mailgun.org"
},
{
"hap": "delivered",
"created_at": "Tue, 07 Feb 2012 07:11:43 GMT",
"message": "Delivered: me@samples.mailgun.org \u2192 Sasha Klizhentas <alex@mailgun.net> 'Hello'",
"type": "info",
"message_id": "20120207071142.26601.64240@samples.mailgun.org"
}
]
}
An example of how to fetch a single 51st log message skipping the first 50, using the API:
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);
}
Response:
{
"total_count": 123,
"items": [
{
"message": "Sent: me@samples.mailgun.org \u2192 sergeyo@profista.com 'Hello' ",
"type": "info",
"created_at": "Mon, 14 Nov 2011 18:06:22 GMT"
}
]
}
An example of how to fetch a single 51st log message skipping the first 50, using the API:
curl -s -k --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 -G \
https://api.mailgun.net/v2/samples.mailgun.org/log \
-d skip=50 \
-d limit=1
Response:
{
"total_count": 1211,
"items": [
{
"hap": "delivered",
"created_at": "Wed, 15 Feb 2012 03:51:52 GMT",
"type": "info",
"message": "Delivered: me@samples.mailgun.org \u2192 Sasha Klizhentas <alex@mailgun.net> 'Hello'",
"message_id": "20120215035151.31904.4170@samples.mailgun.org"
}
]
}
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. |
| campaign-id | The id 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). |
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. |
| campaign-id | The id 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). |
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 two types of unsubscribes: domain level or tag level.
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 |
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:
In the “Unsubscribes” tab of the Control Panel or through the API you can also:
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. |
| campaign-id | The id of the campaign that recipient has unsubscribed from. |
| 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). |
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. |
| 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). |
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:
Bounce tracking is always enabled.
In the “Bounces” tab of the Control Panel or through the API you can:
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. |
| 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). |
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”]. See below. |
| 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). |
“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.
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 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:
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
Response:
{
"total_count": 80,
"items": [
{
"total_count": 9,
"created_at": "Thu, 02 Feb 2012 00:00:00 GMT",
"tags": {},
"id": "4f29e61e4d532a3a823d419d",
"event": "sent"
},
{
"total_count": 1,
"created_at": "Thu, 02 Feb 2012 00:00:00 GMT",
"tags": {},
"id": "4f2ad1e84d532a3a823d5e92",
"event": "opened"
}
]
}
Get stats for ‘open’ and ‘sent’ events sorted by date:
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);
}
Response:
{
"total_count": 29,
"items": [
{
"total_count": 1,
"created_at": "Sat, 12 Nov 2011 00:00:00 GMT",
"tags": {},
"id": "4ebec0166c5f6b0f93598f4d",
"event": "sent"
},
{
"total_count": 192,
"created_at": "Fri, 11 Nov 2011 00:00:00 GMT",
"tags": {
"september_newsletter": 17,
"newsletters": 17
},
"id": "4ebd0aa96c5f6b0f93597659",
"event": "sent"
}
]
}
Get stats for ‘open’ and ‘sent’ events sorted by date:
function get_stats() {
$request =
new HttpRequest('https://api.mailgun.net/v2/samples.mailgun.org/stats',
HttpRequest::METH_GET);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$data = array('event' => array('sent', 'opened'),
'skip' => 1,
'limit' => 1);
$dataString = '';
foreach ($data as $key => $val) {
if (is_array($val)) {
foreach ($val as $v) {
$dataString .= urlencode($key).'='.urlencode($v).'&';
}
} else {
$dataString .= urlencode($key).'='.urlencode($val).'&';
}
}
$request->setQueryData(substr($dataString, 0, -1));
$request->send();
return $request;
}
Response:
{
"total_count": 30,
"items": [
{
"total_count": 52,
"created_at": "Mon, 14 Nov 2011 00:00:00 GMT",
"tags": {
"september_newsletter": 5,
"newsletters": 5
},
"id": "4ec153086c5f6b0f9359a8d8",
"event": "sent"
}
]
}
Get stats for ‘open’ and ‘sent’ events sorted by date:
def get_stats():
r = requests.\
get(("https://api.mailgun.net/v2/samples.mailgun.org/"
"stats"),
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
params={
"event": ["sent", "opened"],
"skip": 1,
"limit": 2
}
)
return r
Response:
{
"total_count": 87,
"items": [
{
"total_count": 178,
"created_at": "Mon, 06 Feb 2012 00:00:00 GMT",
"tags": {
"september_newsletter": 15,
"newsletters": 15
},
"id": "4f2f82b94d532a3a82400d00",
"event": "sent"
},
{
"total_count": 3,
"created_at": "Mon, 06 Feb 2012 00:00:00 GMT",
"tags": {},
"id": "4f2f7bf04d532a3a82400c6b",
"event": "opened"
}
]
}
Get stats for ‘open’ and ‘sent’ events sorted by date:
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);
}
Response:
{
"total_count": 30,
"items": [
{
"total_count": 52,
"created_at": "Mon, 14 Nov 2011 00:00:00 GMT",
"tags": {
"september_newsletter": 5,
"newsletters": 5
},
"id": "4ec153086c5f6b0f9359a8d8",
"event": "sent"
},
{
"total_count": 1,
"created_at": "Sat, 12 Nov 2011 00:00:00 GMT",
"tags": {},
"id": "4ebec0166c5f6b0f93598f4d",
"event": "sent"
}
]
}
Get stats for ‘open’ and ‘sent’ events sorted by date:
curl -s -k --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
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": {}
}
]
}
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:
Warning
Due to the increased cost of storing analytics, the cost of sending a message with a Campaign ID is twice the normal Mailgun rate. For example, if you are using the Pro Plan, sending a message with a Campaign ID will cost an additional $0.50 per 1,000 messages.
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:
def new_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 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 new_campaign() {
$request =
new HttpRequest("https://api.mailgun.net/v2/samples.mailgun.org/campaigns",
HttpRequest::METH_POST);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$request->setPostFields(array('name' => 'Newsletter',
'id' => 'my_campaign_id'));
$request->setOptions(array('connecttimeout' => 15));
$request->send();
return $request;
}
def new_campaign():
return requests.post(
"https://api.mailgun.net/v2/samples.mailgun.org/campaigns",
auth=('api', 'key-3ax6xnjp29jd6fds4gc373sgvjxteol0'),
data={'name': 'Newsletter',
'id': 'my_campaign_id'})
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);
}
curl -s -k --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/samples.mailgun.org/campaigns \
-F name='Newsletter' \
-F id='my_campaign_id'
Note
Supplied id must be pure ASCII and unique across all campaigns for a particular domain.
Note
There are limits on campaigns number for an account: 1 for Free Plans, 20 for Basic Plans and 1000 for Pro 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:
def new_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 ClientResponse PostMessage() {
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 new_campaign_message() {
$request =
new HttpRequest('https://api.mailgun.net/v2/samples.mailgun.org/messages',
HttpRequest::METH_POST);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$request
->setPostFields(array('from' =>'Excited User'.
'<Excited User <me@samples.mailgun.org>>',
'to' => 'roman@mailgun.net',
'subject' => 'Hello',
'text' => 'Testing some Mailgun awesomness!',
'o:campaign' => 'my_campaign_id'
));
$request->send();
return $request;
}
def new_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'})
public static RestResponse PostMessage() {
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);
}
curl -s -k --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'
Alternatively, if you’re building MIME yourself, include X-Mailgun-Campaign-Id header to send a campaign message.
You can include a message in up to three 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:
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:
def get_stats_by_daily_hour
RestClient.get("https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/campaigns/"\
"my_campaign_id/stats?groupby=daily_hour&limit=2")
end
Response:
[
{
"daily_hour": 8,
"total": {
"unsubscribed": 5,
"opened": 7,
"delivered": 15,
"clicked": 6,
"bounced": 0,
"complained": 0
},
"unique": {
"opened": {
"recipient": 3
},
"clicked": {
"recipient": 3,
"link": 3
}
}
},
{
"daily_hour": 13,
"total": {
"unsubscribed": 0,
"opened": 0,
"delivered": 6,
"clicked": 0,
"bounced": 0,
"complained": 0
},
"unique": {
"opened": {
"recipient": 0
},
"clicked": {
"recipient": 0,
"link": 0
}
}
}
]
Or retrieve the entire history for a particular recipient:
def get_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
Response:
[
{
"timestamp": "Wed, 08 Feb 2012 13:44:02 GMT",
"user_vars": {
},
"domain": "mailgunhq.com",
"recipient": "roman@mailgunhq.com",
"tags": [
],
"event": "delivered"
},
{
"timestamp": "Wed, 08 Feb 2012 13:38:35 GMT",
"user_vars": {
},
"domain": "mailgunhq.com",
"recipient": "roman@mailgunhq.com",
"tags": [
],
"event": "delivered"
}
]
You may want to see how’re recipients performing for your campaign during daily hours:
public static ClientResponse GetStatsByRegions() {
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);
}
Response:
[
{
"daily_hour": 8,
"total": {
"complained": 0,
"delivered": 15,
"clicked": 6,
"opened": 7,
"unsubscribed": 5,
"bounced": 0
},
"unique": {
"clicked": {
"link": 3,
"recipient": 3
},
"opened": {
"recipient": 3
}
}
},
{
"daily_hour": 13,
"total": {
"complained": 0,
"delivered": 9,
"clicked": 0,
"opened": 0,
"unsubscribed": 0,
"bounced": 0
},
"unique": {
"clicked": {
"link": 0,
"recipient": 0
},
"opened": {
"recipient": 0
}
}
}
]
Or retrieve the entire history for a particular recipient:
public static ClientResponse GetRecipientHistory() {
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);
}
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": {}
}
]
You may want to see how’re recipients performing for your campaign during daily hours:
function get_stats_groupby_daily_hour() {
$request =
new HttpRequest("https://api.mailgun.net/v2/samples.mailgun.org/campaigns"
."/my_campaign_id/stats?groupby=daily_hour&limit=2",
HttpRequest::METH_GET);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$request->setOptions(array('connecttimeout' => 15));
$request->send();
return $request;
}
Response:
[
{
"daily_hour": 8,
"total": {
"complained": 0,
"delivered": 15,
"clicked": 6,
"opened": 7,
"unsubscribed": 5,
"bounced": 0
},
"unique": {
"clicked": {
"link": 3,
"recipient": 3
},
"opened": {
"recipient": 3
}
}
},
{
"daily_hour": 13,
"total": {
"complained": 0,
"delivered": 9,
"clicked": 0,
"opened": 0,
"unsubscribed": 0,
"bounced": 0
},
"unique": {
"clicked": {
"link": 0,
"recipient": 0
},
"opened": {
"recipient": 0
}
}
}
]
Or retrieve the entire history for a particular recipient:
function get_recipient_history() {
$request =
new HttpRequest("https://api.mailgun.net/v2/samples.mailgun.org/campaigns"
."/my_campaign_id/events?recipient=roman@mailgunhq.com&limit=2",
HttpRequest::METH_GET);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$request->setQueryData(array('limit' => 2));
$request->setOptions(array('connecttimeout' => 15));
$request->send();
return $request;
}
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": {
}
}
]
You may want to see how’re recipients performing for your campaign during daily hours:
def get_stats_by_daily_hour():
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'))
Response:
[
{
"daily_hour": 8,
"total": {
"complained": 0,
"delivered": 15,
"clicked": 6,
"opened": 7,
"unsubscribed": 5,
"bounced": 0
},
"unique": {
"clicked": {
"recipient": 3,
"link": 3
},
"opened": {
"recipient": 3
}
}
},
{
"daily_hour": 13,
"total": {
"complained": 0,
"delivered": 6,
"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:
def get_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'))
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": {}
}
]
You may want to see how’re recipients performing for your campaign during daily hours:
public static RestResponse GetStatsByRegion() {
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);
}
Response:
[
{
"daily_hour": 8,
"total": {
"complained": 0,
"delivered": 15,
"clicked": 6,
"opened": 7,
"unsubscribed": 5,
"bounced": 0
},
"unique": {
"clicked": {
"link": 3,
"recipient": 3
},
"opened": {
"recipient": 3
}
}
},
{
"daily_hour": 13,
"total": {
"complained": 0,
"delivered": 9,
"clicked": 0,
"opened": 0,
"unsubscribed": 0,
"bounced": 0
},
"unique": {
"clicked": {
"link": 0,
"recipient": 0
},
"opened": {
"recipient": 0
}
}
}
]
Or retrieve the entire history for a particular recipient:
public static RestResponse GetRecipientHistory() {
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);
}
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": {
}
}
]
You may want to see how’re recipients performing for your campaign during daily hours:
curl -s -k --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 -G \
-d "groupby=daily_hour&limit=2" \
https://api.mailgun.net/v2/samples.mailgun.org/campaigns/my_campaign_id/stats
Response:
[
{
"daily_hour": 8,
"total": {
"complained": 0,
"delivered": 15,
"clicked": 6,
"opened": 7,
"unsubscribed": 5,
"bounced": 0
},
"unique": {
"clicked": {
"recipient": 3,
"link": 3
},
"opened": {
"recipient": 3
}
}
},
{
"daily_hour": 13,
"total": {
"complained": 0,
"delivered": 9,
"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 -k --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
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.
There are two ways to handle incoming messages using Mailgun:
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.
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) |
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.
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.
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:
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.
| 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
| 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.heroku.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.
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:
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
Response:
{
"message": "Route has been created",
"route": {
"description": "Sample route",
"created_at": "Fri, 03 Feb 2012 17:07:22 GMT",
"actions": [
"forward('http://myhost.com/messages/')",
"stop()"
],
"priority": 1,
"expression": "match_recipient('.*@samples.mailgun.org')",
"id": "4f2c144a3181336846057b77"
}
}
Listing routes:
def list_routes
RestClient.get "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/routes", :params => {
:skip => 1,
:limit => 1
}
end
Response:
{
"total_count": 167,
"items": [
{
"description": "Sample route",
"created_at": "Fri, 03 Feb 2012 17:07:21 GMT",
"actions": [
"forward('http://myhost.com/messages/')",
"stop()"
],
"priority": 1,
"expression": "match_recipient('.*@samples.mailgun.org')",
"id": "4f2c14493181336846057b6d"
}
]
}
Access the route by id:
def access_route_by_id
RestClient.
get("https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/routes/"\
"4e97c1b2ba8a48567f007fb6"){|response, request, result| response }
end
Response:
{
"message": "Route not found"
}
Create a route of the highest priority with multiple actions:
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);
}
Response:
{
"message": "Route has been created",
"route": {
"description": "Sample route",
"created_at": "Wed, 23 Nov 2011 09:37:56 GMT",
"actions": [
"forward(r'http://myhost.com/messages/')",
"stop()"
],
"priority": 1,
"expression": "match_recipient(r'.*@samples.mailgun.org')",
"id": "4eccbef4ba8a4857aa00f35b"
}
}
Listing routes:
public static ClientResponse ListRoutes() {
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);
}
Response:
{
"total_count": 33,
"items": [
{
"description": "Sample route",
"created_at": "Wed, 23 Nov 2011 09:37:41 GMT",
"actions": [
"forward(r'http://myhost.com/messages/')",
"stop()"
],
"priority": 1,
"expression": "match_recipient(r'.*@samples.mailgun.org')",
"id": "4eccbee51476d8676900f270"
}
]
}
Access the route by id:
public static ClientResponse AccessRouteById() {
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);
}
Response:
{
"message": "Route not found"
}
Create a route of the highest priority with multiple actions:
function create_route() {
$request =
new HttpRequest('https://api.mailgun.net/v2/routes',
HttpRequest::METH_POST);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$data = array(
'priority' => 1,
'description' => 'Sample route',
'expression' => 'match_recipient(".*@samples.mailgun.org")',
'action' => array('forward("http://myhost.com/messages/")',
'action' => 'stop()')
);
$dataString = '';
foreach ($data as $key => $val) {
if (is_array($val)) {
foreach ($val as $v) {
$dataString .= urlencode($key).'='.urlencode($v).'&';
}
} else {
$dataString .= urlencode($key).'='.urlencode($val).'&';
}
}
$request->setRawPostData(substr($dataString, 0, -1));
$request->send();
return $request;
}
Response:
{
"message": "Route has been created",
"route": {
"description": "Sample route",
"created_at": "Wed, 23 Nov 2011 09:37:04 GMT",
"actions": [
"forward(\"http://myhost.com/messages/\")",
"stop()"
],
"priority": 1,
"expression": "match_recipient(\".*@samples.mailgun.org\")",
"id": "4eccbec0ba8a4857ab00ef3d"
}
}
Listing routes:
function list_routes() {
$request =
new HttpRequest('https://api.mailgun.net/v2/routes',
HttpRequest::METH_GET);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$request->setQueryData(array('skip' => 1, 'limit' => 1));
$request->send();
return $request;
}
Response:
{
"total_count": 30,
"items": [
{
"description": "Sample route",
"created_at": "Wed, 23 Nov 2011 09:36:51 GMT",
"actions": [
"forward('http://myhost.com/messages/')",
"stop()"
],
"priority": 1,
"expression": "match_recipient('.*@samples.mailgun.org')",
"id": "4eccbeb3ba8a4857ab00ef31"
}
]
}
Access the route by id:
function access_route_by_id() {
$request =
new HttpRequest('https://api.mailgun.net/v2/routes/'.
'4e97c1b2ba8a48567f007fb6',
HttpRequest::METH_GET);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$request->send();
return $request;
}
Response:
{
"message": "Route not found"
}
Create a route of the highest priority with multiple actions:
def create_route():
r = 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()")
]))
return r
Response:
{
"message": "Route has been created",
"route": {
"description": "Sample route",
"created_at": "Tue, 07 Feb 2012 07:15:19 GMT",
"actions": [
"forward('http://myhost.com/messages/')",
"stop()"
],
"priority": 1,
"expression": "match_recipient('.*@samples.mailgun.org')",
"id": "4f30cf87947a8d7bf50245d9"
}
}
Listing routes:
def list_routes():
r = requests.\
get("https://api.mailgun.net/v2/routes",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
params={
"skip": 1,
"limit": 1
}
)
return r
Response:
{
"total_count": 207,
"items": [
{
"description": "Sample route",
"created_at": "Tue, 07 Feb 2012 07:15:18 GMT",
"actions": [
"forward('http://myhost.com/messages/')",
"stop()"
],
"priority": 1,
"expression": "match_recipient('.*@samples.mailgun.org')",
"id": "4f30cf8631813367e902475e"
}
]
}
Access the route by id:
def access_route_by_id():
r = requests.\
get(("https://api.mailgun.net/v2/routes/"
"4e97c1b2ba8a48567f007fb6"),
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"))
return r
Response:
{
"message": "Route not found"
}
Create a route of the highest priority with multiple actions:
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);
}
Response:
{
"message": "Route has been created",
"route": {
"description": "Sample route",
"created_at": "Wed, 23 Nov 2011 09:38:11 GMT",
"actions": [
"forward('http://myhost.com/messages/')",
"stop()"
],
"priority": 1,
"expression": "match_recipient('.*@samples.mailgun.org')",
"id": "4eccbf031476d8676800f003"
}
}
Listing routes:
public static RestResponse ListRoutes() {
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);
}
Response:
{
"total_count": 34,
"items": [
{
"description": "Sample route",
"created_at": "Wed, 23 Nov 2011 09:37:56 GMT",
"actions": [
"forward(r'http://myhost.com/messages/')",
"stop()"
],
"priority": 1,
"expression": "match_recipient(r'.*@samples.mailgun.org')",
"id": "4eccbef4ba8a4857aa00f35b"
}
]
}
Access the route by id:
public static RestResponse AccessRouteById() {
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);
}
Response:
{
"message": "Route not found"
}
Create a route of the highest priority with multiple actions:
curl -s -k --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()'
Response:
{
"message": "Route has been created",
"route": {
"description": "Sample route",
"created_at": "Wed, 15 Feb 2012 12:52:20 GMT",
"actions": [
"forward(\"http://myhost.com/messages/\")",
"stop()"
],
"priority": 1,
"expression": "match_recipient(\".*@samples.mailgun.org\")",
"id": "4f3baa84ba8a481c64004225"
}
}
Listing routes:
curl -s -k --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 -G \
https://api.mailgun.net/v2/routes \
-d skip=1 \
-d limit=1
Response:
{
"total_count": 260,
"items": [
{
"description": "Sample route",
"created_at": "Wed, 15 Feb 2012 12:35:59 GMT",
"actions": [
"forward(\"http://myhost.com/messages/\")",
"stop()"
],
"priority": 1,
"expression": "match_recipient(\".*@samples.mailgun.org\")",
"id": "4f3ba6af31813312ce002ddc"
}
]
}
Access the route by id:
curl -s -k --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/routes/4f3baa84ba8a481c64004225
Response:
{
"route": {
"description": "Sample route",
"created_at": "Wed, 15 Feb 2012 12:52:20 GMT",
"actions": [
"forward(\"http://myhost.com/messages/\")",
"stop()"
],
"priority": 1,
"expression": "match_recipient(\".*@samples.mailgun.org\")",
"id": "4f3baa84ba8a481c64004225"
}
}
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.
Listing all mailboxes:
def get_mailboxes
RestClient.get "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/mailboxes"
end
Response:
{
"total_count": 5,
"items": [
{
"size_bytes": null,
"created_at": "Sat, 29 Oct 2011 08:33:05 GMT",
"mailbox": "gino.heyman@samples.mailgun.org"
},
{
"size_bytes": 0,
"created_at": "Tue, 27 Sep 2011 20:24:22 GMT",
"mailbox": "postmaster@samples.mailgun.org"
},
{
"size_bytes": 0,
"created_at": "Wed, 07 Dec 2011 09:27:54 GMT",
"mailbox": "rahulk@samples.mailgun.org"
},
{
"size_bytes": null,
"created_at": "Mon, 23 Jan 2012 11:10:29 GMT",
"mailbox": "sergeyo@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:
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
Response:
{
"message": "Created 1 mailboxes"
}
Updating the password for a given mailbox:
def update_password
RestClient.put "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/mailboxes/sergeyo",
:password => "supersecret"
end
Response:
{
"message": "Password changed"
}
Deleting a given mailbox:
def delete_mailbox
RestClient.delete "https://api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0"\
"@api.mailgun.net/v2/samples.mailgun.org/mailboxes/sergeyo"
end
Response:
{
"message": "Mailbox has been deleted",
"spec": "sergeyo@samples.mailgun.org"
}
Listing all 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);
}
Response:
{
"total_count": 4,
"items": [
{
"size_bytes": null,
"created_at": "Sat, 29 Oct 2011 08:33:05 GMT",
"mailbox": "gino.heyman@samples.mailgun.org"
},
{
"size_bytes": 0,
"created_at": "Tue, 27 Sep 2011 20:24:22 GMT",
"mailbox": "postmaster@samples.mailgun.org"
},
{
"size_bytes": null,
"created_at": "Fri, 11 Nov 2011 17:53:04 GMT",
"mailbox": "sergeyo@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:
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);
}
Response:
{
"message": "Created 1 mailboxes"
}
Updating the password for a given mailbox:
public static ClientResponse UpdatePassword() {
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);
}
Response:
{
"message": "Password changed"
}
Deleting a given mailbox:
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);
}
Response:
{
"message": "Mailbox has been deleted",
"spec": "sergeyo@samples.mailgun.org"
}
Listing all mailboxes:
function get_mailboxes() {
$request =
new HttpRequest('https://api.mailgun.net/v2/samples.mailgun.org/mailboxes',
HttpRequest::METH_GET);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$request->send();
return $request;
}
Response:
{
"total_count": 4,
"items": [
{
"size_bytes": null,
"created_at": "Sat, 29 Oct 2011 08:33:05 GMT",
"mailbox": "gino.heyman@samples.mailgun.org"
},
{
"size_bytes": 0,
"created_at": "Tue, 27 Sep 2011 20:24:22 GMT",
"mailbox": "postmaster@samples.mailgun.org"
},
{
"size_bytes": null,
"created_at": "Mon, 14 Nov 2011 18:05:48 GMT",
"mailbox": "sergeyo@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:
function create_mailbox() {
$request =
new HttpRequest('https://api.mailgun.net/v2/samples.mailgun.org/mailboxes',
HttpRequest::METH_POST);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$request->setPostFields(array('mailbox' => 'sergeyo@samples.mailgun.org',
'password' => 'secret'));
$request->send();
return $request;
}
Response:
{
"message": "Created 1 mailboxes"
}
Updating the password for a given mailbox:
function update_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;
}
Response:
{
"message": "Password changed"
}
Deleting a given mailbox:
function delete_mailbox() {
$request =
new HttpRequest('https://api.mailgun.net/v2/samples.mailgun.org/mailboxes'.
'/sergeyo',
HttpRequest::METH_DELETE);
$auth = base64_encode('api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0');
$request->setHeaders(array('Authorization' => 'Basic '.$auth));
$request->send();
return $request;
}
Response:
{
"message": "Mailbox has been deleted",
"spec": "sergeyo@samples.mailgun.org"
}
Listing all mailboxes:
def get_mailboxes():
r = requests.\
get(("https://api.mailgun.net/v2/samples.mailgun.org/"
"mailboxes"),
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"))
return r
Response:
{
"total_count": 5,
"items": [
{
"size_bytes": null,
"created_at": "Sat, 29 Oct 2011 08:33:05 GMT",
"mailbox": "gino.heyman@samples.mailgun.org"
},
{
"size_bytes": 0,
"created_at": "Tue, 27 Sep 2011 20:24:22 GMT",
"mailbox": "postmaster@samples.mailgun.org"
},
{
"size_bytes": 0,
"created_at": "Wed, 07 Dec 2011 09:27:54 GMT",
"mailbox": "rahulk@samples.mailgun.org"
},
{
"size_bytes": null,
"created_at": "Mon, 06 Feb 2012 19:50:21 GMT",
"mailbox": "sergeyo@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:
def create_mailbox():
r = requests.\
post(("https://api.mailgun.net/v2/samples.mailgun.org/"
"mailboxes"),
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
data={"mailbox": "sergeyo@samples.mailgun.org",
"password": "secret"}
)
return r
Response:
{
"message": "Created 1 mailboxes"
}
Updating the password for a given mailbox:
def update_password():
r = requests.\
put(("https://api.mailgun.net/v2/samples.mailgun.org/"
"mailboxes/sergeyo"),
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
data={"password": "supersecret"}
)
return r
Response:
{
"message": "Password changed"
}
Deleting a given mailbox:
def delete_mailbox():
r = requests.\
delete(("https://api.mailgun.net/v2/samples.mailgun.org/"
"mailboxes/sergeyo"),
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"))
return r
Response:
{
"message": "Mailbox has been deleted",
"spec": "sergeyo@samples.mailgun.org"
}
Listing all mailboxes:
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);
}
Response:
{
"total_count": 4,
"items": [
{
"size_bytes": null,
"created_at": "Sat, 29 Oct 2011 08:33:05 GMT",
"mailbox": "gino.heyman@samples.mailgun.org"
},
{
"size_bytes": 0,
"created_at": "Tue, 27 Sep 2011 20:24:22 GMT",
"mailbox": "postmaster@samples.mailgun.org"
},
{
"size_bytes": null,
"created_at": "Tue, 15 Nov 2011 09:25:31 GMT",
"mailbox": "sergeyo@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:
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);
}
Response:
{
"message": "Created 1 mailboxes"
}
Updating the password for a given mailbox:
public static RestResponse UpdatePassword() {
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);
}
Response:
{
"message": "Password changed"
}
Deleting a given mailbox:
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);
}
Response:
{
"message": "Mailbox has been deleted",
"spec": "sergeyo@samples.mailgun.org"
}
Creating a new mailbox:
curl -s -k --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 \
https://api.mailgun.net/v2/samples.mailgun.org/mailboxes \
-F mailbox='user@samples.mailgun.org' \
-F password='supasecret'
Response:
{
"message": "Created 1 mailboxes"
}
Updating the password for a given mailbox:
curl -s -k --user api:key-3ax6xnjp29jd6fds4gc373sgvjxteol0 -X PUT \
https://api.mailgun.net/v2/samples.mailgun.org/mailboxes/user \
-F password='abc123'
Response:
{
"message": "Password changed"
}
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.
If you chose option 3, there are three headers we provide for you: X-Mailgun-SFlag, X-Mailgun-SScore and X-Mailgun-SPF.
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.
In addition to our HTTP API, Mailgun servers support all standard email protocols:
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:
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:
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
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = static:postmaster@mydomain.com:password
smtp_sasl_security_options = noanonymous
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
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.