Skip to main content
Hooks give you a way to run arbitrary workflow logic in response to lifecycle events without polluting the main execution graph with observability concerns. A hook is simply a reference to a named template that the engine fires when a specific phase transition occurs — when the workflow starts, when a task succeeds, when a task is cancelled, or when anything terminates. Because hooks are declared on the workflow resource itself, they are version-controlled alongside the rest of the workflow definition and require no changes to executor code or host applications.

The Hook System

Hooks are implemented through the hook.Notifier interface. When an event fires, the engine calls Notifier.Notify() with the event type, the workflow run ID, and the task run ID (if applicable). The notifier then executes the referenced template — typically by submitting a new single-task sub-workflow or dispatching the template through the broker. Hook execution is fire-and-forget: if a hook template fails, that failure is reported to the errsink.ErrorSink but does not affect the phase of the triggering task or the overall workflow state. This ensures hooks never block the primary execution path. To enable hooks, inject a hook.Notifier implementation when creating the engine:

Where Hooks Are Declared

Hooks can be attached at two levels.

Workflow-level hooks

Declared on spec.hooks. Fire once for the entire workflow run based on the workflow’s terminal phase.

Task-level hooks

Declared on individual DAG task nodes via the hooks field. Fire when that specific task transitions phases.

Workflow-Level Hooks

Workflow-level hooks are declared on spec.hooks and fire based on the overall workflow outcome:

Workflow Hook Events

onExit is an always-run hook — use it for cleanup, notification, or audit logging that must happen regardless of outcome.

Task-Level Hooks

Task-level hooks are declared on DAG task nodes (call sites), not on the template definition. This means the same template can have different hooks depending on where it is called from:

Task Hook Events

onSuspend and onResume are meaningful only for tasks that use the suspend/resume pattern. They fire on every suspend/resume cycle, not just the first.

Suspend/Resume Hook Lifecycle

The full hook sequence for a task that suspends once then completes:
1

Task dispatched to executor

onStart fires.
2

Executor returns ExecCodeSuspended

Task moves to PhaseSuspended. onSuspend fires.
3

Engine.Resume() called with approval payload

Task is re-dispatched with merged inputs. onResume fires.
4

Executor returns ExecCodeSucceeded

Task moves to PhaseSucceeded. onSuccess fires.
5

Exit hook runs

onExit fires (always runs on terminal phases).

Cancellation Hooks

Cancel hooks fire when a workflow is stopped via Engine.Cancel(). Workflow-level onCancel fires once for the workflow; task-level onCancel fires for each task that was non-terminal at the time of cancellation:
And on the specific task node that may be cancelled:

Hook Reference Format

Each hook is an object with a single template field naming the template to execute:
The referenced template must exist in the workflow’s spec.templates list. The engine does not pass the triggering task’s inputs or outputs to the hook template automatically — design your hook templates to be self-contained or read state from an external store if needed.
Hook templates run as standalone task invocations. They execute after the phase transition is committed to the store, so the triggering task’s final phase and outputs are already readable when the hook runs.

Fire-and-Forget Semantics

Hook execution is intentionally decoupled from the primary workflow phase machine:
  • A hook template that fails is reported to the errsink.ErrorSink but does not change the triggering task’s phase.
  • Hook failures do not trigger retries or cascade to the workflow’s terminal phase.
  • If no hook.Notifier is configured (the WithHookNotifier option is omitted), hook declarations in the workflow document are silently ignored — the workflow runs normally without notifications.
This design ensures that observability hooks never become a single point of failure for production workflows.
Because hooks are fire-and-forget, avoid using them for operations that must succeed for the workflow to be correct. Use regular DAG task dependencies for that. Hooks are best suited for notifications, audit logging, and metrics emission.