KapoMail

Documentation

Version 1.0 Effective 2026-08-19 Updated 2026-08-21

Everything you need to send your first email, and the honest version of what goes wrong afterwards.

Quickstart

Three steps. Most people are sending within ten minutes, and the slow part is almost always DNS propagation rather than anything you have to do.

  1. Add your domain. Paste it in, and we generate a DKIM key pair. The private key never leaves us; you publish the public half.
  2. Publish three DNS records. We show you the exact values, and we check them for you continuously. See the per-registrar notes below — this is where virtually every problem happens.
  3. Create a credential and send. A credential can only send from the domains you bind to it, so a leaked key cannot be used to send as someone else.

SMTP settings

If your application already sends mail, this is usually a four-line change and no code at all.

Setting Value
Host smtp.kapomail.com
Port 587 (STARTTLS) or 465 (implicit TLS)
Username your credential's name
Password the credential key
Encryption required — we refuse unencrypted connections

Port 25 is not offered. Most consumer ISPs and many hosting providers block it outbound, so supporting it would mean supporting a port that silently fails for a large share of users.

Sending from Laravel

Install the package:

composer require kapomail/laravel

Add the credential to your .env:

MAIL_MAILER=kapomail
KAPOMAIL_KEY=kapo_live_your_key_here
MAIL_FROM_ADDRESS=orders@yourdomain.com

Then send exactly as you already do — Mail::to() continues to work unchanged:

use Illuminate\Support\Facades\Mail;

Mail::to($order->email)->send(new OrderShipped($order));

When a send fails, the exception carries our plain-English reason rather than a generic transport error. That is the whole point: 550 5.1.1 in a log at midnight tells you nothing, and "the recipient address does not exist and has been suppressed automatically" tells you what happened and what to do.

Sending with PHPMailer

$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host       = 'smtp.kapomail.com';
$mail->SMTPAuth   = true;
$mail->Username   = 'your-credential-name';
$mail->Password   = 'kapo_live_your_key_here';
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port       = 587;

$mail->setFrom('orders@yourdomain.com', 'Your Shop');
$mail->addAddress('customer@example.com');
$mail->Subject = 'Your order has shipped';
$mail->Body    = 'Tracking number: AB123456789GB';

$mail->send();

Sending from WordPress

Install the KapoMail plugin, paste your key, and wp_mail() routes through us with no other changes — including mail sent by WooCommerce, Contact Form 7 and anything else that uses the WordPress mail function.

One thing worth knowing before you start: WordPress stores plugin settings in wp_options, which is readable by every other plugin on the site and included in most backups. That is not a KapoMail decision, it is how WordPress works. Use a credential created specifically for that site so it can be revoked on its own if the site is ever compromised, and do not reuse your main API key there.

Sending over the API

curl https://api.kapomail.com/v1/messages \
  -H "Authorization: Bearer kapo_live_your_key_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1234-shipped" \
  -d '{
    "from": "orders@yourdomain.com",
    "to": ["customer@example.com"],
    "subject": "Your order has shipped",
    "text": "Tracking number: AB123456789GB"
  }'

The Idempotency-Key header is worth using for anything triggered by a webhook or a queue. Retry with the same key and the same body and you get the original result back rather than a second email. Retry with the same key and a different body and you get a 409 — we will not quietly overwrite one message with another, because that hides a bug rather than absorbing it.

Sending from Node

const res = await fetch('https://api.kapomail.com/v1/messages', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.KAPOMAIL_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    from: 'orders@yourdomain.com',
    to: ['customer@example.com'],
    subject: 'Your order has shipped',
    text: 'Tracking number: AB123456789GB',
  }),
});

if (!res.ok) {
  const err = await res.json();
  // err.message is written for a human, not a machine.
  console.error(err.message);
}

DNS records, by registrar

This section exists because DNS is where essentially every setup problem happens, and the failures are silent — the record looks right in the control panel and simply does not work.

We check your records continuously and tell you which record is wrong and why, rather than showing a red cross and leaving you to guess.

The three failure modes

Almost every broken record is one of these three:

1. The host field gets the domain appended twice. Most registrars want only the prefix. Type kapo1._domainkey.yourdomain.com into a field that already appends your domain and you publish kapo1._domainkey.yourdomain.com.yourdomain.com, which resolves to nothing. If our checker sees this we say so explicitly.

2. The DKIM value is truncated at 255 characters. A 2048-bit DKIM key is longer than a single DNS string can hold. Some panels split it correctly; some silently cut it off. A truncated key fails authentication in a way that looks exactly like no key at all.

3. Whitespace or line breaks get pasted in. Copying from a formatted page can bring a trailing space or a newline. Some resolvers tolerate it, some do not, which produces the worst kind of bug — one that works for half your recipients.

Cloudflare

The easiest of the group. Set the host to just the prefix (kapo1._domainkey), paste the value, and make sure the record is DNS-only — grey cloud, not orange. Proxying a TXT record does nothing useful; proxying a CNAME breaks verification outright.

GoDaddy

The Host field wants the prefix only. GoDaddy appends your domain automatically, and this is the registrar where the doubled-domain mistake happens most often. GoDaddy also strips some trailing characters silently, so verify after saving rather than assuming.

123-reg

Long TXT values are the usual problem here. If the DKIM record will not save, or saves and then fails verification, check whether the panel has truncated it — compare the last few characters against what we show you.

Namecheap

Use Advanced DNS. The Host field takes the prefix only, and @ means the root domain. Namecheap's TXT handling is reliable once the host is right.

IONOS

IONOS occasionally reformats pasted values. After saving, reopen the record and compare it against ours — if it does not match exactly, delete and re-add rather than editing in place.

Everyone else

The principles are the same everywhere: prefix only in the host field, the value exactly as shown with no added spaces, and TXT records unproxied. If your registrar is not listed and something is not working, our checker will name the specific problem, and you can send that to us at support@kapomail.com">support@kapomail.com.

What happens when a message fails

Every failure gets classified, and the classification decides what we do:

What happened What we do
The address does not exist Suppressed automatically. Remove it from your database too.
The mailbox is full Retried. Not suppressed — a full mailbox is a person on holiday.
The server asked us to slow down Retried with increasing gaps. Not a failure.
Greylisting Retried. Normally arrives minutes later.
We were blocked on reputation Retried, and it does not count against you. This one is ours to fix.
Authentication failed We tell you which DNS record is wrong.
Marked as spam by the recipient Suppressed immediately.
Something we have not seen before Treated as temporary and retried, never assumed fatal.

That last row matters more than it looks. An unfamiliar rejection is far more likely to be wording we have not catalogued yet than a reason to permanently destroy one of your customers' addresses — so we retry and flag it for review rather than guessing. The two mistakes do not cost the same.

Suppressions

An address that hard bounces or complains is suppressed across your whole organisation, not just the project that sent to it. It has not stopped existing for a sibling project, and mailing it again from one damages the same reputation.

You can view, export and remove suppressions at any time — if a customer fixes their mailbox and asks why they are not hearing from you, you can clear it yourself without contacting us.

Getting help

Email support@kapomail.com">support@kapomail.com. A person reads it.

If you have found a security issue, security@kapomail.com">security@kapomail.com and our security page covers our disclosure policy, including safe harbour for good-faith research.