End-to-end: inventory guard
You will build: the live inventory pipeline from the Retail use case — a continuously updated position per SKU fed by orders and restocks, with a low-stock rule firing before the oversell.
Prerequisites
- The quickstart completed. No LLM needed — this pipeline is fully deterministic.
1 · Scaffold
pulse new inventory --source webhook \
--stage streaming:position --stage rule-based:guard --sink webhook
2 · The position stage — one running total per SKU
stages:
- name: position
engine: streaming
operators:
- type: aggregate
key: sku
aggregations:
sold: "sumWhere(type == 'order')"
received: "sumWhere(type == 'restock')"
Unlike a window, a keyed aggregate keeps a continuous running state per key — exactly what an inventory position is.
3 · The guard stage
- name: guard
engine: rule-based
rules:
- { condition: "received - sold < 10", action: flag }
4 · The sink
pulse secret set OPS_WEBHOOK https://webhook.site/<your-id>
sink:
kind: webhook
url: ${secret:OPS_WEBHOOK}
5 · Deploy and run a sale day
pulse secret set PULSE_WEBHOOK_SECRET dev-secret # the scaffolded webhook source requires it
pulse deploy .
pulse events publish --topic inventory.in --value '{"sku":"tee-black-m","type":"restock","qty":1,"amount":12}'
for i in $(seq 1 8); do
pulse events publish --topic inventory.in --value '{"sku":"tee-black-m","type":"order","qty":1,"amount":1}'
done
pulse events tail --topic inventory.guard.out
As orders eat into the position, the moment received - sold crosses the safety threshold the guard fires — and your ops webhook receives the low-stock alert while the SKU is still sellable, not in tomorrow's sync report.
6 · Prove per-SKU isolation
pulse events publish --topic inventory.in --value '{"sku":"mug-blue","type":"order","qty":1,"amount":1}'
mug-blue has its own independent position — tee-black-m's numbers don't move.
What just happened
The nightly inventory sync became a subscription: every order and restock updates one keyed state the moment it happens, and every channel that needs the truth reads the same topics (Call your app from anywhere). The "oversold because the channels disagreed for six hours" failure mode has no room to exist.
Troubleshooting
The guard never fires
Check inventory.position.out — the running sold/received totals must actually cross your threshold. Field names in the rule must match the aggregation names exactly.
Next
- Stream-stream joins — correlate orders with fulfilment scans
- End-to-end: card-fraud triage