> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.detectant.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.detectant.com/_mcp/server.

# Finding webhooks

Detectant can send an HTTPS webhook when a direct API scan or enabled S3 integration finds a suspicious or malicious file. Configure the destination and select **Webhook** under **Dashboard → Settings → Notification settings**. You can use webhook delivery by itself or together with email.

## Event

Every notification uses the `finding.detected` event type. The request body is JSON and the `detected_at` value is an ISO 8601 timestamp.

For a direct API scan:

```json
{
  "type": "finding.detected",
  "source": {
    "type": "api"
  },
  "finding": {
    "scan_id": "scan_01JEXAMPLE",
    "verdict": "malicious",
    "detection": "Malicious content",
    "reason": "The file matched one or more malicious indicators.",
    "size_bytes": 24812,
    "sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "detected_at": "2026-08-30T10:00:00.000Z"
  },
  "file": {
    "filename": "invoice.pdf"
  }
}
```

For an S3 integration, `source` identifies the integration and `file` identifies the exact object:

```json
{
  "type": "finding.detected",
  "source": {
    "type": "s3",
    "integration_id": "162f1d7b-0f97-48af-b7ea-1fbc7a7c19e4",
    "integration_name": "Customer uploads"
  },
  "finding": {
    "scan_id": "scan_01JEXAMPLE",
    "verdict": "suspicious",
    "detection": "File type mismatch",
    "reason": "The file content does not match its declared type.",
    "size_bytes": 24812,
    "sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "detected_at": "2026-08-30T10:00:00.000Z"
  },
  "file": {
    "bucket": "customer-uploads",
    "object_key": "incoming/invoice.pdf"
  }
}
```

`verdict` is either `suspicious` or `malicious`. Treat fields you do not recognize as forwards-compatible additions.

## Verify the signature

Detectant signs the exact raw request body using the signing secret shown in notification settings. The request includes these Standard Webhooks headers:

* `webhook-id`: a stable identifier for this finding notification. Use it to deduplicate processing.
* `webhook-timestamp`: the signing time as Unix seconds.
* `webhook-signature`: one or more versioned signatures, currently `v1,<base64-signature>`.

To calculate the expected signature:

1. Remove the `whsec_` prefix from the signing secret and Base64-decode the remainder.
2. Join the webhook ID, timestamp and unmodified raw body with periods: `<id>.<timestamp>.<raw-body>`.
3. Calculate HMAC-SHA256 with the decoded secret and Base64-encode the result.
4. Compare it with the `v1` signature using a timing-safe comparison.

Reject requests with missing headers, an invalid signature, or a timestamp outside a short tolerance such as five minutes. Verify the signature before parsing JSON, and never log the signing secret or full request headers.

## Delivery behavior

The endpoint must use HTTPS and a public hostname. Detectant waits up to five seconds and accepts any `2xx` response as successful. Redirects and non-`2xx` responses are treated as failures. Failed deliveries are retried independently with capped exponential backoff; the saved finding remains available and the file is not scanned again. Because a request can succeed immediately before its response is lost, use `webhook-id` to safely ignore duplicate deliveries. Respond quickly after safely recording the event and perform slower work asynchronously.

Keep the signing secret private. If it is exposed, select **Rotate signing secret** in notification settings and update your receiver immediately. New delivery attempts use the rotated secret; a request already in flight when rotation completes may still carry the previous signature.