Saltar al contenido principal

Esta página no está disponible en castellano; se muestra en inglés.

Email delivery

Memship sends transactional email for address confirmations, password resets, activity confirmations, waitlist promotions, payment receipts, and overdue reminders. Email is disabled until you configure a transport.

There are two ways to configure one, and they are not equivalent — the settings screen wins over .env. Read Which one is in effect before editing anything, especially on an installation that is already running.

WhereProvidersBest for
Settings → Integrations → MailingResend, any SMTP server (Gmail is a preset)Almost everyone. Takes effect on the next request, no restart.
.envResend, any SMTP serverPreseeding credentials at deploy time, and installations older than the settings screen.

SMTP means a server that accepts STARTTLS, usually on port 587, or no encryption at all. A server reachable only on port 465, which encrypts from the first byte, is not supported.

Option A — the settings screen

Sign in as superadmin and go to Settings → Integrations → Mailing.

The full walkthrough — obtaining the credentials, the DNS records Resend needs, the details of your SMTP server (or a Gmail app password), the Send test button, and a troubleshooting table — is in the Integrations setup guide, section 4. Two points from it are worth repeating here because they are the ones that silently bite:

  • Saving credentials does not activate them. The card badge goes to Ready, not Active. You must then set the Active provider selector and save again. Configure → test → activate, in that order. The order is enforced: a provider cannot be activated until a send through its current saved credentials has passed.
  • With no active provider, mail is silently skipped — logged, not queued, no error. Nothing in the interface reports a send that never happened.

Each provider card also shows whether its credentials are known to work: Not verified, Working, or Failing with a reason. It is updated by every email sent through that provider, the test included, and it applies to credentials that come from .env too.

Approval and rejection emails are sent the moment the admin decides, so the admin can be told whether they went out. If the provider could not be reached or its sending limit was hit, they are tried again automatically after 1, 5 and 25 minutes, and the approval message says so. Any other failure, such as rejected credentials, is reported straight away and not retried, because it would fail the same way again. The retries run on the worker service, which must be up.

Option B — .env

Set the variables and recreate the backend so it picks them up:

docker compose up -d --force-recreate api

Resend:

RESEND_API_KEY=re_your_key_here
RESEND_FROM_EMAIL=noreply@yourdomain.org

SMTP (any server, Gmail included — for Gmail use smtp.gmail.com and an app password):

SMTP_HOST=smtp.yourprovider.com
SMTP_PORT=587
SMTP_USER=your-username
SMTP_PASSWORD=your-password
SMTP_FROM=noreply@yourdomain.org
SMTP_TLS=true

The full screen-field → environment-variable mapping is in Integrations setup guide, section 6.

Which one is in effect

Three cases, and the difference matters:

SituationWhat sends
The Mailing screen has never been savedThe .env values, exactly as described above: Resend if RESEND_API_KEY is set, otherwise SMTP if SMTP_HOST is set, otherwise nothing.
The screen has been saved and an active provider chosenThat provider. Per field, a value saved in the screen wins; a field left empty there falls back to the matching environment variable.
The screen has been saved but no active provider chosenNothing. Mail is silently skipped, whatever .env says.

The quick-start stack relies on the first row: its compose file points SMTP_HOST at a local test inbox, so saving the screen replaces the test inbox — with the provider chosen there, or with nothing if none is.

The consequence to watch for: once anyone has saved the Mailing screen, editing .env may have no visible effect — the saved value wins and no error is raised. If you configured Resend in the screen and later change RESEND_API_KEY in .env, the old key keeps sending. Clear the field in the screen to fall back to the environment value.

The screen marks fields it is reading from the environment with a “from environment” badge, which is the quickest way to see which source is live. Port and TLS always have a value, 587 and on, so they carry the badge only when SMTP_PORT or SMTP_TLS is actually set.

Upgrading from a version that offered “Gmail” as a provider: it was always the SMTP transport, and is now called that. Mail keeps going where it went before:

  • An installation that set up Gmail in the screen becomes the SMTP provider with Google’s server (smtp.gmail.com, port 587, STARTTLS) saved explicitly. It does not start following SMTP_HOST.
  • An installation configured only through .env keeps using its SMTP_HOST, and is now reported as SMTP rather than Gmail.

Either way, the card reads Not verified until the next email goes through, because its health record belonged to the old provider. It stays active meanwhile.

Verify

Trigger a real send to confirm delivery — for example, request a password reset from the login page, or approve a pending registration and check the approval email arrives. Emails are localized (ES/CA/EN) based on the recipient’s language.

If you configured through the settings screen, prefer its Send test button: it sends through one specific provider even when that provider is not the active one, so you can verify credentials before switching real traffic to them.

Use a member or club admin account for the reset test. Super admins are excluded from that flow by design — their password is reset from the host with python -m app.cli.seed, see Recovering the super admin password — so testing with one looks like mail that never arrives.

What a member can switch off

Each member has a Receive club emails toggle on their profile. It covers the optional templates — booking and activity confirmations, and club announcements — and nothing else:

TierExamplesMember opt-out
Mandatoryaddress confirmation, password resetnever applies
Operationalreceipts, payment reminders, a cancelled bookingnever applies
Optionalbooking and registration confirmations, announcementsapplies

A member who opts out still receives their invoices and the notices that precede a debt, which a club may be obliged to send. Staff can set the toggle on a member’s behalf from the member record, for someone who asks at the desk or has no portal login.

Which templates sit in which tier is Settings → Communications, and the organization’s own switch is checked first: a template switched off there does not send to anybody, whatever the member chose.

Apart from the mandatory mails, templates start switched off, with one exception: a new install starts with the registration approval email on, because an approved applicant has no other way to learn they were let in. It can be switched off like any other. An install created before this default existed keeps whatever it had; if approval emails never arrive, check that switch first. Approving a registration also tells the admin whether the email was sent, switched off, or failed, and why.

Members cannot sign in before email works

Signing in requires a confirmed address, and the only self-service way to confirm one is the link sent at sign-up. So until a provider is configured:

  • Self-registration is refused, with a message telling the visitor to contact the club, rather than creating an account whose owner could never use it.
  • A member who registered earlier and never confirmed shows as active and cannot sign in. Open their member record: it says the address is unconfirmed and offers Confirm address. That needs users.write, and it is recorded in the audit log as confirmed by an administrator rather than by the member — confirm only an address you have established is theirs by some other means.
  • From the host, the same thing without the UI: python -m app.cli.verify_email --email someone@example.org, or --list to see who is waiting.

A provider that is configured but broken — a wrong API key, say — is not caught by the registration guard: it looks usable and fails at the transport. Those members get stuck the same way, and Confirm address is the way out.

Troubleshooting

  • No email at all — check in this order: (1) Settings → Integrations → Mailing, is Active provider set to something other than None? A saved-but-not-activated provider sends nothing. (2) If the screen has never been used, confirm RESEND_API_KEY or SMTP_HOST is set in .env; if neither is, email is intentionally disabled.
  • Changing .env did nothing — the Mailing screen has a saved value for that field, and it wins. See Which one is in effect.
  • Resend rejects the send (403, “domain is not verified”) — the From address is not on a domain verified in Resend, or its DNS records have not propagated. Resend refuses any From address outside a verified domain. The one exception is onboarding@resend.dev, which needs no domain but delivers only to the address that owns the Resend account — usable for a test, not for real traffic.
  • Emails delayed — sending is asynchronous via Celery. Check the worker logs: docker compose logs -f worker.
  • Links point to the wrong host — set FRONTEND_URL to your public site URL (see the Configuration reference).

Documentación generada desde memship v2.19.0-43-g14d28f4 (rama main) el . Sigue el desarrollo, no la última versión publicada, así que puede describir cambios aún sin publicar.