Webhooks
A webhook is an inbound endpoint. An external system — GitHub, GitLab, Jira, Bitbucket, or anything that can send an HTTP POST — sends events to its URL, and FYAI starts a run of the webhook's linked workflow for each event that passes the webhook's filters.
Webhooks belong to a project. Manage them in Settings → Webhooks (under Project).
How a delivery is handled
POST to webhook URL
│
├─ unknown URL ............................ 404
├─ webhook disabled ....................... 410
├─ signature missing or wrong ............. 401 (see "Signing")
├─ repeat of a delivery already run ....... 409 (GitHub / GitLab delivery ID)
│
├─ work out the event type
├─ check event filters ── no match ........ 200 "filtered"
├─ duplicate suppression ── duplicate ..... 200 "deduplicated"
│
└─ start a mission from the Linked Workflow
├─ started .......................... 200 "accepted"
└─ could not start .................. error code (for example 423 if the
workflow is deactivated, 502 if the
run could not be handed over)
Every delivery — including rejected ones — is written to the webhook's delivery log. The receiver accepts up to 120 requests a minute from one source IP address.
The response body tells the sender what happened:
{
"status": "accepted",
"event_type": "github.pull_request.opened",
"matched": true,
"match_details": "PASS: event_type equals github.pull_request.opened [mission=123]",
"error": null
}
status is accepted, filtered, deduplicated or error.
Webhook settings
| Field | Description |
|---|---|
| Name | Required. |
| Provider | GitHub, GitLab, Jira, Bitbucket, Slack or Custom. Decides how deliveries are signed and which filter shortcuts are offered. |
| Description | Optional. |
| Linked Workflow | The workflow to run. None accepts and logs deliveries without running anything. |
| Active / Disabled | A disabled webhook answers every delivery with 410. |
| Tags | Comma-separated. |
| Event filters | Rules a delivery must pass. See below. |
| Duplicate suppression | Optional. See below. |
After saving, the ENDPOINT box shows POST and the webhook URL (with Copy URL), and the start of the signing secret (with Copy Secret). The list shows each webhook's provider, how many times it has triggered, and Active or Disabled.
The URL contains a long random token. Anyone with the URL can post to it, so treat it as a secret.
Signing
Every webhook gets a signing secret when it is created. Regen Secret replaces it ("Existing integrations using the old secret will break").
| Provider | What FYAI checks |
|---|---|
| GitHub | The X-Hub-Signature-256 HMAC-SHA256 signature (or the older X-Hub-Signature SHA-1) made with the secret. A delivery without a signature is rejected. |
| GitLab | The X-Gitlab-Token header must equal the secret. A delivery without it is rejected. |
| Jira, Bitbucket, Slack, Custom | The URL token alone authenticates the delivery. If the delivery does carry a GitHub or GitLab signature header, it must be valid. |
For GitHub and GitLab, a signed delivery whose delivery ID (X-GitHub-Delivery or X-Gitlab-Event-UUID) already started a run in the last 5 minutes is rejected as a replay (409).
Event types
FYAI derives an event type from each delivery, which you can filter on:
| Source | Event type |
|---|---|
GitHub (X-GitHub-Event header) | github.<event>.<action>, for example github.pull_request.opened, or github.<event> when there is no action, for example github.push |
GitLab (X-Gitlab-Event header) | gitlab. + the header in lower case with underscores, for example gitlab.push_hook, gitlab.merge_request_hook |
Jira (webhookEvent in the body) | jira.<webhookEvent>, for example jira.jira:issue_created |
Bitbucket (X-Event-Key header) | bitbucket.<key>, for example bitbucket.repo:push |
| Anything else | The body's event_type field, or the X-Event-Type header |
Event filters
Click Add Filter to add a rule, or click a provider shortcut chip (for example Push, PR Opened, PR Closed for GitHub). With no filters, "all events will be accepted".
Each rule has a Field, an Operator and a Value. A delivery must pass every rule.
Field can be:
| Field | Reads |
|---|---|
event_type | The derived event type. |
payload.<path> | A value in the JSON body, by dotted path. Numbers index into lists, for example payload.commits.0.id. |
header.<name> | A request header, for example header.x-github-event. |
<key> | A top-level key in the JSON body. |
Operators: Equals, Not Equals, Contains, Starts With, Ends With, Exists (no value), Regex. A rule whose field is missing from the delivery fails.
Through the API, a rule can instead be a group — {"any_of": [...]} or {"all_of": [...]} of nested rules — to express "this OR that".
Duplicate suppression
Senders often fire several events for one piece of work: GitHub sends check_suite.completed once per check suite, so one commit can wake a workflow three times. Turn on DUPLICATE SUPPRESSION to stop a second run starting while the first is still going.
| Setting | Description |
|---|---|
| Key field | Payload paths that identify the piece of work. The values found are joined into a key; paths missing from a delivery are skipped. Click Add Key Field for more. |
| Window (seconds) | How far back to look. Default 600. |
| Preset chip | GitHub, GitLab and Bitbucket offer One run per commit; Jira offers One run per change. |
A delivery is suppressed only while a run started by an earlier delivery with the same key is still in progress and within the window. Once that run finishes, the next event starts a fresh run. If no key field resolves, the delivery runs normally. Choose fields that change when the work changes (a commit SHA rather than a pull request number).
Suppressed deliveries are logged and answered with deduplicated. Through the API you can also set deferral rules, which record each suppressed delivery in the workflow's run state so a later run can pick it up.
Delivery log
Select a webhook and click Logs to see RECENT DELIVERIES: when each arrived, its event type, Matched or No match, and the response code returned. The API returns the full headers, payload and match details for each delivery (GET /webhooks/{id}/logs).
What the workflow receives
A webhook run's initial message is built from the event:
Triggered by webhook event: github.pull_request.opened
<short summary of the key fields, e.g. repository, pull request, action>
Raw event payload:
{ ...the JSON body, cut at 4,000 characters... }
Agents and functions with an empty input message receive this text, and any node can insert it with ${initial_message}. Webhook runs get no attribute values. The run is recorded with Trigger shown as Webhook.
Webhooks and data sources
A webhook can also deliver its payloads to a data source instead of, or as well as, a workflow. This target (data_source_id) is set through the API.
Related
- Triggering Workflows with Webhooks — step-by-step setup
- Event Sources — live streams from MCP servers
- Workflows
- Scheduling Workflows