Getting Started
Quick Start
Prerequisites
Node.js 18+ and npm or pnpm.
Install
1npm install @loop-engine/sdkDefine a loop
1import { LoopBuilder } from '@loop-engine/sdk'2 3const approval = LoopBuilder4 .create('expense.approval', 'finance')5 .description('Expense report approval')6 .state('SUBMITTED')7 .state('UNDER_REVIEW')8 .state('APPROVED', { isTerminal: true })9 .state('REJECTED', { isTerminal: true })10 .initialState('SUBMITTED')11 .transition({12 id: 'start_review',13 from: 'SUBMITTED',14 to: 'UNDER_REVIEW',15 actors: ['automation']16 })17 .transition({18 id: 'approve',19 from: 'UNDER_REVIEW',20 to: 'APPROVED',21 actors: ['human']22 })23 .transition({24 id: 'reject',25 from: 'UNDER_REVIEW',26 to: 'REJECTED',27 actors: ['human']28 })29 .outcome({30 id: 'expense_approved',31 description: 'Expense report approved',32 valueUnit: 'expense_approved'33 })34 .build()Run it
1import { aggregateId, transitionId } from '@loop-engine/core'2import { createLoopSystem } from '@loop-engine/sdk'3 4const { engine, eventBus } = createLoopSystem({ loops: [approval] })5eventBus.subscribe(async (event) => console.log(event.type))6 7await engine.start({8 loopId: 'expense.approval',9 aggregateId: aggregateId('EXP-2026-001'),10 actor: { type: 'automation', id: 'system:intake', serviceId: 'intake' }11})12 13await engine.transition({14 aggregateId: aggregateId('EXP-2026-001'),15 transitionId: transitionId('start_review'),16 actor: { type: 'automation', id: 'system:router', serviceId: 'router' }17})18 19await engine.transition({20 aggregateId: aggregateId('EXP-2026-001'),21 transitionId: transitionId('approve'),22 actor: { type: 'human', id: 'manager@acme.com', userId: 'manager-1', displayName: 'Manager' },23 evidence: { comment: 'Approved for Q1 budget' }24})25 26const state = await engine.getState(aggregateId('EXP-2026-001'))27console.log(state?.currentState) "cmt">// APPROVED28console.log(state?.status) "cmt">// CLOSED