From 74fccaa65e6b8b4f1a32dca3b901d3ee8b8e589a Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 20 Jun 2026 11:35:33 +0000 Subject: [PATCH] docs(asm): add OpenAPI 3.1 webhooks section for scan status events The POST /scans description mentions subscribing to webhooks to track async scan completion, but no webhook payload was documented anywhere. This adds a first-class `webhooks:` section (native to OpenAPI 3.1) covering scan.completed, scan.failed, and scan.cancelled events, including HMAC-SHA256 signature verification guidance and full request body examples. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01ECLzW74k3Uct8QrnxnwfWH --- asm/openapi.yaml | 100 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/asm/openapi.yaml b/asm/openapi.yaml index 228f34f..ccb6350 100644 --- a/asm/openapi.yaml +++ b/asm/openapi.yaml @@ -393,6 +393,106 @@ paths: '401': $ref: '#/components/responses/Unauthorized' +# ── Webhooks ────────────────────────────────────────────────────────────────── +# +# HailBytes POSTs to your configured endpoint whenever a scan changes status. +# Configure webhook URLs in the dashboard under Settings → Webhooks. +webhooks: + scanStatusChanged: + post: + summary: Scan status change + description: | + Delivered whenever a scan transitions to `completed`, `failed`, or + `cancelled`. Configure delivery endpoints in the HailBytes dashboard + under **Settings → Webhooks**. + + ## Signature Verification + + Every request includes an `X-HailBytes-Signature-256` header — an + HMAC-SHA256 signature of the raw request body keyed with your webhook + secret. Always verify the signature before processing the payload: + + ```python + import hmac, hashlib + + def is_valid(secret: str, body: bytes, header: str) -> bool: + expected = "sha256=" + hmac.new( + secret.encode(), body, hashlib.sha256 + ).hexdigest() + return hmac.compare_digest(expected, header) + ``` + + Return any **2xx** status to acknowledge receipt. HailBytes retries + up to **5 times** with exponential back-off on non-2xx responses or + network timeouts. Returning **410 Gone** permanently removes the + endpoint from future deliveries. + tags: [Scans] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [event, timestamp, data] + properties: + event: + type: string + enum: [scan.completed, scan.failed, scan.cancelled] + description: Event type that triggered this delivery + example: scan.completed + timestamp: + type: string + format: date-time + description: ISO 8601 timestamp of when the event was emitted + example: '2025-06-20T14:23:00Z' + data: + $ref: '#/components/schemas/ScanDetail' + examples: + completed: + summary: Scan completed successfully + value: + event: scan.completed + timestamp: '2025-06-20T14:23:00Z' + data: + id: scn_a1b2c3d4e5f60001 + label: 'Q2 perimeter sweep' + status: completed + scan_type: full + targets: ['acmecorp.com', '203.0.113.0/24'] + created_at: '2025-06-20T14:00:00Z' + started_at: '2025-06-20T14:01:12Z' + completed_at: '2025-06-20T14:23:00Z' + summary: + assets_discovered: 42 + assets_updated: 7 + vulnerabilities_found: 3 + vulnerabilities_by_severity: + critical: 1 + high: 2 + medium: 0 + low: 0 + informational: 0 + failed: + summary: Scan failed + value: + event: scan.failed + timestamp: '2025-06-20T14:10:00Z' + data: + id: scn_a1b2c3d4e5f60002 + label: null + status: failed + scan_type: discovery_only + targets: ['203.0.113.0/24'] + created_at: '2025-06-20T14:05:00Z' + started_at: '2025-06-20T14:06:00Z' + completed_at: null + error_message: 'Network unreachable for target range' + responses: + '200': + description: Delivery acknowledged. Any 2xx is accepted. + '410': + description: Endpoint removed — HailBytes will stop delivering to this URL. + components: securitySchemes: bearerAuth: