Skip to main content

Webhooks

Webhooks allow Ariftly to push scan results to your systems in real time — no polling required.

Setting up a webhook

  1. Navigate to Settings → Webhooks in the dashboard
  2. Click Add Endpoint
  3. Enter your endpoint URL
  4. Select the events to subscribe to
  5. Copy the signing secret for payload verification

Or via API:

curl -X POST https://api.ariftly.io/v1/webhooks \
-H "Authorization: Bearer $ARIFTLY_API_KEY" \
-d '{
"url": "https://myapp.example.com/webhooks/ariftly",
"events": ["scan.completed", "scan.failed"],
"description": "CI/CD pipeline notifications"
}'

Events

EventTrigger
scan.createdA new scan has been queued
scan.startedA scan has begun running
scan.completedA scan has finished successfully
scan.failedA scan has encountered a fatal error
scan.cancelledA scan was manually cancelled
finding.newA new finding was detected (not seen in previous scans)
threshold.exceededA scan result exceeded a configured risk threshold

Payload format

All webhook payloads share the same envelope:

{
"event": "scan.completed",
"event_id": "evt_abc123",
"created_at": "2026-03-25T10:01:45Z",
"data": { ... }
}

scan.completed payload

{
"event": "scan.completed",
"event_id": "evt_abc123",
"created_at": "2026-03-25T10:01:45Z",
"data": {
"scan_id": "scan_xyz789",
"project_id": "proj_abc123",
"status": "completed",
"risk_score": 65,
"risk_level": "high",
"detectors": {
"accessibility": { "risk_score": 30, "risk_level": "low" },
"security": { "risk_score": 82, "risk_level": "critical" }
},
"completed_at": "2026-03-25T10:01:45Z"
}
}

threshold.exceeded payload

{
"event": "threshold.exceeded",
"event_id": "evt_def456",
"created_at": "2026-03-25T10:01:45Z",
"data": {
"scan_id": "scan_xyz789",
"project_id": "proj_abc123",
"threshold": {
"detector": "security",
"limit": 50,
"actual": 82
}
}
}

Verifying webhook signatures

All webhook deliveries include a signature header. You must verify this to confirm the request came from Ariftly.

Ariftly-Signature: sha256=7d38cdd689735b008b3c702edd92eea23791c5f6
Ariftly-Timestamp: 1711362060

Verification example (Node.js)

const crypto = require('crypto');

function verifyWebhook(payload, signature, timestamp, secret) {
const body = `${timestamp}.${payload}`;
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');

const receivedHash = signature.replace('sha256=', '');

// Constant-time comparison to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(receivedHash, 'hex')
);
}

Verification example (Python)

import hmac
import hashlib

def verify_webhook(payload: bytes, signature: str, timestamp: str, secret: str) -> bool:
body = f"{timestamp}.{payload.decode()}"
expected = hmac.new(
secret.encode(),
body.encode(),
hashlib.sha256
).hexdigest()
received = signature.replace("sha256=", "")
return hmac.compare_digest(expected, received)
warning

Always verify webhook signatures. Reject requests where verification fails.

Also check that the Ariftly-Timestamp is within 5 minutes of the current time to prevent replay attacks.

Retries

If your endpoint returns a non-2xx response, Ariftly retries with exponential backoff:

AttemptDelay
1st retry5 seconds
2nd retry30 seconds
3rd retry5 minutes
4th retry30 minutes
5th retry2 hours

After 5 failed attempts, the event is marked as failed and no further retries occur.

Webhook logs

The dashboard shows a delivery log for each webhook endpoint, including the request/response for each delivery attempt.