> ## 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.

# EDI Quickstart

> Run the StateSet EDI gateway, register a trading partner, and process an 850 through to a labelled ASN.

# EDI Quickstart

## Run the gateway

```bash theme={null}
EDI_API_TOKEN=$(openssl rand -hex 24) cargo run
# stateset-edi listening on 0.0.0.0:8480
```

The server **refuses to start without `EDI_API_TOKEN`** unless `EDI_AUTH_DISABLED=true`
(development only). It speaks plain HTTP — terminate TLS at a reverse proxy such as nginx,
Caddy, or an ALB.

<Warning>
  Set `EDI_TRUSTED_PROXY_HOPS` to the number of proxies in front of the gateway so the rate
  limiter reads the real client IP from `X-Forwarded-For`. Left at `0` it ignores the header and
  keys on the socket peer, so a directly-reachable listener can't be spoofed.
</Warning>

With Docker:

```bash theme={null}
docker build -t stateset-edi .
docker run -p 8480:8480 -v edi-data:/data -e EDI_API_TOKEN=... stateset-edi
```

`docker compose up` works with a `.env` (see `.env.example`). For Kubernetes,
`deploy/k8s/stateset-edi.hetzner.yaml` carries HA API and dashboard deployments, probes,
disruption budgets, authenticated Prometheus scraping, alert rules, and Secret-based
environment contracts.

Interactive API docs are served at `/docs`, and the OpenAPI 3 document at
`/api-docs/openapi.json`.

## Receive a PO, ship it

```bash theme={null}
AUTH="Authorization: Bearer $EDI_API_TOKEN"

# 1. Register the trading partner
curl -X PUT localhost:8480/v1/partners/acme -H "$AUTH" \
  -H 'content-type: application/json' -d @examples/partner.acme.json

# 2. Inbound 850 — the response includes the parsed PO and the 997 to return
curl -X POST localhost:8480/v1/edi/inbound -H "$AUTH" \
  --data-binary @examples/po.edi

# 3. Outbound 856 — the response body is wire-ready X12. If AS2 is configured,
#    a tracked delivery is dispatched to the partner at the same time.
curl -X POST localhost:8480/v1/edi/outbound/856/acme -H "$AUTH" \
  -H 'content-type: application/json' -d '{
  "shipment_id": "SHIP-9001", "ship_date": "2026-06-12",
  "carrier_scac": "FDEG", "tracking_number": "794611223344",
  "orders": [{ "po_number": "PO-2026-4411",
               "items": [{ "sku": "SHOE-RED-10", "quantity": "12" }] }]
}'

# 4. A UCC-128 / SSCC-18 shipping label (ZPL) for that ASN
curl -X POST localhost:8480/v1/labels/856/acme -H "$AUTH" \
  -H 'content-type: application/json' -d @asn.json

# 5. Sync queued events to the StateSet sequencer
curl -X POST localhost:8480/v1/outbox/flush -H "$AUTH"
```

<Tip>
  Starting from a known retailer? Skip step 1 — `POST /v1/connectors/{name}/partners/{id}`
  creates a partner from one of the 40+ packaged retailer connectors, prefilling its
  implementation guide. Browse them with `GET /v1/connectors`.
</Tip>

## Shipping labels (UCC-128 / SSCC-18)

Retailers reject ASNs whose cartons aren't labelled with a scannable SSCC-18 in a GS1-128
barcode. With `EDI_GS1_COMPANY_PREFIX` set, `POST /v1/labels/856/{partner}` takes an 856 ASN
and returns a minted SSCC-18 (with GS1 mod-10 check digit) plus a ZPL label — a Code128
subset-C GS1-128 barcode with human-readable `(00)` SSCC, ship-to, and PO — ready for a Zebra
printer.

The 856 itself carries a carton (`HL*P`) level with one `MAN*GM` SSCC-18 per carton, so the
SSCC on each carton label matches the ASN. That is what QVC-style pack compliance requires.

Related endpoints:

| Endpoint                                      | Purpose                                                                  |
| --------------------------------------------- | ------------------------------------------------------------------------ |
| `POST /v1/labels/856/{partner}`               | One shipment-level label                                                 |
| `POST /v1/labels/856/{partner}/cartons`       | One label per carton from the ASN's carton structure                     |
| `POST /v1/labels/856/{partner}/level/{level}` | GS1 labels at a specific pack level — `carton` (SSCC, AI 00), and higher |
| `POST /v1/packing-slip/856/{partner}`         | Printable HTML slip + ZPL summary label                                  |
| `POST /v1/packing-slip/856/{partner}/pdf`     | The same, streamed as native PDF                                         |

## Authentication

Two credential tiers:

* The **admin token** (`EDI_API_TOKEN`) has full access to all `/v1` routes. It selects a
  tenant with an `x-tenant-id` header, defaulting to `default`.
* **Partner API keys** are scoped to that partner's own routes. They are SHA-256-hashed at
  rest, shown once, and rotated by re-issuing via `POST /v1/partners/{id}/api-key`. A partner
  key implies its tenant.

Authorization is checked **before** request bodies are parsed.

## Embedding the codecs

The X12 and EDIFACT codecs are standalone, publishable crates with no transport, HTTP, or
database dependencies — embed them in any Rust EDI pipeline:

```bash theme={null}
cargo add stateset-edi-x12
cargo add stateset-edi-edifact
```

## Next steps

<CardGroup cols={2}>
  <Card title="Transaction sets" icon="table-list" href="/stateset-edi/transaction-sets">
    Every supported set and its direction.
  </Card>

  <Card title="Transports" icon="network-wired" href="/stateset-edi/transports">
    Move a partner to SFTP, AS2, or AS1.
  </Card>

  <Card title="Operations" icon="gauge" href="/stateset-edi/operations">
    Reconciliation, evidence, deductions, alerting.
  </Card>

  <Card title="MCP server" icon="plug" href="/stateset-edi/mcp-server">
    Drive all of this from an agent.
  </Card>
</CardGroup>
