Skip to content

api_trigger fires a run for every webhook delivery with no event/action filter #605

Closed as not planned

Description

@kayahk

What happens

An api_trigger node fires one automation run per HTTP delivery, with no way to narrow deliveries by event type or action. Pointing a GitHub repository webhook at <public-url>/api/v1/webhooks/automations/<nodeId> therefore runs the whole graph for every subscribed event, including events the automation cannot possibly care about.

Observed on our deployment (one automation, one api_trigger, one target task) after enabling the events a PR-review automation plausibly wants — pull_request, push, check_run:

  • 3,291 runs in ~2.5 days (~1,300/day), all against the same task.
  • Of the last 100 webhook deliveries, 85 were check_run: GitHub emits check_run per job per state (queued → in_progress → completed), so a single push to a repo with ~10 CI jobs fans out into dozens of deliveries within a minute. Runs arrive in bursts of ~20/minute, matching the deliveries one-for-one.
  • Nothing downstream can prevent it: the graph's github.pr_state condition runs after the trigger, so it suppresses work but not the run — and once the run reaches a trigger_ai_agent node, suppression is moot: a sandbox Job has already been requested.

The only workaround today is to uncheck event types in the GitHub hook, which is coarse: dropping check_run is fine, but pull_request still fires on opened, synchronize, labeled, closed, review_requested, … and the automation has no way to say "only opened and synchronize".

Why the code cannot already filter

ReceiveWebhook (services/api/internal/transport/http/handler/automation_handler.go) reads exactly one thing from the request — the X-Webhook-Token header — and calls HandleWebhookTrigger(ctx, nodeID, token). The body is never read and X-GitHub-Event is never inspected, so the delivery's identity is discarded before the service layer sees it. TriggerConfig (services/api/internal/domain/automation/entity.go) has narrowing fields for status, tag, sprint, due date and watched tasks, but nothing for an external webhook's event.

Proposed fix

An optional, GitHub-shaped allow-list on the trigger config:

{
  "target_task_id": "746e4bfb-…",
  "github_events": ["pull_request:opened", "pull_request:synchronize", "push"]
}
  • Each entry is event or event:action, matched against the X-GitHub-Event header and the JSON body's action string.
  • Empty/absent github_events keeps today's behaviour exactly (fire on every delivery) — no migration, no breaking change for non-GitHub senders.
  • All entries are OR'd; a delivery matching none is accepted and discarded: still 202, no run, no stream entry, so the sender never retries and the token-verification response stays indistinguishable (no event-name probing).
  • Validated at save time like the other narrowing fields (ErrNodeConfigInvalid): non-empty event, at most one :, non-empty action when the : is present; rejected on any trigger type other than api_trigger.

Deliberately GitHub-shaped rather than a general matcher: action + header is exactly the discriminator every GitHub integration has, and a generic JSONPath/expression language would be a much larger surface for the same result. A plugin-level filter can come later if a non-GitHub sender needs it.

Acceptance criteria

  • A run is enqueued only for deliveries matching github_events; a delivery that matches nothing returns 202 and leaves automation_runs untouched.
  • An api_trigger with no github_events behaves exactly as today.
  • Invalid filter entries are rejected by the existing node-save validation path.
  • Unit tests cover matching (event, event:action, no filter, mismatch, malformed body, missing header) and the validation rules.

Impact where this was found

~1,300 runs/day collapse to the handful of PR events that actually matter, which also keeps trigger_ai_agent from requesting a sandbox per CI job — a 2 CPU / 4 Gi sandbox against a 12-pod namespace quota.

Activity

  1. pullfrog commented on Oct 19, 2026

    @pullfrog
    Contributor
  2. kayahk commented on Oct 19, 2026

    @kayahk
    ContributorAuthor

    Implemented in #606: optional github_events on the api_trigger config (event or event:action), matched against X-GitHub-Event + the body's action; a non-matching delivery is accepted (202) and dropped without creating a run. Covered by a unit test on the matcher, save-time validation tests, and an e2e that asserts zero runs for a check_run delivery and a fired run for pull_request:opened.

  3. kayahk commented on Oct 19, 2026

    @kayahk
    ContributorAuthor

    Closing: this moved to the GitHub plugin, where GitHub event handling belongs (per the maintainer's guidance on #606).

    The original report's evidence still holds: ~3,300 runs in 2.5 days on one automation, ~85% of deliveries check_run, because an api_trigger had no way to filter. That is now handled on the sender side (the hook no longer subscribes to check_run) and, in the plugin, in front of the database.

    GitHub issue transfer between the two repos isn't available to a non-maintainer, hence the fresh issue over there rather than a move.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions