Skip to content

Build1 publisher3 min readPublished

Buy transactional email on recovery controls, not send price

A dev.to checklist argues the real procurement test is whether a small team can explain every accepted, deferred, bounced or blocked message during an incident.

The Engineer · Build desk

Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

What happened

  • The article's short answer: choose a transactional email API only after its custom domain verification, DKIM rotation, suppression export, event history and rollback controls let a small team explain every accepted, deferred, bounced or blocked message.
  • A transactional email API is only simple while delivery state stays simple; the operational constraint is recovery, not the length of the send request.
  • The author has been paged for missed jobs and duplicate deliveries, and says email creates the same class of incident: an application retries because it cannot tell what happened, then either drops a message or sends it twice.
  • A low send price is useful but cannot compensate for a sender identity nobody can rotate safely or a suppression list nobody can inspect.
  • Start with a short proof using a subdomain separate from employee mail; verify the service can establish the custom domain through DNS records you control, show each record's status independently, and preserve the previous signing configuration while a new DKIM selector is being rolled out.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

A write-up published on dev.to argues that a transactional email API should only be chosen after its custom domain verification, DKIM rotation, suppression export, event history and rollback controls let a small team explain every accepted, deferred, bounced or blocked message [1]. The framing matters because it moves the buying decision off unit price and onto recovery: the author's position is that the operational constraint is recovery, not the length of the send request, and that an API is only simple while delivery state stays simple [2].

The failure mode is familiar from job queues. The author says they have been paged for missed jobs and duplicate deliveries, and that email produces the same class of incident: the application retries because it cannot tell what happened, then either drops a message or sends it twice [3]. A low send price does not compensate for a sender identity nobody can rotate safely or a suppression list nobody can inspect [4].

The proposed evaluation is concrete enough to run before signing. Start on a subdomain kept separate from employee mail, and confirm the service can establish the custom domain through DNS records you control, show each record's status independently, and preserve the previous signing configuration while a new DKIM selector is rolled out [5]. One green "verified" badge is not evidence a runbook can use [6]. Then trace a single synthetic message from the application's request ID to the provider's message ID to the final event, and check that the API distinguishes request acceptance from actual delivery, because collapsing those two states makes retry policy dangerous [7]. Ask how long event data stays queryable, whether webhook events can be replayed or recovered, and whether a human can export the same data mid-incident [8].

Suppression gets its own test: what creates a suppression, its scope, how it is queried, and what review is required before removal [9]. An unsubscribe, a permanent delivery failure and an operator block can all stop a send while meaning different things, so the guidance is to store the provider's reason and timestamp next to your own recipient policy rather than reducing all three to one boolean [10].

Authentication is treated the same way. DMARC evaluates alignment between the visible author domain and authenticated identifiers, and RFC 7489 also defines aggregate and failure reporting plus policy modes covering monitoring and requested handling of failed messages [11]. That makes reporting an ongoing operational signal, and the runbook should name who reads the reports and what change triggers an investigation [12].

On your side of the boundary, the article puts the ledger in the application: an immutable idempotency key per business notification, a recorded content version and recipient policy decision, an attempt row written before the external call, the provider message ID attached on acceptance, and webhooks advancing that attempt through explicit states instead of overwriting the business record or silently triggering a fresh send [13]. The accompanying Go sketch reserves in the ledger first, returns early if the reservation fails, and treats an acceptance with no provider message ID as an error [14].

Two open items are worth watching. The author lists six conceptual states, queued, accepted, delivered, deferred, failed and suppressed, but says the exact mapping must follow each provider's documented events and doubts a universal vocabulary exists at a useful level of detail; a migration test against both providers' event taxonomies is what would settle it [15][16][19]. The sketch also leaves a deliberate unresolved state if the process stops after the send but before the ledger update [17]. Ask for these behaviours in writing, then test them, since a label like "simple" does not define an incident boundary [18].

Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories