Client
API reference for createEmailClient — client options, send and sendBatch, adapter helpers, and the normalized response.
Complete reference for the email client: constructor options, every client method, per-send options, and the normalized response shape. For how routing behaves at runtime, see fallbacks and retries.
createEmailClient(options)
import { createEmailClient } from "@opencoredev/email-sdk";
import { resend } from "@opencoredev/email-sdk/resend";
import { smtp } from "@opencoredev/email-sdk/smtp";
const email = createEmailClient({
adapters: [resend({ apiKey: process.env.RESEND_API_KEY! }), smtp({ host: process.env.SMTP_HOST! })],
defaultAdapter: "resend",
fallback: ["smtp"],
retry: { retries: 2 },
});Prop
Type
retry options are documented in fallbacks and retries, hooks in hooks, and plugins in the plugin API. providers and defaultProvider are accepted aliases.
Construction fails fast with an EmailValidationError for a duplicate adapter routing name (Duplicate email adapter "x".) or plugin id (Duplicate email plugin "x".), and for an empty client (createEmailClient requires a default adapter.). A defaultAdapter that is not registered throws EmailProviderNotFoundError.
email.send(message, options?)
send(message: EmailMessage, options?: SendOptions): Promise<EmailProviderResponse>Validates the message, builds the route [adapter, ...fallbackAdapters] (duplicates removed), retries each adapter per the retry config, and resolves with the first successful response. One failed route rethrows the adapter's error as-is; multiple failed routes throw EmailSdkError with code all_providers_failed — see errors.
const result = await email.send(message, {
adapter: "resend",
fallbackAdapters: ["smtp"],
retries: 2,
idempotencyKey: "receipt:order_123",
metadata: { route: "checkout.receipt" },
});SendOptions
Prop
Type
provider and fallbackProviders are accepted aliases. Send-options metadata is unrelated to message.metadata — the message field goes to the provider, the send option only flows to hooks.
email.sendBatch(messages, options?)
sendBatch(messages: SendBatchItem[], options?: SendOptions): Promise<SendBatchResult[]>Sends sequentially, one message at a time in array order, and always resolves with one result per item — a failed item never throws or stops the batch:
type SendBatchResult =
| { ok: true; index: number; response: EmailProviderResponse }
| { ok: false; index: number; error: unknown };Each item is an EmailMessage plus optional adapter and fallbackAdapters that override the call-level options for that item:
const results = await email.sendBatch([
{ ...welcome, adapter: "postmark" },
receipt, // uses call-level or client defaults
]);
for (const result of results) {
if (!result.ok) console.error(`message ${result.index} failed`, result.error);
}email.adapter(name)
adapter<TProvider extends EmailProvider = EmailProvider>(name: string): TProviderReturns the registered adapter or throws EmailProviderNotFoundError. Useful for adapter escape hatches like email.adapter("memory").raw.sent in tests.
email.withAdapter(name)
withAdapter(name: string): Pick<EmailClient, "send" | "sendBatch">Returns a client pinned to one route — send and sendBatch behave as if { adapter: name } were passed every time. The name is checked immediately: an unregistered name throws EmailProviderNotFoundError at withAdapter time, not at send time.
const transactional = email.withAdapter("postmark");
await transactional.send(message);Properties
Prop
Type
Response
Every successful send resolves with a normalized EmailProviderResponse. provider is always set — it names the adapter that actually delivered, which matters when a fallback route handled the send.
Prop
Type
Aliases
adapter and provider mean the same thing everywhere: providers, defaultProvider, fallbackProviders, email.provider(name), email.providers, and email.withProvider(name) are compatibility aliases for their adapter spellings. Prefer adapter in new code — see naming aliases.
