Docs
Workflows Overview

Workflows Overview

What workflows are, how they differ from chat, and the full lifecycle.

Chat vs Workflow

ChatWorkflow
TriggerUser messageScheduled cron or manual run
ExecutionReal-time, streamingBackground, step-by-step via QStash
DurationBounded by one serverless invocation (~60s)Unlimited — each step is a separate invocation
StateEphemeral per messagePersisted in messages table (Chat-as-State)
Human inputEvery turnOptional approval gates at specific steps

Workflow Lifecycle

POST /api/workflows (goal + schedule)
  → LLM generates plan (WorkflowPlanSchema)
  → Save workflow (status: "draft") + steps
  → Send plan approval email (24h JWT)

User clicks "Approve" in email
  → GET /api/workflows/[id]/approve?token=
  → status: "active"
  → Create conversation + workflowRun
  → Enqueue step 0 via QStash

QStash POSTs to /api/workflows/step
  → Execute step tool via ToolLoopAgent
  → Save response (saveAgentResponse)
  → If step has requiresApproval: true
      → Send step approval email (2h JWT)
      → status: "awaiting_approval"
  → Else enqueue next step

All steps done → status: "completed" → send completion email
Any step throws → status: "failed" → send failure email

Status State Machine

Workflow statuses (on the workflows table):

StatusMeaning
draftPlan generated, awaiting user approval via email
activeApproved, can be run manually or on schedule
archivedRetired

WorkflowRun statuses (on the workflowRuns table):

StatusMeaning
runningCurrently executing steps
awaiting_approvalPaused at a step pending human review
approvedUser approved via email, re-queued
completedAll steps succeeded
failedA step threw an unrecoverable error

Database Tables

TableDescription
workflowsThe plan: goal, title, status, cron schedule
workflowStepsImmutable ordered steps with toolName, requiresApproval, approvalPrompt
workflowRunsOne row per execution: tracks currentStepIndex and status
conversationsEach run gets its own conversation — the state store

Why QStash

Vercel serverless functions time out at 60 seconds on the Hobby plan (300 seconds on Pro). A workflow with 8 steps, each making LLM calls, would easily exceed this.

QStash solves this by executing each step as an independent HTTP POST to /api/workflows/step. The platform never executes more than one step per serverless invocation — the total workflow duration is unbounded.