Send emails with Phoenix
Set the Swoosh adapter to PostShiba.Swoosh.Adapter.
What you'll build
A Swoosh mailer in your Phoenix app that delivers through PostShiba. Existing Swoosh.Email code keeps working.
Before you start
- An API key with Send only access. Full access also works. See Create an API key. Support keys cannot send.
- A verified sending domain. See Add and verify a domain.
frommust be an address on it. - A cluster that is ready to send (
sending_readyistrue). See Clusters. - Elixir 1.15 or later.
Install
The package is on GitHub, not Hex. Add it to deps/0 in mix.exs, keep :swoosh, then run mix deps.get.
Send your first email
- Point your mailer at the adapter in
config/runtime.exsorconfig/config.exs. The adapter reads:api_key, and optionally:base_urland:team_id.
- Keep the mailer module Phoenix generated in
lib/my_app/mailer.ex.
- Build a message and deliver it.
Run it with mix run send.exs. deliver/1 returns {:ok, map}, where the map is the JSON body of the 201 response. The message then shows under Events in the dashboard.
The adapter sends X-Capsule-Unique-Args as unique_args. It always uses your first cluster that is ready to send.
The adapter makes its own HTTP requests and ignores Swoosh's :api_client. If Swoosh fails to start because Hackney is missing, set config :swoosh, :api_client, false.
Attachments and templates
Attach a file
Use Swoosh's attachment/2 with a path or a Swoosh.Attachment. The adapter base64-encodes each file for the API. Up to 10 files, 18 MB in total. See Attachments for allowed types.
Hosted PostShiba templates go through the Elixir client with template, not through Swoosh.
Keep the key safe
Use a Send only key in your app. It can send email and call GET /api/v1/users/me. Every other request returns 403 forbidden.
| If this key leaks | Send only | Full access |
|---|---|---|
| Send mail from your verified domains | Yes | Yes |
| Read events, recipients, templates, and sent messages | No | Yes |
| See SMTP passwords and webhook secrets | No | Yes |
| Change clusters, domains, credentials, webhooks, and suppressions | No | Yes |
- Keep Full access keys for setup scripts, the CLI, and MCP. Don't deploy one with your app.
- Set
api_key: System.fetch_env!("POSTSHIBA_API_KEY")inconfig/runtime.exsinstead of a string inconfig/config.exs. Don't commit the key, log it, or put it in a URL. - Send from your server. Never put a key, even a Send only key, in browser or mobile code.
- Create one key per app and environment. If a key leaks, delete it on API keys and create a new one. Deleting stops it straight away.
Troubleshooting
A failed send returns {:error, %PostShiba.Error{}} with error, field, and message.
| Status | error |
What to do |
|---|---|---|
401 |
Invalid token or no user signed in |
The API key is missing, mistyped, or deleted. Copy it again from API keys. |
403 |
forbidden |
Support keys cannot send. A Send only key gets this on any call other than sending. |
403 |
domain_unverified |
from is not on a verified domain. |
403 |
kyc_required |
Your team is pending production approval. Until then, send only to team members, live inbox addresses, and inbox forward-to addresses. |
422 |
invalid |
field names the problem, such as a missing to or a rejected attachment. |
429 |
throttled |
The cluster hit its hourly throttle or 24-hour cap. Retrying right away hits the same cap. See What happens at your cap. |
429 |
rate_limited |
More than 300 send requests in one minute on one API key. Slow down. |
Every other status is on Errors.
Next steps
- Send emails with Elixir for the plain client.
- Unique args to get your own ids back on delivery webhooks.
- Managing webhooks to receive delivery events.
- SDKs for every language client.