Decorators
Decorators
Wrap any channel transport with retry, fallback, preview, or idempotency.
Decorators wrap another transport. The channel sender still calls send the same way — only the transport path changes.
Retry, fallback, and weighted fallback work for email, SMS, WhatsApp, and push.
Preview and idempotency remain email-oriented.
The one rule
Pass the outermost decorator to the matching channel sender (
createMailer, createSmsSender, …).Available decorators
| Decorator | Import | Channels |
|---|---|---|
| Preview | sently/transports/preview | |
| Retry | sently/transports/retry | Email, SMS, WhatsApp, Push |
| Fallback | sently/transports/fallback | Email, SMS, WhatsApp, Push |
| Weighted fallback | sently/transports/weighted-fallback | Email, SMS, WhatsApp, Push |
| Idempotency | sently/idempotency |
Typical stack (email)
import { createMailer } from "sently/mailer";
import { ResendTransport } from "sently/transports/resend";
import { RetryTransport } from "sently/transports/retry";
import { FallbackTransport } from "sently/transports/fallback";
const transport = new FallbackTransport([
new RetryTransport(new ResendTransport({ apiKey: process.env.RESEND_API_KEY! })),
new ResendTransport({ apiKey: process.env.RESEND_BACKUP_KEY! }),
]);
const mailer = await createMailer({ transport });Typical stack (SMS)
import { createSmsSender } from "sently/sms";
import { FallbackTransport } from "sently/transports/fallback";
import { TaqnyatSmsTransport } from "sently/transports/taqnyat-sms";
import { UnifonicTransport } from "sently/transports/unifonic";
const sms = createSmsSender({
transport: new FallbackTransport([
new TaqnyatSmsTransport({ bearerToken: process.env.TAQNYAT_TOKEN!, sender: "MyBrand" }),
new UnifonicTransport({ appSid: process.env.UNIFONIC_APPSID!, senderId: "MyBrand" }),
]),
});