User Manual

User Manual

Introduction

This document is meant to be an overview of all of the capabilities of Mailgun and how you can best leverage those capabilities. It is organized around the three major features that Mailgun provides:

At the heart of Mailgun is the API. Most of the Mailgun service can be accessed through the RESTful HTTP API without the need to install any libraries.

You can also access many Mailgun features through your Mailgun Control Panel using your browser and logging in at https://mailgun.net/cp.

In addition to the API, Mailgun supports standard email protocols (SMTP, POP3 and IMAP). We have included some instructions on how to use Mailgun with these protocols at the end of the User Manual.

If you are anxious to get started right away, feel free to check out the Quickstart Guide or API Reference. There are also FAQ’s and Email Best Practices that you can reference.

Finally, always feel free to contact us: support@mailgunhq.com.

Getting Started

We’ve tried to make the sign-up and on-boarding process as intuitive as possible. However, there are a few things to mention.

Pricing & Features Overview

Pricing

Pricing is a usage-based, monthly subscription with a minimum cost per month. Usage is based on messages sent and received and per GB of storage (if you are using Mailboxes). You can think of the minimum as a credit towards your usage costs. If you go over the minimum amount, just the additional usage cost is charged on top of the minimum.

We have High Volume plans that are more economical as you approach 1 million emails per month. Please contact sales@mailgunhq.com for more details.

Features

Most of Mailgun’s features are exposed for all of our plans. There are a few exceptions:

  • The Free Plan does not allow for custom domains / white labeling, it is limited to 200 messages per day (sent and received) and storage for mailboxes is temporary.
  • Only the Pro Plan and custom High Volume Plans have dedicated IP addresses assigned to the domain(s). Shared IP addresses may affect deliverability and timing of delivery to some extent, as you are sharing your traffic with other customers.
  • You can find a full list of features in our feature brochure.

Configuring a Domain

When you sign up for an account, you will need to pick a domain name. You have the ability to either use 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.

Sending Messages

There are two ways to send messages using Mailgun:

  • HTTP API
  • SMTP

Both methods work great and support the same feature set, so choose one based on your preferences and requirements.

Sending via API

When sending via HTTP API, Mailgun offers two options:

  • You can send emails in MIME format, but this would require you to use a MIME building library for your programming language.
  • You can submit the individual parts of your messages to Mailgun, such as text and html parts, attachments, and so on. This doesn’t require any MIME knowledge on your part.

Note

Mailgun supports maximum messages size of 25MB.

See sending messages section in our API Reference for a full list of message sending options.

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>"
}

Sending via SMTP

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.

Message Queue

When you submit messages for delivery Mailgun places them in a message queue.

  • You can submit a large amount of messages and Mailgun will automatically queue the delivery in compliance with the receiving domains’ guidelines and maximum allowed sending rate optimized for each ESP (email service provider) such as Yahoo, GMail, etc.
  • The Queue is dynamic so as you send more messages, your sending rates will increase, assuming you are sending quality traffic. (See Email Best Practices about warming up IP addresses.) Do not get discouraged if your messages take longer to be delivered at the beginning. As your reputation grows, your sending rate will grow too.

The queueing algorithms are one of the most important features of Mailgun. If you try to send bulk mailings all at once, most ISPs will block you, or worse, just drop your messages without telling you. In addition, it is important to gradually increase your sending rates according to many factors, including consistency of traffic, IP address sending history, and domain reputation.

Mailing Lists

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.

Scheduling Delivery

Mailgun also allows you to request a specific time for your message delivery by using the o:deliverytime parameter if sending via the API, or X-Mailgun-Deliver-By MIME header if sending via SMTP.

While messages are not guaranteed to arrive at exactly the requested time due to the dynamic nature of the queue, Mailgun will do it’s best.

Scheduling Delivery API Example

Supply RFC 2822 or Unix epoch time to schedule your message:

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>"
}

Sending in Test Mode

