Hooks And Automations
Configure event-driven node automations that react to Flywheel artifacts.
Flywheel Hooks are durable rules configured on nodes. A hook watches for a supported event, checks whether the event is in scope, evaluates the workflow filter, and creates an observable asynchronous run when the event matches.
Use hooks when a graph needs repeatable automation around artifacts: notify an external service, enrich a node after a submission, write a derived artifact, or tag a participant attempt for later review.
Core Terms
- An event is a durable record that something happened. Supported trigger
events are
artifact.finalized, emitted after artifact finalize writes succeed, andnode.published, emitted when an eligible submission node becomes public. - A hook is the durable rule stored on an owner node.
- The owner node determines who owns the hook and where hook secrets live.
- Scope controls which event source nodes the hook can match.
- A workflow is the
workflow_v1YAML stored inworkflow_yaml. - A secret is an encrypted, node-scoped credential referenced from workflow
YAML as
${{ secrets.NAME }}. - A run is one execution record for one hook reacting to one event.
- Run history is where you inspect queued, running, succeeded, and failed hook runs.
Hook execution is asynchronous. A hook failure does not roll back the artifact write that created the event.
Trigger And Scope
Declare one or more supported trigger events under workflow_yaml.on.
artifact.finalized runs after successful artifact finalize writes.
node.published runs after a node changes from non-public to public and has an
eligible submission artifact.
The hook scope determines which source-node events can match:
self: match events sourced from the hook owner node only.subtree: match events sourced from the hook owner node itself, plus descendant or attempt nodes under that owner.graph: match source-node events where the hook owner is the source node or appears in the source node ancestry. This is not a separate graph-level event stream.
Use self for local node automation, subtree for organizer-owned automation
over participant attempt nodes, and graph only when current API or MCP
contract guidance calls for graph-scope matching.
Workflow Filters
Set workflow_yaml.if when the hook should only run for a specific event shape.
The supported deterministic operators are all, any, not, event, any_artifact.
Predicate operators include eq, in, and exists.
This filter matches finalized public attempt artifacts with
metadata.campaign_role equal to submission, and it also matches
publication-triggered submission processing when an eligible private attempt
later becomes public:
on:
artifact.finalized: {}
node.published: {}
if:
all:
- any:
- event:
field: event_type
eq: artifact.finalized
- event:
field: event_type
eq: node.published
- any_artifact:
field: metadata.campaign_role
eq: submission
jobs:
main:
steps:
- id: notify
uses: flywheel/http_request@v1
with:
url: https://example.invalid/flywheel-hooks/artifact-finalizedUse not: { any_artifact: ... } for a negative artifact match:
if:
not:
any_artifact:
field: metadata.campaign_role
eq: submissionRun cardinality is fixed: workflow-if evaluation yields one boolean per
hook/event and enqueues at most one run per (hook_id, event_id). Hooks do not
fan out into one run per artifact.
Campaign submission artifacts follow the campaign's submission visibility
policy. Current public-policy campaigns require the attempt node to be public
before finalizing an artifact with metadata.campaign_role = "submission".
Invalid non-public submissions fail during artifact finalization, before a
submission hook run is created.
The node.published trigger can process already-finalized artifacts after a
node becomes public, but it does not make rejected campaign submissions valid.
Default rerun policy is if_inputs_changed: unchanged inputs for the same
hook and target node do not enqueue another run, while changed inputs can run
again.
Create A Hook In The Web UI
Open a node and use the Hooks panel in the node details area.
- Add a Hook Name.
- Declare one or more supported events in
workflow_yaml.on. - Choose Scope:
self,subtree, orgraph. - Paste the Workflow YAML.
- Add Secrets if the workflow calls an authenticated service.
- Use Enable immediately when the hook should start matching new events.
- Finalize an artifact or publish an eligible submission node and inspect Recent Runs.
Recent Runs show the run status, attempts, step summaries, and error messages needed to debug a workflow. A failed run means the automation failed; it does not mean the source artifact write was reverted.
Create A Hook With MCP
MCP users can manage the same hook surface with the hook tool family:
flywheel_create_hookflywheel_update_hookflywheel_set_hook_enabledflywheel_delete_hookflywheel_list_hooksflywheel_create_hook_secretflywheel_update_hook_secretflywheel_delete_hook_secretflywheel_list_hook_secretsflywheel_list_hook_runs
Use the MCP tools overview for tool-family orientation and the
generated hook API reference
for exact HTTP/MCP payload details. Your MCP host can also call
flywheel_get_contract or flywheel_get_contract_section for the current
contract.
Generic HTTP Automation
Use flywheel/http_request@v1 when a finalized artifact or a newly public
submission artifact should call an external endpoint. The default HTTP method is
POST; set method, headers, body, and timeout fields when the endpoint
needs them.
on:
artifact.finalized: {}
node.published: {}
if:
all:
- any:
- event:
field: event_type
eq: artifact.finalized
- event:
field: event_type
eq: node.published
- any_artifact:
field: metadata.campaign_role
eq: submission
jobs:
main:
steps:
- id: call_service
uses: flywheel/http_request@v1
with:
method: POST
url: https://example.invalid/flywheel-hooks/artifact-finalized
headers:
content-type: application/json
authorization: "Bearer ${{ secrets.NAME }}"
body:
event_id: "${{ event.event_id }}"
source_node_id: "${{ event.source_node_id }}"
artifact_ids: "${{ event.payload.artifact_ids }}"Campaign Submission Automation
Campaigns are challenge-style Flywheel graphs where an organizer owns the root, defines the objective and submission contract, and participants do their work in attempt nodes.
A common pattern is a root-owned subtree hook that reacts to participant
submission artifacts. In this pattern, organizer-owned hooks can react to
participant submission artifacts without requiring participants to have
root-owner permissions. The hook runs under the organizer-owned root and can add
review tags, call an external evaluator, or write organizer-owned artifacts
without changing participant ownership of the attempt node.
For public-policy campaigns, only public attempt nodes can produce valid campaign submission hook events.
This example tags an attempt node when a submitted artifact is finalized:
on:
artifact.finalized: {}
node.published: {}
if:
all:
- any:
- event:
field: event_type
eq: artifact.finalized
- event:
field: event_type
eq: node.published
- any_artifact:
field: metadata.campaign_role
eq: submission
jobs:
main:
steps:
- id: tag_attempt
uses: flywheel/add_node_tags@v1
with:
node_id: "${{ event.source_node_id }}"
tag_ids:
- tag-submission-receivedflywheel/add_node_tags@v1 is additive: it preserves existing graph tags on the
target node and adds the requested existing graph tags. The hook owner must be
the root that owns those graph tags. This v1 step does not support one-only
tags.
This example does not imply Flywheel owns scoring, leaderboard truth, or an external evaluator state machine. Hooks can automate side effects around submitted artifacts, but each campaign should define its own scoring contract and output source.
Other Workflow Steps
flywheel/http_request@v1: make one HTTP request and record the response.flywheel/http_poll@v1: poll an HTTP endpoint until the configured success condition or timeout.flywheel/json_extract@v1: extract fields from a previous step response.flywheel/load_artifact@v1: load artifact payload data for later steps.flywheel/upsert_artifact@v1: create or update an artifact selected bywith.match.flywheel/add_node_tags@v1: add existing graph tags to a target node.
For flywheel/upsert_artifact@v1, with.match is required. Use
match.metadata for subset matching when artifact IDs are not known ahead of
time. Keep selectors unique because ambiguous selectors fail terminally.
Secrets
Hook secrets are scoped to the hook owner node. Create a secret before enabling a hook that references it, rotate it with the update tool when the credential changes, and delete it only after all workflows stop referencing it.
Hook secret values are write-only after create or update. Listing secrets returns metadata such as name, version, and timestamps, not plaintext values.
Reference a secret from workflow YAML with ${{ secrets.NAME }}:
headers:
authorization: "Bearer ${{ secrets.NAME }}"Troubleshooting Recent Runs
Use run history when a hook does not behave as expected:
queued: the event matched and the run is waiting for execution.running: the workflow is executing.succeeded: all workflow steps completed.failed: at least one step failed or the workflow configuration was invalid.
Inspect attempts, step summaries, outputs, and error messages. Missing secrets, unsupported step types, ambiguous upsert selectors, and non-retryable HTTP statuses are terminal workflow problems. Network failures and retryable HTTP statuses may retry according to the current hook runtime contract.