Skip to main content

Execution

Running a workflow produces a WorkflowExecution — a record of what happened: status, input, output, and a log entry per node. This page covers how to start, inspect, and resume executions.

Starting an execution

All execution goes through Qanna\WorkflowEngine\Engine\Contracts\ExecutionManagerContract, most conveniently via the Workflow facade (see Workflow Facade for the full method list):

use Qanna\WorkflowEngine\Facades\Workflow;

// Fires the trigger and, if it decides to proceed, runs the workflow immediately.
$execution = Workflow::run('send-welcome-email', ['email' => 'ada@example.com']);

// Queues the workflow for background execution instead (dispatched onto your default queue).
Workflow::dispatch('send-welcome-email', ['email' => 'ada@example.com']);

Both accept the same arguments: the workflow id, a payload array (available to the trigger and, from there, every node as {{trigger.*}} — see Expressions), an optional $triggeredBy label (defaults to 'manual'; built-in triggers pass 'webhook', 'schedule', or 'model-event'), and an optional $executionId if you want to control the id yourself.

run() returns the WorkflowExecution (or null if the trigger declined to run — see Triggers). dispatch() returns void, since the workflow hasn't run yet by the time it returns.

The WorkflowExecution model

final class WorkflowExecution
{
public readonly string $id;
public readonly string $workflowId;
public readonly int $workflowVersion;
public readonly ExecutionStatus $status;
public readonly array $input;
public readonly array $output;
public readonly array|Collection $logs; // ExecutionLogEntry[]
public readonly ?string $errorMessage;
public readonly ?Carbon $startedAt;
public readonly ?Carbon $finishedAt;
public readonly array $meta;
}

By default, $output is whichever node ran last — not necessarily the node that produced the value you actually care about, once side-effecting steps (send a notification, log something) run afterward. Add a Set output node at the end of a flow to shape $output explicitly instead.

Useful methods:

$execution->durationMs(); // ?int — null until finishedAt is set
$execution->isTerminal(); // true for Succeeded, Failed, or Cancelled

Status

enum ExecutionStatus: string
{
case Pending = 'pending';
case Running = 'running';
case Succeeded = 'succeeded';
case Suspended = 'suspended';
case Failed = 'failed';
case Cancelled = 'cancelled';
}

Suspended means the execution is paused, not finished — a node (e.g. Wait or Call Workflow) asked to pause until something happens. Cancelled is what a Stop node produces on an intentional (non-error) stop.

Resuming a suspended execution

Workflow::resume($execution->id);

This is how the engine itself resumes a Wait node once its delay elapses, or a Call Workflow node once its child workflow finishes — both are wired up automatically. You'd call resume() yourself if you're building your own suspend point (see Custom Nodes § Suspending and resuming) and need to trigger the resume from outside the engine's own scheduling.

Execution history

Qanna\WorkflowEngine\Storage\Contracts\ExecutionRepositoryContract (bound to whichever storage driver is configured) lets you query past executions:

use Qanna\WorkflowEngine\Storage\Contracts\ExecutionRepositoryContract;

$repository = app(ExecutionRepositoryContract::class);

$repository->find($executionId); // ?WorkflowExecution
$repository->listForWorkflow('send-welcome-email'); // WorkflowExecution[], newest first
$repository->countForWorkflow('send-welcome-email'); // int
$repository->listByStatus(ExecutionStatus::Failed); // WorkflowExecution[]
$repository->deleteForWorkflow('send-welcome-email'); // int deleted
$repository->purgeOlderThan(now()->subDays(90)); // int deleted

Logs

Every node that actually runs produces an ExecutionLogEntry:

final class ExecutionLogEntry
{
public readonly string $executionId;
public readonly string $nodeId;
public readonly string $type; // the node's type string
public readonly LogLevel $level; // Debug | Info | Warning | Error
public readonly string $message;
public readonly array|Fluent $context; // includes 'status' (e.g. 'success', 'failed', 'waiting')
public readonly ?Carbon $timestamp;
public readonly ?int $durationMs;
}

$execution->logs holds them in order. $entry->ran() distinguishes a completed entry from a transient 'running' marker.

Next

  • Expressions — referencing trigger and node data from config.
  • Testing — asserting on executions in your test suite without hitting real side effects.