Email sending provider
@template-harness/email is a small server-side package for submitting one
transactional email through Resend. It does not start a service, read process
environment variables, consume SQS messages, or choose a deployment target. A
future Lambda, edge function, or other consumer reads its own configuration and
passes it explicitly to createResendEmailSender.
Configuration
The process that sends email needs these private values:
| Variable | Purpose |
|---|---|
RESEND_API_KEY | Resend API key with sending access, scoped to the sending domain where possible. |
EMAIL_FROM | Sender address, optionally with a display name, such as Bookygram <mail@example.com>. |
Store both values in the deployment platform's secret store. Do not place them in browser-visible configuration or commit them to an environment file. The current Fastify API does not use either setting, so it does not require them at startup.
Before deploying a sender, add and verify the domain used by EMAIL_FROM in
Resend. Resend accepts a send request before it reaches an inbox; a returned
email ID means the provider accepted the request, not that the recipient has
received it.
Sending a message
import { createResendEmailSender } from "@template-harness/email";
const sender = createResendEmailSender({
apiKey: process.env.RESEND_API_KEY ?? "",
from: process.env.EMAIL_FROM ?? "",
});
const result = await sender.send({
to: "reader@example.com",
subject: "Welcome",
text: "Thanks for joining.",
idempotencyKey: "welcome-user/user-123",
});
console.log(result.id);
Every request needs an idempotency key from 1 through 256 characters. Reuse the same key and message content when retrying the same logical email. Resend keeps the key for 24 hours, so a delivery runtime that retries beyond that window must provide its own duplicate-prevention policy.
The package returns a safe failure when Resend rejects or cannot acknowledge a request. It does not log credentials, recipients, subjects, content, or raw provider errors.
Relation to SQS
The API can already submit opaque bodies to its semantic email queue. This package does not define that body, consume a queue, or decide acknowledgement, retry, or dead-letter behavior. Those choices belong to the future feature and delivery runtime that will send the emails.
See SQS job boundary for the current producer contract.