Skip to content

Webhooks

A webhook is an HTTP POST from BCP Portal to your endpoint when an event happens. An alternative to API polling.

Why

  • Real-time finding delivery into your SIEM / ticketing / Slack.
  • Automate reactions (e.g., on critical finding create a Jira ticket).
  • Integrate with internal systems without polling.

Configuration

  1. Settings → Integrations → Webhooks → New.
  2. Enter:
  3. URL — where to POST.
  4. Events — which events to subscribe to (see below).
  5. Secret — for HMAC signature. Auto-generated, can be changed.
  6. Active — on/off.
  7. Save → the platform immediately sends a test ping to verify.

Event types

Event When it fires
finding.created New finding
finding.updated Status or detail change
finding.resolved Closed as resolved or false_positive
scan.completed Scan finished
scan.failed Scan error
report.generated Report generated
member.added User added to organization
member.removed User removed

You can subscribe to specific events or all.

Payload

Standard format:

json { "event": "finding.created", "timestamp": "2026-06-29T10:23:45Z", "organization_id": "org_abc123", "data": { // event-specific payload } }

Example: finding.created

json { "event": "finding.created", "timestamp": "2026-06-29T10:23:45Z", "organization_id": "org_abc123", "data": { "id": "find_xyz789", "severity": "critical", "source": "github", "title": "AWS Access Key in public repo", "description": "Found AWS Access Key (AKIA...) in commit abc123 of repo example/test", "data_source": "yourcompany.com", "discovered_at": "2026-06-29T10:23:40Z", "status": "new", "url": "https://bcp.bytecode.team/findings/find_xyz789", "external_url": "https://github.com/example/test/blob/abc123/file.env#L42", "tags": ["aws", "production"] } }

Signature verification

Every webhook includes an X-BCP-Signature header with HMAC-SHA256 of the payload.

Python

```python import hmac import hashlib

def verify_signature(payload_body: bytes, signature_header: str, secret: str) -> bool: expected = hmac.new( secret.encode(), payload_body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(f"sha256={expected}", signature_header) ```

Node.js

```javascript const crypto = require('crypto');

function verifySignature(payloadBody, signatureHeader, secret) { const expected = crypto .createHmac('sha256', secret) .update(payloadBody) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(sha256=${expected}), Buffer.from(signatureHeader) ); } ```

⚠️ Use constant-time comparison (hmac.compare_digest / crypto.timingSafeEqual), not plain == — this protects against timing attacks.

Retry policy

If your endpoint returns a non-2xx:

  • 3 attempts with exponential backoff: 1s, 10s, 60s.
  • After 3 failures the webhook is marked failed and recorded in the Delivery log.
  • After 10 consecutive failed deliveries the webhook is automatically deactivated, and an email is sent to the owner.

Delivery log

Settings → Integrations → Webhooks → {your webhook} → Delivery log — history of all deliveries with:

  • Status (delivered / failed / retrying).
  • HTTP response code.
  • Latency.
  • Payload (can be re-sent).

Kept for 30 days.

Best practices

  • Respond fast — return 200 immediately, handle the payload asynchronously. If your endpoint takes > 10 seconds we count it as a timeout.
  • Idempotency — the same webhook may arrive again on retry. Check data.id and event to avoid duplicating actions.
  • Verify signature — otherwise an attacker can forge a webhook and trick your systems.
  • Logging — keep your own logs for troubleshooting.

Local testing

Use ngrok or a similar tunnel to test against a local endpoint.

Alternatively — webhook.site as a quick debug receiver.