Transactional email infrastructure I built from API to mail server.
Most alternatives stop at the queue and hand the third box to a supplier. That box is the one I chose to own.

The problem
Four problems, and only one of them is technical.
Discovery
No client brief and no research budget. The question was whether this market had a real gap or just a crowded middle.
Signed up to all four and sent real mail through each
| What I compared | All four | Sendpository |
|---|---|---|
| Transactional emailThe category everyone serves well. | Yes | Yes |
| Bills in your own currency (₹)They bill in USD, so there is conversion on top. | No | Yes |
| Indian GST tax invoiceWithout one, the GST paid cannot be claimed back. | No | Yes |
| Marketing in the same planMost split the two into separate subscriptions. | No | Yes |
| No surprise overage billOverage billing is the most common complaint in this category. | No | Yes |
| Two DNS records to startThree or four records, plus an MX entry on a subdomain. | No | Yes |
The opportunity was never better delivery. It was simpler economics, packaging and setup.
The line that runs from what people complained about to what the product refuses to do.
Research
Four competitors used as a customer, plus forums and issue trackers.
What recurred
Surprise overage bills. Marketing sold separately. Invoices with no claimable tax.
The opening
Not deliverability - economics, packaging and setup.
The decision
Price in rupees, one quota for both kinds of mail, a hard stop, two DNS records.
TL;DRThe opportunity was not better email delivery. It was simpler economics, packaging and setup.
What came out of it was narrower than where I started. My first instinct was a general-purpose email API; the research said that competing on deliverability alone against providers with a decade of IP reputation was unwinnable. The gap was somewhere else - in billing, in packaging, and in how much setup a developer is asked to do before anything works.
Product decisions
Acting as the product manager on my own work meant deciding what to refuse.
Should marketing email be a second product?
One quota for both.
How much setup can we demand before someone gives up?
Reversed: three records, not two.
Where should the customer publish our records?
A dedicated subdomain, never the apex.
Is verifying a domain once enough?
Re-check every domain, every day.
What happens when a customer exceeds their plan?
Sending stops at the quota.
Who is this actually for?
Indian businesses, priced in rupees.
Build the hard part, or rent it?
Run the mail servers myself.
What is an admin screen actually for?
Show values, with an Edit button.
What blocks a send, what is merely recommended, and which name each one sits on.
Required to send
Recommended, never blocking
Everything but the DKIM key sits on an sp. subdomain the customer uses for nothing else, so the SPF record they already publish on their apex for Google Workspace is never touched. An earlier version put SPF on the apex and required only two records - it read as less setup, but the record was never the one evaluated, and SPF could not align for DMARC at all.
A solo build has one scarce resource, and it is not ideas.
Built
Deliberately not built
Every one is a real feature of a competing product. None of them gets a developer closer to their first successful send.
Does a developer need this before their first successful send? A template builder and contact segmentation are genuinely useful and genuinely expensive, and neither gets anyone closer to a working integration. The alternatives page on the product says so plainly, including where competitors are the better choice - a comparison that only flatters itself is dismissed by exactly the reader it was written for.
TL;DREvery major decision traded short-term revenue for trust and simplicity.
The product
Everything a developer needs to send their first email, and everything a business needs to be billed for it.
Request
POST /v1/emails
Authorization: Bearer sk_live_...
Idempotency-Key: 8f14e45f
{
"from": "billing@yourdomain.com",
"to": "customer@example.com",
"subject": "Your invoice",
"html": "<p>Thanks!</p>"
}What arrives
Read out of the received headers on a real delivery, not assumed.
One POST, and a message that authenticates on arrival.
One POST with from, to, subject and body. Attachments, CC and BCC, custom headers, reply-to, and an idempotency key so a retried request never sends twice.
Pass a timestamp and the message is held until then, cancellable right up to the moment it is handed to the mail servers.
A typed client published to npm as `sendpository` - zero dependencies, dual ESM and CommonJS builds, and webhook signature verification included.
The first place anyone looks when an email did not arrive.
Every message with its status, events and timing. Searchable, filterable, exportable - the first place anyone looks when an email did not arrive.
Delivery, bounce and complaint events with an HMAC signature and the timestamp inside the signed payload, so a captured request cannot be replayed.
Hard bounces and spam complaints suppress the address automatically. Sending to a dead address twice is how a sender reputation dies.
Domains, money and the console that runs the whole thing.
Generates an RSA keypair per domain, shows the exact DNS records, and checks them against live DNS. Three records, on a subdomain that cannot collide with the customer’s existing mail.
Gapless invoice numbering, CGST/SGST/IGST by place of supply, credit notes on refund, Razorpay checkout and autopay, coupons applied before tax.
Clients, plans, coupons, invoices, refunds, impersonation with an audit trail, and an infrastructure page that reports what is misconfigured.
A full docs site with quickstart, guides and API reference - plus llms.txt, so an AI assistant can answer questions about the product accurately.
And the sixth box is the one almost every competitor rents.
POST /emails
One HTTPS request in.
Authentication
API key hashed, scope checked.
Domain verification
Is this domain proven yours?
Quota check
Reserved inside the UPDATE, so two requests cannot both pass.
Suppression check
Bounced and complained addresses never sent to twice.
Queue
Accepted, durable, scheduled if asked.
Mail server
Postal, on my own IP. This is the part I chose to own.
Inbox
SPF, DKIM and DMARC passing on arrival.
Inside the box everyone else rents
Developer experience
The API is what the product does. The SDK is what using it feels like - so it is designed, not generated.
Three lines to a delivered email.
import { Sendpository } from "sendpository";
const sendpository = new Sendpository(process.env.SENDPOSITORY_API_KEY);
const { id } = await sendpository.emails.send({
from: "Acme <hello@mail.acme.com>",
to: ["customer@example.com"],
subject: "Your code is 481920",
html: "<p>It expires in 10 minutes.</p>",
});The key is read from the environment when you do not pass one, because the most common mistake in this category is a key that ends up in a browser bundle.
The scope split exists so that the credential in your application is not the credential that can create more of them.
sending
What your application should use
full_access
Kept out of the app
A sending key that leaks can send email. It cannot mint more keys - which is the difference between an incident and a catastrophe.
TL;DRZero dependencies is a security decision as much as a size one. Every package a client library pulls in is a package that can reach the key it holds.
Full reference in the SDK guide.
The process
In the order the risks needed answering, not the order that demos well. Pick a step.
01
The first thing built was not a dashboard - it was a real message, from a real domain, arriving in a real inbox with SPF, DKIM and DMARC all passing. Everything else is worthless if that never works, so it came first rather than last.
Architecture
Pick any piece to see what it does. The mail server is the one that makes this a platform rather than a wrapper.
Postal (my VPS)
A real MTA with its own IP and reverse DNS, TLS submission on 587, inbound bounce handling on 25, and a provisioning service so domains register themselves.
The piece almost every competitor rents.
Prove a customer owns a domain, then sign as them - without ever holding a key that could leak, and without letting one customer send as another.
Customer adds a domain
Platform generates an RSA keypair
Customer publishes SPF and DKIM
Platform checks live DNS
Ownership confirmed
Messages signed with the private key
Send allowed
The key cannot leak. The private half is encrypted at rest with AES-256-GCM under a key that lives only in the environment, so a database dump yields nothing signable. It is decrypted in memory at send time and never rendered to a browser.
Customer A cannot send as Customer B. Tenant isolation was tested on every endpoint; every cross-account attempt returns 404 rather than a permission error that would confirm the record exists.
The interesting parts were rarely the features. They were the choices underneath.
TL;DRI did not just build the API. I built the infrastructure underneath it.
Security
A platform holding other people’s signing keys has to be tested as an attacker would, not as its author would.
Attacked against the live system, not a staging copy
Tenant isolation held on every endpoint. Parameterised SQL throughout. No open relay. Every admin and cron path refused unauthenticated access.
Three real findings
TL;DRThree real findings, all fixed and covered by tests. The rest held.
Billing
Indian tax law is specific about invoices. A wrong number is a filing error, not a formatting bug - so try it.
The total is identical either way - the split is what changes, and getting it wrong is a filing error rather than a display bug.
Beyond the split: gapless invoice numbering that holds under concurrent requests, credit notes that reverse tax proportionally on refund, coupons applied to the taxable value before tax rather than to the total, and pro-rated upgrades that charge only for the days remaining in the period. All of it arithmetic in whole paise, verified by tests before a rupee moved.
Outcome
Live, authenticating and documented - with the infrastructure work most similar products pay a vendor to avoid.
SPF, DKIM and DMARC all passing from a domain and IP built from nothing - verified in received headers, not assumed.
12 suites covering tax arithmetic, quota races, tenant isolation, SSRF, encryption, MFA and retention - run against a real database, not mocks.
Cross-tenant access, injection, open relay and auth bypass all tested live. Two findings fixed; the rest held.
A full docs site, an SDK published to npm, and structured data plus llms.txt so AI assistants answer questions about it correctly.
No team, no handover. Every decision was mine to make and mine to live with.
Pick any one of them to see what it actually meant here.
Most full-stack work stops at the application boundary. This one goes past it - into DNS, TLS, mail transfer agents, IP reputation and tax law - because a transactional email product that stops at the API is a wrapper, not a platform. Taking it end to end meant being the product manager deciding what not to build, the designer drawing the screens, the engineer writing the tax arithmetic, and the operator on the server at two in the morning when the reverse DNS was wrong.