You can send messages in test mode by setting o:testmode parameter to true. When you do this, Mailgun will accept the message but will not send it. This is useful for testing purposes.

Tracking Messages

Once you start sending and receiving messages, it’s important to track what’s happening with them. We try to make tracking your messages as easy as possible.

Mailgun allows you to:

  • Access real-time message logs through the Control Panel or the API to see every message that you have sent or received and what has happened with it.
  • Access data on Events through the Control Panel or the API.
  • Receive notifications of events through a webhook each time an Open, Link Click, Bounce, Unsubscribe or Spam Complaint occurs.
  • Create Campaigns and access detailed analytics on those Campaigns through the Control Panel or the API.

There are three methods for accessing tracking data on your messages:

  • Control Panel: There are summary tracking stats, tables with event details and campaign reports.
  • HTTP API: You can programmatically access event data and tracking stats.
  • Events: Mailgun can perform an HTTP request into your application when an event occurs.

Events/Webhooks

When you submit a message to Mailgun to delivery, a number of events may happen to it. Mailgun keeps track of eight different types of such events:

Event Notes Webhook Available?
Message Sent Always enabled No
Deliveries Always enabled Yes
Bounces Always enabled Yes
Drops Always enabled Yes
Complaints Always enabled Yes
Unsubscribes Available if enabled in control panel Yes
Opens Available only if enabled in the control panel or via X-Mailgun-Track header Yes
Link Clicks Available only if enabled in the control panel or via X-Mailgun-Track header Yes

The events are logged and you can query them using our HTTP API. Additionally, Mailgun can make an HTTP POST request into your app (webhook) to notify you when an event happens.

If you would like Mailgun to POST event notifications (webhooks), you need to provide a callback URL in the respective tab of the Control Panel. 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:

  • Concatenate timestamp and token values.
  • Encode the resulting string with the HMAC algorithm (using your API Key as a key and SHA256 digest mode).
  • Compare the resulting hexdigest to the signature.
  • Optionally, you can check if the timestamp is not too far from the current time.

Note

Due to potentially large size of posted data, Mailgun computes an authentication signature based on a limited set of HTTP headers.

Below is a Python code sample used to verify the signature:

import hashlib, hmac

def verify(api_key, token, timestamp, signature):
    return signature == hmac.new(
                             key=api_key,
                             msg='{}{}'.format(timestamp, token),
                             digestmod=hashlib.sha256).hexdigest()

And here’s a sample in Ruby:

require 'openssl'

def verify(api_key, token, timestamp, signature):
    return signature == OpenSSL::HMAC.hexdigest(
                            OpenSSL::Digest::Digest.new('sha256'),
                            api_key,
                            '%s%s' % [timestamp, token])
end

Enable Tracking

Tracking for Messages Sent, Bounces and Complaints are always enabled.

You can enable Unsubscribes tracking in the “Unsubscribes” tab of the Control Panel. You can also manage unsubscribes per message by using the %unsubscribe_url% variable (see Tracking Unsubscribes)

You can enable Opens & Clicks tracking on two levels: per sending domain and per message.

  • You can enable Open & Click tracking on per domain basis in the “Tracking” tab of the control panel.
  • Tracking can also be toggled by setting o:tracking option when sending your message. This will override the domain-level setting.

Attaching Data to Messages

When sending, you can add custom X-Mailgun-Variables header to your message with a JSON string and it will be posted into your webhook handler. Several such headers may be included and their values will be combined.

Example:

X-Mailgun-Variables: {"first_name": "John", "last_name": "Smith"}
X-Mailgun-Variables: {"my_message_id": 123}

Note

Value of X-Mailgun-Variables header should be valid JSON string, otherwise Mailgun won’t be able to parse it.

Tagging

Sometimes it’s helpful to categorize your outgoing email traffic based on some criteria, perhaps separate signup emails from password recovery emails or from user comments. Mailgun lets you tag each outgoing message with a custom value. When you access stats on you messages, they will be aggregated by these tags.

