Spring Boot + MailKite
Spring's JavaMailSender is the framework's swappable mail seam — the same
role Rails' ActionMailer delivery methods and Laravel's Mail::extend
play. Point it at MailKite's SMTP relay with two properties, or install
mailkite-spring-boot-starter for a native bean backed by the API (templates,
batch sends, scheduling — things SMTP can't express). Either way, your @Service
code that calls JavaMailSender doesn't change.
What you need
- A verified domain with SPF + DKIM published
- Your API key (
mk_live_…) - Spring Boot 3.x (Java 17+)
Quick start: SMTP relay
spring-boot-starter-mail already wraps javax.mail/jakarta.mail
over SMTP — no new dependency, just point it at MailKite:
# application.properties
spring.mail.host=smtp.mailkite.dev
spring.mail.port=587
spring.mail.username=mailkite
spring.mail.password=${MAILKITE_API_KEY}
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
Autowire JavaMailSender like any other Spring bean:
// OrderMailService.java
@Service
public class OrderMailService {
private final JavaMailSender mailSender;
public OrderMailService(JavaMailSender mailSender) {
this.mailSender = mailSender;
}
public void sendConfirmation(Order order) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom("hello@yourdomain.com");
message.setTo(order.getEmail());
message.setSubject("Order #" + order.getId() + " confirmed");
message.setText("Thanks for your order!");
mailSender.send(message);
}
} Recommended: mailkite-spring-boot-starter
For templates, batch sends, scheduled sends, and dashboard-visible delivery status, add the
starter instead of (or alongside — it backs off if you already have a MailSender
bean) spring-boot-starter-mail:
<!-- pom.xml -->
<dependency>
<groupId>dev.mailkite</groupId>
<artifactId>mailkite-spring-boot-starter</artifactId>
<version>0.1.0</version>
</dependency> # application.properties
mailkite.api-key=${MAILKITE_API_KEY}
That's it — auto-configuration registers a MailKiteMailSender bean implementing
both MailSender and JavaMailSender the moment mailkite.api-key
(or the env var MAILKITE_API_KEY) is set. OrderMailService above
doesn't change — it still just calls JavaMailSender. HTML bodies and attachments
work through Spring's own MimeMessageHelper:
// Same JavaMailSender, now with an HTML body + attachment
MimeMessage mime = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mime, true);
helper.setFrom("hello@yourdomain.com");
helper.setTo("ada@example.com");
helper.setSubject("Your invoice");
helper.setText("<p>Thanks for your order!</p>", true);
helper.addAttachment("invoice.pdf", new ByteArrayResource(invoicePdfBytes));
mailSender.send(mime); Test it
# Run the starter's demo app (starters/spring-boot in the MailKite monorepo)
mvn spring-boot:run
# Or exercise your own controller once MAILKITE_API_KEY is set
curl -X POST http://localhost:8080/send \
-d "to=you@yourdomain.com" -d "subject=Test" -d "body=Hello from Spring Boot"
A full runnable example (a form + controller wired to MailKiteMailSender) lives at
starters/spring-boot in the MailKite SDK
source — see the library guide.
Receive inbound email
MailKite can POST inbound email to a Spring MVC controller.
Verify the signature with the Java SDK's static MailKite.verifyWebhook(...) —
no client instance, no hand-rolled HMAC:
// InboundEmailController.java
@RestController
public class InboundEmailController {
@Value("${mailkite.webhook-secret}")
private String webhookSecret;
@PostMapping("/webhooks/mailkite")
public ResponseEntity<String> handle(
@RequestHeader("x-mailkite-signature") String signature,
@RequestBody String rawBody) {
boolean verified = MailKite.verifyWebhook(signature, rawBody, webhookSecret);
if (!verified) {
return ResponseEntity.status(401).build();
}
// Parse rawBody as JSON and process: create a ticket, log, trigger a job, ...
return ResponseEntity.ok()
.header("Content-Type", "application/json")
.body("{\"status\":\"ok\"}");
}
} Troubleshooting
- Connection refused (SMTP) — you're probably on port 25. Set
spring.mail.port=587. - 535 Authentication failed (SMTP) —
spring.mail.passwordmust be yourmk_live_…API key, not a separate SMTP credential. - No qualifying bean of type 'JavaMailSender' — with the starter, this means
mailkite.api-key/MAILKITE_API_KEYisn't set (auto-configuration only activates when it's present) and no other mail starter is on the classpath either. - Wrong From address — set it explicitly in code (Spring has no app-wide default); it must be on a verified domain.
- Starter silently not sending through MailKite — the starter backs off if a
MailSenderbean already exists (e.g.spring-boot-starter-mail's SMTP one). ExcludeMailSenderAutoConfigurationor remove the SMTP starter if you want MailKite's bean to win.
See the SMTP relay docs for the full connection reference, or Send API for using the MailKite Java SDK directly.