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

# Sync Server gRPC Dispatch

> How StateSet API dispatches orders to the Sync Server over gRPC.

# StateSet API → StateSet Sync Server (gRPC) Order Dispatch Flow

This document describes a practical architecture for sending orders from the StateSet Rust API (`stateset-api`) to the StateSet Sync Server (`stateset-sync-server`) using the Sync Server’s existing gRPC API.

## Goal

When an order is created (or updated) in `stateset-api`, reliably dispatch it to `stateset-sync-server` so the Sync Server can orchestrate downstream integrations (NetSuite, DCL, Cart.com, etc.) via its gRPC surface.

## Key Interfaces (as implemented today)

* **Sync Server proto**: `proto/order_integration.proto` (`stateset.sync.v1.OrderIntegrationService`)
  * Primary RPC: `SubmitOrder(SubmitOrderRequest) returns (SubmitOrderResponse)`
* **Sync Server gRPC server**: `src/grpc/mod.rs` (implements `submit_order`)
* **Sync Server listen port**: `src/server.rs` (`server.grpc_port`, default `50051`)
* **Sync Server auth**: `x-stateset-api-key` metadata header (`src/http/auth.rs`)
* **StateSet API durable trigger**: outbox enqueue on order create (`stateset-api/src/services/orders.rs`)
  * Outbox worker: `stateset-api/src/events/outbox.rs`
  * Event loop hook point: `stateset-api/src/events/mod.rs` (`handle_order_created`)

## High-Level Flow

1. A client creates an order in `stateset-api` (HTTP or gRPC).
2. `stateset-api` commits the order + items to its DB and enqueues an `OrderCreated` outbox row in the same transaction.
3. An outbox worker polls for pending events and triggers a **Sync dispatch** step.
4. The dispatcher loads the order + items, builds a `SubmitOrderRequest`, and calls `stateset.sync.v1.OrderIntegrationService/SubmitOrder` on the Sync Server.
5. The Sync Server validates tenant + API key, then invokes its per-tenant orchestrators to call downstream integrations.
6. On success, the outbox row is marked delivered; on retryable failure, it is rescheduled with backoff.

## Sequence Diagram

```mermaid theme={null}
sequenceDiagram
  autonumber
  participant Client
  participant API as stateset-api
  participant DB as Orders DB
  participant Outbox as outbox_events
  participant Worker as Outbox worker
  participant Dispatch as Sync gRPC dispatcher
  participant Sync as stateset-sync-server (gRPC)
  participant Orch as Tenant orchestrators
  participant Vendors as NetSuite/DCL/Cart.com
  participant StateSet as StateSet integration

  Client->>API: Create order (HTTP/gRPC)
  API->>DB: INSERT orders + order_items (txn)
  API->>Outbox: INSERT OrderCreated event (txn)
  DB-->>API: COMMIT

  Worker->>Outbox: Claim pending events (SKIP LOCKED)
  Worker->>Dispatch: Process OrderCreated(order_id)
  Dispatch->>DB: Load order + items
  Dispatch->>Sync: SubmitOrder(tenant_id, stateset_order?, netsuite?/dcl?/cart?) + x-stateset-api-key

  Sync->>Sync: Validate tenant + API key
  Sync->>Orch: create_stateset_order (optional)
  Sync->>Orch: create_netsuite_sales_order (optional)
  Sync->>Orch: create_dcl_batch (optional)
  Sync->>Orch: create_cart_order (optional)
  Orch->>StateSet: Create/update StateSet order (optional)
  Orch->>Vendors: Push downstream payloads (optional)

  Sync-->>Dispatch: SubmitOrderResponse(success, errors)
  Dispatch-->>Outbox: Mark delivered / schedule retry
```

## Request Construction (what `stateset-api` sends)

### Minimal viable request (using existing proto)

`SubmitOrderRequest` requires `tenant_id` and accepts optional payloads:

* `stateset_order` (`CreateOrderPayload`) for “StateSet order create” inside the Sync Server
* `netsuite`, `dcl`, `cart` (all `google.protobuf.Struct`) for direct downstream submission

