1. HTTP API REAL
POST /api/workflow/execute ?preview=true
Direct invocation. preview=true sets dry_run=true in opts — runs through all stages but skips writes inside executeFanOut (each step traces with status:'dry_run'). Used by ops console + manual triggers. src/index.ts:14304.
2. Event-driven REAL
drainEvents() → subscription match → executeWorkflowContract
Cron-fired drainer reads events table since last cursor, matches against event_subscriptions, applies input_mapper JSONPath translation, then invokes the runner with invoked_by: "event:<type>:<id>". KV concurrency lock prevents double-fire. See substrate-event-ledger.
3. Chat-tool invocation STUB
execute_workflow chat tool (admin-gated)
Tool registered in palette; calls into the same executeWorkflowContract entry. Most chat surfaces stage a workflow_* proposed_action first and run on approval (HITL invariant per ADR-031).
4. Cron-driven REAL
scheduled handler → verify scheduler + cron-triggered drains
Scheduled workflows (e.g. monthly_margin_review) fire from the cron handler. Also: the verify-check scheduler at 45 5 * * * reads pending workflow_verify_results rows and runs the verify SQL. src/index.ts:33255.