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: