End-to-end: card-fraud triage
You will build: the complete fraud pipeline from the Banking use case: per-card velocity in a sliding window, deterministic triage in microseconds, an LLM ruling only on the gray zone, and a webhook to your case system. This is the marketing page's promise, made runnable.
Prerequisites
- The quickstart completed (local Pulse running).
- An LLM provider for step 4 —
ANTHROPIC_API_KEYor local Ollama. Without one, stop after step 3: the deterministic half of the pipeline is already a working detector. - A webhook URL to receive cases (a webhook.site URL works for the exercise).
1 · Scaffold the four stages
pulse new fraud --source webhook \
--stage streaming:velocity --stage rule-based:triage --stage llm:judge \
--sink webhook
2 · The velocity stage — per-card features in a sliding window
stages:
- name: velocity
engine: streaming
operators:
- { type: keyBy, field: card_id }
- type: window
spec: "sliding(5m,1m)"
aggregations:
tx_count: "count()"
spend: "sum(amount)"
hi_amount: "countWhere(amount > 500)"
Every card gets its own window; every aggregation is validated at deploy.
3 · The triage stage — the obvious cases never cost a token
- name: triage
engine: rule-based
rules:
- { condition: "tx_count > 20 || spend > 10000", action: flag }
Only windows that break the velocity thresholds pass through — the LLM will never see normal traffic.
4 · The judge stage — reasoning over the residue
- name: judge
engine: llm
systemPrompt: |
You receive per-card velocity features that crossed
fraud thresholds. Assess likelihood (low|medium|high),
justify in one sentence, and recommend: allow, review, or block.
temperature: 0.2
5 · The sink — cases out
pulse secret set CASE_WEBHOOK https://webhook.site/<your-id>
sink:
kind: webhook
url: ${secret:CASE_WEBHOOK}
6 · Deploy and simulate a card gone hot
pulse secret set PULSE_WEBHOOK_SECRET dev-secret # the scaffolded webhook source requires it
pulse deploy .
for i in $(seq 1 25); do
pulse events publish --topic fraud.in --value '{"card_id":"c-77","amount":'"$((RANDOM % 900 + 100))"'}'
done
7 · Watch each altitude of the decision
pulse events tail --topic fraud.velocity.out # features accumulating per card
pulse events tail --topic fraud.triage.out # the moment thresholds break
pulse events tail --topic fraud.judge.out # the model's ruling on the flagged window
And check your webhook receiver: the case arrived with the velocity features and the LLM's recommendation attached.
8 · Prove the isolation
pulse events publish --topic fraud.in --value '{"card_id":"c-01","amount":25}'
c-01 builds its own quiet window: never flagged, never judged, never a token spent. That asymmetry — µs rules for the many, model judgment for the few — is the economics of the whole design.
What just happened
You composed all four decision altitudes into one declarative file and watched an event stream sort itself: raw auths → per-card features → deterministic flags → reasoned rulings → an acted-upon case. Every intermediate step is a durable topic you tailed — which is also your audit story: the pipeline's reasoning is inspectable at every seam.
Troubleshooting
Nothing reaches triage
The window has to actually break a threshold: 25 events in under 5 minutes on one card_id does it. Check fraud.velocity.out first — if features flow but no flag, your thresholds are higher than the simulated burst.
judge is unhealthy
No LLM provider configured. Export ANTHROPIC_API_KEY (or point at Ollama) and restart — or stop at step 3 and ship the deterministic detector; it's already useful.
Next
- Keyed timers — detect the silence — add the "card that went quiet" signal
- Crash-resume — prove the pipeline survives a kill mid-burst