Skip to main content
One idea at a time

SMTP acceptance is not inbox delivery

Taskboard uses email for invitations, verification, and password resets. The worker constructs a message and submits it to an SMTP server through Nodemailer. The database records the work; SMTP carries the message beyond the app.

There are several distinct outcomes. A message can be queued in Postgres, accepted by SMTP, rejected later by a recipient's server, placed in spam, or opened by the recipient. The worker directly observes only its submission result.

The duplicate window​

Consider a successful send followed by a crash:

  1. SMTP accepts Maya's invitation email.
  2. The worker stops before recording completion.
  3. The claim expires.
  4. Another worker sends the same job again.

Maya may receive two messages. The outbox prevented lost work, but it could not atomically commit SMTP acceptance and the Postgres completion update.

A stable message identifier can help some mail systems correlate attempts. It does not establish a universal deduplication contract. The accurate guarantee is durable, retried delivery attempts with possible duplicates.

The invitation link carries an expiring token. The database stores its hash and purpose. Accepting the invitation consumes the token under the server's transaction rules. A duplicate email therefore does not grant a second membership or create a second invitation acceptance.

Password resets follow the same principle. The email may be duplicated, but the token must expire and the accepted reset must invalidate further use. The server should not expose token-bearing links in logs.

In a local environment, a mail capture service lets a reader or developer inspect messages without contacting real recipients. Production needs an SMTP provider, verified sending configuration, secret storage, and a policy for bounced mail. Those operational settings are separate from rendering an HTML template.

Taskboard's SMTP code is in Read api/src/jobs.ts. Token creation and consumption are in Read api/src/auth.ts and Read api/src/tenancy.ts.

Take away

SMTP success means the server accepted a submission. Make the action behind an email safe when the message appears twice.

Can the API say that Maya received the invitation?

It can say the invitation was recorded or submitted for delivery. Proving receipt requires additional provider evidence, and even a delivery report does not prove that Maya read the email.