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

# Operations

> Reconciliation, evidence bundles, deductions, monitoring and alerting, the operator dashboard, and multi-tenancy.

# Operations

Moving documents is the easy half. This page covers the operational surface an EDI program
actually runs on.

## Reconciliation

Two levels:

| Endpoint                          | Scope                                                                                                                            |
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `GET /v1/reconciliation`          | **Acknowledgment** reconciliation — outbound documents and their 997 status. Filter with `?outstanding=true` or `?overdue=true`. |
| `GET /v1/business-reconciliation` | **Business** reconciliation across the PO / ASN / 945 / invoice / payment chain.                                                 |

An outbound document with no 997 after `EDI_ACK_SLA_HOURS` (default 24) is **overdue**. The
monitor fires an alert when the overdue set grows.

Trace a single reference end to end with `GET /v1/lifecycle/{reference}`.

## Evidence bundles

Compliance evidence, exportable for audit:

* `GET /v1/documents/{id}/evidence` — one document and its related order chain.
* `GET /v1/evidence/{reference}` — the bundle for a PO, shipment, or invoice reference.
* `GET /v1/artifacts/conformance` — WS-1 acceptance evidence: for each packaged retail
  connector, instantiate and exercise it.

## Deductions

Chargeback and deduction recovery, sourced from inbound 812s or keyed by an operator.

| Endpoint                              | Purpose                                                           |
| ------------------------------------- | ----------------------------------------------------------------- |
| `GET /v1/deductions`                  | List with an exposure/recovery summary                            |
| `POST /v1/deductions`                 | Capture a deduction                                               |
| `GET /v1/deductions/{id}`             | One deduction with its dispute history                            |
| `POST /v1/deductions/{id}/categorize` | Assign a root-cause category                                      |
| `POST /v1/deductions/{id}/dispute`    | Open a dispute                                                    |
| `POST /v1/deductions/{id}/evidence`   | Attach a supporting note and optional link                        |
| `POST /v1/deductions/{id}/resolve`    | Resolve to a terminal outcome — recovered, denied, or written off |

## Monitoring and alerting

`GET /v1/monitoring` returns a real-time snapshot: per-partner acknowledgment SLA status and
more. `POST /v1/monitoring/evaluate` evaluates monitoring and **fires** every critical alert
through the configured channels.

Alerts fan out to every configured channel independently — a Slack-compatible **webhook**
(`EDI_ALERT_WEBHOOK_URL`) and/or **email** via the [stateset-mail](/stateset-mail/overview)
connector (`EDI_MAIL_*` plus `EDI_ALERT_EMAIL_TO`). The reconciliation monitor and delivery
worker fire on:

* The overdue-997 set growing — a partner failing to acknowledge.
* The AS2 certificate nearing expiry (`EDI_CERT_EXPIRY_WARN_DAYS`, default 21 days).
* AS2 deliveries failing after retries.

Alerts fire **on change**, not every cycle, so they don't flood the channel. Repeats of the
same alert kind and partner within `EDI_ALERT_THROTTLE_SECONDS` (default 300) are counted
rather than re-sent; the next delivery carries the count.

Set `EDI_ALERT_WEBHOOK_SECRET` and each alert POST carries an
`X-StateSet-Signature: sha256=<hex>` HMAC of the body. Webhook URLs are validated at startup —
a private or loopback URL fails fast unless `EDI_ALERT_WEBHOOK_ALLOW_PRIVATE=true`.

## Analytics

* `GET /v1/analytics` — volume, error rate, throughput trend.
* `GET /v1/analytics/export` — per-partner analytics as a CSV download.
* `GET /v1/fulfillment` — 3PL scorecard: fill rate, perfect-order rate, at-risk shipments.
* `GET /v1/network/readiness` — trading-network readiness across the tenant's partners.

## Documents and replay

The document ledger is durable and paged. In HA deployments it is shared Postgres; a JSONL
fallback covers single-node development.

| Endpoint                            | Purpose                                                                        |
| ----------------------------------- | ------------------------------------------------------------------------------ |
| `GET /v1/documents`                 | Paged ledger, newest first                                                     |
| `GET /v1/documents/{id}`            | One document                                                                   |
| `POST /v1/documents/{id}/reprocess` | Re-run validation, translation, and queueing for a stored **inbound** document |
| `POST /v1/documents/reprocess-bulk` | Bulk reprocess matching a filter                                               |
| `POST /v1/documents/manual`         | Manual entry for partners without back-end integration                         |

Events the sequencer permanently rejected land in the dead-letter store: inspect with
`GET /v1/deadletter`, and push them all back into the active outbox with
`POST /v1/deadletter/replay`.

## Multi-tenancy

Every trading partner belongs to a **tenant**, and partner data is isolated per tenant. The
credential carries the tenant: a partner API key implies its tenant, while the admin token
selects one with an `x-tenant-id` header (default `default`). Reads and writes are scoped so
one tenant never sees another's partners.

Provision tenants via `GET`/`POST /v1/tenants`. Cross-instance isolation requires Postgres;
without `DATABASE_URL` the deployment is effectively single-tenant.

## Operator dashboard

A Vite + React console behind a WorkOS AuthKit gateway: OAuth at the edge, a sealed session
cookie, and a tenant-pinning proxy to the API. The browser never holds a backend credential,
and each signed-in organization maps to a tenant.

It surfaces document throughput, live alerts, 997 reconciliation, tracked AS2 deliveries,
partners, and connectors, with trend sparklines. Operators can:

* Create and edit partner identities and transports.
* Page through the durable document ledger.
* Reprocess inbound documents behind a confirmation.
* Inspect immutable, actor-attributed audit events (`GET /v1/audit`).
* Use an **EDI agent** whose mutations require signed, single-use confirmation tokens.

The dashboard is tested in CI (lint, vitest, build) and fails closed in production builds.

## Health and metrics

| Route          | Auth   | Purpose                                                           |
| -------------- | ------ | ----------------------------------------------------------------- |
| `GET /live`    | public | Liveness — always 200, no DB probe                                |
| `GET /health`  | public | Readiness plus outbox depth; 503 when Postgres is unreachable     |
| `GET /metrics` | admin  | Prometheus metrics (the ServiceMonitor supplies the bearer token) |

## Data retention

All `*_RETENTION_DAYS` sweeps run hourly on the cluster monitor leader. `0` (the default)
keeps rows forever.

| Variable                        | Prunes                                                                                  |
| ------------------------------- | --------------------------------------------------------------------------------------- |
| `EDI_DOCUMENT_RETENTION_DAYS`   | The document and raw-wire detail JSONL audit logs — the fastest-growing store           |
| `EDI_DELIVERY_RETENTION_DAYS`   | **Terminal** AS2/AS1 delivery rows, payloads included; in-flight rows are never touched |
| `EDI_USAGE_RETENTION_DAYS`      | Usage-ledger rows                                                                       |
| `EDI_SEEN_RETENTION_DAYS`       | Interchange-dedup entries (Postgres backend only)                                       |
| `EDI_DEADLETTER_RETENTION_DAYS` | Dead-lettered outbox events not replayed within the window                              |

<Warning>
  Pruning dedup entries re-opens the replay window past the cutoff, which lets legitimately
  wrapped control numbers through — but also lets a genuine replay through. Set
  `EDI_SEEN_RETENTION_DAYS` deliberately.
</Warning>