Tagging Code Samples

Supply one or more o:tag parameters to tag the message.

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

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"
      }
  ]
}

Tracking Opens

Mailgun can keep track of every time a recipient opens your messages. You can access this data through the Control Panel. In addition, you can be notified through a webhook.

Opens are tracked by including a transparent .png file, which will only work if there is an HTML component to the email (i.e., text only emails will not track opens). You should note that many email service providers disable images by default, so this data will only show up if the recipient clicks on display images button in his/her email.

Note

Return Path certification allows your images to be enabled by default at many ISPs. Please contact us if you would like to get your IP Address certified.

You can enable Open tracking by clicking on the checkbox in the ‘Tracking’ tab of your Control Panel and adding the appropriate CNAME records to your DNS as specified in the ‘Domain’ tab of your Control Panel.

Opens Webhook

You can specify a webhook URL in the ‘Tracking’ tab of your Control Panel. When a user opens one of your emails, your URL will be invoked with the following parameters.

Parameter Name Description
event Event name (“opened”).
recipient Recipient who opened.
domain Domain that sent the original message.
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).

Tracking Clicks

Mailgun can keep track of every time a recipient clicks on a link in your messages. You can access this data through the Control Panel. In addition, you can be notified through a webhook.

All links will be overwritten and pointed to our servers so we can track clicks. This is only possible if you add the appropriate CNAME records to your DNS (as specified in the ‘Domain’ tab of your Control Panel).

You must enable Click tracking by clicking on the checkbox in the ‘Tracking’ tab of your Control Panel.

Clicks Webhook

You can specify a webhook URL in the ‘Tracking’ tab of your Control Panel. Every time a user clicks on a link inside of your messages, your URL will be called with the following parameters:

Parameter Name Description
event Event name (“clicked”).
recipient Recipient who clicked.
domain Domain that sent the original message.
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).

Tracking Unsubscribes

Mailgun can keep track of every time a recipient requests to be unsubscribed from your mailings. Mailgun can also insert unsubscribe links and remove those recipients from your mailings automatically for you.

Mailgun supports two types of unsubscribes: domain level or tag level.

  • Domain level: Once recipient selects to unsubscribe from domain, he will not receive any more messages from this sending domain.
  • Tag level: Sometimes you need to separate traffic by types, for example provide newsletter mailings, security updates mailings and so on. Recipients may want to unsubscribe from your newsletters but still receive security updates. For this purpose you can use tags: mark your messages by setting approriate X-Mailgun-Tag header and use special %tag_unsubscribe_url% variable (see below).

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:

  • Enable unsubscription feature for your domain.
  • Remove text in the html and text footer templates so they won’t be appended automatically.
  • Insert a variable in the html and text bodies of your email when you need unsubscribe links.
  • This variable will be replaced by the corresponding unsubscribe link.

In the “Unsubscribes” tab of the Control Panel or through the API you can also:

  • View/get a list of unsubscribed addresses.
  • Remove an unsubscribed address from the list.

Take a look at Unsubscribes section of the API refrence to learn how to programmatically manage lists of unsubscribed users.

Unsubscribes Webhook

You can specify a webhook URL in the ‘Unsubscribes’ tab of your Control Panel. When a user unsubscribes, Mailgun will invoke the webhook with the following parameters:

Parameter Name Description
event Event name (“unsubscribed”).
recipient Recipient who unsubscribed.
domain Domain that sent the unsubscribe request.
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).

Tracking Spam Complaints

Mailgun automatically keeps track of every time a recipient complains that a message is spam. You can access this data through the Control Panel or through the API. In addition, you can be notified through a webhook.

Email service providers (“ESPs”) are very sensitive to users clicking on spam complaint buttons and it’s important to monitor that activity to maintain a good sending reputation. While, not every ESP supports Feedback Loop (“FBL”) notifications, we make sure that you get data on all of the ones that do. We will remove recipients from future messages if a complaint has been filed by that recipient. This is necessary to maintain your reputation and not have your emails automatically sent to spam folders.

