Door naar de inhoud

Begrijpen: hoe Elv8 e-mails verstuurt

Het outbox-concept: waarom e-mails worden gewachtrijd, hoe de achtergrondworker ze aflevert via Resend, en wat retries en idempotentie garanderen.

Elv8 verstuurt e-mails niet direct op het moment dat er iets gebeurt. In plaats daarvan worden e-mails eerst in een wachtrij gezet en daarna afgeleverd door een achtergrondproces. Dit patroon heet een outbox en is de reden waarom e-mails betrouwbaar aankomen — ook als er even iets misgaat met de verbinding naar de e-mailprovider.

Waarom een outbox?

Het alternatief is simpel: e-mail versturen op het moment van de gebeurtenis, in dezelfde databasetransactie als de boeking of betaling. Dat werkt tot het mis gaat: als de verbinding naar de e-mailprovider even wegvalt terwijl de betaling al is verwerkt, is de transactie geslaagd maar de e-mail nooit verstuurd. Of omgekeerd: de e-mail is verstuurd maar de database-update is mislukt.

Een outbox lost dit op door de twee stappen te ontkoppelen:

  1. De applicatie schrijft de e-mail als PENDING-rij naar de outbox_messages-tabel in de database. Dit gebeurt in dezelfde transactie als de boeking of betaling — of direct daarna, maar nooit geblokkeerd door een e-mailprovider.
  2. Een achtergrondworker leest de wachtrij, verstuurt de e-mail via de e-mailprovider (Resend in productie), en markeert de rij als SENT.

Als stap 1 slaagt maar stap 2 tijdelijk faalt, probeert de worker het opnieuw. Als stap 1 faalt, is er geen e-mail aangemaakt en ook geen verwarring.

Hoe de worker werkt

De outbox-worker draait als achtergrondproces naast de applicatie. Hij scant de outbox_messages-tabel periodiek (elke 60 seconden in productie, via WORKER_INTERVAL_MS) en verwerkt berichten die klaar zijn om verstuurd te worden.

Levenscyclus van een bericht: PENDINGIN_PROGRESSSENT (succes), of IN_PROGRESSFAILED (wacht op nieuwe poging) → terug naar IN_PROGRESS, en na de laatste poging → DEAD_LETTER.

Per batch claimt de worker een bericht met een lease: hij zet zichzelf als eigenaar van het bericht en geeft aan wanneer de lease verloopt. Als een andere worker of instantie hetzelfde bericht probeert te claimen, lukt dat niet zolang de lease actief is. Dit voorkomt dat hetzelfde bericht twee keer wordt verstuurd bij meerdere draaiende workers.

Na een succesvolle verzending wordt de status bijgewerkt naar SENT. Bij een fout wordt de status FAILED en plant de worker een nieuw poging in met exponentiële backoff: de wachttijd verdubbelt bij elke mislukte poging, met een beetje willekeur (jitter) om opstoppingen te voorkomen. Na het maximumaantal pogingen (maxAttempts, standaard 5) gaat het bericht naar DEAD_LETTER — een eindstatus die aangeeft dat aflevering is mislukt en handmatige aandacht vereist.

Idempotentie: geen dubbele e-mails

Elk bericht in de outbox kan een idempotencyKey hebben — een unieke sleutel die garandeert dat hetzelfde bericht niet twee keer wordt aangemaakt. Als de applicatie probeert een bericht aan te maken dat al bestaat (zelfde sleutel), negeert de database de tweede invoeg stil en geeft null terug in plaats van een fout.

Een voorbeeld: de factuure-mail krijgt de sleutel invoice:{factuurnummer}:{e-mailadres}. Zelfs als de applicatie door een tijdelijke storing tweemaal probeert dezelfde factuurmail in te plannen, wordt er maar één bericht aangemaakt en maar één e-mail verstuurd.

E-mailprovider: Resend in productie

In productie levert de worker e-mails af via Resend (RESEND_API_KEY + RESEND_FROM_EMAIL). Resend is de provider die verzorgt dat e-mails ook daadwerkelijk aankomen in de inbox van de ontvanger — inclusief SPF/DKIM-verificatie en bezorgbaarheid.

Voor zelf-gehoste Elv8-installaties (EMAIL_DELIVERY_PROVIDER=smtp) kan ook een eigen SMTP-server worden geconfigureerd. Het outbox-principe is hetzelfde; alleen de afleverstap verschilt.

Wat dit betekent voor jou als operator

Vanuit jouw perspectief als studio-eigenaar of coach is het outbox-systeem onzichtbaar. Je ziet niet of een e-mail in de wachtrij staat of al verstuurd is — je ziet alleen dat e-mails gaan. Dat is de bedoeling.

Wat het systeem wél garandeert:

  • E-mails gaan altijd via de outbox, nooit direct vanuit een API-aanroep. Er is geen snelpad dat de betrouwbaarheidsgarantie omzeilt.
  • Bij tijdelijke storingen bij Resend of netwerkstoringen worden e-mails automatisch opnieuw geprobeerd, zonder dat je iets hoeft te doen.
  • Dezelfde e-mail wordt nooit twee keer verstuurd door de idempotentiesleutel.

Wat het systeem niet garandeert: dat e-mails binnenkomen in de inbox van de ontvanger. Dat hangt af van de configuratie van de ontvanger, spamfilters en de bezorgbaarheid van het afzenderdomein. Zie Transactionele e-mails voor een overzicht van alle e-mailtypen en wat er in elk bericht staat.

Zie ook

Was dit nuttig?

Laatst bijgewerkt op 24 juni 2026