Import a channel, call it, done. The provider and credentials come from environment
variables, so the same line of code works whichever provider is behind it — and on the
email side, handing mail() a FormData renders it as a tidy HTML table.
Features
- 👨💻 Zero configuration: works out of the box with minimal setup.
- 🔌 Provider-based: swap providers on any channel without changing your code.
- 💬 Five channels, one shape:
sms(),whatsapp(),push(),slack()and friends resolve, hook and error exactly likemail(). - 📡 Multi-channel
send(): fan out to every channel, or stop at the cheapest one that works. - 📝 Smart FormData parsing: automatically converts
FormDatato HTML tables. - 🎯 Grouped fields: organise form fields with
fieldset→fieldsyntax. - 📎 Attachments: attach files directly from form inputs or
Fileobjects. - 📮 Hosted forms: no backend? Point any HTML
<form>at a hosted endpoint and submissions land in your inbox, spam-checked. - 🎨 Bring your own templates:
bodytakes any HTML (or a promise of it), and the optionalpostboi/maizzlehelper renders Maizzle templates straight into it. - 🛡️ Type-safe: full TypeScript support with normalised error handling.
How it fits together
There are three ways to use Postboi, from least to most explicit:
- Zero-config channel calls —
mail(),sms(),whatsapp(),push(),slack()and friends: each reads its provider and defaults from a committedpostboi.config.ts, and secrets from the environment. Best for apps where one provider per channel is set per environment. postboi/kitform actions: a one-line SvelteKit action that readsFormData, sends it, and returns a result.- A provider instance:
new Resend({ ... })ornew Twilio({ ... })when you want an explicit instance, or credentials that don’t come from the environment.
Start with the Quick start and let the CLI wire everything up for you, or set things up by hand with Manual setup.