Sync Server proto: `proto/order_integration.proto`.

### Mapping from `stateset-api` order model to `CreateOrderPayload`

`stateset-api` canonical entities:

* Order: `stateset-api/src/entities/order.rs`
* Order items: `stateset-api/src/entities/order_item.rs`

Sync Server expects (subset shown):

* `shopify_order_id` (string)
* `order_number` (string)
* `created_at` (`google.protobuf.Timestamp`)
* `line_items[]` with `title`, `quantity`, `price`, `sku`, etc.

Pragmatic mapping:

* `CreateOrderPayload.order_number` ← `orders.order_number`
* `CreateOrderPayload.created_at` ← `orders.created_at` (or `order_date`, choose one consistently)
* `OrderLineItem.title` ← `order_items.name`
* `OrderLineItem.quantity` ← `order_items.quantity` (convert `i32` → `f64`)
* `OrderLineItem.price` ← `order_items.unit_price` (convert `Decimal` → `f64`, mind rounding)
* `OrderLineItem.sku` ← `order_items.sku`
* `CreateOrderPayload.shopify_order_id`:
  * If you have a true external/channel order id, use it.
  * If not, use the `stateset-api` order UUID string as a stable idempotency key.

Important: the Sync Server’s existing idempotency logic for `stateset_order` keys off `shopify_order_id` (`src/orchestrators/order_sync.rs`), so this field must be stable for retries.

## Auth & Tenancy

* Set gRPC metadata header `x-stateset-api-key` (same key used for the Sync Server’s HTTP API).
* Provide `tenant_id` in every request; Sync Server resolves tenant configuration and validates the key before executing orchestrators (`src/grpc/mod.rs`).

## Delivery Semantics (reliability)

### Recommended: outbox-driven gRPC dispatch

To get “at-least-once” delivery to the Sync Server, perform the gRPC call as part of the outbox processing step and only mark the outbox row `delivered` after the RPC is successful (or after a “non-retryable” error policy decision).

This matches the existing outbox pattern in `stateset-api` (`stateset-api/src/events/outbox.rs`) but changes the “delivery target” from “in-process channel send” to “cross-service gRPC”.

### Retry policy

* Retry on transport/transient errors (`UNAVAILABLE`, `DEADLINE_EXCEEDED`, timeouts).
* Do not retry on permanent validation errors without operator action (`INVALID_ARGUMENT`, `UNAUTHENTICATED`, `PERMISSION_DENIED`).
* Treat application-level partial failures carefully:
  * `SubmitOrder` can return `OK` but `success=false` with `errors[]` per integration target (`src/grpc/mod.rs`).
  * Decide which targets are “required” vs “best-effort” and only consider the outbox event delivered when your required targets succeeded.

## Observability

Suggested minimum telemetry on the `stateset-api` side for each dispatch attempt:

* `order_id`, `tenant_id`, target `grpc_addr`
* attempt count + backoff delay
* gRPC status code on failure
* `SubmitOrderResponse.success` and `errors[].target/message` on application-level failures

Sync Server already logs a “gRPC server listening” line and uses tracing around orchestrator operations; correlate logs via request ids where possible.

## Implementation Notes (code placement)

In `stateset-api`, the simplest integration point is the existing event processing hook:

* Add a Sync dispatcher call in `stateset-api/src/events/mod.rs` inside `handle_order_created`.
* For true durability, move the gRPC call into the outbox worker path (`stateset-api/src/events/outbox.rs`) so outbox “delivered” reflects remote delivery rather than local enqueue.

## Open Questions / Future Improvements

* The current Sync Server gRPC proto is integration-oriented and uses `google.protobuf.Struct` for downstream payloads. If you want Sync Server to derive NetSuite/DCL/Cart payloads from the canonical `stateset-api` order model, add a new RPC/message that accepts a first-class “StateSet order” envelope rather than expecting pre-mapped JSON structs.
* TLS: the Sync Server gRPC listener is plaintext by default; deploy behind a TLS-terminating proxy or add tonic TLS configuration if needed.
