Short answer: put a background queue in front of Resend, Postmark, or SES, and make the worker own both send pace and idempotency. For a property-management system, the same queue pattern safely expires reservations after their hold window; the scheduler should enqueue work, never try to send a whole campaign in one run.
Start with the failure mode, not the provider
Direct request-time email looks tidy until a provider throttles a burst. A reservation-created request then waits on an external API, a retry from the browser can send twice, and an on-call engineer has no durable list of what still needs attention. A queue turns that burst into work that can be paced, retried, inspected, and drained after an incident.
The operational rule is simple: one message represents one intended side effect, and the message carries a stable idempotency key such as reservation-expired:res_4831 . A standard queue is at-least-once, so the consumer must check that key in a durable store before calling the provider.…