# EDIFACT transport patterns

Receive and send EDIFACT over the mailbox networks, file drops and HTTP endpoints your trading partners use.

An EDIFACT interchange is sent over the carrier the trading partners agreed on - a mailbox network, a file drop, an HTTP endpoint or email - so there is no dedicated EDIFACT connection type. The UNB-UNZ envelope holds the addressing, the channel that receives the text invokes your service with it, and `self.request.edifact` is the interchange parsed into typed messages, whichever channel delivered it.

Unlike [HL7 v2 over MLLP](https://zato.io/docs/dev/healthcare/hl7v2/mllp/index.html), where the protocol includes a live socket and an acknowledgment loop, EDIFACT networks are store-and-forward - delivering to the network is the transport-level confirmation.

## The transport patterns {#the-transport-patterns}

- **Mailbox networks** - national or sector-wide networks give every participant a mailbox, and clients poll for inbound messages over an HTTP-based API, commonly SOAP or REST. Dutch healthcare, for example, exchanges its EDIFACT traffic this way.
- **File drops** - a shared directory, an SMB share, or an SFTP server where one side writes interchange files and the other picks them up.
- **Direct APIs** - a partner exposes an HTTP endpoint that accepts the interchange text as the request body.
- **Email** - the oldest pattern, one interchange per message, still in use between long-standing partners.

Zato covers each with connections it already has - REST and SOAP outgoing connections, file transfer channels, SFTP, IMAP and SMTP connections, and [scheduler jobs](https://zato.io/docs/dev/examples/scheduler.html) for polling.

## Receiving from a mailbox {#receiving-from-a-mailbox}

A scheduler job polls the mailbox API, and each retrieved interchange goes through the same parsing path. The job reads the provider's address from an outgoing REST connection. Create the connection under **Connections** > **Outgoing** > **REST**, with **EDI Mailbox** as its name:

[Video: Connection to the mailbox provider](https://zatosource-production.b-cdn.net/docs/gfx/edifact/mailbox-connection-create.webm?v=1791298923)

Dashboard menu: Connections > Outgoing > REST

The service lists pending messages, retrieves each one and parses it. Mailbox providers differ in the details of their APIs, but a polling service follows this structure:

```python
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service
from zato.edifact import parse_edifact

# Registers the example dialect - use your own here
import zato.edifact.nl

class PollMailbox(Service):
    name = 'demo.edifact.poll-mailbox'

    def handle(self) -> 'None':

        # An outgoing REST connection to the mailbox provider
        conn = self.rest['EDI Mailbox']

        # List and retrieve pending messages ..
        response = conn.get({'action': 'list'})

        for item in response.data['messages']:

            wire_text = item['body']

            # .. parse each interchange ..
            interchange = parse_edifact(wire_text)

            # .. and log who sent it and how many messages are in it.
            sender = interchange.header.sender.identification
            count = len(interchange.messages)

            self.logger.info('Received %d messages from %s', count, sender)
```

## Receiving from a file drop {#receiving-from-a-file-drop}

A file transfer channel picks up interchange files as they appear and invokes a service with each file. The service reads the parsed interchange from `self.request.edifact`:

```python
class HandleFile(Service):
    name = 'demo.edifact.handle-file'

    def handle(self) -> 'None':

        # The channel read the file and the request parsed it
        interchange = self.request.edifact

        for msg in interchange.messages:
            message_type = msg.unh.identifier.message_type
            self.logger.info('Received %s', message_type)
```

## Receiving over email {#receiving-over-email}

An [IMAP connection](https://zato.io/docs/dev/examples/imap.html) with a schedule invokes a service once per new e-mail. An interchange sent this way is the plain-text body of the e-mail and `self.request.edifact` reads it from there:

```python
class HandleEmail(Service):
    name = 'demo.edifact.handle-email'

    def handle(self) -> 'None':

        interchange = self.request.edifact

        # A bare message without a UNB envelope has no header - the message itself says who sent it
        report = interchange.message
        self.logger.info('Received %s', report.unh.identifier.message_type)
```

## Receiving over HTTP {#receiving-over-http}

A partner posting interchanges directly uses a REST channel, where the request body is the interchange text, or a SOAP channel, where the text is the content of the operation element. The service is the same in both cases:

```python
class HandleHTTP(Service):
    name = 'demo.edifact.handle-http'

    def handle(self) -> 'None':

        interchange = self.request.edifact

        sender = interchange.header.sender.identification
        count = len(interchange.messages)

        self.logger.info('Received %d messages from %s', count, sender)
```

## Where the text comes from {#where-the-text-comes-from}

`self.request.edifact` parses the interchange once, on first access, and every later access returns the same object. The text it parses depends on the channel:

- **File transfer** - the bytes of the received file
- **IMAP** - the first plain-text part of the e-mail
- **REST, MLLP, AMQP, Kafka, IBM MQ** - the request body, as text or bytes
- **SOAP** - the text of the operation element inside the SOAP body, or the text of its single child element

Bytes are decoded by the syntax identifier in the UNB header - UNOA and UNOB as ASCII, UNOC as ISO-8859-1, UNOD as ISO-8859-2, UNOE as ISO-8859-5, UNOF as ISO-8859-7 and UNOY as UTF-8. Bare messages without a UNB header decode as ISO-8859-1.

Text that reaches the service some other way, e.g. as the response to an outgoing call as in the mailbox example above, goes through `parse_edifact` directly.

## Sending {#sending}

Serialization produces the wire text, and any outgoing connection delivers it:

```python
# Zato
from zato.edifact.nl import MEDVRI
from zato.edifact.nl.segments import TXT

class SendReply(Service):
    name = 'demo.edifact.send-reply'

    def handle(self) -> 'None':

        # A short free-text message confirming the results arrived ..
        reply = MEDVRI()

        # .. the reference and the message identity go in its UNH header ..
        reply.unh.reference_number = '8002'
        reply.unh.identifier.message_type = 'MEDVRI'
        reply.unh.identifier.version = '1'

        # .. the sender and the date follow ..
        reply.sender.person_name = 'E. Vermeer'
        reply.sender.institution_name = 'GGZ De Linde'

        reply.det.date.year  = '26'
        reply.det.date.month = '08'
        reply.det.date.day   = '31'

        # .. the text is one TXT segment per line ..
        line = TXT()
        line.text = 'Results received, thank you'
        reply.text = [line]

        # .. and the UNT trailer closes the message.
        reply.unt.segment_count    = '5'
        reply.unt.reference_number = '8002'

        # Serialization produces the wire text ..
        wire_text = reply.serialize()

        # .. an outgoing REST connection delivers it ..
        conn = self.rest['EDI Mailbox']
        _ = conn.post(wire_text)

        # .. an outgoing REST connection delivers it ..
        conn = self.rest['EDI Mailbox']

        # .. so do send it now.
        _ = conn.post(wire_text)
```

## Acknowledgments {#acknowledgments}

EDIFACT has service messages for acknowledgments (CONTRL, APERAK), but many networks - healthcare mailbox networks among them - do not use them, relying on the network's own delivery guarantees instead. If a partner does require them, they are ordinary messages: define their classes per [Dialects and profiles](https://zato.io/docs/dev/healthcare/edifact/dialects/index.html), build them like any other message, and send them back over the same carrier.

## See also {#see-also}

- [Message parsing](https://zato.io/docs/dev/healthcare/edifact/parsing/index.html) - What parse\_edifact does with the text a carrier delivers
- [Scheduler examples](https://zato.io/docs/dev/examples/scheduler.html) - Creating the jobs that poll mailboxes on a schedule
- [EDIFACT in healthcare tutorial](https://zato.io/tutorials/healthcare/edifact.html) - A REST channel receiving lab results and sending a letter

## Learn more {#learn-more}

- [Healthcare interface engine](https://zato.io/docs/dev/healthcare/) - Clinical messages, FHIR, conversion and operations
- [HL7 v2 parsing](https://zato.io/docs/dev/healthcare/hl7/v2/parsing/) - Messages as typed objects, with field access and validation
- [MLLP channels](https://zato.io/docs/dev/healthcare/hl7v2/mllp/) - Receiving and sending HL7 v2 over MLLP sockets
- [FHIR integrations](https://zato.io/docs/dev/healthcare/hl7/fhir/) - Read and write FHIR resources, with paths, bundles and extensions
- [HL7 v2 to FHIR](https://zato.io/docs/dev/healthcare/hl7/to-fhir/) - Converting v2 messages into FHIR bundles, codes and references included
- [EDIFACT](https://zato.io/docs/dev/healthcare/edifact/) - Parsing interchanges, dialects and the transports they arrive on
- [Transformation](https://zato.io/docs/dev/healthcare/transformation/) - One message in, another standard out, in ordinary Python
- [Audit log](https://zato.io/docs/dev/healthcare/audit-log.html) - Every message with its acknowledgment, searchable by patient identifier
- [AI in clinical interfaces](https://zato.io/docs/dev/healthcare/ai/) - Services calling LLMs and AI agents calling clinical services as tools
