Workflows

Trigger workflows, render executionPath, and resume NEEDS_INPUT.

Use the SDK to start workflow executions, wait for results, and render progress in a custom UI. These examples assume an authenticated lunnoa client from the overview.

Trigger and wait

Start an execution, then wait until it finishes (or pauses for input). Prefer executeAndWait when you do not need the intermediate ID.

workflow-execute.ts
const { id } = await lunnoa.workflows.execute(workflowId, {  customerEmail: 'olivia@example.com',});const execution = await lunnoa.executions.waitUntilFinished(id, {  expansion: ['executionPath'],});// Or in one call:// const done = await lunnoa.workflows.executeAndWait(workflowId, inputs);

Manual-trigger input fields come from workflows.get(id, { expansion: ['triggerNode'] })triggerNode.value.customInputConfig. See Discovery.

Render progress from executionPath

Render progress from the executionPath expansion, not from raw nodes / edges. It is the public, business-readable step list:

execution-path.ts
const execution = await lunnoa.executions.get(id, {  expansion: ['status', 'executionPath'],});for (const step of execution.executionPath ?? []) {  // step: nodeId, label, appId, actionId?, status, statusMessage?,  //       startTime?, endTime?, iterationOf?, iterationIndex?, iterationCount?  console.log(`[${step.status}] ${step.label}`);}

Step statuses include RUNNING, SUCCESS, FAILED, NEEDS_INPUT, SCHEDULED, RETRYING, and WAITING. Nest steps that carry iterationOf under their loop parent. Resolve appId / actionId to icons via workflowApps.list().

For a live view, poll executions.get with executionPath every 1–2 seconds while the status is non-terminal. Only the single-execution read honours the executionPath expansion.

Resume a NEEDS_INPUT execution

Workflows can pause for human input. The waiting step appears in executionPath with status: 'NEEDS_INPUT'. Render your own form, then submit values keyed by field ID.

needs-input.ts
const execution = await lunnoa.executions.get(executionId, {  expansion: ['status', 'executionPath'],});const waiting = execution.executionPath?.find(  (s) => s.status === 'NEEDS_INPUT',);await lunnoa.executions.submitInput(executionId, waiting!.nodeId, {  approved: true,  comment: 'Release the payment.',});const finished = await lunnoa.executions.waitUntilFinished(executionId);

waitUntilFinished stops on NEEDS_INPUT by default so you can show a form. Pass stopOnNeedsInput: false to wait through it. submitInput is acknowledged immediately; the run resumes asynchronously.

For Request Approval nodes (eligible approvers, quorum, approve/reject), use lunnoa.approvals or executions.submitApproval instead of submitInput. See Approvals.

Next

  • Approvals: pending Request Approval inbox and decide
  • Queues: human-in-the-loop work items outside a single execution
  • Errors: handle failed starts and forbidden roles
  • TypeScript client SDK: overview and auth patterns