> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stateset.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Transports

> HTTP, file/VAN directory, native SFTP, AS2, and AS1 — five ways documents enter and leave the StateSet EDI gateway.

# Transports

The same pipeline — dedup → validate → translate → queue → acknowledge — runs behind every
transport, so a partner can move between them without any code changes.

## HTTP API

The direct path. `POST /v1/edi/inbound` takes X12 or EDIFACT (auto-detected) and returns the
997/CONTRL plus parsed JSON. Outbound endpoints take StateSet-shaped JSON and return wire
format:

```
POST /v1/edi/outbound/{tx}/{partner_id}          # X12
POST /v1/edifact/outbound/{invoic,desadv}/{id}   # EDIFACT
```

The HTTP inbound API is deliberately **single-interchange** — batch splitting applies only to
the file transports.

## File / VAN directory

Set `EDI_INBOUND_DIR` and `EDI_OUTBOUND_DIR` to directories served by your SFTP server or
synced from a VAN. The worker picks up `.edi`, `.x12`, and `.txt` files, writes
`*.997.edi` / `*.ta1.edi` responses, and moves consumed files to `processed/` — or to
`failed/` with an `.error.txt`. Nothing is ever consumed silently.

Files modified within the last `EDI_SETTLE_SECONDS` (default 2) are skipped, so an upload
still in flight is never read half-written.

<Note>
  A pickup holding several concatenated `ISA..IEA` interchanges — the classic VAN batch shape —
  is split and each interchange processed independently. Acks get a `.N` infix
  (`batch.1.997.edi`), and one bad interchange quarantines the file with a note naming it while
  its siblings still process. This applies to all three file transports: directory, SFTP, and
  VAN mailbox.
</Note>

| Variable                               | Default | Purpose                                          |
| -------------------------------------- | ------- | ------------------------------------------------ |
| `EDI_INBOUND_DIR` / `EDI_OUTBOUND_DIR` | —       | Directory transport paths                        |
| `EDI_POLL_SECONDS`                     | `10`    | Inbound directory poll interval                  |
| `EDI_SETTLE_SECONDS`                   | `2`     | Files must be unmodified this long before pickup |
| `EDI_VAN_ROOT`                         | —       | Root directory for file-backed VAN mailboxes     |

## Native SFTP mailbox

When the partner or VAN hosts the mailbox, the gateway connects out as an SFTP **client**
(pure-Rust `russh`, no OpenSSL). It polls the remote inbound directory, processes each file,
writes the 997/TA1 to the remote outbound directory, and moves sources into
`processed/` / `failed/`.

| Variable                                         | Default                | Purpose                                                                    |
| ------------------------------------------------ | ---------------------- | -------------------------------------------------------------------------- |
| `EDI_SFTP_HOST`                                  | —                      | SFTP host; presence enables the transport                                  |
| `EDI_SFTP_PORT`                                  | `22`                   | Port                                                                       |
| `EDI_SFTP_USER`                                  | `edi`                  | Username                                                                   |
| `EDI_SFTP_PASSWORD`                              | —                      | Password auth, or…                                                         |
| `EDI_SFTP_KEY_FILE`                              | —                      | …OpenSSH private-key file for key auth                                     |
| `EDI_SFTP_HOST_KEY`                              | —                      | Pinned host fingerprint (`SHA256:…`); unset accepts any key with a warning |
| `EDI_SFTP_INBOUND_DIR` / `EDI_SFTP_OUTBOUND_DIR` | `inbound` / `outbound` | Remote mailbox directories                                                 |
| `EDI_SFTP_POLL_SECONDS`                          | `30`                   | Poll interval                                                              |

<Warning>
  Leaving `EDI_SFTP_HOST_KEY` unset accepts any host key with only a warning. Pin the
  fingerprint in production.
</Warning>

## AS2 (EDI over HTTP, with receipts)

[AS2](https://datatracker.ietf.org/doc/html/rfc4130) answers what file transport can't: *did
the partner receive this document, intact?* The gateway computes a **MIC** (message integrity
check) over what it sends; the partner returns an **MDN** echoing a MIC over what they
received. A match is cryptographic proof of intact receipt.

### Receiving

`POST /v1/as2` — partners deliver here. The endpoint processes the EDI and returns a
synchronous MDN. Per the AS2 spec it answers `200 OK` even when the EDI is rejected; the
*disposition* inside the MDN carries the verdict. It is unauthenticated by transport design —
partners are identified by AS2 identity and S/MIME signature.

Set `EDI_AS2_REQUIRE_VERIFIED_INBOUND` (default `true`) to fail closed: every inbound message
must be signed and verified against the partner's configured certificate.

### Sending with tracked delivery

Dispatching to an AS2 partner records a `pending` **delivery** and returns immediately. A
background worker (`EDI_DELIVERY_POLL_SECONDS`, default 60) sends each due delivery, confirms
its MDN, and on failure **retries with exponential backoff (1m → 30m)**. After the attempt cap
it marks the delivery `failed` and fires a critical alert.

Inspect deliveries at `GET /v1/deliveries`; re-drive one with
`POST /v1/deliveries/{id}/redeliver` or all failures with
`POST /v1/deliveries/redeliver-bulk`. Delivery state is shared via Postgres, so any replica
can drive a delivery.

### Synchronous and asynchronous MDNs

* `mdn_mode: sync` (default) confirms the delivery inline from the HTTP response.
* `mdn_mode: async` parks the delivery `awaiting_mdn` and does not retry it until the partner
  POSTs its MDN to `POST /v1/as2/mdn`. That endpoint matches it to the tracked delivery by
  `Original-Message-ID`, recomputes the MIC from the delivery's stored payload to verify
  integrity, and flips it to `delivered` — or `failed` with an alert. An `awaiting_mdn`
  delivery older than `EDI_MDN_TIMEOUT_HOURS` (default 24) is re-driven by the worker.

### S/MIME

Pure-Rust RustCrypto. Detached CMS `SignedData` (`multipart/signed`, SHA-256 + RSA) for
signing and verification, and CMS `EnvelopedData` (AES-256-CBC + RSA key transport) for
encryption. Set `EDI_AS2_KEY_FILE` and `EDI_AS2_CERT_FILE` plus a partner's
`as2.partner_cert_pem` to sign-then-encrypt outbound and decrypt-then-verify inbound.

The MIC is computed over pre-encryption content on both ends, so receipts verify regardless of
encryption.

Rotate the signing/decryption identity without a restart via `POST /v1/as2/identity`. By
default a rotation is replica-local; set `EDI_AS2_PERSIST_IDENTITY=true` to persist it to the
state backend so it survives restarts and reaches all replicas — at the cost of putting key
PEM in the database or data directory.

## AS1 (EDI over email)

Partners who exchange EDI by email are served over AS1. Set a partner's `as1.partner_email`
and every outbound document is delivered as an `application/edi-x12` attachment via the
[stateset-mail](/stateset-mail/overview) connector — best-effort, never blocking the API
response. Inbound AS1 arrives at `POST /v1/as1/inbound`, where the mail service forwards the
message.

Configure the connector with `EDI_MAIL_API_URL`, `EDI_MAIL_API_KEY`, and `EDI_MAIL_FROM`.

## Related

* [Configuration](/stateset-edi/configuration) — the full environment-variable reference
* [Operations](/stateset-edi/operations) — deliveries, reconciliation, and alerting
