Webhooks

Get every matching event POSTed to your endpoint, signed and retried. A Pro feature.

  1. Create a webhook

    On Webhooks, click New webhook, give it a name and an HTTPS URL. The signing secret is shown once — store it.

  2. Pick filters

    Filters are AND-ed; leave one empty to match everything. Filter by platform (e.g. only Sherlock), program type (bug bounty vs competition), event type (e.g. only commit.new), or tag. To cut commit noise, enable code changes only: commits that touched solely docs, tests, or CI are suppressed, so you only get commits that changed in-scope source. This only gates commits — releases and tags always deliver.

  3. Send a test event

    Use Send test on the webhook to fire a test.synthetic delivery and verify your endpoint before real traffic arrives.

Delivery format

Each event is an HTTP POST with a JSON body:

{
  "id": "dlv_...",
  "type": "commit.new",
  "program_id": "prg_...",
  "asset_id": "ast_...",
  "data": { "repo": "...", "branch": "...", "sha": "...", "message": "...", "url": "..." }
}

data varies by event type. Event types: commit.new, pr.new, release.new, tag.new, asset.added, program.created, program.updated, audit.published, competition.opened, competition.judging, competition.completed, and test.synthetic.

On commit.new, data also carries:

  • code_changedtrue when the commit touched in-scope source, false when it changed only docs, tests, or CI. Omitted when we couldn’t determine the changed files (e.g. a merge commit).
  • summary — a one-line description of what changed (e.g. "adds nonReentrant guard to withdraw()"). Omitted when unavailable.

Headers on every delivery:

  • X-BountyHunt-Signature: v1=<hex> — HMAC signature (below)
  • X-BountyHunt-Timestamp — unix seconds when the delivery was signed
  • X-BountyHunt-Event — the event type
  • X-BountyHunt-Delivery — the delivery id

Verifying signatures

The signature is HMAC-SHA256 over timestamp.delivery_id.event_type.body, keyed with your webhook secret:

const expected = crypto
  .createHmac('sha256', SECRET)
  .update(`${ts}.${deliveryId}.${eventType}.${rawBody}`)
  .digest('hex')
// compare against the hex after "v1=" using a constant-time comparison

ts, deliveryId, and eventType come from the headers above; rawBody is the exact request body. Reject if the signature mismatches or the timestamp is older than a few minutes.

Retries and failures

Respond with a 2xx within 10 seconds. Anything else is retried up to 5 times with backoff (30s, 2m, 10m, 30m, 1h). After 10 consecutive exhausted deliveries the webhook is auto-disabled — re-enable it from the dashboard once your endpoint is healthy. Every attempt is visible in the webhook’s delivery log.

Limits

Up to 10 webhooks per account; test events are limited to 30 per hour. Deliveries pause while your plan is Free and resume when you’re back on Pro.