One command, fully set up.
The first prompt is the only real decision: send with Postboi, or bring your own provider (Resend, SES, Mailgun, Postmark, …). Everything after that is the same, and so is every line of code you write.
Sending with Postboi
Pick Postboi and the CLI opens your browser, authorises the device, and writes one env var. No provider account, no API key to copy from a dashboard, no DNS, no card:
You don’t need the token to start building: until one is set, mail() prints each message to the console in development instead
of failing, and only throws once you deploy.
That’s the setup. Mail sends from your account’s you@send.postboi.email address — a real
deliverable address, but not an inbox, so set reply_to if you want replies — until you verify a domain of your own.
Because Postboi knows your account, init can wire up things a bring-your-own provider
can’t:
- Typed
from— narrowed to your sending address and verified domains, so a wrong one is a type error instead of a runtimefrom_not_allowed. The types are generated insidenode_modules: nothing to commit, and always optional. - Managed captcha — the publishable key is baked in, so
<Captcha />works with no Cloudflare account and no keys. - Webhook secrets — every endpoint secret written as
POSTBOI_WEBHOOK_SECRET, soreceive()verifies signatures without a copy-paste.
Re-running init is safe: a working POSTBOI_TOKEN is reused rather than replaced, so you
can walk the prompts again any time to revisit defaults. bunx postboi sync refreshes the
generated pieces (types, captcha key, webhook secrets) after adding a domain.
The same token also covers the rest of the platform from the same import — no second SDK:
Message status and the log, lists, broadcasts and double opt-in, notifications, suppressions, scheduling and batch sends with idempotency keys are all documented in The Postboi provider, along with plan limits.
Bringing your own provider
Pick Bring your own provider and the CLI asks which one, then collects its credentials (an API key, plus a domain for Mailgun or a region for SES) — printing the exact dashboard page each one comes from. If you’re signed in and your team has synced the credential before, the prompt answers itself: the key is pulled from your account and you type nothing. What you do type is synced up for the next teammate, so any credential is typed once, on one machine, ever.
Either way it writes only secrets to your env file and everything else — the
provider, defaults, and non-secret options — to a committed postboi.config.ts. The best case is a single env var:
mail() picks that up on every call, exactly as it does with a POSTBOI_TOKEN — the
sending code never names a provider, so swapping later is a one-line config change.
What it does either way
- Optionally collects
from/toand other defaults applied to every send, and writes them (with hooks, later) to the committedpostboi.config.ts. - Installs
postboiif it isn’t installed yet. - Offers to push your env vars to your host (Vercel, Cloudflare, Netlify, Railway) — no globally installed host CLI required, and it offers to link the project first when it isn’t yet — and to gitignore the env file if it isn’t already.
- Offers to install the postboi agent skill into
.claude/skills/: a condensed cheat-sheet that teaches AI coding agents the library’s conventions. It ships inside the package, and what’s installed is a symlink to it — so upgrading postboi upgrades the skill, with no diff in your repo. (Where symlinks aren’t available the file is copied instead, andpostboi synckeeps that copy current.)
Beyond email
The same init sets up the other channels — --sms, --whatsapp, --push, --chat —
and each channel’s call works exactly the way mail() does. Start with Multi-channel send(), or jump straight to SMS, WhatsApp, Push, Slack, Discord, Teams or Telegram.
Signed in to the Postboi provider, init also syncs the credentials it collects to
your account, so a teammate’s postboi sync fills in their env file with no ceremony —
see Team credentials.
Prefer to do it yourself?
Skip the CLI and write the config file plus the credential env vars by hand, or construct a provider instance directly. See Manual setup and Providers.