Skip to main content
Version: v0.0.4

Send email over SMTP

An smtp destination sends each output as an email through a mail server you name. By default the email holds only a subject and a short text, filled from templates, and never the message: a person reading it learns that a message is ready and which one, and fetches it from where it is kept. Set attach: true to attach the message itself.

What email does with patient data​

TLS protects an email only on its first hop, from Pathways to your mail server. From there the mail server sends it on, and every server on its way can read it, keep it, and send it over a connection that may not be encrypted. A recipient's mailbox keeps it until someone deletes it.

So:

  • Keep attach: false, the default, unless the recipients need the message in the email. The engine logs smtp-attach at every start when it is on.
  • Keep the subject and text free of patient data. The templates can name the message's id, its output number, the channel and ctx.meta keys; a subject is the part of an email most often shown, logged and forwarded.
  • If an email can carry protected health information, your mail provider needs a signed Business Associate Agreement with you, and so does any service it hands the mail to.

Send to a mail server​

channels/results-mail/channel.yaml
source:
file: { dir: ../../data/results }
destinations:
- smtp:
host: smtp.example.test
tls: {}
user: { env: SMTP_USER }
password: { env: SMTP_PASSWORD }
from: pathways@example.test
to: [lab-desk@example.test]
cc: [ward-clerk@example.test]
subject: "Result ready: message {{ messageId }}-{{ seq }}"
body: "Channel {{ channel }} has a result for you in the lab system."
  • tls: {} runs TLS from the first byte, on port 465 unless port says otherwise. This is the way to run SMTP we recommend.
  • tls: { mode: starttls } connects on port 587 and upgrades with STARTTLS. A server that does not take STARTTLS fails the attempt; the email is never sent without TLS. Anything the server sends between its answer to STARTTLS and the handshake is thrown away, never read as if it came over TLS.
  • tls.ca names a CA to trust in place of Node.js's built-in roots, for a server with a certificate from your own CA. The server's certificate is always verified, and TLS 1.2 is the oldest version used.
  • plaintext: true, in place of tls, sends without TLS, on port 25 by default, to a relay on your own network. The engine logs plaintext for the destination at every start, and a block with plaintext: true cannot log in: a password is never sent without TLS.
  • user and password are secrets, read when the engine starts, and go together. Pathways logs in only once TLS is up, with the first of AUTH PLAIN, AUTH LOGIN and AUTH CRAM-MD5 the server offers.
  • from, to and cc are plain addresses, such as lab-desk@example.test, written in the block. Recipients never come from the message.

Fill the subject and text​

subject and body are templates. Each {{ name }} is filled when the email is sent:

PlaceholderFilled with
{{ messageId }}the stored message's id
{{ seq }}which output of the transform this is, from 0
{{ channel }}the channel's name
{{ meta.<key> }}the ctx.meta key, or nothing when it is not set

Any other placeholder stops the engine at start, naming the file and the key. Line breaks are taken out of the filled subject, so no text can add a header. ctx.meta is empty in this release (see Use the message context), so a {{ meta.<key> }} is filled with nothing for now.

Without subject, the subject is Pathways message {{ messageId }}-{{ seq }}. Without body, the text names the channel, the message id and the output.

Each email's Message-ID is made of the channel, the message id and the output's seq, at the sender's domain, such as <pathways.results-mail.12.0@example.test>. A resend has the same Message-ID, so a mail client, or a person, can tell it from a new message.

Attach the message​

channels/results-mail/channel.yaml
source:
file: { dir: ../../data/results }
destinations:
- smtp:
host: smtp.example.test
tls: { mode: starttls, ca: ../../certs/mail-ca.pem }
user: { env: SMTP_USER }
password: { env: SMTP_PASSWORD }
from: pathways@example.test
to: [lab-desk@example.test]
attach: true
maxSize: 5MB

With attach: true, each output goes as a file named message-<id>-<seq> with the format's extension (message-12-0.hl7). maxSize, 10 MB by default, caps the whole email as sent; a larger one is rejected before Pathways connects, and so is one larger than the size the server says it takes.

What each answer means​

An email is delivered only when the server answers 250 to its data. Everything else is read by where it came and its reply code:

What happenedOutcome
a 4xx anywhere, such as a 421 greeting or a 452 to a recipientretried, by the destination's retry settings
the connection could not be made, was lost, or sat idle past a time limitretried
a 530, 534 or 535 (a missing or refused login), any other 5xx before the sender is named, a certificate that does not verify, STARTTLS refused where mode: starttls needs it, a server that offers no loginheld: tried again at the longest retry wait, retry.max, 5 minutes by default, while you fix the configuration
a 5xx to the sender, a recipient or the data, such as 550 or 552rejected: the delivery fails for good
the server takes some recipients and refuses othersrejected (SMTP_RCPT_PARTIAL), or delivered with onPartialRefusal: delivered; see below
every recipient refusedretried when any refusal was a 4xx, otherwise rejected

Codes name the stage and the reply, such as SMTP_AUTH_535 or SMTP_RCPT_452, never the server's own words, which can echo an address. Logs, errors and codes lists them.

When some recipients are refused​

When the server takes the email for some recipients and refuses it for others, the recipients it took have the email, and the others never get it. By default the delivery is marked rejected, with the reason SMTP_RCPT_PARTIAL, so it shows among the failed deliveries for an operator to look at; a resend goes to every recipient again, with the same Message-ID. Set onPartialRefusal: delivered to mark it delivered instead. Either way the engine logs smtp-recipients-refused with how many were refused and which outcome was taken, never an address.

Time limits​

KeyDefaultHow long
connectTimeout10ssetting the session up may take: the connection, with implicit TLS its handshake, the greeting, and with STARTTLS the upgrade
greetingTimeout10sthe server may take to greet once connected, within connectTimeout
socketTimeout20sthe connection may sit idle waiting for an answer

The whole attempt runs under the destination's timeout, 30s by default, set beside smtp:. Each of the three must fit inside it: a longer one stops the engine at start, and a default longer than it is cut to it.