Self-hosted · Runs on your AWS account
Amazon SES sends the mail. This is the dashboard it never shipped.
Templates, a delivery timeline for every message, DKIM setup that writes its own DNS records, inbound mail, and reputation gauges — the parts a hosted sending service charges you for, running on the cheapest sender there is.
Bring an IAM access key and a region. Nothing else to configure.
Your receipt
to someone@example.com
- 14:02:11SendAccepted by SES · transactional
- 14:02:12DeliveryAccepted by mx1.recipient.com in 1.4s
- 14:09:47OpenFirst open
- 14:10:03Clickhttps://example.com/orders/8812
Every message keeps this. A bounce or a complaint lands in the same place, with the reason SES gave.
- Amazon SES$10
- Resend$90
- Postmark$115
- SendGrid$249
- Mailgun$215
Provider list prices as published, August 2026. SES bills per message with no minimum, so the same send at 10,000 or 10 million scales the same way. Pigeonhole adds nothing to it — there is no per-email fee here.
$0.10
per 1,000 sent
You keep SES pricing
Pigeonhole charges nothing per message and holds no mail account of its own. Your AWS bill is the bill.
Your account
your reputation
The sending domain stays yours
No shared IP pool, no provider whose other customers can damage your delivery, no migration to run if you outgrow a plan.
4 env vars
to run it
Self-hosted, on your own Postgres
A database URL, an auth secret, a public origin, and an encryption key. Every AWS and DNS credential lives in your database, managed in the app.
Per org
isolation
Teams without leaking credentials
Connections, messages, events, and keys belong to an organization, and no route reads across them. Members can send and edit templates without ever seeing an AWS key.
Four steps from an access key to a delivery timeline.
SES is a set of APIs and a lot of DNS. The work below is the work Pigeonhole does for you — in order, because none of it runs until the step above it does.
Connect an AWS account
Paste an IAM access key and pick a region. The key is encrypted before it is stored and never rendered back into a form. Test it against STS and SES first — you get back the account id, sending status, and daily quota before you rely on it.
Two fields. No CloudFormation stack, no role you have to trust us with.
Verify a domain without leaving the page
Add a domain and Pigeonhole reads the zone at your DNS provider — Cloudflare, Route 53, Vercel, Porkbun, or Namecheap — shows exactly which DKIM records are missing, and writes only those. It never edits or deletes a record that already exists; a conflict is reported and left to you.
Hosted somewhere else? Every record is listed with a copy button.
Send from the app or from your code
Compose a one-off message, or render a stored SES template and preview it through SES itself so what you see is what the recipient gets. Mint a bearer key and the same send runs from anywhere over one HTTP call.
Keys are scoped to an organization and can be pinned to one AWS account.
Watch what happened to it
Every message keeps its full timeline — delivered, opened, clicked, bounced, complained — straight off the SES event stream, next to the raw events. Bounce and complaint gauges are scaled to the rates that put an AWS account under review, not to 100%.
You see the number AWS is measuring you on, before AWS acts on it.
One HTTP call from anything you already run.
Mint a key at /settings/api-keys and your app, your cron job, or your staging environment sends through the same account — and lands in the same message list, with the same event timeline, as anything sent from the dashboard.
Pass templateName and templateData instead of a subject and body to send a stored template. Only the hash of a key is kept; the token is shown once.
curl -X POST https://your-host/api/send \ -H "Authorization: Bearer $PIGEONHOLE_KEY" \ -H "Content-Type: application/json" \ -d '{ "from": "hello@example.com", "to": "someone@example.com", "subject": "Your receipt", "html": "<p>Thanks for your order.</p>", "configurationSet": "transactional" }'Everything SES can do, with a place to do it.
Deliverability
- DKIM, custom MAIL FROM, and one-step DNS publishing
- Warm-up plans that ramp a new address on a schedule
- The account suppression list, readable and editable
- Bounce and complaint gauges keyed to AWS review thresholds
Sending
- SES templates with real SES-rendered previews
- Configuration sets and their SNS event destinations
- POST /api/send with per-organization bearer keys
- Send volume and delivery rates on the dashboard
Receiving
- Inbound mail parsed out of your own S3 bucket
- Original .eml and attachments downloadable from S3
- Setup checks that name the one prerequisite you are missing
- Every SNS payload verified against Amazon's signature
Your credentials, your account, your data.
Pigeonhole never falls back to ambient AWS credentials — not a profile, not an environment variable, not an instance role. A connection without a key fails rather than quietly sending as whoever the host happens to be. Each stored message remembers the account that sent or received it, so its raw MIME is always fetched with that account's key.
- Credentials at rest
- Encrypted with a key you generate, never rendered back into a form.
- Inbound buckets
- An allowlist per connection. No other bucket is read, whatever a notification claims.
- Invitations
- Expire in 24 hours, and are mailed through your own account — not ours.
Signing up creates your organization. Add an AWS account and the rest of the dashboard comes to life.