
Editorial date: . First published October 19, 2026.
Why we built SendHog: SES is transport, not your email operation
Amazon SES transports application email. Your application still needs to identify each logical send, handle an uncertain response, and act on feedback. We built SendHog to manage that boundary, not to make email guarantees SES cannot make.
The send call is the smallest part
An application wants to send a receipt. SES wants an authenticated request, a verified sending identity, available regional capacity, and a message it can accept. Those are reasonable requirements. They also describe only the beginning of the application’s responsibility.
The receipt also needs an identity that survives a worker restart. Otherwise the next worker can’t distinguish unfinished work from another receipt it should send. That’s an application problem even when the transport is working correctly.
AWS documents the underlying pieces clearly. Its notification guide says senders must have a system for managing bounces and complaints. Its configuration sets connect messages to event destinations. These are useful building blocks, not omissions in SES.
The distinction is between moving a message and maintaining the evidence needed to decide what happens next.
A domain needs more than a green DNS check
Domain verification answers a narrow question: does this sender control the identity? It doesn’t answer whether an application should currently be permitted to use it, how much it can send, or which user may create credentials for it.
There are at least three separate relationships:
- A DNS owner authorizes a provider to sign mail for a domain.
- An application account controls access to that domain inside a service.
- A sending credential permits a particular application to perform particular operations.
Conflating them creates awkward security boundaries. A domain member shouldn’t automatically get access to every domain owned by the payer. A send-only key shouldn’t become a message-reading key because a new dashboard feature shipped.
Authentication has another boundary. DMARC alignment relates the visible From domain to an authenticated SPF or DKIM domain. Publishing a DMARC TXT record isn’t proof that a real message passed alignment. Neither operation proves that a mailbox provider will put that message in the inbox.
An operator should be able to name which relationship a green check verifies, not infer permission from its color.
Acceptance and delivery need different names
A successful provider response is evidence of acceptance, not evidence of reading, inbox placement, or even final delivery. Those are different observations made by different systems.
A production design should give each logical send a stable application identifier and bind any provider-facing idempotency key to its content. Persist an attempt before provider I/O, and retain an uncertain state when the response doesn’t establish what happened. Don’t assume the transport exposes an idempotency mechanism; verify the API you’re actually using.
Illustrative receipt trace: one business event, one uncertain attempt
This is a design example, not a production log, benchmark, or claim about an API’s exact fields. Suppose order 1842 is paid. The logical send is receipt/order-1842/paid-v1; attempt A is one submission of that receipt. A provider message ID, if recovered, identifies the transport’s accepted message. These identifiers answer different questions.
| Observation | Durable record | Safe next action |
|---|---|---|
| Payment event arrives | Logical send and pending attempt A | Submit once, using the send API’s stable key if supported |
| Submission leaves; response is lost | Attempt A becomes unknown | Keep its identity and content; don’t invent attempt B |
| Payment event is delivered again | Same logical send already exists | Inspect A rather than create another receipt |
| Authentic feedback matches A | Preserve provider evidence and reconcile A | Record the supported outcome; don’t send a replacement |
| Order 1843 is paid by the same customer | A different logical send | Create a separate receipt identity, despite the shared recipient |
If no matching evidence arrives, A remains unresolved. A worker restart isn’t evidence of failure. Nor does changing the subject or amount make a replay of the original business event safe: changed content needs explicit review, not a fresh key that bypasses duplicate protection.
The practical takeaway is to key the receipt to the business event, not the recipient or the queue execution. This won’t prove exactly-once email. It prevents your own retries from turning missing knowledge into duplicate submissions. A delivery event still means receiving-server acceptance, not inbox placement or reading.
Feedback is part of the sending path
Feedback serves two decisions in that trace: it can reconcile A, and it can restrict future sends to its recipient. A dashboard chart alone does neither. The event must be authentic, associated with the original attempt, and persisted as a policy input.
SNS signature verification is therefore a safety boundary, not optional webhook polish. A forged complaint shouldn’t let an attacker suppress an arbitrary recipient or disable an unrelated sender. A genuine event shouldn’t select a tenant using an untrusted request parameter either.
AWS also notes that multiple notification methods can produce duplicate events. Reprocessing a complaint must not create a second side effect; a delayed delivery must not clear it.
If you build this yourself, start with the storage and state transitions. The chart can wait. Our bounce, complaint, and suppression article works through that loop in more detail.
Usage is a concurrency problem before it’s a billing screen
A monthly plan tells a customer how much allowance they bought. It doesn’t establish a safe instantaneous sending rate. SES has separate regional daily and per-second limits, and its quotas count recipients, including To, CC, and BCC.
Inside an application service, those constraints meet concurrent requests. Reading a counter, checking it, and incrementing it later can overspend under load. Reserving recipient units atomically before dispatch gives every request the same accounting boundary.
An ambiguous send makes accounting deliberately uncomfortable. Refunding the allowance immediately might let an application repeatedly submit messages that actually went out. Keeping the reservation is conservative, but needs a useful explanation and an operator reconciliation path. The interface shouldn’t pretend uncertainty disappeared because that makes the meter look nicer.
Payment is another independent input. An active subscription supplies allowance; it mustn’t clear a recipient suppression or an abuse hold.
The boundary we chose to own
SendHog, which we build, is a managed application-email service on Amazon SES. We chose to own this operating layer rather than require every customer to assemble an AWS sending stack.
The current service provides domain setup with provider-issued DKIM records, domain-scoped sending keys, JavaScript/TypeScript and Python SDKs, retained message outcomes, recipient accounting, idempotency, and bounce/complaint suppression handling. Verified domains receive automatic sending approval; manual and abuse disables remain independent controls. Customers don’t need their own AWS account.
Those are specific implementation boundaries, not a promise that email has stopped being email. Customers still own recipient consent, useful content, domain DNS, and the application events that cause sends. New domains still build reputation. A monthly allowance doesn’t guarantee a particular burst rate.
The native API and supported provider-shaped SDK methods use SendHog authentication. They are not an AWS SDK/SigV4 endpoint replacement. Receiving is not live. To inspect the public contract, use the API reference for domain-scoped permissions and saved-message reads, the sending guide for outcome meanings and the SDK’s no-automatic-resend behavior, and the quickstart for setup. These are product documentation, not independent evidence of delivery performance.
When not to put another service in the middle
Direct SES is a good choice when you already own the AWS operational model, have a maintained feedback pipeline, and need direct control over regions, IAM, or provider-specific features. Don’t replace working infrastructure merely to make a diagram shorter. The production setup review is a useful checklist for that route.
An established ESP can be better when its particular support model, workflow, integration, or retention policy is what you need. For example, Postmark documents separate transactional and broadcast message streams, while Resend documents its sending and email-management features. Compare the actual product behavior, not just the shortest SDK sample.
Self-hosting is also a real choice when control of the mail system is the objective and somebody owns its maintenance. It isn’t automatically a sensible replacement for an application sending API. The ESP, SES, and self-hosting comparison separates those responsibilities.
Whichever route you choose, try the receipt trace against its failure model. Can a replacement worker identify the original business event, find the uncertain attempt, and avoid sending again without evidence? That’s a more useful acceptance test than counting the lines in a successful send example.
Primary sources reviewed October 19, 2026. Provider capabilities and pricing can change.