Everything you need to send your first email, and the honest version of what goes wrong afterwards.
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.
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.
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.
$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();
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.
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.
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);
}
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.
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.
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.
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.
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.
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 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.
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.
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.
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.
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.