Email API Key Security: 7 Practices That Protect Your Transactional Email
A leaked email API key is worse than a leaked database password. A database breach is contained — you rotate credentials, you audit rows, you move on. A leaked email API key lets an attacker send mail as your domain. They can blast phishing emails from [email protected], burn your sender reputation, and get your domain onto blocklists in hours. Recovery takes weeks.
Here are seven practices that keep your transactional email safe.
1. Scope the key, don't reuse the master key
Most email APIs ship with one all-powerful key that can read, send, and delete. Don't put that key in your production code.
Create a scoped key for each job:
- A
send-onlykey for your API server - A separate key for your admin dashboard
- A different key for each environment (dev, staging, prod)
If a scoped key leaks, the blast radius is one capability in one environment — not your whole account.
2. Store keys in the environment, never in the repo
This is the most common leak in the wild. A developer drops the key in a config file, commits it, pushes to GitHub, and the key is public within minutes.
Read from the environment every time:
export MAILANVIL_API_KEY="ma_live_xxxxxxxx"
const apiKey = process.env.MAILANVIL_API_KEY;
if (!apiKey) throw new Error("MAILANVIL_API_KEY not set");
Then add a pre-commit hook that fails on anything that looks like a live key.
3. Rotate on a schedule — and on every team change
Keys should expire. Set a rotation cadence — 90 days is a reasonable default for a low-churn team — and rotate immediately when:
- A developer with access leaves the company
- A key is committed to a repo, even for a second
- A key shows up in logs or error messages
Rotation is only painful if you hardcoded the key in ten places. If you followed practice #2, it's a one-line change per environment.
4. Prefer providers that hash keys at rest
Ask what happens when the provider's database is dumped. If the keys are stored in plaintext, every customer's key is in that dump.
A provider that stores keys as a SHA-256 hash can't leak your key in a database breach — the plaintext only exists at the moment you create it. This is a "boring infrastructure" feature you'll never see in a demo, and it's exactly the one that matters when it matters.
5. Revoke fast — a kill switch beats a graceful migration
When a key leaks, you don't have time for a "planned migration". You need to revoke it in one click and rotate in minutes, not days.
Test your revoke path before you need it. If you don't know how long a key rotation takes in production, you don't have a security process — you have a hope.
6. Don't ship keys to the browser
Transactional email belongs in your backend. A key in client-side JavaScript is public the moment the page loads — the browser's devtools will hand it to anyone.
If you need browser-triggered email (a "contact us" form, a password reset), route it through your own server. The browser hits your endpoint; your server holds the key.
7. Watch for the key in unexpected places
Keys leak into logs, error trackers, and support tickets all the time. A stack trace that dumps the request headers will dump your Authorization header too.
- Sanitize headers before logging
- Set up a GitHub secret-scanning alert
- Grep your own logs for your own key prefix once a month
What this looks like in practice
A tight setup for an Indonesian SaaS sending order confirmations:
| Layer | Practice |
|---|---|
| Key | Scoped send-only key, rotated every 90 days |
| Storage | Environment variable, injected at deploy time |
| Provider | Hashes keys at rest (SHA-256), one-click revoke |
| Code | Backend-only, headers sanitized before logging |
| Runtime | No key in browser, no key in repo, no key in logs |
The bottom line
Email API key security isn't glamorous, and it isn't hard. It's seven boring practices that take an afternoon to set up and save you a month of reputation repair when — not if — a key leaks.
The first question to ask any email provider is simple: "If your database is dumped, is my key in it?" If they can't answer "no" with a straight face, keep looking.