Webhook Event Design

Suggested payload model for downstream event processing.

  • webhooks
  • events

Omar Gate can emit webhook events to notify downstream systems about scan results. Configure a webhook URL and secret in your workflow or Sentinelayer dashboard.

Event Types

| Event Type | Trigger | Description |

|------------|---------|-------------|

| `scan.completed` | Every successful scan | Scan finished and gate decision was made (passed or blocked). |

| `scan.blocked` | Findings above gate threshold | Subset of `scan.completed` where `gate_status` is `blocked`. |

| `scan.failed` | Runtime or config error | Scan could not complete due to configuration, provider, or infrastructure issues. |

| `scan.skipped` | Rate limit or cooldown hit | Scan was skipped due to `max_daily_scans` or `min_scan_interval_minutes`. |

Common Payload Fields

Every webhook event includes these fields:

| Field | Type | Description |

|-------|------|-------------|

| `event_type` | string | One of the event types above. |

| `run_id` | string | Unique scan identifier. Use as idempotency key. |

| `timestamp` | string | ISO 8601 timestamp of the event. |

| `repo` | string | Full repository name (e.g. `acme/backend`). |

| `branch` | string | Branch or PR head ref that was scanned. |

| `commit_sha` | string | Commit SHA that was analyzed. |

| `pr_number` | integer or null | Pull request number, if applicable. |

| `gate_status` | string | `passed`, `blocked`, or `error`. |

| `severity_counts` | object | Counts by severity: `{ "P0": 0, "P1": 2, "P2": 3, "P3": 5, "info": 1 }`. |

| `total_findings` | integer | Total number of findings. |

| `artifact_links` | object | URLs to download artifacts from the Actions run. |

| `estimated_cost_usd` | string | Estimated LLM cost for this scan. |

Example: scan.completed


{

  "event_type": "scan.completed",

  "run_id": "run-20260223-001",

  "timestamp": "2026-02-23T08:15:30Z",

  "repo": "acme/backend",

  "branch": "feature/auth",

  "commit_sha": "a1b2c3d4e5f6",

  "pr_number": 142,

  "gate_status": "blocked",

  "severity_counts": {

    "P0": 0,

    "P1": 2,

    "P2": 3,

    "P3": 5,

    "info": 1

  },

  "total_findings": 11,

  "artifact_links": {

    "findings": "https://api.github.com/repos/acme/backend/actions/artifacts/12345/zip",

    "summary": "https://api.github.com/repos/acme/backend/actions/artifacts/12346/zip"

  },

  "estimated_cost_usd": "0.0042"

}

Example: scan.failed


{

  "event_type": "scan.failed",

  "run_id": "run-20260223-002",

  "timestamp": "2026-02-23T09:01:12Z",

  "repo": "acme/backend",

  "branch": "main",

  "commit_sha": "b2c3d4e5f6a7",

  "pr_number": null,

  "gate_status": "error",

  "error_code": 2,

  "error_message": "Missing required input: sentinelayer_token",

  "severity_counts": {},

  "total_findings": 0,

  "artifact_links": {},

  "estimated_cost_usd": "0.00"

}

Webhook Security

Webhooks include an `X-Sentinelayer-Signature` header containing an HMAC-SHA256 signature of the payload body using your webhook secret. Always verify this signature before processing events.

Routing Recommendations

  • **scan.blocked** → Slack incident channel or PagerDuty
  • **scan.completed** → SIEM ingestion or dashboard update
  • **scan.failed** → Ops alert channel for configuration review
  • **scan.skipped** → Daily digest or ignore

Structured Answers

What should be event idempotency key?

Use run_id as the primary idempotency key. Each scan produces exactly one run_id that is unique across all events for that scan.

How do I verify webhook authenticity?

Check the X-Sentinelayer-Signature header against an HMAC-SHA256 of the raw payload body using your webhook secret. Reject any request where the signature does not match.

Which event should trigger a Slack alert?

Route scan.blocked to your incident response channel for immediate attention. Use scan.completed for informational updates and scan.failed for ops-level configuration alerts.