Spam Complaint tracking is always enabled.

Mailgun provides Spam complaints API to programmatically control the lists of users who have complainted.

Spam Complaints Webhook

You can specify a webhook URL in the ‘Complaints’ tab in the control panel. When a user reports one of your emails as spam, Mailgun will invoke the webhook with the following parameters:

Parameter Name Description
event Event name (“complained”).
recipient Recipient who clicked spam.
domain The domain that sent the complaint.
message-headers String list of all MIME headers of the original message dumped to a JSON string (order of headers preserved).
campaign-id The id of campaign triggering the event.
tag Message tag, if it was tagged. See Tagging.
mailing-list The address of mailing list the original message was sent to.
“custom variables” Your own custom JSON object included in the header (see Attaching Data to Messages).
timestamp Number of second passed since January 1, 1970 (see securing webhooks).
token Randomly generated string with length 50 (see securing webhooks).
signature String with hexadecimal digits generate by HMAC algorithm (see securing webhooks).

Tracking Bounces

Mailgun automatically keeps track of every time a message bounces (is not accepted by the recipient). You can access this data through the Control Panel or through the API. In addition, you can be notified through a webhook.

Mailgun classifies bounces into two groups:

  • Hard bounces: Recipient is not found either due to a bad domain name or the recipient is unknown. Mailgun stops attempting delivery to invalid recipients after one Hard Bounce.
  • Soft bounces: Email is not delivered because the mailbox is full or for other reasons. Mailgun will attempt to deliver messages multiple times depending on feedback from the ESP but will eventually quit attempting delivery in order to maintain your sending reputation.

Bounce tracking is always enabled.

In the “Bounces” tab of the Control Panel or through the API you can:

  • View/get a list of bounced addresses.
  • Remove a bounced address from the list.

You can have programmatic control over list of bounced recipients. See Bounces section in the API Reference for more details.

Bounce Event Webhook

You can specify a webhook URL in the ‘Bounces’ tab of your Control Panel. If you do, every time a message experiences a hard bounce, your URL will be invoked with the following parameters:

Parameter Name Description
event Event name (“bounced”).
recipient Recipient who could not be reached.
domain Domain of the recipient who could not be reached.
message-headers String list of all MIME headers of the original message dumped to a JSON string (order of headers preserved).
code SMTP bounce error code in form (X.X.X).
error SMTP bounce error string.
notification Detailed reason for bouncing (optional).
campaign-id The id of campaign triggering the event.
tag Message tag, if it was tagged. See Tagging.
mailing-list The address of mailing list the original message was sent to.
“custom variables” Your own custom JSON object included in the header (see Attaching Data to Messages).
timestamp Number of second passed since January 1, 1970 (see securing webhooks).
token Randomly generated string with length 50 (see securing webhooks).
signature String with hexadecimal digits generate by HMAC algorithm (see securing webhooks).

Tracking Failures

If you want to get notified every time a message fails to be delivered, you need to handle the “drop” event in your application.

Drop Event Webhook

In the Logs tab, you can specify a webhook URL to be notified every time a messages is dropped. There are a few reasons why Mailgun needs to stop attempting to deliver messages and drop them. The most common reason is that Mailgun received a Hard bounce or repeatedly received Soft bounces and continuing attempting to deliver may hurt your reputation with the receiving ESP. Also, if the address is on one of the ‘do not send lists’ because that recipient had previously bounced, unsubscribed or complained of spam, we will not attempt delivery and drop the message. If one of these events occur we will POST the following parameters to your URL:

Parameter Name Description
event Event name (“dropped”).
recipient Intended recipient.
domain Domain that sent the message.
message-headers String list of all MIME headers of the original message dumped to a JSON string (order of headers preserved).
reason Reason for failure. Can be one of [“hardfail”, “maxfails”]. 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.

