Amazon Simple Email Service
Developer Guide

Sending Raw Email Using the Amazon SES API

Sometimes you might want more control over how Amazon SES composes and sends email than automatic formatting provides. If so, you can use the Amazon SES raw email interface to specify email headers and MIME types, to send highly customized messages to your recipients.

This section introduces you to some common email standards and how Amazon SES uses them. It also shows how to construct and send raw email from the command line and from the Amazon SES API.

About Email Header Fields

Simple Mail Transfer Protocol (SMTP) specifies how email messages are to be sent by defining the mail envelope and some of its parameters, but it does not concern itself with the content of the message. Instead, the Internet Message Format (RFC 5322) defines how the message is to be constructed.

With the Internet Message Format specification, every email message consists of a header and a body. The header consists of message metadata, and the body contains the message itself. For more information about email headers and bodies, see Email Format and Amazon SES.

Using MIME

The SMTP protocol is designed for sending email messages composed of 7-bit ASCII characters. While this works well for many use cases, it is insufficient for non-ASCII text encodings (such as Unicode), binary content, or attachments. The Multipurpose Internet Mail Extensions standard (MIME) was developed to overcome these limitations, making it possible to send many other kinds of content using SMTP.

The MIME standard works by breaking the message body into multiple parts and then specifying what is to be done with each part. For example, one part of an email message body might be plain text, while another might be an image. In addition, MIME allows email messages to contain one or more attachments. Message recipients can view the attachments from within their email clients, or they can save the attachments.

The message header and content are separated by a blank line. Each part of the email is separated by a boundary, a string of characters that denotes the beginning and ending of each part.

Here is an example of the raw text of a multipart MIME email message:

