Skip to content

Defining flows

A flow is the template; an approval is one run of that template against a record. This page is the field reference for flows and steps.

Flows

ApprovalFlow fields:

Field Type Notes
name string Human label, shown in the panel.
model_type string Fully-qualified model class the flow applies to, e.g. App\Models\PurchaseOrder.
is_active bool Only an active flow is picked up by submitForApproval(). Default true.

One active flow per model type

submitForApproval() with no explicit flow calls ApprovalFlow::activeFor($model::class), which returns the first active flow for that model type:

public static function activeFor(string $modelType): ?ApprovalFlow
{
    return static::query()
        ->where('model_type', $modelType)
        ->where('is_active', true)
        ->first();
}

Keep exactly one active flow per model type, or pass the flow explicitly:

$po->submitForApproval(flow: $specificFlow);

If no active flow is found, submitForApproval() throws ApprovalException::noActiveFlow(). If the flow has no steps, it throws ApprovalException::flowHasNoSteps().

Steps

ApprovalStep fields:

Field Type Notes
sort_order int Steps run ascending. Start at 0.
name string Human label for the step.
mode any | all How the step passes. See below. Default any.
approver_type role | users How approvers are resolved. Default role.
approver_role string|null Role name when approver_type is role.
approver_ids array|null User IDs when approver_type is users.
allow_delegation bool Whether approvers may delegate this step. Default true.
escalate_after_hours int|null Per-step escalation window; falls back to the config default.

Step modes

  • any — the step passes as soon as one assigned approver approves. Use for "any manager can sign".
  • all — the step passes only when every resolved approver has approved (parallel sign-off). Use for "both finance officers must agree".
$flow->steps()->create([
    'sort_order' => 0,
    'name' => 'Any manager',
    'approver_type' => 'role',
    'approver_role' => 'manager',
    'mode' => 'any',
]);

$flow->steps()->create([
    'sort_order' => 1,
    'name' => 'Both signatories',
    'approver_type' => 'users',
    'approver_ids' => [$cfo->id, $ceo->id],
    'mode' => 'all',
]);

With mode all and a role approver, "everyone" means every user currently holding that role. Adding a user to the role mid-run adds them to the required set. For a fixed set of signatories, use approver_type = users.

Ordering and advancing

When a step passes, the plugin looks up the next step by sort_order (ApprovalStep::nextStep()). If there is none, the run is marked approved and ApprovalCompleted fires. Rejection at any step ends the run immediately with status rejected.

Editing flows at runtime

Because flows and steps are ordinary rows, you can manage them from the bundled Approval Flows resource (or your own UI) with no deploy. Changes affect new runs; approvals already in progress keep the step rows they were created against.

Next: Approver resolution.