Build a production send pipeline
Validate before queueing, preserve durable state, and use retries and fallback without hiding delivery uncertainty.
The core SDK owns one process-local delivery attempt. Your application owns durable queueing, persisted idempotency, and recovery after process failure.
Construct explicit routes
import { createEmailClient } from "@opencoredev/email-sdk";
import { resend } from "@opencoredev/email-sdk/resend";
import { smtp } from "@opencoredev/email-sdk/smtp";
export const email = createEmailClient({
adapters: [
resend({ apiKey: process.env.RESEND_API_KEY! }),
smtp({
name: "backup",
host: process.env.SMTP_HOST!,
auth: { user: process.env.SMTP_USER!, pass: process.env.SMTP_PASS! },
}),
],
defaultAdapter: "resend",
retry: { maxAttempts: 2 },
fallback: {
adapters: ["backup"],
onUnknownDelivery: "stop",
},
});Choose the backup from the field support and capability tables. SMTP is not a universal backup because it rejects attachments, tags, metadata, and schedules.
Validate before accepting work
Use the same public no-network boundary as send before committing a job to your queue.
await email.validate(message, {
fallback: { adapters: ["backup"] },
});
await queue.add("send-email", { message, idempotencyKey });Send with a stable idempotency key
await email.send(job.message, {
idempotencyKey: job.idempotencyKey,
metadata: { jobId: job.id },
signal: job.signal,
});The key is effective only where the selected adapter supports it. Persist your own business-level deduplication before invoking the SDK.
Handle delivery certainty
- Retry
not_sentfailures whenretryableis true. - Let configured fallback advance after terminal
not_sentfailures. - Stop on
unknownby default and reconcile provider state before retrying. - Opt into
onUnknownDelivery: "continue"only when a duplicate is safer than a missed message.
Record safe facts
Use hooks or observabilityPlugin for adapter, attempt, stable error code, duration, and redacted message facts. Avoid bodies, addresses, credentials, idempotency keys, provider response bodies, URLs, and filesystem paths.
Use a durable integration when needed
Convex Email provides queue state, attempts, cancellation, test-mode redirection, and webhook history for Convex applications.
Troubleshoot failures
Inspect typed errors and delivery classification.
