Skip to content
All docs

Outcome Contracts

State the result you expect, and have it verified.

Why contracts exist

Execution monitoring answers two questions: did it run, and did it error. Neither tells you whether the job got done. An Outcome Contract is where you write down the answer you actually care about, so that something can check it.

A workflow with a contract is healthy only when every rule in the contract holds. One failed rule produces OUTCOME_FAILED, with the failing rule named.

A worked example

A daily import that feeds a reporting table. The contract reads:

  1. Every weekday after 08:30 JST the workflow must run.
  2. The execution must succeed.
  3. It must output at least 1 item.
  4. It must finish in under 5 minutes.
  5. A downstream probe must confirm that the target table was updated.

At 08:45 JST — the deadline plus a grace period — OutcomeGuard evaluates all five. If the run happened, succeeded, carried 112 items, took 90 seconds and the probe saw a fresh row, the workflow is healthy. If the run happened and carried 0 items, the workflow is not healthy, no matter what n8n recorded.

Rule types

must_run
An execution must have started within the contract window.
must_succeed
That execution must have finished successfully.
items_out_gte
The final node must have emitted at least N items. This is the rule that turns “empty is suspicious” into “empty is a failure”.
runtime_lt_ms
The execution must finish within a maximum duration. Useful where late is as bad as missing.
last_node_is
The run must end at a specific node — the one that writes the result. Catches a run that stopped one branch early.
probe
An external check, run by OutcomeGuard, that confirms the target actually changed. The engine judges the probe result; it does not invent one.

Deadline, timezone and grace

A contract has a deadline expressed as a cron expression and a timezone, plus a grace period in minutes — 15 by default. OutcomeGuard waits for the grace period before judging, so a run that is two minutes late is not an incident.

Set the timezone to the one the business works in, not the one the server happens to use. A report that has to be on a desk by 09:00 JST has a deadline in JST; whether the instance runs in UTC is an implementation detail.

Downstream probes

A probe is the only part of verification that looks outside n8n. It answers one question: did the target actually change? In practice that is a freshness check — the newest row, the modified timestamp on a file, a field on a record — and the probe reports the observed freshness alongside a pass or fail.

Probes are read-only by design. A probe reads the target to confirm a change; it never writes to it.

Writing a contract that will not cry wolf

  • Use the deadline the business would actually complain about, not the schedule time. A job scheduled at 08:30 that matters by 10:00 should have a 10:00 deadline.
  • Set items_out_gte to the smallest number that is still meaningful. 1 is usually right; a number close to the average will fire on quiet days.
  • Set runtime_lt_ms generously — above the observed p95, not at the median.
  • Start with must_run and must_succeed. Add item counts once you have watched the numbers for a week, and a probe only where a wrong answer is expensive.