Companion to [PRODUCT-PLAN-v3.1-DELIVERY-PLAN.md](../PRODUCT-PLAN-v3.1-DELIVERY-PLAN.md) Phase 2 (Sprints 2.1–2.2). Depends on [Phase 1](PHASE-1-MONEY-FX-PAYMENTS-CONTRACT.md) (Money/PriceSnapshot/PaymentIntent) being implemented first — an Order line references a `priceSnapshotId` from that contract.
**Status: ready to build.** No open decisions block this phase.
---
## 1. Why this exists
Today `AdminOrdersLocalGateway` is a static 24-row in-memory seed with no create path — a real order can never appear. `AdminOrderWatcherService` already polls for new orders to toast/badge the admin, but is functionally inert against the mock. This contract makes both real.
## 2. Multi-seller model — Sprint 0.1 decision: unified
**One `Order` per checkout, regardless of how many sellers are represented.** Lines are grouped into per-seller `Fulfillment` entries internally. There is no parent/child order splitting, no separate order-per-seller. A seller only ever sees their own `Fulfillment` group within a shared order (see [Phase 5 contract](PHASE-5-SELLER-PORTAL-CONTRACT.md) for the seller-scoped view).
### 3.1 Public addressing and snapshot completeness
Added 2026-08-21 (FH-2.13).
-`Order` carries a `publicToken`: at least 24 random bytes, base64url, unique. **Every customer-facing route addresses an order by this token, never by `id`.** Order confirmation links, status polling, and support lookups all use it. A sequential or guessable public identifier turns "check my order" into an enumeration of the tenant's order book.
-`GET /api/v2/storefront/orders/{publicToken}` is scoped to the resolved tenant. A valid token from a different marketplace is `404`.
-`OrderLine` already snapshots SKU, title and price. Extend that to **everything that must survive a later edit**: currency, per-line discount, delivery option and price, and the tax/fee components. Together with `OrderContactSnapshot`, an order must be fully reconstructable from its own rows — renaming an offer, changing a price, or deleting a delivery option must not alter what a historical order says was bought and charged.
- Snapshot fields are written once at order creation and never updated. A correction is a new order event, a refund, or an amendment record — never an in-place rewrite of what the customer agreed to.
GET /api/admin/v2/orders?marketplaceId=&status=&source=&page=&pageSize=
GET /api/admin/v2/orders/{id}
PATCH /api/admin/v2/orders/{id}/status { status }
POST /api/admin/v2/orders/{id}/refund-request { reason }
POST /api/admin/v2/orders/{id}/notes { note, internal: boolean }
POST /api/admin/v2/orders/{id}/archive
POST /api/admin/v2/orders/{id}/restore
DELETE /api/admin/v2/orders/{id}
GET /api/seller/v1/orders?fulfillmentStatus=&page=&pageSize=
-> returns Order + only the Fulfillment groups belonging to the authenticated seller,
OrderLines filtered to that seller's lines. Never the full order's other-seller lines.
```
Replaces `AdminOrdersLocalGateway` behind the `ADMIN_ORDERS_GATEWAY` token already wired this session (see [BACKEND-API-REFERENCE.md §8](../../BACKEND-API-REFERENCE.md)) — no facade change needed, only binding a real `AdminOrdersApiGateway`.
Backend owns the bus implementation (queue, pub/sub, whatever fits existing infra). Frontend's only contract: the Notification entity below, and the requirement that `order.paid` always produces a backoffice notification **even if every external channel is down** (see [Phase 8](PHASE-8-IDENTITY-MESSAGING-CONTRACT.md) §5 for the messenger-side orchestration).
GET /api/admin/v2/notifications?marketplaceId=&unreadOnly=&eventType=
PATCH /api/admin/v2/notifications/{id}/read
```
Invariant: a `DeliveryAttempt` failure on an external channel **never** prevents the `Notification` row itself from being created and visible in the backoffice unread queue.
## 7. What the frontend will start doing once this ships
- Repoint `AdminOrderWatcherService` from polling `AdminOrdersLocalGateway` to the event stream / `GET /api/admin/v2/notifications?unreadOnly=true`.
- Build the backoffice **Notifications** section (unread queue, severity, marketplace/event-type filter) — currently missing from admin nav entirely.
- Wire admin order actions (assign, resend notification, replay sync, cancel/refund, comment, export) to the endpoints in §4.