Tracking Deliveries

If you want to get notified every time a message is delivered, you need to handle the “delivered” event in your application.

Delivered Event Webhook

In the Logs tab, you can specify a webhook URL to be notified every time a message is delivered. If the message is successfully delivered to the intended recipient, we will POST the following parameters to your URL:

Parameter Name Description
event Event name (“delivered”).
recipient Intended recipient.
domain Domain that sent the message.
message-headers String list of all MIME headers dumped to a JSON string (order of headers preserved).
Messsage-Id String id of the original message delivered to the recipient.
“custom variables” Your own custom JSON object included in the header of the original message (see Attaching Data to Messages).
timestamp Number of seconds passed since January 1, 1970 (see securing webhooks).
token Randomly generated string with length 50 (see securing webhooks).
signature String with hexadecimal digits generate by HMAC algorithm (see securing webhooks).

Note

Unlike other event webhooks (due to frequency of delivered events), Delivered Event will only POST once, right after delivery, and won’t attempt again in case of failure to POST successfully.

Stats

Stats provide you with the summary of the events that occur with your messages. We keep track of events for 6 months and stats are aggregated by tag, see Tagging above.

You can see your current statistics in the control panel, or download them using the API

Get stats for ‘open’ and ‘sent’ events sorted by date:

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": {}
      }
  ]
}

Campaign Analytics

Mailgun allows you to easily create Campaigns either using the API or the Campaigns Tab in the Control Panel. Campaigns are a tool to segment your email traffic for analysis. When you include messages in a Campaign, Mailgun tracks those messages and creates detailed analytics on those messages.

Campaign Analytics allow you to do things like:

  • Track events down to the individual recipient.
  • Compare Campaigns for A/B testing.
  • Optimize segmentation of Mailing Lists and content by geo location and recipient domain (Gmail, Hotmail, etc.).
  • Optimize delivery times by seeing engagement levels (opens & clicks) throughout the day.

Warning

Due to the increased cost of storing analytics, the cost of sending a message with a Campaign ID is 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:

  • Comparison of events across campaigns for A/B testing (including opens, clicks, bounces, unsubscribes and complaints).
  • Performance of link clicks in a campaign.
  • Comparison of events across Domains (Gmail, Yahoo, etc.).
  • Timeline of events occurring throughout the day to measure optimal delivery time (which can be used in conjunction with Scheduling Delivery).
  • Geographic performance of opens and clicks.

You can also create your own reports by accessing the data via the API.

You may want to see how’re recipients performing for your campaign during daily hours:

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.

Receiving Messages

There are two ways to handle incoming messages using Mailgun:

  • Forward incoming messages using Routes to a URL or to another email address.
  • Store incoming messages in Mailboxes.

Routes

You can define a list of routes to handle incoming emails. This idea of routes is borrowed from MVC web frameworks like Django or Ruby on Rails: if a message matches a route expression, Mailgun can forward it to your application via HTTP or to another email address.

You can define routes visually in the Control Panel, or programmatically using the API.

Mailgun route is a pair of filter+action. Each incoming message is passed to a filter expression, and if it evaluates to true, the action is executed.

Here’s a more formal list of route properties:

Field Description
Priority Integer indicating the priority of route execution. Lower numbers have higher priority.
Filter Filters available in routes - match_recipient() match_header() catchall() (see below for description).
Actions Type of action to take when a filter is triggered - forward() stop() (see below for description).
Description Arbitrary string to describe the route (shown in the Control Panel UI)

Route Filters

match_recipient(pattern)

Matches smtp recipient of the incoming message against the regular expression pattern. For example this filter will match messages going to foo@bar.com:

match_recipient("foo@bar.com")

You can use Python-style regular expression in your filter. For example this will match all messages coming to any recipient at @bar.com:

match_recipient(".*@bar.com")

Mailgun supports regexp captures in filters. This allows you to use captured values inside of your actions. The example below captures the local name (the part of email before @) and passes it as a mailbox parameter to an application URL:

