Example flows

Three end-to-end flows that exercise the full contract: outbound picking, inbound receiving, and poll-mode dispatch. Each flow uses the recommended webhook transport for dispatch except where noted.

Outbound picking

The planner releases a shipper. The executor expands it into floor tasks, executes them, posts realtime feedback, and posts an authoritative shift summary at close.

Planner                                       Executor
   │                                              │
   │  1. Release shipper SH-2026-000183           │
   ├─────────────────────────────────────────────►│
   │     POST <executor>/webhooks/dispatch        │
   │     { kind: SHIPPER_RELEASED, routing: …,    │
   │       correlation_id: cid-101 }              │
   │                                              │
   │                          2. Expand to tasks; │
   │                             assign workers   │
   │                                              │
   │  3. PICK movement (SKU x12, A.12.3.1)        │
   │◄─────────────────────────────────────────────┤
   │     POST /wes/v1/realtime/movements          │
   │     { kind: PICK, qty: 12, …,                │
   │       correlation_id: cid-201 }              │
   │                                              │
   │  4. EXCEPTION short-pick (3 short)           │
   │◄─────────────────────────────────────────────┤
   │     POST /wes/v1/realtime/exceptions         │
   │     { code: SHORT_PICK, severity: HIGH }     │
   │                                              │
   │  5. SHIP movement                            │
   │◄─────────────────────────────────────────────┤
   │                                              │
   │                                14:00 — shift │
   │                                       close. │
   │                                              │
   │  6. Shift summary for window 06:00–14:00     │
   │◄─────────────────────────────────────────────┤
   │     POST /wes/v1/confirmation/shift-summary  │
   │                                              │
   │  7. Reconcile: sum(realtime) vs summary.     │
   │     If equal → RECONCILED. If not →          │
   │     DISCREPANCY queued for supervisor.       │
   │                                              │

Key invariants:

  • cid-101 (dispatch) is generated by the planner; the executor stores it for idempotency.

  • cid-201 (realtime) and the confirmation correlation_id are generated by the executor.

  • The exception’s correlation_id is independent — exceptions are separate events, not metadata on the movement.

Inbound receiving

Planner                                       Executor
   │                                              │
   │  1. Create receiver RCV-2026-005512 from PO  │
   │                                              │
   │  2. RECEIVER_EXPECTED dispatch               │
   ├─────────────────────────────────────────────►│
   │                                              │
   │                  3. Orchestrate dock arrival,│
   │                     inspection, putaway      │
   │                                              │
   │  4. RECEIVE movements (per pallet)           │
   │◄─────────────────────────────────────────────┤
   │                                              │
   │  5. PUT movements (to bin)                   │
   │◄─────────────────────────────────────────────┤
   │                                              │
   │  6. Shift summary or inventory snapshot      │
   │◄─────────────────────────────────────────────┤
   │                                              │
   │  7. Reconcile                                │
   │                                              │

For ASN-driven receiving, the dispatch payload includes the expected lines[] with SKUs and quantities; the executor compares actuals against expected and emits EXCEPTION records for any mismatch (WRONG_SKU, DAMAGE, missing quantity).

Poll-mode dispatch

Used when the executor cannot accept inbound webhooks and cannot reach a shared Kafka — typically when the executor sits behind a strict egress-only firewall.

Executor                                      Planner
   │                                              │
   │  1. GET /wes/v1/dispatch/pending?            │
   │       since=cur-100&limit=100                │
   ├─────────────────────────────────────────────►│
   │                                              │
   │  ◄── 200 OK                                  │
   │       [event-101, event-102, ..., event-145] │
   │       next_cursor: cur-145                   │
   │                                              │
   │  2. Durably store all 45 events;             │
   │     expand to tasks; assign work             │
   │                                              │
   │  3. POST /wes/v1/dispatch/ack                │
   │       { cursor: cur-145 }                    │
   ├─────────────────────────────────────────────►│
   │                                              │
   │  4. Repeat in a steady loop                  │
   │                                              │

Important properties:

  • Step 2 must complete (durably persist) before Step 3. An ack before durable storage means lost events on a crash.

  • An ack is a no-op if the cursor is earlier than the planner’s last acked cursor. Cursors are monotone.

  • The executor should run this pull loop at a cadence appropriate to dispatch volume — typical: every 5–30 seconds. Higher rates are fine but produce more idle round-trips.

Discrepancy resolution

A supervisor workflow, not a contract endpoint, but worth illustrating because it is the most common operational path after rollout:

  1. Confirmation arrives at the planner; reconciler finds the realtime sum and the confirmation summary disagree.

  2. Planner marks the window DISCREPANCY. A supervisor queue entry is created.

  3. Planner fires an URGENT notification (NovaBell on the FG.AI WMS side; vendor’s equivalent elsewhere).

  4. The supervisor opens the queue entry. The planner UI shows: confirmation totals, realtime sum, per-SKU breakdown, exceptions logged in the window.

  5. The supervisor selects one of:

    • Accept confirmation — planner overwrites the realtime contribution with the confirmation totals.

    • Accept realtime — planner discards the confirmation deltas.

    • Manual adjustment — planner creates compensating ADJUST movements and re-runs reconciliation.

  6. Window moves to RECONCILED. wes.window.reconciled.v1 is emitted with resolved_by: <supervisor_id> and resolution_reason: <text>.

The reconciler does not retry. A window stays in DISCREPANCY until a supervisor acts. Auto-resolution is not in scope for the contract.