Send email with Mandrill
Transactional email is the mail your app sends one recipient at a time — receipts, password resets, magic links, notifications. This guide sends one through Mandrill (Mailchimp Transactional), 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.
- A Mandrill API key from your Mailchimp Transactional account.
Part 1 — Send with Mandrill
1. Authenticate your domain
So your mail passes authentication and lands in the inbox, add a sending
domain under Settings → Domains, then publish the records it lists —
two DKIM CNAMEs and a DMARC policy — and click verify:
Type Name Value
CNAME mte1._domainkey.yourdomain.com dkim1.mandrillapp.com
CNAME mte2._domainkey.yourdomain.com dkim2.mandrillapp.com
TXT _dmarc.yourdomain.com v=DMARC1; p=none; Mandrill shows the exact records for your domain in the dashboard — copy them from there, since the DKIM host and value are account-specific. Mandrill rechecks DNS and marks the domain verified once the records resolve.
2. Send over the API
POST to Mandrill's messages/send endpoint. Your key
goes in the JSON body, and the from_email must be on the domain
you just authenticated:
curl -X POST https://mandrillapp.com/api/1.0/messages/send.json \
-H "Content-Type: application/json" \
-d '{
"key": "YOUR_API_KEY",
"message": {
"from_email": "hello@yourdomain.com",
"to": [{ "email": "ada@example.com", "type": "to" }],
"subject": "Your invoice #1042",
"html": "<p>Thanks! Your receipt is attached.</p>"
}
}'
A 200 returns an array — one object per recipient, with its
delivery status and message _id:
← 200 OK
[
{ "email": "ada@example.com", "status": "queued", "_id": "abc123…" }
] 3. Or send over SMTP
Prefer SMTP? Point your app at Mandrill's relay. The username is your account username, and the password is any valid Mandrill API key:
Host: smtp.mandrillapp.com
Port: 587 # TLS (or 465 for SSL)
Username: you@youraccount.com # your Mailchimp/Mandrill account username
Password: md-… # any valid Mandrill API key
From: hello@yourdomain.com That's a transactional send on Mandrill: authenticate the domain, then call the API or the SMTP relay.
Part 2 — The same, on MailKite
When we built sending into MailKite, we wanted three things to be true: one set of DNS records, one API key that works for both HTTP and SMTP, and a body you don't have to nest. 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:
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:
Messages → Compose
From hello@myapp.ai
To ada@example.com
Subject Your invoice #1042
Body Thanks! Receipt attached.
Click [ Send ]Email ada@example.com from hello@myapp.ai
with subject "Your invoice #1042" and body "Thanks! Receipt attached."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>",
});import os
from mailkite import MailKite
mk = MailKite(os.environ["MAILKITE_API_KEY"])
res = mk.send({
"from": "hello@myapp.ai",
"to": "ada@example.com",
"subject": "Your invoice #1042",
"html": "<p>Thanks! Receipt attached.</p>",
})<?php
$mk = new \MailKite\Client(getenv('MAILKITE_API_KEY'));
$res = $mk->send([
'from' => 'hello@myapp.ai',
'to' => 'ada@example.com',
'subject' => 'Your invoice #1042',
'html' => '<p>Thanks! Receipt attached.</p>',
]);MailKite mk = new MailKite(System.getenv("MAILKITE_API_KEY"));
Object res = mk.send(Map.of(
"from", "hello@myapp.ai",
"to", "ada@example.com",
"subject", "Your invoice #1042",
"html", "<p>Thanks! Receipt attached.</p>"
));mk := mailkite.New(os.Getenv("MAILKITE_API_KEY"))
res, err := mk.Send(mailkite.Message{
From: "hello@myapp.ai",
To: "ada@example.com",
Subject: "Your invoice #1042",
HTML: "<p>Thanks! Receipt attached.</p>",
})require "mailkite"
mk = Mailkite::Client.new(ENV["MAILKITE_API_KEY"])
res = mk.send(
"from" => "hello@myapp.ai",
"to" => "ada@example.com",
"subject" => "Your invoice #1042",
"html" => "<p>Thanks! Receipt attached.</p>"
)curl https://api.mailkite.dev/v1/send \
-H "Authorization: Bearer $MAILKITE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "hello@myapp.ai",
"to": "ada@example.com",
"subject": "Your invoice #1042",
"html": "<p>Thanks! Receipt attached.</p>"
}' Messages → Sent
● queued msg_2Hk9… ada@example.com Your invoice #1042Sent — message msg_2Hk9…, status queued.← 202 Accepted
{ "id": "msg_2Hk9…", "status": "queued" }← 202 Accepted
{ "id": "msg_2Hk9…", "status": "queued" }← 202 Accepted
{ "id": "msg_2Hk9…", "status": "queued" }← 202 Accepted
{ "id": "msg_2Hk9…", "status": "queued" }← 202 Accepted
{ "id": "msg_2Hk9…", "status": "queued" }← 202 Accepted
{ "id": "msg_2Hk9…", "status": "queued" }← 202 Accepted
{ "id": "msg_2Hk9…", "status": "queued" } 3. Or send over SMTP
The same key is your SMTP password — no separate credential to generate:
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>",
});import os, smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg["From"] = "hello@myapp.ai" # an address on a verified domain
msg["To"] = "ada@example.com"
msg["Subject"] = "Your invoice #1042"
msg.set_content("Thanks! Receipt attached.")
# Standard library — no dependency to install.
with smtplib.SMTP("smtp.mailkite.dev", 587) as s:
s.starttls()
s.login("mailkite", os.environ["MAILKITE_API_KEY"]) # password = your key
s.send_message(msg)<?php
use PHPMailer\PHPMailer\PHPMailer;
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = 'smtp.mailkite.dev';
$mail->SMTPAuth = true;
$mail->Username = 'mailkite'; // any username
$mail->Password = getenv('MAILKITE_API_KEY'); // your mk_live_… key
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587; // or 465 for implicit TLS
$mail->setFrom('hello@myapp.ai'); // an address on a verified domain
$mail->addAddress('ada@example.com');
$mail->Subject = 'Your invoice #1042';
$mail->Body = 'Thanks! Receipt attached.';
$mail->send();Properties p = new Properties();
p.put("mail.smtp.host", "smtp.mailkite.dev");
p.put("mail.smtp.port", "587"); // 465 for implicit TLS
p.put("mail.smtp.auth", "true");
p.put("mail.smtp.starttls.enable", "true");
Session s = Session.getInstance(p, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
// username = anything · password = your mk_live_… key
return new PasswordAuthentication("mailkite", System.getenv("MAILKITE_API_KEY"));
}
});
Message msg = new MimeMessage(s);
msg.setFrom(new InternetAddress("hello@myapp.ai")); // a verified domain
msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse("ada@example.com"));
msg.setSubject("Your invoice #1042");
msg.setText("Thanks! Receipt attached.");
Transport.send(msg);auth := smtp.PlainAuth("", "mailkite", os.Getenv("MAILKITE_API_KEY"), "smtp.mailkite.dev")
msg := []byte("From: hello@myapp.ai\r\n" + // an address on a verified domain
"To: ada@example.com\r\n" +
"Subject: Your invoice #1042\r\n\r\n" +
"Thanks! Receipt attached.\r\n")
// STARTTLS on 587; SendMail upgrades the connection before AUTH.
err := smtp.SendMail("smtp.mailkite.dev:587", auth,
"hello@myapp.ai", []string{"ada@example.com"}, msg)require "mail"
Mail.defaults do
delivery_method :smtp, {
address: "smtp.mailkite.dev",
port: 587, # 465 for implicit TLS
user_name: "mailkite", # any username
password: ENV["MAILKITE_API_KEY"], # your mk_live_… key
enable_starttls_auto: true,
}
end
Mail.deliver do
from "hello@myapp.ai" # an address on a verified domain
to "ada@example.com"
subject "Your invoice #1042"
body "Thanks! Receipt attached."
end 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.