--- type: Guide title: Rails integration (euromailing-rails) description: The euromailing-rails gem — ActionMailer delivery method for transactional mail, contact/list client, Syncable concern for mirroring users, ActionMailbox inbound. resource: https://github.com/rogerheykoop/euromailing-rails tags: [rails, gem, actionmailer, sync] timestamp: 2026-07-06T00:00:00Z --- # Rails integration: the euromailing-rails gem Source & full README: https://github.com/rogerheykoop/euromailing-rails (MIT, Ruby ≥ 3.1, no runtime dependencies beyond what Rails ships). ```ruby # Gemfile gem "euromailing-rails", github: "rogerheykoop/euromailing-rails", require: "euromailing" ``` The `require:` is not optional. The gem is named `euromailing-rails` but its entry point is `lib/euromailing.rb`, so Bundler's auto-require on the gem name does not load it — and without the module loaded there is no railtie, no `:euromailing` delivery method, and no clear error saying why. A git-sourced gem also needs `git` present wherever `bundle install` runs. Slim container images often do not ship it. ```ruby # config/initializers/euromailing.rb Euromailing.configure do |c| c.api_key = Rails.application.credentials.dig(:euromailing, :api_key) end ``` **Configure it this way, not through `config.euromailing`.** The railtie does read `config.euromailing.api_key`, but it reads it in a railtie initializer — and those run *before* everything in `config/initializers/`. Setting it there means the railtie already read an empty value and you get `Euromailing::ConfigurationError: ... is required` at the first send. `config.euromailing.api_key` does work from `config/application.rb` or an environment file, because those are evaluated earlier. If you prefer that form, put it there. ## 1. Transactional mail through ActionMailer ```ruby # config/environments/production.rb config.action_mailer.delivery_method = :euromailing ``` Existing mailers work unchanged. The `from:` must be on the API key's bound sending domain. One recipient per message; prefer `deliver_later` (the API allows 60 requests/minute per key). ## 2. Contacts & lists ```ruby client = Euromailing.client client.upsert_contact(email: "jan@example.com", first_name: "Jan") client.delete_contact(email: "jan@example.com") client.subscribe(list_id: "LIST-UUID", email: "jan@example.com") client.unsubscribe(list_id: "LIST-UUID", email: "jan@example.com") ``` Errors raise `Euromailing::ApiError` with `#status` and `#code` (codes documented in [the API reference](../api/index.md)). ## 3. Sending domains (send as your customers' domains) — gem ≥ 0.2.0 For a partner integration: one account, many customer domains, one key with `transactional:send_any_domain` + `sending_domains:read|write`. ```ruby client = Euromailing.client dom = client.create_sending_domain("coachklant.nl") dom["dns_records"] # show these to the customer client.sending_domain("coachklant.nl") # poll client.verify_sending_domain("coachklant.nl") # fresh DNS check client.delete_sending_domain("coachklant.nl") # when a customer leaves ``` Once verified, existing mailers with `from: "coach@coachklant.nl"` deliver as that domain — no per-domain key. Euromailing is the source of truth for verification (poll, don't check DNS yourself). See the [sending domains API](../api/sending-domains.md). ## 4. User sync ```ruby class User < ApplicationRecord include Euromailing::Syncable euromailing_sync lists: ["NEWSLETTER-LIST-UUID"], if: ->(u) { u.newsletter? } def euromailing_contact_attributes { email: email, first_name: first_name, last_name: last_name, custom_fields: { plan: plan } } end end ``` Creates/updates upsert the contact and subscribe the configured lists; destroys delete it — all via `Euromailing::SyncJob` (ActiveJob, retries with backoff, 404-on-delete counts as success). ## 5. Inbound mail See [Inbound to ActionMailbox](inbound-actionmailbox.md).