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 orgId: 'acme',11 actor: { type: 'system', id: 'system:intake' }12})13 14await engine.transition({15 aggregateId: aggregateId('EXP-2026-001'),16 transitionId: transitionId('start_review'),17 actor: { type: 'automation', id: 'system:router' }18})19 20await engine.transition({21 aggregateId: aggregateId('EXP-2026-001'),22 transitionId: transitionId('approve'),23 actor: { type: 'human', id: 'manager@acme.com' },24 evidence: { comment: 'Approved for Q1 budget' }25})26 27const state = await engine.getState(aggregateId('EXP-2026-001'))28console.log(state?.currentState) "cmt">// APPROVED29console.log(state?.status) "cmt">// CLOSED