Triggers
A trigger step declares the events that start a graph. Add one and the graph runs on its own
whenever a matching event arrives, instead of waiting for someone to run it. emit steps send
events out; trigger steps bring them in.
This step starts the graph whenever a run in the project fails:
- id: on_failure type: trigger config: { event: run.failed }When you publish the graph, its trigger steps register it as an
event handler, the same wiring spool handler set does.
Two transports: bus and webhook
Section titled “Two transports: bus and webhook”A trigger step’s transport says how events reach it:
bus(the default) subscribes to the project’s event bus. The graph runs whenever a matching event arrives: a finished run, aspool event emit, a forge delivery.webhookgives the graph its own inbound HTTP endpoint. Publishing mints the URL and a secret, and a POST to that URL starts the graph.
Bus triggers
Section titled “Bus triggers”A bus trigger names the event pattern that starts the run in event:, as in the example
above. It takes the same patterns as a handler’s --on: an exact type, a prefix wildcard such
as run.*, or * for every event (Matching has the rules).
run.* also matches a run stopping for an approval, so name run.succeeded, run.failed or
run.cancelled when you mean a run finishing. The
event catalog lists every lifecycle type.
Deliveries to a webhook you created are bus events too. A forge’s are typed
<webhook>.<forge event>[.<action>] (see Forge deliveries),
so a bus trigger on github.pull_request.* runs the graph for every pull request action on
that webhook.
Webhook triggers
Section titled “Webhook triggers”A webhook trigger owns an endpoint the graph provisions on publish:
- id: on_pr type: trigger config: transport: webhook manifest: github event: pull_request.*Use one when this graph is the whole reaction to one sender and its endpoint should live and die with the graph: a form submission, a device checking in, a vendor callback, an alert from something that only speaks webhooks.
For a repo, use spool repo connect instead:
spool repo connect https://github.com/me/app --repo-credential gh-readThat keeps one webhook per forge however many graphs or pipelines react to it, and builds the pipeline or graph the pushed commit shipped with. With a trigger step per graph, ten graphs on one repo mean ten forge webhooks to create and later delete. CI from a repo covers the repo path.
The step’s config:
manifest(optional) selects signed-delivery verification and payload shaping for a known sender.githubverifies theX-Hub-Signature-256signature, works for GitHub, Gitea and Forgejo, and lifts fields likesha,refandrepoonto the event. Leave it off for a plain shared-secret webhook.event(optional,manifestonly) picks which deliveries start the run. It is matched against the delivery’s event name plus itsaction, the way a forge delivery’s type is built:push,pull_request.opened, orpull_request.*for every pull request action. A barepull_requestnever fires, because pull request deliveries always carry an action. Omiteventto run on every delivery, including the forge’sping.
The endpoint is an ordinary webhook named graph.<graph>.<step>. A token
trigger emits that name as its type; a manifest trigger emits
graph.<graph>.<step>.<event>[.<action>], for example graph.ci.on_pr.pull_request.opened.
Renaming the trigger step re-mints the endpoint. Publishing after a rename drops
graph.<graph>.<old-id> and creates graph.<graph>.<new-id> with a new URL and secret, so the
sender’s posts go nowhere. Re-register the sender, or leave a live trigger step’s id alone.
Connecting the sender
Section titled “Connecting the sender”When you publish, the trigger step’s inspector shows the ingest URL, the secret (once; a
re-publish keeps it), and a Register on forge control that creates the webhook on a
GitHub, Gitea or Forgejo repo given a forge admin credential. spool workflow set <name> <file> --publish prints the secret too. You can also paste the URL and secret into the repo’s
webhook settings by hand; spool webhook get graph.<graph>.<step> shows the URL.
To replace the secret, rotate it under Settings > Connections > Webhooks or with
spool webhook rotate graph.<graph>.<step>. For a manifest trigger that mints a new signing
key, which you then give the forge.
What the step outputs
Section titled “What the step outputs”The trigger step’s output is the event that started the run, and the graph also receives it as
its run input, so downstream steps can read either nodes.<trigger-id>.output or input. A
run.failed bus trigger reads input.payload.error, input.payload.run_id and the rest of
the lifecycle payload.
A webhook delivery is in input.payload, as {"body": …, "query": …}. A POST of
{"sku": "TENT-2P", "qty": 12} to …/ingest?source=shop gives:
{ "body": { "sku": "TENT-2P", "qty": 12 }, "query": { "source": "shop" } }Read it as input.payload.body.sku and input.payload.query.source.
What a delivery looks like covers how body and query
values are typed, and has a worked curl for a token trigger.
A manifest trigger’s delivery has the same payload, with the forge’s JSON at
input.payload.body (input.payload.body.action, input.payload.body.pull_request.number).
The shaped fields sit at the top: input.sha, input.ref, input.head_ref,
input.base_ref, input.number, input.repo, input.sender, input.paths.
version: hyperspool-graph/v1nodes: - id: on_pr type: trigger config: { transport: webhook, manifest: github, event: pull_request.* } - id: say type: code config: script: echo "PR ${ACTION} on ${REPO} at ${SHA}" env: ACTION: { $expr: "input.payload.body.action" } REPO: { $expr: "input.repo" } SHA: { $expr: "input.sha" }edges: - { from: on_pr, to: say }A pull request opened on acme/web prints PR opened on acme/web at <head sha>.
Publishing registers a handler
Section titled “Publishing registers a handler”When you publish a graph, from the canvas or with spool workflow set <name> <file> --publish,
its trigger steps become one event handler named after the graph. Its on list combines every
trigger step’s pattern, so a graph with bus triggers on run.failed and deploy-done
registers the same thing as:
spool handler set <graph> -w <graph> --on run.failed,deploy-doneA webhook trigger provisions its endpoint at the same moment and adds the endpoint’s event to
that list, so there is nothing else to set up. spool handler list shows the result with a
graph-trigger tag.
Remove every trigger step and republish, and the handler and any webhooks the graph owns are dropped; the graph goes back to being one you run by hand or spawn. Deleting the graph drops them too.
If a handler you made yourself already has the graph’s name, publishing the graph’s trigger steps would replace it, and only a project admin can do that; anyone else’s publish is refused. Give a hand-made handler for this graph a different name and publishing leaves it alone.
If a delivery didn’t start the graph, the webhook delivery log shows the type each delivery became and whether any handler matched it.
Worked example: notify on failure
Section titled “Worked example: notify on failure”A notify graph wires one trigger step to one connector step. The trigger starts the graph on a failed run; the connector posts the error to Matrix:
version: hyperspool-graph/v1nodes: - id: on_failure type: trigger config: { event: run.failed } - id: post type: matrix.send_message config: credential: matrix with: homeserver: https://matrix.example room_id: "!abc:example" input: text: { $template: "run failed: {{ input.payload.error }}" }edges: - { from: on_failure, to: post }Publish it and a run.failed on the project bus posts to the room. That covers runs you
submit, schedules, and runs started with spool spawn, including builds from
spool repo connect. Some runs never announce their ending, such as one another handler
started; Runs that don’t announce lists
them.
See also
Section titled “See also”- Connectors: the outbound API call the example uses.
- Events and handlers: the bus, handlers and what a handler receives.
- Webhooks: inbound endpoints, forge deliveries and trust.
- Event catalog: the event types you can put in a trigger step.