Direct Secure Messaging
Receive referrals, discharge summaries and results over Direct - an IMAP connection to your HISP reads the inbox and your service gets each clinical document, out of its attachment or its XDM package, in Python.
Direct Secure Messaging is how providers in the United States send referrals, discharge summaries and results to one another - an encrypted email from one Direct address to another, with the clinical document, usually a C-CDA, as an attachment. Zato reads the messages from your inbox, hands each document to your service and lets the service send Direct messages back, all in Python.
The encryption and the trust between organizations are the work of the HISP - the Health Information Service Provider that hosts your Direct address. The HISP signs and encrypts outgoing messages with S/MIME, decrypts and verifies incoming ones against the DirectTrust trust bundles, and sends the message disposition notifications that confirm delivery to the sender's HISP. What reaches your inbox is a plain email with its attachments, which makes Zato an edge client of the HISP - a program that reads the inbox over IMAP and sends through the HISP's SMTP relay, with no certificates to manage.
Connect to your HISP
An IMAP connection points at the inbox the HISP gives you, and its Scheduler section makes it poll the inbox and invoke your service with what arrives. Go to Connections > Outgoing > E-mail > IMAP in the Dashboard, click Create a new IMAP connection and fill in the form:
- Name: Direct Inbox
- User / e-mail: your Direct address, e.g.
referrals@direct.example.com - Click Toggle options in the Generic IMAP row and fill in Host with the IMAP server of your HISP, e.g.
imap.hisp.example.com - Click Toggle options in the Scheduler row and fill in Run every 1 minute, a Start time, the Service to invoke and Invoke with set to Each attachment
- Click OK
Saving the connection creates a scheduler job for it. On each run, the job reads the unseen messages in the inbox and, in the Each attachment mode, invokes your service once per attachment, so a message with a C-CDA and its PDF rendering invokes the service twice. A message is marked as seen once all of its attachments were handled without an exception, and one that raised stays unseen, so the next run delivers it again and nothing is lost. The connection's password is set after the connection is created, through the Change password link on its row.
The same connection in an enmasse file:
email_imap:
- name: Direct Inbox
host: imap.hisp.example.com
port: 993
username: referrals@direct.example.com
password: Zato_Enmasse_Env.DirectInboxPassword
mode: ssl
get_criteria: UNSEEN
scheduler_service: direct.referral-intake
scheduler_run_every: 1
scheduler_run_unit: minutes
scheduler_start_date: 2026-10-05T08:00:00
scheduler_invoke_with: each_attachment
A complete service
A service is a Python class that Zato runs when something invokes it. The service below receives each attachment of each Direct message, converts the C-CDA it carries to FHIR and stores the result in a FHIR server through an outgoing FHIR connection:
# -*- coding: utf-8 -*-
# Zato
from zato.server.service import Service
class ReferralIntake(Service):
""" Receives the attachments of Direct messages and stores each clinical document they carry in a FHIR server.
"""
name = 'direct.referral-intake'
def handle(self) -> 'None':
# The attachment the message carried, with the subject and the sender of the message it came from ..
attachment = self.request.input
self.logger.info('Received `%s` from %s -> %s', attachment.filename, attachment.sent_from, attachment.subject)
# .. each document inside it - the attachment itself or, if it is an XDM package, each document the package names ..
for document in attachment.documents():
# .. the human-readable renderings that travel alongside are not converted ..
if document.mime_type == 'application/pdf':
continue
# .. and each clinical document becomes a FHIR bundle that goes to the server as a whole.
bundle = self.ccda.to_fhir(document.data)
client = self.fhir['FHIR.Registry']
client.execute('', method='post', data=bundle.to_dict())
The documents() call is what makes the service the same whether the sender attached the C-CDA directly or packaged it as XDM - the service reads documents, not attachments. Each document has data, file_name, mime_type and size, and a document that came out of an XDM package also has the title, patient ID and document codes its metadata gave it, as IHE XDM document packages describes.
Documents packaged as XDM
Many senders put the documents of a message in an IHE XDM package - a zip with a metadata file describing each document - rather than attaching them one by one, and a message whose subject starts with XDM/1.0/DDM carries one. The documents() call above opens the package and returns each document it names, with the metadata, so the service above handles such messages with no change. IHE XDM document packages covers what is in a package and what is checked when it is read.
Send a Direct message
A Direct message is sent the way any email is - through an SMTP connection pointing at the HISP's relay, from your Direct address to the recipient's. The HISP signs and encrypts the message once it leaves your service:
# -*- coding: utf-8 -*-
# Zato
from zato.common import SMTPMessage
from zato.server.service import Service
class SendReferral(Service):
""" Sends a referral with its C-CDA to another provider's Direct address.
"""
name = 'direct.send-referral'
def handle(self) -> 'None':
conn = self.email.smtp.get('HISP Relay').conn
msg = SMTPMessage()
msg.from_ = 'referrals@direct.example.com'
msg.to = 'intake@direct.clinic.example.com'
msg.subject = 'Referral for Mary Jones'
msg.body = 'Please see the attached continuity of care document.'
# Delivery notifications come back to this address, and the Message-ID is how they are matched to this message
msg.headers['Disposition-Notification-To'] = 'referrals@direct.example.com'
msg.headers['Message-ID'] = '<referral-20260114-0042@direct.example.com>'
msg.attach('CCD.xml', self.request.input['document'])
conn.send(msg)
Receipts and delivery notifications
Direct confirms delivery with message disposition notifications - MDNs - and both of them are sent by the receiving HISP, not by the program reading the inbox. A processed MDN says the HISP decrypted and verified the message, and a dispatched MDN says the HISP placed it in the recipient's inbox. Your service receives Direct messages without sending anything in return, and a message was received once it was read from the inbox.
When your service is the sender, the MDNs for its messages arrive in your inbox as messages of their own, with a multipart/report content type and the Message-ID of the original message in their Original-Message-ID field, which is how a service tracking its own deliveries recognizes them. A sender that does not track deliveries lets the HISP handle them, which is what most do.
Troubleshooting
Nothing arrives. The connection's host is the HISP's IMAP server rather than its web address, the user is the full Direct address, and the Scheduler row has its service and start time filled in - a connection without them reads nothing on its own. The job the connection created is listed under Scheduler as imap.Direct Inbox.
Every message arrives twice. The service raised an exception on the first run, so the message stayed unseen and the next run delivered it again. The exception is in the server log under the service's name.
The attachment is a zip. It is an XDM package, and documents() returns the documents inside it rather than the zip - a service reading attachment.data directly reads the zip itself. IHE XDM document packages has the details.
The MDN never came back. MDNs are sent by the recipient's HISP once it processed the message, and a recipient whose HISP does not send dispatched MDNs confirms nothing beyond processed. The Disposition-Notification-To header is what asks for them.
See also
| Page | What it covers |
|---|---|
| IHE XDM document packages | The packages that Direct messages carry their documents in |
| C-CDA to FHIR | Converting the documents that arrive over Direct |
| IMAP | Reading email on a schedule, with messages or attachments on input |
| SMTP | Sending email through a connection |
| FHIR connections | The outgoing connection that stores the bundle |
Learn more
Schedule a meaningful demo
Book a demo with an expert who will help you build meaningful systems that match your ambitions
"We evaluated 12 integration platforms and Zato was the only one to score 100%."
Philip Zuñiga, Assistant Professor, University of the Philippines