Skip to content

Webhook Rule Filter Expressions

Each WebhookRule carries a filter_expression written in CEL (Common Expression Language). When a webhook is delivered, the gateway evaluates every rule's expression and runs the rule's handler only when the expression is truthy. If no rule matches, the delivery is recorded but no handler runs.

Recorded deliveries are published to the Iggy events stream, topic gateway, and land in the ClickHouse events table through the sink.

Evaluation context

The expression is evaluated against the same data the gateway materializes into the activity-feed events row (imbi.common.models.Event). The project-independent fields of that row are exposed as top-level variables:

Variable Type Contents
type string Resolved event type — the value selected by the service's event_type_selector (e.g. the X-GitHub-Event header value). '' when no selector is configured.
third_party_service string The service slug (e.g. github).
attributed_to string The resolved Imbi user; '' when the delivery maps to no user.
metadata.headers map The request headers, keys lower-cased, sensitive values redacted (see below).
payload map The webhook request body, exactly as received.

Note: the webhook body is under payload, not at the top level. A filter on a body field is payload.action == "opened", not action == "opened".

Examples

// Match on the X-GitHub-Event header
metadata.headers["x-github-event"] == "push"

// Same, via the resolved event type (requires event_type_selector:
// X-GitHub-Event on the service)
type == "push"

// Match on a body field
payload.action == "opened"

// Combine
type == "deployment_status" && payload.deployment_status.state == "success"

Header names in metadata.headers are lower-cased, so always index with the lower-case form (metadata.headers["x-github-event"]).

Action handler_config uses the same shape

Action handler_config CEL expressions (committish_expression, version_expression) and JSON-Pointer selectors (title_selector, status_selector, …) resolve against this same event context. So the webhook body is under /payload for pointers and payload.<field> for CEL — identical to a rule's filter_expression. For example, a body field is payload.deployment.sha in both a filter and a committish_expression, and a selector reads /payload/deployment/ref.

Rule ordering

Matching rules run in ascending ordinal order, and for the release actions that order is load-bearing rather than cosmetic:

  1. create_release — creates the Imbi Release the rest key off.
  2. add_deployment_event — looks the release up and drops the event when it is missing, so it must run after create_release.
  3. publish_release / block_release — ratify or block that release once the deployment reports its outcome.

Rules that key off a deployment state should enumerate the states they act on rather than negating one. state != "success" sweeps in inactive, which means the deployment was superseded by a newer one — the normal end of every deployment's life, not a failure. The publish_release and block_release actions enforce this themselves (they fire only on states mapping to success and failed respectively), so a filter can be as broad as type == "deployment_status".

Redacted headers

Headers that may carry credentials or webhook signatures are replaced with [redacted] before they reach both ClickHouse and the filter context. You cannot filter on the value of these headers (only on their presence):

authorization, cookie, set-cookie, proxy-authorization, x-hub-signature, x-hub-signature-256, x-gitlab-token, x-pagerduty-signature, x-sonar-webhook-hmac-sha256.

Available functions

In addition to the CEL standard functions, expression evaluation uses cel-python. A failed or non-boolean evaluation logs a warning and is treated as non-matching for that rule (it never raises into the request).