MailKite
Get your API key
All guides Sending email

Send email with Amazon SES

Transactional email is the mail your app sends one recipient at a time — receipts, password resets, magic links, notifications. This guide sends one through Amazon SES, over both the API and SMTP, then shows the same send on MailKite.

What you'll need

  • A domain you control, with access to its DNS records.
  • An AWS account with Amazon SES, plus production access so you can send to any recipient.
  • AWS credentials for the API, or a set of SMTP credentials for the SMTP interface.

Part 1 — Send with Amazon SES

1. Verify your domain

In the SES console, under Configuration → Identities, create a domain identity. SES generates three Easy DKIM keys; publish the three CNAME records it shows you so SES can confirm you own the domain and sign your mail:

DNS — yourdomain.com
Type    Name                                Value
CNAME abc123._domainkey.yourdomain.com abc123.dkim.amazonses.com
CNAME def456._domainkey.yourdomain.com def456.dkim.amazonses.com
CNAME ghi789._domainkey.yourdomain.com ghi789.dkim.amazonses.com

The exact tokens are shown per identity in the console — copy them from there. New accounts also start in the SES sandbox, which only sends to verified addresses; to reach any recipient you must request production access for the account.

2. Send over the API

The SES API v2 send endpoint is POST https://email.<region>.amazonaws.com/v2/email/outbound-emails. Every request has to be signed with AWS Signature Version 4, so most people call it through an AWS SDK or the CLI, which handle the signing for you. Here it is with the CLI — the from address must be on the domain you just verified:

send
aws sesv2 send-email \
--from-email-address hello@yourdomain.com \
--destination ToAddresses=ada@example.com \
--content '{
"Simple": {
"Subject": { "Data": "Your invoice #1042" },
"Body": { "Html": { "Data": "<p>Thanks! Your receipt is attached.</p>" } }
}
}'

The response returns a MessageId:

response
{ "MessageId": "0100018f2c9a1b4e-3f7a…-000000" }

3. Or send over SMTP

Prefer SMTP? Point your app at the SES SMTP endpoint, email-smtp.<region>.amazonaws.com, on port 587 with STARTTLS. The username and password are SMTP credentials — generated in the SES console and derived from an IAM user, not your AWS access keys:

smtp
Host:      email-smtp.us-east-1.amazonaws.com
Port: 587 # STARTTLS
Username: AKIA… # SMTP username — generated in the SES console
Password: B… # SMTP password — derived from an IAM user
From: hello@yourdomain.com

That's a transactional send on Amazon SES: verify the domain, request production access, then call the signed API or the SMTP interface.

Part 2 — The same, on MailKite

When we built sending into MailKite, we wanted three things to be true: a plain bearer token instead of per-request SigV4 signing, no sandbox to apply your way out of, and one API key that's also your SMTP password — no separately-derived credential. Here's the same message.

1. Authenticate your domain

Publish three TXT records — SPF, a MailKite DKIM key, and DMARC. The same domain also receives mail once its MX points at MailKite:

DNS — yourdomain.com
Type   Name                              Value
TXT yourdomain.com v=spf1 include:mailkite.dev ~all
TXT mailkite._domainkey.yourdomain.com v=DKIM1; k=rsa; p=…
TXT _dmarc.yourdomain.com v=DMARC1; p=none;

The exact DKIM value is shown in the dashboard; MailKite verifies the records for you over DNS.

2. Send over the API

One POST /v1/send with a flat from/to/ subject/html body. A 202 means it's queued:

Pick your language in the card — the dashboard and the AI assistant do the same send, and every SDK takes the same fields:

send
import { MailKite } from "mailkite";

const mk = new MailKite(process.env.MAILKITE_API_KEY);

const { id, status } = await mk.send({
from: "hello@myapp.ai",
to: "ada@example.com",
subject: "Your invoice #1042",
html: "<p>Thanks! Receipt attached.</p>",
});
Install Docs →
response
202 Accepted
{ "id": "msg_2Hk9…", "status": "queued" }

3. Or send over SMTP

The same key is your SMTP password — no separate credential to generate:

smtp
import nodemailer from "nodemailer";

const tx = nodemailer.createTransport({
host: "smtp.mailkite.dev",
port: 587, // STARTTLS · use 465 for implicit TLS
auth: {
user: "mailkite", // any username works
pass: process.env.MAILKITE_API_KEY, // your mk_live_… key
},
});

await tx.sendMail({
from: "hello@myapp.ai", // an address on a verified domain
to: "ada@example.com",
subject: "Your invoice #1042",
html: "<p>Thanks! Receipt attached.</p>",
});

See Send API and SMTP relay for the full reference — attachments, headers, and delivery events.

A little about MailKite

MailKite is programmable email for developers and AI agents. One API key and one set of DNS records cover both directions: send with a single POST (or the SMTP relay), and the same verified domain receives replies as a signed webhook of clean JSON. Behind the send, MailKite routes across multiple providers to keep you in the inbox — and any address can be handed to an agent that reads and replies on its own. Sending and receiving are on the free tier.

Next: the Send API for attachments and delivery tracking, or the Quickstart to get a verified domain and API key.