route filter : match_recipient("(.*)@bar.com")
route action : forward("http://myhost.com/post/?mailbox=\1")

You can use named captures as well:

route filter : match_recipient("(?P<user>.*?)@(?P<domain>.*)")
route action : forward("http://mycallback.com/domains/\g<domain>/users/\g<user>")

match_header(header, pattern)

Similar to match_recipient but instead of looking at a message recipient, it applies the pattern to an arbitrary MIME header of the message. The example below matches any message with a word “support” in its subject:

match_header("Subject", ".*support")

catch_all()

Matches if no preceeding routes matched. Usually you need to use it in a route with a lowest priority, to make sure it evaluates last.

Route Actions

If a route expression evaluates to true, Mailgun executes the corresponding action. Currently you can use the folowing two actions in your routes: forward() and stop().

forward(destination)

Forwards the message to a specified destination, which can be another email address or a URL. A few examples:

forward("mailbox@myapp.com")
forward("http://myapp.com/messages")

You can combine multiple destinations separating them by a comma:

forward("http://myapp.com/messages, mailbox@myapp.com")

stop()

Simply stops the priority waterfall so the subsequent routes will not be evaluated.

Receiving Messages via HTTP

When you specify a URL of your application as a route destination, Mailgun can perform HTTP POST request into it using one of two following formats:

  • Fully parsed: Mailgun will parse the message, process attachments, attempt to separate quoted parts from the actual message. This is the preferred option.
  • Raw MIME: message is posted as-is. In this case you are responsible for parsing MIME. To receive raw MIME messages, the destination URL must end with mime.

Regardless of which delivery method you choose, Mailgun will transcode the original message into UTF-8 encoding, even for raw MIME. We do this because we love our users: most MIME parsers are not good at dealing with all of the various broken MIME messages that exist in the wild.

Below are two tables of HTTP parameters that you can expect to be posted into your application.

Note

In addition to these parameters Mailgun will post all MIME headers.

Parsed Messages Parameters

Parameter Type Description
recipient string recipient of the message as reported by MAIL TO during SMTP chat.
sender string sender of the message as reported by MAIL FROM during SMTP chat. Note: this value may differ from From MIME header.
from string sender of the message as reported by From message header, for example “Bob Lee <blee@mailgun.net>”.
subject string subject string.
body-plain string text version of the email. This field is always present. If the incoming message only has HTML body, Mailgun will create a text representation for you.
stripped-text string text version of the message without quoted parts and signature block (if found).
stripped-signature string the signature block stripped from the plain text message (if found).
body-html string HTML version of the message, if message was multipart. Note that all parts of the message will be posted, not just text/html. For instance if a message arrives with “foo” part it will be posted as “body-foo”.
stripped-html string HTML version of the message, without quoted parts.
attachment-count int how many attachments the message has.
attachment-x string attached file (‘x’ stands for number of the attachment). Attachments are handled as file uploads, encoded as multipart/form-data.
timestamp int number of second passed since January 1, 1970 (see securing webhooks).
token string randomly generated string with length 50 (see securing webhooks).
signature string string with hexadecimal digits generate by HMAC algorithm (see securing webhooks).
message-headers string list of all MIME headers dumped to a json string (order of headers preserved).
content-id-map string JSON-encoded dictionary which maps Content-ID (CID) of each attachment to the corresponding attachment-x parameter. This allows you to map posted attachments to tags like <img src='cid'> in the message body.

Note the message-headers parameter. It was added because not all web frameworks support multi-valued keys parameters. For example Ruby on Rails requires a special syntax to post params like that: you need to add [] to a key to collect it’s values on the server side as an array. Below is a Ruby on Rails example of obtaining MIME headers via message-headers parameter:

def mailgun_posted_params
  message_headers = JSON.parse(params["message-headers"])
  message_headers.each do |header|
    key, value = header
    puts "header key: #{key}, header value: #{value}"
  end
