{"title":"Mailers","description":"Generate and deliver email with Amber V2's first-party mailer API","section":"guides","version":"v2","path":"guides/mailers","canonical_url":"https://amberframework.org/docs/v2/guides/mailers","markdown_url":"https://amberframework.org/docs/v2/guides/mailers.md","inherited":false,"content_markdown":"# Mailers\n\nAmber V2 includes `Amber::Mailer::Base`, MIME generation, attachments, an\nin-memory delivery adapter, and SMTP delivery. Generate an ECR-backed mailer\nwith the standalone CLI.\n\n**Run from: the application root.**\n\n```bash\namber generate mailer Digest --actions=weekly\n```\n\nThe generator writes `src/mailers/digest_mailer.cr`, an ECR template under\n`src/views/digest_mailer/`, and a mailer spec. The generated class implements\nthe required HTML and text bodies.\n\n**File: `src/mailers/digest_mailer.cr` — edit the generated class in place.**\n\n```crystal\nclass DigestMailer < Amber::Mailer::Base\n  def initialize(@user_name : String, @user_email : String)\n  end\n\n  def html_body : String?\n    ECR.render(\"src/views/digest_mailer/weekly.ecr\")\n  end\n\n  def text_body : String?\n    \"Hello, #{@user_name}!\"\n  end\nend\n```\n\n**File: `src/views/digest_mailer/weekly.ecr` — edit the generated HTML body and\nescape user-provided values.**\n\n```crystal\n<h1>Hello, <%= HTML.escape(@user_name) %>!</h1>\n```\n\n## Delivery configuration\n\nThe memory adapter is the default and is appropriate for tests. Configure SMTP\nat application startup before delivering production mail.\n\n**File: `config/application.cr` — append this configuration after\n`require \"amber\"`.**\n\n```crystal\nAmber::Mailer::Configuration.configure do |config|\n  config.adapter = :smtp\n  config.smtp_host = ENV[\"SMTP_HOST\"]\n  config.smtp_port = ENV.fetch(\"SMTP_PORT\", \"587\").to_i\n  config.smtp_username = ENV[\"SMTP_USERNAME\"]?\n  config.smtp_password = ENV[\"SMTP_PASSWORD\"]?\n  config.use_tls = true\n  config.default_from = ENV.fetch(\"MAIL_FROM\", \"noreply@example.com\")\n  config.helo_domain = ENV.fetch(\"SMTP_HELO_DOMAIN\", \"localhost\")\nend\n```\n\nDo not commit SMTP credentials.\n\n## Build and deliver\n\n**File: the controller action or job that owns delivery, for example\n`src/jobs/digest_delivery_job.cr` — build the message before calling\n`.deliver`.**\n\n```crystal\nresult = DigestMailer.new(\"Alice\", \"alice@example.com\")\n  .to(\"alice@example.com\")\n  .subject(\"Your weekly digest\")\n  .deliver\n\nraise result.error.to_s unless result.is_successful\n```\n\nUse `.from`, `.cc`, `.bcc`, `.reply_to`, `.header`, `.attach`, or\n`.attach_file` before `.deliver` when the message needs them. The Quartz-Mailer\nand Slang examples on the V1 page do not describe Amber V2's mailer API."}