IntegrationsOverview

Integrations

Pencheff fans out findings to chat, paging, and SIEM destinations based on per-integration severity filters. Each integration is encrypted at rest (Fernet) and configured once via the SaaS /integrations API, the dashboard, or the MCP export_to_* tools.

Supported destinations

KindUse forProtocol
SlackTeam chat summariesIncoming webhook
Microsoft TeamsTeam chat summariesIncoming webhook (Connector)
Google ChatTeam chat summariesIncoming webhook
DiscordChat + community notificationsWebhook with embeds
PagerDutyPaging on critical / highEvents API v2
OpsgeniePaging + routing rulesAlerts API v2
Splunk HECSIEM ingestionHTTP Event Collector
Generic signed webhookAnything else — SOAR, custom endpointsHMAC-signed HTTP POST
GitHub IssuesIssue creation on critical/highgh CLI / REST
JiraTicket creation per finding, comment on updateREST v3

Three-stage filter

Every event flows through three independent filters before it leaves Pencheff. An integration only fires when all three pass:

event ──> [ enabled? ] ──> [ target in scope? ] ──> [ event type subscribed? ] ──> [ severity above filter? ] ──> dispatch

1. Per-target scope

Each integration carries a target_ids: list[str] | NULL column.

  • NULL (or empty list) → fires for every target in the workspace (DAST URL targets and repo-mirror targets — see repos as targets).
  • Populated → fires only for scans against those targets.

This lets you wire e.g. a PagerDuty integration scoped to one critical-tier production target, while a Slack channel takes the firehose for every target.

2. Per-event filter

Each integration carries an events: list[str] | NULL column. The five lifecycle events are:

EventWhen it firesPayload
scan_startedscan_runner accepts a new scantarget name + profile
scan_donescan finishes successfullygrade + severity counts
scan_failedunhandled exception in the workererror message
finding_neweach finding persisted at scan completionseverity + CVSS + title + endpoint
finding_changedanalyst updates verification, suppresses, unsuppresses, or rechecksdiff string (verification_status: unverified → true_positive)

NULL (or empty list) → all five fire. Populate to subscribe to a subset — e.g. PagerDuty might only want scan_failed + finding_new, while a Jira integration only wants finding_new + finding_changed.

3. Severity filter

Each integration has a severity_filter — applies only to finding-level events (finding_new, finding_changed). Scan-level events (scan_started, scan_done, scan_failed) bypass it. Default is high.

severity_filterRouted severities
criticalcritical only
highhigh + critical
mediummedium + high + critical
lowlow + medium + high + critical
infoeverything

Pair a critical-only PagerDuty integration with a medium-and-above Slack integration to avoid pager fatigue without losing visibility.

When they fire

TriggerSource
scan_startedscan_runner.run_scan line 195 enqueues notify_event.delay(scan_id, "scan_started")
scan_done + per-finding finding_newnotify_scan_findings.delay(scan_id) at end-of-success in scan_runner; one Celery task fans both out
scan_failedscan_runner exception handler
finding_changedfindings router endpoints (POST /findings/{id}/status, /suppress, /unsuppress, /recheck)

The MCP export_to_* tools also exist for ad-hoc dispatch from the agent — they bypass the Celery pipeline and target one integration directly.

Testing

The dashboard and API both expose a POST /integrations/{id}/test endpoint. It sends a minimal “Pencheff integration test” payload so you can verify the destination is reachable before a real scan runs. For Jira, the test creates an issue under the configured project key and immediately deletes it (best-effort cleanup) so the connectivity check confirms both auth and write access without polluting the board.