Received: from ( by (; Wed, 2 Mar 2011 11:39:39 -0800 From: "Bob" <> To: "Andrew" <> Date: Wed, 2 Mar 2011 11:39:34 -0800 Subject: Customer service contact info Message-ID: <> Accept-Language: en-US Content-Language: en-US Content-Type: multipart/mixed; boundary="_003_97DCB304C5294779BEBCFC8357FCC4D2" MIME-Version: 1.0 --_003_97DCB304C5294779BEBCFC8357FCC4D2 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable Hi Andrew. Here are the customer service names and telephone numbers I promised you. See attached. -Bob --_003_97DCB304C5294779BEBCFC8357FCC4D2 Content-Type: text/plain; name="cust-serv.txt" Content-Description: cust-serv.txt Content-Disposition: attachment; filename="cust-serv.txt"; size=1180; creation-date="Wed, 02 Mar 2011 11:39:39 GMT"; modification-date="Wed, 02 Mar 2011 11:39:39 GMT" Content-Transfer-Encoding: base64 TWFyeSBEYXZpcyAtICgzMjEpIDU1NS03NDY1DQpDYXJsIFRob21hcyAtICgzMjEpIDU1NS01MjM1 DQpTYW0gRmFycmlzIC0gKDMyMSkgNTU1LTIxMzQ= --_003_97DCB304C5294779BEBCFC8357FCC4D2

Note the following aspects of this example:

  • A blank line separates the header from the body.

  • The content type is "multipart/mixed," which indicates that the message has many parts and the receiver must handle each part separately.

  • The "boundary" parameter specifies where each part begins and ends. In this case, the boundary is a unique string of characters that the sender's email client generates.

  • There are two parts to the body, a plain text message and an attachment. The email client will display the plain text part, and it will handle the attachment separately.

  • The "Content-Disposition" field specifies how the client should handle the attachment: When the reader clicks the attachment, the email client will attempt to save it to a text file named "cust-serv.txt".

MIME Encoding

Because of the 7-bit ASCII restriction of SMTP, any content containing 8-bit characters must first be converted to 7-bit ASCII before sending. MIME defines a Content-Transfer-Encoding header field for this purpose.

By convention, the most common encoding scheme is base64, where 8-bit binary content is encoded using a well-defined set of 7-bit ASCII characters. Upon receipt, the email client inspects the Content-Transfer-Encoding header field, and can immediately perform a base64 decode operation on the content, thus returning it to its original form. With most email clients, the encoding and decoding occur automatically, and the user need not be aware of it.

In the example above, the "cust-serv.txt" attachment must be decoded from base64 format in order to be read. Some email clients will encode all MIME parts in base64 format, even if they were originally in plain text. This is not normally an issue, since email clients perform the encoding and decoding automatically.


For a list of MIME types that Amazon SES accepts, see Appendix: Unsupported Attachment Types.

If you want certain parts of a message, like some headers, to contain characters other than 7-bit ASCII, then you must use MIME encoded-word syntax (RFC 2047) instead of a literal string. MIME encoded-word syntax uses the following form: =?charset?encoding?encoded-text?=. For more information, see RFC 2047. If you want to send to or from email addresses that contain unicode characters in the domain part of an address, you must encode the domain using Punycode. For more information, see RFC 3492.


The Amazon SES API provides the SendRawEmail action, which lets you compose and send an email message in the format that you specify. For a complete description of SendRawEmail, go to the Amazon Simple Email Service API Reference.


For tips on how to increase your email sending speed when you make multiple calls to SendRawEmail, see Increasing Throughput with Amazon SES.

The message body must contain a properly formatted, raw email message, with appropriate header fields and message body encoding. Although it is possible to construct the raw message manually within an application, it is much easier to do so using existing mail libraries.


The following code sample shows how to use the JavaMail library and the AWS SDK for Java to compose and send an email.

import; import; import; import java.nio.ByteBuffer; import java.util.Properties; import java.util.UUID; // These are from the JavaMail API, which you can download at // Be sure to include the mail.jar library in your project. In the build order, mail.jar should precede the AWS SDK for Java library. import javax.activation.DataHandler; import javax.activation.DataSource; import javax.activation.FileDataSource; import javax.mail.Address; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; // These are from the AWS SDK for Java, which you can download at // Be sure to include the AWS SDK for Java library in your project. import com.amazonaws.AmazonClientException; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.profile.ProfileCredentialsProvider; import com.amazonaws.regions.Region; import com.amazonaws.regions.Regions; import; import; import; public class ComposeAndSendMIMEEmail { // IMPORTANT: To successfully send an email, you must replace the values of the strings below with your own values. private static String EMAIL_FROM = "SENDER@EXAMPLE.COM"; // Replace with the sender's address. This address must be verified with Amazon SES. private static String EMAIL_REPLY_TO = "REPLY-TO@EXAMPLE.COM"; // Replace with the address replies should go to. This address must be verified with Amazon SES. private static String EMAIL_RECIPIENT = "RECIPIENT@EXAMPLE.COM"; // Replace with a recipient address. If your account is still in the sandbox, // this address must be verified with Amazon SES. private static String EMAIL_ATTACHMENTS = "ATTACHMENT-FILE-NAME-WITH-PATH"; // Replace with the path of an attachment. Must be a valid path or this project will not build. // Remember to use two slashes in place of each slash. // IMPORTANT: Ensure that the region selected below is the one in which your identities are verified. private static Regions AWS_REGION = Regions.US_WEST_2; // Choose the AWS region of the Amazon SES endpoint you want to connect to. Note that your sandbox // status, sending limits, and Amazon SES identity-related settings are specific to a given AWS // region, so be sure to select an AWS region in which you set up Amazon SES. Here, we are using // the US West (Oregon) region. Examples of other regions that Amazon SES supports are US_EAST_1 // and EU_WEST_1. For a complete list, see private static String EMAIL_SUBJECT = "Amazon SES email test"; private static String EMAIL_BODY_TEXT = "This MIME email was sent through Amazon SES using SendRawEmail."; public static void main(String[] args) throws AddressException, MessagingException, IOException { Session session = Session.getDefaultInstance(new Properties()); MimeMessage message = new MimeMessage(session); message.setSubject(EMAIL_SUBJECT, "UTF-8"); message.setFrom(new InternetAddress(EMAIL_FROM)); message.setReplyTo(new Address[]{new InternetAddress(EMAIL_REPLY_TO)}); message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(EMAIL_RECIPIENT)); // Cover wrap MimeBodyPart wrap = new MimeBodyPart(); // Alternative TEXT/HTML content MimeMultipart cover = new MimeMultipart("alternative"); MimeBodyPart html = new MimeBodyPart(); cover.addBodyPart(html); wrap.setContent(cover); MimeMultipart content = new MimeMultipart("related"); message.setContent(content); content.addBodyPart(wrap); String[] attachmentsFiles = new String[]{ EMAIL_ATTACHMENTS }; // This is just for testing HTML embedding of different type of attachments. StringBuilder sb = new StringBuilder(); for (String attachmentFileName : attachmentsFiles) { String id = UUID.randomUUID().toString(); sb.append("<img src=\"cid:"); sb.append(id); sb.append("\" alt=\"ATTACHMENT\"/>\n"); MimeBodyPart attachment = new MimeBodyPart(); DataSource fds = new FileDataSource(attachmentFileName); attachment.setDataHandler(new DataHandler(fds)); attachment.setHeader("Content-ID", "<" + id + ">"); attachment.setFileName(fds.getName()); content.addBodyPart(attachment); } html.setContent("<html><body><h1>HTML</h1>\n" + EMAIL_BODY_TEXT + "</body></html>", "text/html"); try { System.out.println("Attempting to send an email through Amazon SES by using the AWS SDK for Java..."); /* * The ProfileCredentialsProvider will return your [default] * credential profile by reading from the credentials file located at * (~/.aws/credentials). * * TransferManager manages a pool of threads, so we create a * single instance and share it throughout our application. */ AWSCredentials credentials = null; try { credentials = new ProfileCredentialsProvider().getCredentials(); } catch (Exception e) { throw new AmazonClientException( "Cannot load the credentials from the credential profiles file. " + "Please make sure that your credentials file is at the correct " + "location (~/.aws/credentials), and is in valid format.", e); } // Instantiate an Amazon SES client, which will make the service call with the supplied AWS credentials. AmazonSimpleEmailServiceClient client = new AmazonSimpleEmailServiceClient(credentials); Region REGION = Region.getRegion(AWS_REGION); client.setRegion(REGION); // Print the raw email content on the console PrintStream out = System.out; message.writeTo(out); // Send the email. ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); message.writeTo(outputStream); RawMessage rawMessage = new RawMessage(ByteBuffer.wrap(outputStream.toByteArray())); SendRawEmailRequest rawEmailRequest = new SendRawEmailRequest(rawMessage); client.sendRawEmail(rawEmailRequest); System.out.println("Email sent!"); } catch (Exception ex) { System.out.println("Email Failed"); System.err.println("Error message: " + ex.getMessage()); ex.printStackTrace(); } } }