Examples
Expense Approval
What this example shows
The foundational Loop Engine pattern. An expense submission enters a two-step approval loop. A human-only guard on the approval transition ensures automation and AI actors can never self-approve.
Loop diagram
1SUBMITTED --------[submit_expense]-------> PENDING_APPROVAL2PENDING_APPROVAL ------[approve]---------> APPROVED (terminal)3PENDING_APPROVAL -------[reject]---------> REJECTED (terminal)Actors
| Actor | Type | Transitions | Guards |
|---|---|---|---|
| submitter | human or automation | submit_expense | none |
| approver | human | approve, reject | human-only (hard) |
Key annotated snippet
1import { LoopBuilder } from "@loop-engine/sdk";2 3"cmt">// The human-only guard is the load-bearing governance rule.4const expenseApproval = LoopBuilder5 .create("expense.approval", "finance")6 .description("Expense report approval")7 .state("SUBMITTED")8 .state("PENDING_APPROVAL")9 .state("APPROVED", { isTerminal: true })10 .state("REJECTED", { isTerminal: true })11 .initialState("SUBMITTED")12 .transition({13 id: "submit_expense",14 from: "SUBMITTED",15 to: "PENDING_APPROVAL",16 actors: ["human", "automation"]17 })18 .transition({19 id: "approve",20 from: "PENDING_APPROVAL",21 to: "APPROVED",22 actors: ["human"],23 guards: [24 {25 id: "human-only" as never,26 description: "Only a human approver can approve expenses",27 severity: "hard",28 evaluatedBy: "runtime"29 }30 ]31 })32 .transition({ id: "reject", from: "PENDING_APPROVAL", to: "REJECTED", actors: ["human"] })33 .build();What emitted events look like
1{2 type: "loop.transition.executed",3 loopId: "expense.approval",4 aggregateId: "EXP-2026-001",5 transitionId: "approve",6 fromState: "PENDING_APPROVAL",7 toState: "APPROVED",8 actor: { type: "human", id: "manager@acme.com" },9 occurredAt: "2026-03-13T12:05:10.991Z"10}Try it yourself
1cd loop-examples/expense-approval2pnpm install3pnpm dev