Skip to content

Triggers and webhook conditions

Every GitHub event and action a workflow can start on, every condition you can narrow it with, how storms and forks are handled, and why a trigger did not fire.

Updated 28 Oct 2026Checked against vdb-site@284f1d6, website@a31d470

A webhook trigger starts a run when a GitHub delivery matches its event and its conditions. The GitHub App already sends a delivery for every event on every installed repository, so you never add a repository webhook.

Every condition is checked against the delivery as GitHub sent it. No condition calls GitHub or spends quota. A condition the delivery has no value for fails closed: the trigger does not fire on a guess.

Events and actions

event is a base event, which matches every action unless conditions.actions narrows it, or an exact event.action pair such as pull_request.opened. Using both an exact pair and conditions.actions is refused with action_conflict.

Event Actions Run subject
pull_request opened, synchronize, reopened, closed, labeled, unlabeled, ready_for_review, converted_to_draft, edited, review_requested pr:N
pull_request_review submitted, edited, dismissed pr:N
pull_request_review_comment created, edited, deleted pr:N
check_run completed, created, rerequested pr:N when on a pull request
check_suite completed, requested, rerequested pr:N when on a pull request
workflow_run completed, requested, in_progress pr:N when on a pull request
merge_group checks_requested, destroyed none
push none none
release published, created, released, prereleased, edited release:TAG
issues opened, labeled, reopened, closed, unlabeled, edited, assigned, milestoned issue:N
issue_comment created, edited, deleted issue:N, also for a comment on a pull request
installation_repositories added, removed none
repository created, archived, unarchived, publicized, privatized, renamed, transferred, edited none
dependabot_alert created, dismissed, fixed, reintroduced, reopened, auto_dismissed, auto_reopened dependabot:N
code_scanning_alert created, appeared_in_branch, fixed, reopened, closed_by_user, reopened_by_user code-scanning:N
secret_scanning_alert created, resolved, reopened, validated, publicly_leaked secret:N
secret_scanning_alert_location created secret:N
registry_package published, updated package:name@version
deployment_status created deploy:<id>

Some events need App permissions or subscriptions your installation may not have yet. The Organisations panel on the Repos page shows each event’s state. See Managed scope.

Conditions

Every condition you set must match. Names may be written snake_case (base_branch); they are stored camelCase.

Condition Values Matches
actions from the table above the delivery’s action
baseBranch globs, or @default a pull request’s base branch; a run’s pull request base, else its head branch; a push’s branch
headBranch globs, or @default a pull request’s head branch; a run’s head branch; a push’s branch. A tag push never matches.
paths, pathsIgnore globs, push only the files added, changed or removed by the push’s commits
labels {any: [...], all: [...]} the pull request’s or issue’s labels, without case
authorType any, user, bot whether the author is a bot (type: Bot, or a login ending [bot]). Default any.
authors {allow: [...], deny: [...]} the author, pusher, commenter, reviewer or run actor, as globs without case. deny also checks the sender and wins over allow.
draft include, exclude, only a pull request’s draft state. Default include.
origin any, same_repo, fork whether the change comes from a fork. Default any.
subject any, pr, issue, none what the run is about. Default any.
conclusion success, failure, cancelled, timed_out, neutral, skipped, action_required, stale, startup_failure the conclusion of a workflow_run, check_run or check_suite
conclusion on deployment_status success, failure, error, inactive, in_progress, queued, pending the deployment status’s state
reviewState approved, changes_requested, dismissed, commented a pull_request_review’s state
severity critical, high, medium, low, error, warning, note a Dependabot alert’s advisory severity, or a code scanning rule’s security severity
environment globs, without case a deployment_status’s environment
packageType container, docker, npm, maven, rubygems, nuget a registry_package’s type

A push trigger can also set ref on the trigger itself: tags matches only tag pushes; default (or no ref) matches branch pushes and never a tag. A push that deletes its ref never matches.

Conditions that only make sense on some events are refused elsewhere: conclusion_unsupported_event, review_state_unsupported_event, severity_unsupported_event, environment_unsupported_event and package_type_unsupported_event. paths on anything but push is refused with paths_need_push, because a pull request delivery does not carry its file list and getting it would cost a GitHub call. An unknown condition name is refused with unknown_condition, and a blank glob with invalid_glob.

A push carries at most 20 commits. On a very large push, paths matches on the paths it can see.

Globs

* matches any run of characters except /. ** matches any run including /. **/ matches zero or more directories. ? matches one character except /. Everything else is literal. Branches and paths match with case, logins and labels without. A list holds at most 30 entries. A branch, path or environment glob is at most 200 characters; a label or login at most 100. @default is the repository’s default branch.

Rate and storm limits

Key Range Default Effect
debounceSeconds 0 to 3,600 30 for a push or a pull_request synchronize, else 0 Deliveries for the same trigger, repository and pull request or ref inside the window start one run. The first one wins.
maxRunsPerHour 1 to 1,000 60 At most this many runs per trigger in the last hour.

Safeguards

A delivery that matches can still be stopped. Each stop is recorded as a trigger.skipped event with a reason. A delivery that simply did not match a condition records nothing.

Reason Why
own_branch The head branch starts with the factory’s own branch prefix (github.branch_prefix, belai/K- by default).
own_identity The author or sender is the Vulnetix App’s bot, pix[bot] or pix-bot[bot], or the head commit is by Pix [bot] or security@vulnetix.com.
coalesced Another delivery in the same debounce window already started the run.
rate_limited The trigger hit maxRunsPerHour.
fanout_cap The delivery already started the most runs one delivery may start.
org_disabled The repository’s organisation is not managed.
label_untrusted_sender A bot added the label, or someone outside the team used a /sf command.
fork_needs_human An untrusted event would reach a crew without a person approving first.

The kill switch, the workflow’s scope and unmanaged repositories also stop a fire. An empty scope uses the event’s own repository, as long as it is managed.

Forks and outsiders

A pull request from a fork is untrusted. So is an issue opened or reopened by someone whose association with the repository is not owner, member or collaborator. At publish, a pull_request or pull_request_review trigger that accepts forks must have a human step on every path to a crew or draft pull request, or publish fails with fork_pr_needs_human. At fire time the same check runs for every untrusted event. Setting origin: same_repo keeps outsiders out entirely.

What the run keeps

A run started by a webhook keeps the event’s facts: event and action, repository, number, head commit, branch, base branch, author and author type, labels, draft, origin and whether it is untrusted, and the delivery id. It keeps no title, body or comment text. The facts are written to trigger/context.json, shown on the run page, and quoted in the crew’s card.

Other trigger kinds

  • finding fires when a scanner reports a finding of a listed severity: critical, high, medium or low.
  • kanban fires when a card moves from fromList to toList.
  • manual fires when a person starts a run.
  • cron: see Schedules.
  • label: see Labels and commands.
  • firewall: see Source events.

Finding out why a trigger did not fire

  1. Open Events and filter on the workflow. A trigger.skipped event names the reason.
  2. GET /v1/factory/events/unanswered?days=7 lists deliveries that started no run at all.
  3. If there is nothing, the delivery did not match. Check each condition against the event on GitHub.
Edit this pageSources: vdb-site/docs/factory.md, vdb-site/api/internal/handler/factory_trigger_conditions.go, vdb-site/api/internal/handler/factory_trigger_validate.go, vdb-site/api/internal/handler/factory_engine_events.go
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.