Skip to main content
GET /v2/merchant/orders returns the authenticated merchant’s recent OrderV2 rows, JOINed against the Schema C repayment_history_attestations collection for each row’s attestation block, plus a server-computed KPI envelope and pagination. This endpoint is the data source for the merchant Orders V2 page (Designer spec § 9) and the corresponding MCP get_merchant_orders tool (Pillar 4).
Live on dev as of 2026-06-14; PROD ETA after next GTFU. Shipped via PR #2104. Pillar 2 Trust Fabric (Schema C JOIN) × Pillar 4 MCP (server-computed KPIs) dual-purpose surface — keep the read path scoped to the merchant rather than forking a separate MCP-only endpoint.

GET /v2/merchant/orders

Authentication

Obtain a merchant JWT via POST /merchant/admin/login — see Authentication. Any ?merchantId= / ?shopId= query parameter is silently dropped (OWASP A01:2021 IDOR mitigation; mirrors MerchantInventoryAttestationsController and MerchantX402EarningsController).

Query parameters

Example

With filters:

Response — 200 OK

Fields

Errors

Schema C JOIN

Each rows[].attestation block comes from a JOIN against the Schema C repayment_history_attestations collection — the on-chain rollup of order repayment history that backs the Trust Fabric. Implementation details:
  • Single bulk query. The JOIN is a single $in query against the sparse orderId index on repayment_history_attestations. No N+1; the service collects every orderId from the Prisma fetch first, then issues one Schema C read for the page.
  • Newest-wins. If multiple Schema C rows reference the same orderId (re-mint flows), the most recently rolledUpAt row is returned. The collection retains history; the JOIN reads the latest.
  • Schema UID. Every returned block carries the Schema C UID (0xSCHEMAC placeholder above; the production value is registered on Base mainnet — see the EAS Schema v2 shipped 2026-06-11 note in the trust-fabric concept page). Clients can use this to disambiguate from Schemas A / B / D in a future mixed-schema view.
  • Missing coverage. Orders with no Schema C row land attestation.status: "NOT_APPLICABLE" (orders that don’t qualify, e.g. refunded-before-settlement) or "PENDING" (qualifying orders not yet rolled up). Rows are never dropped from rows[] because of a missing attestation — coverage is signaled via the per-row status field and aggregated into kpis.attestationCoveragePctBps.

KPI envelope

The KPI envelope is computed server-side so the FE never recomputes totals from the visible page (which would understate the window). All amounts are USD cents to keep aggregation float-safe; attestationCoveragePctBps is basis points (0..10000) for the same reason — 25.00% ships as 2500, no 0.25 floats on the wire.
  • totalOrdersInWindow — order rows matching the filter across the full window (not just the visible page).
  • settledRevenueUsdCentsΣ grossUsdCents for rows whose FE bucket is SETTLED. Refunded / disputed rows are excluded.
  • avgOrderValueUsdCentssettledRevenueUsdCents / count(SETTLED rows), integer division. 0 when no SETTLED rows in window.
  • attestationCoveragePctBpsfloor(10000 × count(attestation.status === "MINTED") / totalOrdersInWindow). Clamped to 0..10000. 0 when the window is empty.

Per-section fail-open

The endpoint follows the Stripe Reliability + Shopify Platform fail-open discipline established by PR #1975 + #2042 — partial failures degrade per-section, never propagate as 5xx: The merchant never sees a partial-Orders-page error in the dashboard.

Notes

  • 8→4 status mapping. The Prisma OrderStatus enum has 8 values (CREATED, CONFIRMED, PROCESSING, SHIPPED, DELIVERED, REFUNDED, CANCELLED, DISPUTED); the FE bucket has 4 (SETTLED / PENDING / REFUNDED / DISPUTED). Mapping is centralized in MerchantOrdersService.mapStatusBucket():
    • DELIVERED + SHIPPED + PROCESSING + CONFIRMEDSETTLED
    • REFUNDED + CANCELLEDREFUNDED
    • DISPUTEDDISPUTED
    • CREATED and unmapped → PENDING (excluded from SETTLED revenue; included in totalOrdersInWindow).
  • Caching. The response carries Cache-Control: private, max-age=60. Clients may see up to a 60s lag for fresh order events through CDN caches.
  • PSP filter is in-memory. Prisma’s Mongo provider does not efficiently filter on a composite-type payment.provider field, so the PSP filter is applied in-memory after the Prisma fetch. Pagination math is over the filtered set.
  • Auth scope. shopId from req.user.shopId only. Any ?merchantId= / ?shopId= query parameter is silently dropped. A merchant cannot read another merchant’s orders through this endpoint.
  • Currency. All monetary fields are USD cents, integer — same contract as x402 Earnings (Merchant). Divide by 100 for major-unit display.
  • MCP twin. The same data feeds the get_merchant_orders MCP tool (Pillar 4). The wire shape is identical so a single OpenAPI schema generates both clients.