Eloquent

Documentation

Workflow Engine

The Workflow Engine enables multi-step process automation with support for human-in-the-loop approvals, AI-powered decisions, and complex branching logic.

Overview

Workflows are visual process definitions that execute automatically:

  • Multi-step processes: Chain multiple operations together
  • Human approvals: Pause for human review and approval
  • AI integration: Use AI agents for intelligent decisions
  • Conditional branching: Route based on data conditions

Core Concepts

Workflow Definition

A reusable workflow template:

interface WorkflowDefinition {
  id: string;
  name: string;
  description?: string;
  trigger: WorkflowTrigger;
  nodes: WorkflowNode[];
  edges: WorkflowEdge[];
  variables: WorkflowVariable[];
}

Workflow Instance

A running execution of a workflow:

interface WorkflowInstance {
  id: string;
  definitionId: string;
  status: "pending" | "running" | "paused" | "completed" | "failed";
  currentNodeId?: string;
  state: WorkflowState;
  createdAt: string;
  updatedAt: string;
}

Node Types

TypeDescription
triggerStarting point (manual, schedule, event)
actionExecute an operation (API call, entity create)
conditionBranch based on expression
approvalPause for human approval
aiCall AI agent for decision
delayWait for specified duration
endWorkflow completion

Creating Workflows

Workflow Definition

const invoiceWorkflow: WorkflowDefinition = {
  name: "Invoice Approval",
  description: "Automated invoice processing with approval",
  trigger: {
    type: "entity_created",
    entityName: "invoice",
  },
  nodes: [
    {
      id: "start",
      type: "trigger",
      position: { x: 0, y: 0 },
    },
    {
      id: "check-amount",
      type: "condition",
      config: {
        expression: "{{ invoice.amount > 10000 }}",
      },
      position: { x: 200, y: 0 },
    },
    {
      id: "auto-approve",
      type: "action",
      config: {
        action: "update_entity",
        params: {
          entityName: "invoice",
          id: "{{ invoice.id }}",
          fields: { status: "approved" },
        },
      },
      position: { x: 400, y: -100 },
    },
    {
      id: "request-approval",
      type: "approval",
      config: {
        approvers: ["finance-team"],
        message: "Invoice {{ invoice.id }} requires approval: ${{ invoice.amount }}",
      },
      position: { x: 400, y: 100 },
    },
    {
      id: "end",
      type: "end",
      position: { x: 600, y: 0 },
    },
  ],
  edges: [
    { source: "start", target: "check-amount" },
    { source: "check-amount", target: "auto-approve", condition: "false" },
    { source: "check-amount", target: "request-approval", condition: "true" },
    { source: "auto-approve", target: "end" },
    { source: "request-approval", target: "end" },
  ],
};

Executing Workflows

Start Workflow Instance

const instance = await createWorkflowInstance({
  definitionId: "invoice-workflow-123",
  variables: {
    invoice: {
      id: "inv-456",
      amount: 15000,
      vendor: "Acme Corp",
    },
  },
});

Poll for Status

import { useWorkflowInstance } from "@/hooks/use-workflow-instance";

function WorkflowStatus({ instanceId }: { instanceId: string }) {
  const { instance, loading, refetch } = useWorkflowInstance(instanceId);

  useEffect(() => {
    const interval = setInterval(refetch, 5000);
    return () => clearInterval(interval);
  }, [refetch]);

  return (
    <div>
      <p>Status: {instance?.status}</p>
      <p>Current Step: {instance?.currentNodeId}</p>
      {instance?.status === "paused" && (
        <ApprovalButtons instanceId={instanceId} />
      )}
    </div>
  );
}

Human-in-the-Loop Approvals

Approval Node

{
  id: "manager-approval",
  type: "approval",
  config: {
    approvers: ["manager-role"],
    timeout: "24h",
    message: "Please review and approve: {{ request.summary }}",
    actions: [
      { id: "approve", label: "Approve", color: "green" },
      { id: "reject", label: "Reject", color: "red" },
      { id: "request-info", label: "Request More Info", color: "yellow" },
    ],
  },
}

Processing Approvals

// User approves
await submitApproval({
  instanceId: "wf-inst-123",
  nodeId: "manager-approval",
  action: "approve",
  comment: "Looks good, approved.",
});

// Workflow continues automatically

AI Agent Nodes

AI Decision Node

{
  id: "ai-categorize",
  type: "ai",
  config: {
    agentId: "support-agent-123",
    prompt: "Categorize this support ticket: {{ ticket.description }}",
    outputVariable: "category",
  },
}

API Endpoints

MethodPathDescription
GET/api/v1/workflowsList definitions
POST/api/v1/workflowsCreate definition
POST/api/v1/workflows/{id}/instancesStart instance
GET/api/v1/workflow-instances/{id}Get instance status
POST/api/v1/workflow-instances/{id}/approveSubmit approval

Best Practices

  1. Use meaningful node names - Clear step descriptions
  2. Handle timeouts - Set appropriate timeouts for approvals
  3. Log state changes - Track workflow progression
  4. Test with small batches - Validate before bulk execution
  5. Use variables - Pass data between nodes via variables

Next Steps