Event timelines
Sent, delivered, delayed, bounced, complained, rejected, opened, clicked, failed and suppressed, recorded per recipient, with the provider’s own response attached.
One call to send. A durable queue, DKIM-signed delivery through Amazon SES, and an event timeline for every message, in your own AWS account and your own database.
No vendor lock-in · your IPs, your data, your bill from AWS
curl https://api.relykit.com/emails \
-H "Authorization: Bearer rlk_live_..." \
-H "Idempotency-Key: order-6be1f2-receipt" \
-H "Content-Type: application/json" \
-d '{
"from": "Acme <receipts@acme.com>",
"to": "kai@example.com",
"subject": "Your receipt",
"html": "<p>Thanks for your order.</p>",
"tags": { "order_id": "6be1f2" }
}'
import { RelyKit } from 'relykit';
const relykit = new RelyKit({ apiKey: process.env.RELYKIT_API_KEY });
const email = await relykit.emails.send({
from: 'Acme <receipts@acme.com>',
to: 'kai@example.com',
subject: 'Your receipt',
html: '<p>Thanks for your order.</p>',
tags: { order_id: '6be1f2' },
}, { idempotencyKey: 'order-6be1f2-receipt' });
console.log(email.id, email.status); // → 'queued'
# The same message, for code that only speaks SMTP.
Host: smtp.relykit.com
Port: 587 (STARTTLS)
Username: api
Password: rlk_live_...
# Anything you send is validated, queued and tracked
# exactly like a call to POST /emails.
then
What you get
Every message keeps its own record: what you sent, what Amazon did with it, what the receiving server said back, and everything that happened afterwards.
| Day | Delivered | Bounced | Complained | Never sent | Total |
|---|---|---|---|---|---|
| 17 Sep | 1,512 | 9 | 0 | 1 | 1,522 |
| 16 Sep | 1,488 | 13 | 1 | 2 | 1,504 |
| 15 Sep | 1,421 | 10 | 0 | 1 | 1,432 |
| 14 Sep | 771 | 7 | 1 | 0 | 779 |
| 13 Sep | 806 | 8 | 0 | 0 | 814 |
| 12 Sep | 1,338 | 31 | 2 | 2 | 1,373 |
| 11 Sep | 1,290 | 96 | 5 | 11 | 1,402 |
| 10 Sep | 1,367 | 58 | 3 | 4 | 1,432 |
| 9 Sep | 1,402 | 15 | 2 | 0 | 1,419 |
| 8 Sep | 1,310 | 12 | 0 | 1 | 1,323 |
| 7 Sep | 688 | 9 | 1 | 3 | 701 |
| 6 Sep | 742 | 6 | 0 | 0 | 748 |
| 5 Sep | 1,244 | 11 | 0 | 1 | 1,256 |
| 4 Sep | 1,180 | 14 | 1 | 2 | 1,197 |
email.queued
email.sent
email.delivered
email.opened
email.clicked
The shape of it
Which is why the bill splits in two, and why the second half is Amazon's price rather than somebody's margin on it.
One HTTPS call, or SMTP on port 587 if that is what you already speak.
Idempotency, the queue, suppression, DKIM signing, tracking and the timeline.
billed by RelyKit
The reputation, the IP pools and the actual handoff to the receiving server.
billed by Amazon, at cost
Or a bounce, a complaint, or a deferral — each one a fact you get back.
Everything that happens comes back. SES publishes to SNS, RelyKit drains it from SQS, and the result reaches your timeline and your signed webhook — delivered, bounced, complained, opened, clicked.
On the wire
Behind every send is an SMTP exchange with the receiving server. Amazon SES reports how that exchange ended, and each outcome becomes an event on the message and, if you want it, a signed POST to your endpoint.
email.delivered if it subscribes to it.email.bounced with the diagnostic code.How it works
Adding a domain creates an Amazon SES identity with its own DKIM keys and a custom MAIL FROM subdomain, so SPF aligns and DMARC passes. You publish six records; we check each one and say exactly which is wrong.
One call to POST /domains returns the records to publish, already filled
in with your keys and region.
Records are re-checked against public resolvers every five minutes, and the moment DNS and SES agree the domain goes live.
Messages queue in Postgres, send through SES, and report back every delivery, bounce, complaint, open and click.
| Type | Host | Value | Purpose |
|---|---|---|---|
| CNAME | k1._domainkey.acme.com | k1.dkim.amazonses.com | DKIM signature |
| CNAME | k2._domainkey.acme.com | k2.dkim.amazonses.com | DKIM rotation |
| CNAME | k3._domainkey.acme.com | k3.dkim.amazonses.com | DKIM rotation |
| MX | send.acme.com | 10 feedback-smtp.eu-west-1.amazonses.com | Bounce return path |
| TXT | send.acme.com | v=spf1 include:amazonses.com ~all | SPF alignment |
| TXT | _dmarc.acme.com | v=DMARC1; p=none; rua=mailto:… | DMARC reporting |
Built in
Sent, delivered, delayed, bounced, complained, rejected, opened, clicked, failed and suppressed, recorded per recipient, with the provider’s own response attached.
HMAC-SHA256 headers with a five minute replay window, retries at 1m, 5m, 30m, 2h and 8h, and an endpoint that disables itself and emails you when it keeps failing.
Retry a send after a timeout and get the original message back instead of a duplicate in someone’s inbox.
Messages land in Postgres before anything tries to deliver them. Workers claim rows with locks, back off on throttling, and pick up where a crash left off.
On per account, through a separate SES configuration set, so untracked mail carries no pixel and no rewritten links.
Deliverability
On shared infrastructure one bad list ruins everyone's inbox placement. Amazon suspends senders above 10% bounces or 0.5% complaints. These limits are half of that, so sending stops here first.
Webhooks
Every event is delivered as a signed JSON POST carrying the message it belongs to, so your handler never has to call back for context.
{
"id": "5f2c1b7e-9d34-4a11-9d1e-5b2a0c7e4f31",
"type": "email.bounced",
"created_at": "2026-09-16T09:41:12.004Z",
"data": {
"email_id": "1f0a9c22-0e51-4d0c-8c5a-7f9b2a1d3e44",
"recipient": "kai@example.com",
"from": "Acme <receipts@acme.com>",
"subject": "Your receipt",
"tags": { "order_id": "6be1f2" },
"bounce_type": "hard",
"sub_type": "General",
"diagnostic_code": "smtp; 550 5.1.1 user unknown"
}
}
import { verifyWebhook } from 'relykit';
app.post('/hooks/relay', express.raw({ type: 'application/json' }), (req, res) => {
const ok = verifyWebhook({
secret: process.env.RELYKIT_WEBHOOK_SECRET,
headers: req.headers,
body: req.body.toString('utf8'),
});
if (!ok) return res.sendStatus(400);
const event = JSON.parse(req.body);
if (event.type === 'email.bounced') suspend(event.data.recipient);
res.sendStatus(200);
});SDK
Retries on 429 and 5xx, honours Retry-After, and turns every API error into one typed exception.
npm install relykittry {
await relykit.emails.send(payload, { idempotencyKey: order.id });
} catch (error) {
// 429 and 5xx were already retried, honouring Retry-After.
if (error.code === 'daily_limit_exceeded') return sendTomorrow(payload);
if (error.code === 'domain_not_verified') return alertOps(error.message);
throw error;
}
Pricing
Sending runs in your own AWS account, so the delivery cost lands on your AWS bill at Amazon's rate and nobody marks it up. These are the plans for the part around it: the queue, the tracking, the signing and the suppression.
Freeno card, no expiry
Enough to build against and to run something small.
$19a month
For a product with real users.
$99a month
For a business whose email volume is part of the business.
Talk to usvolume by agreement
Negotiated volume, with the limits lifted.
Including the bill you pay Amazon, which most comparisons leave out. Drag it to your volume. Where someone else is cheaper, it says so.
RelyKit Growth + Amazon
$124
$99 to us, $25 to Amazon
Resend Pro
$200
delivery included, at their rate
$76
a month cheaper here
Where Resend has more than one plan that covers a volume, the table quotes their cheapest, not the one that flatters us. SendGrid is not quoted at all: moved to Twilio during the merger; we could not read a current price, so we are not quoting one. Their prices are on their own page.
| A month | Plan | To us | To Amazon | Total | Resend | Difference |
|---|---|---|---|---|---|---|
| 3,000 | Free | $0 | $0.30 | $0.30 | $0 | $0.30 cheaper there |
| 10,000 | Starter | $19 | $1 | $20 | $20 | the same |
| 50,000 | Starter | $19 | $5 | $24 | $20 | $4 cheaper there |
| 100,000 | Starter | $59 | $10 | $69 | $65 | $4 cheaper there |
| 250,000 | Growth | $99 | $25 | $124 | $200 | $76 cheaper here |
| 500,000 | Growth | $99 | $50 | $149 | $425 | $276 cheaper here |
| 1,000,000 | Growth | $349 | $100 | $449 | $875 | $426 cheaper here |
| 2,500,000 | By agreement | — | $250 | — | $2,225 | — |
| 5,000,000 | By agreement | — | $500 | — | $4,475 | — |
Resend prices read from their pricing page on 2026-09-17. Amazon SES is $0.10 per thousand.
Nothing is billed today. Usage has been metered from the first message, so when there is a price you will be able to see exactly what it would have cost.
Sign in with an email address, add a domain, publish six DNS records, and send. The dashboard shows every message and everything that happened to it.