end

MIME Messages Parameters

Parameter Type Description
recipient string recipient of the message.
sender string sender of the message as reported by SMTP MAIL FROM.
from string sender of the message as reported by From message header, for example “Bob Lee <blee@mailgun.net>”.
subject string subject string.
body-mime string full MIME envelope. You will need a MIME parsing library to process this data.
timestamp int number of second passed since January 1, 1970 (see securing webhooks).
token string randomly generated string with length 50 (see securing webhooks).
signature string string with hexadecimal digits generate by HMAC algorithm(see securing webhooks).

Note

To receive raw MIME messages and perform your own parsing you must configure a route with a URL ending with “mime”, like http://myhost/post_mime.

Note

Consider using http://postbin.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.

API Routing Samples

Most users prefer to define their routes using the Control Panel. However, you can define routes programmatically using our HTTP API like in these examples.

Create a route of the highest priority with multiple actions:

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"
  }
}

Mailboxes

Mailgun gives you the ability to programmatically create mailboxes which can be used to receive and store mail. Mailboxes are accessible via POP3 or IMAP so you could connect to them with a typical email client. We charge per aggregate storage used, not per mailbox.

We don’t have an HTTP API to access data in Mailboxes, but context.io has some great APIs for this.

Mailbox API Examples

Listing all mailboxes:

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"
}

Spam Filter

If you are receiving email, you need spam filtering. Mailgun spam filtering is powered by an army of SpamAssassin machines. Mailgun gives you three ways to configure spam filtering. You can select the appropriate option in the Control Panel when you click on a domain name in the ‘Domains’ tab.

  • Disabled (default)
  • Delete spam (spam is removed and you won’t see it)
  • Mark spam with MIME headers and you decide what to do with it

If you chose option 3, there are three headers we provide for you: X-Mailgun-SFlag, X-Mailgun-SScore and X-Mailgun-SPF.

X-Mailgun-SFlag
Inserted with the value ‘Yes’ if the message was classified as a spam.
X-Mailgun-SScore

A ‘spamicity’ score that you can use to calibrate your own filter. Inserted for every message checked for a spam. The score ranges from low negative digits (very unlikely to be spam) to 20 and occasionally higher (very likely to be spam).

At the time of writing this, we are filtering spam at a score of around 5.0 but we are constantly calibrating this.

X-Mailgun-Spf
Mailgun will perform an SPF validation, and results will be stored in this header. Possible values are: ‘Pass’, ‘Neutral’, ‘Fail’ or ‘SoftFail’.

SMTP, POP3 and IMAP

In addition to our HTTP API, Mailgun servers support all standard email protocols:

  • You can send using SMTP with or without TLS.
  • You can receive mail using POP3 or IMAP.

Please consult a standard library documentation for language of your choice to learn how to use these protocols. Below are some helpful links for a few popular languages:

SMTP Relay

You can also configure your own mailserver to relay mail via Mailgun as shown below. All of them require these three variables which you can look up in the Control Panel:

  • Your SMTP username
  • Your SMTP password
  • SMTP host name mailserver (these instructions will use smtp.mailgun.org as an example)

You have an SMTP username and password for each domain you have at Mailgun. To send mail from a particular domain, just use the appropriate credentials.

Postfix Instructions

You have to configure a relay host with SASL authentication like shown below:

# /etc/postfix/main.cf:

mydestination = localhost.localdomain, localhost
relayhost = smtp.mailgun.org
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

Using Standard Email Clients

Standard email clients like Thunderbird or Outlook can also be used to send and fetch mail.

Settings for fetching mail:

POP3 server: pop.mailgun.org
IMAP server: imap.mailgun.org

Settings for sending mail:

SMTP server: smtp.mailgun.org

Note

Use a full mailbox name like “user@mymailgundomain.com” as a login for POP, IMAP and SMTP. SSL or TLS are supported for all protocols.