This guide wires mailglass into a Phoenix app and sends one message through the
stable v1.x lane.
Prerequisites
- Elixir
~> 1.18with OTP 27+ - Phoenix
~> 1.8 - Ecto and PostgreSQL configured
- Swoosh adapter credentials in your runtime environment
1) Install and verify
mix deps.get
mix mailglass.install
mix ecto.migrate
mix compile --warnings-as-errors
2) Configure mailglass
# config/runtime.exs
config :mailglass,
repo: MyApp.Repo,
adapter:
{Mailglass.Adapters.Swoosh,
swoosh_adapter: {Swoosh.Adapters.Postmark, api_key: System.fetch_env!("POSTMARK_API_KEY")}},
telemetry: [default_logger: true]3) Mount preview and webhook routes
# lib/my_app_web/router.ex
defmodule MyAppWeb.Router do
use Phoenix.Router
import MailglassAdmin.Router
import Mailglass.Webhook.Router
if Application.compile_env(:my_app, :dev_routes) do
scope "/dev" do
pipe_through :browser
mailglass_admin_routes "/mail"
end
end
scope "/" do
pipe_through :api
mailglass_webhook_routes "/webhooks"
end
end4) Send your first message
defmodule MyApp.UserMailer do
use Mailglass.Mailable, stream: :transactional
def welcome(user) do
new()
|> to(user.email)
|> from({"MyApp", "support@example.com"})
|> subject("Welcome")
|> html_body("<h1>Welcome to MyApp</h1>")
|> text_body("Welcome to MyApp")
|> Mailglass.Message.put_function(:welcome)
end
end
{:ok, _delivery} =
%{email: "alice@example.com"}
|> MyApp.UserMailer.welcome()
|> Mailglass.deliver()End-to-End Example
mix deps.get
mix mailglass.install
mix ecto.migrate
mix compile --warnings-as-errors
Troubleshooting the Installer
mix mailglass.install fails to find endpoint.ex
- Ensure you are running the task from the root of your Phoenix application.
- If your application has a non-standard directory structure, you may need to manually wire the components described in the Webhooks Guide.
Webhooks return 401 after installation
mix mailglass.install now fails closed when it detects an unmanaged Plug.Parsers plug
(one that lacks a body_reader option) in your endpoint.ex. It exits non-zero via
Mix.raise and prints an actionable error — this prevents the silent production webhook
401 that occurred with earlier versions.
If the installer stopped with a conflict error:
- Re-run with
--forceto bypass the check and proceed:mix mailglass.install --force. The installer will insert the managedPlug.Parsersblock (withbody_reader: {Mailglass.Webhook.CachingBodyReader, :read_body, []}) above your existing parser. Use--forceonly if you understand the parser ordering implications. - After a successful install, run
mix mailglass.doctorto confirm theMailglass.Webhook.CachingBodyReaderwiring is present in your endpoint parser. The command exits non-zero if the wiring is absent.
If you installed mailglass with an older installer:
Earlier installer versions warned instead of failing closed on this conflict. Check
lib/my_app_web/endpoint.ex manually: if your Plug.Parsers block lacks a body_reader
option, add the CachingBodyReader wiring as shown in the Webhooks guide,
then run mix mailglass.doctor to verify.
Next steps
The first hour is behind you. Here is a natural week-one path:
- What you can do with mailglass — the JTBD map; read once straight through when evaluating.
- Authoring mailables — native setter API, HEEx layouts, reusable components.
- Preview — dev preview at
/dev/mail; device-width and dark-mode toggles. - Webhooks — webhook ingest, verification, and suppression wiring.
- Testing — Fake adapter, TestAssertions, and the
deliver/2baseline. - Telemetry and operating —
[:mailglass, :outbound, :dispatch, ...]events and alerting.
For a fuller ordered index, see the learning path.