generated: '2026-08-04' method: derived source: >- openapi/malwarebytes-threatdown-nebula-openapi.json — the "Webhooks" tag description, which is the provider's own webhook documentation, verbatim in substance summary: >- ThreatDown publishes a real, well-specified webhook surface: 18 event types, an HMAC-SHA256 signature scheme, an explicit delivery envelope, and documented retry semantics. It does NOT publish an AsyncAPI document — the whole event contract lives in prose inside the OpenAPI tag description. This artifact captures that catalog so the event surface is machine-addressable; it is a Webhooks catalog, not a fabricated AsyncAPI. asyncapi_published: false webhooks: published: true transport: HTTP POST to a subscriber-supplied URL payload_format: application/json subscription_management: API-only subscription_note: >- "ThreatDown provides you with REST APIs for creating, retrieving, and deleting subscriptions. Currently, subscriptions can be registered only through APIs." There is no console UI for webhook subscriptions. multiple_handlers_per_event: true subscription_operations: - api: nebula operationId: api.v2.nebula.post.webhooks.subscriptions method: POST path: /nebula/v1/webhooks/subscriptions summary: Create subscription scope: write user_permission: notifications.createOwn - api: nebula operationId: api.v2.nebula.webhooks.subscriptions.get method: GET path: /nebula/v1/webhooks/subscriptions summary: Get all subscriptions scope: read user_permission: notifications.viewOwn - api: nebula operationId: api.v2.nebula.webhooks.subscriptions.get.id method: GET path: /nebula/v1/webhooks/subscriptions/{id} summary: Get a subscription scope: read user_permission: notifications.viewOwn - api: nebula operationId: api.v2.nebula.post.webhooks.subscriptions.update method: PUT path: /nebula/v1/webhooks/subscriptions/{id} summary: Update a subscription scope: write user_permission: notifications.editOwn - api: nebula operationId: api.v2.nebula.post.webhooks.subscriptions.delete method: DELETE path: /nebula/v1/webhooks/subscriptions/{id} summary: Delete subscription scope: write user_permission: notifications.deleteOwn - api: oneview operationId: api.v2.rmm.post.webhooks.subscriptions method: POST path: /oneview/v1/accounts/{account_id}/webhooks/subscriptions summary: Create webhook subscription scope: write user_permission: notifications.createOwn - api: oneview operationId: api.v2.rmm.webhooks.subscriptions.get method: GET path: /oneview/v1/accounts/{account_id}/webhooks/subscriptions summary: Get all webhook subscriptions scope: read user_permission: notifications.viewOwn - api: oneview operationId: api.v2.rmm.webhooks.subscriptions.get.id method: GET path: /oneview/v1/accounts/{account_id}/webhooks/subscriptions/{id} summary: Get webhook subscription scope: read user_permission: notifications.viewOwn - api: oneview operationId: api.v2.rmm.post.webhooks.subscriptions.update method: PUT path: /oneview/v1/accounts/{account_id}/webhooks/subscriptions/{id} summary: Update a subscription scope: write user_permission: notifications.editOwn - api: oneview operationId: api.v2.rmm.post.webhooks.subscriptions.delete method: DELETE path: /oneview/v1/accounts/{account_id}/webhooks/subscriptions/{id} summary: Delete webhook subscription scope: write user_permission: notifications.deleteOwn envelope: description: Every notification is wrapped in a common envelope carrying metadata. fields: - name: event type: string description: The event type — one of the supported events below. - name: subscription_id type: string description: Unique identifier of the subscription that matched this event. - name: account_id type: string description: Nebula account id (site) generating this event. - name: payload type: object description: Event-specific data; its model varies by the value of `event`. dispatch_note: >- Consumers switch processing on `event` and interpret `payload` accordingly. events: - name: job_status_change description: >- Sent when a job issued through the Jobs APIs changes status — the way to track scan, isolate, remediate and reboot completion without polling. payload_fields: - name: id type: string description: Unique identifier of the job - name: machine_id type: string description: Unique identifier of the endpoint - name: command type: string description: Type of command (see the Jobs API) - name: issued_by type: string description: User issuing the job - name: status type: string description: Job status, including PENDING, STARTED, TIMED_OUT and COMPLETED - name: detection description: A threat detection was reported by the ThreatDown Endpoint Agent. - name: machine_events description: Endpoint/machine lifecycle events. - name: threat_events description: Threat-related events. - name: agent_events description: ThreatDown Endpoint Agent events. - name: quarantine_events description: Quarantine add/restore/delete events. - name: edr_events description: Endpoint Detection and Response events. - name: syslog_events description: Syslog forwarding events. - name: exclusion_events description: Exclusion created, edited or deleted. - name: scheduled_events description: Scheduled task events. - name: policy_events description: Policy created, edited or deleted. - name: user_events description: User administration events. - name: drive_events description: Drive encryption events. - name: cve_events description: Vulnerability (CVE) events, including vulnerability-resolved. - name: dns_events description: DNS filtering events. - name: license_events description: Licensing events. - name: appblock_events description: Application block events. - name: endpoint_events description: Endpoint events. event_count: 18 subscription_payload: source: POST /nebula/v1/webhooks/subscriptions requestBody schema required: [url, enabled, events] fields: - name: url type: string constraint: 'must match ^https://.{4,}$ — HTTPS only' - name: enabled type: boolean - name: events type: array min_items: 1 enum_note: >- The schema enum carries the 18 event names above PLUS the wildcard value "all", which subscribes to every event type in one subscription. - name: description type: string - name: custom_headers type: array description: Key/value headers ThreatDown will include on each delivery to your endpoint. - name: max_retries type: integer default: 10 maximum: 10 exclusive_minimum: 0 - name: secret_token type: string description: The signing key used to compute the HMAC of the payload. security: signature_header: X-MWB-Signature algorithm: HMAC-SHA256 computation: HMAC_SHA256(secret_token, webhookPayload) secret: >- A `secret_token` parameter supplied by the subscriber when creating the subscription; used as the signing key. verification: >- Compute the HMAC over the raw request body and compare with X-MWB-Signature. A match establishes ThreatDown as the sender. quoted: >- "ThreatDown will sign the webhook events it sends to your server. We do so by including a signature in each event's `X-MWB-Signature` header." delivery: acknowledgement: Return any 2xx HTTP status code. failure_definition: >- Any response outside 2xx — explicitly including 3xx — is treated as a failure. retry_strategy: exponential backoff default_max_retries: 5 configurable: true configuration_param: max_retries discrepancy: >- The Webhooks prose states "The default maximum number of retries is `5`", but the request schema for POST /nebula/v1/webhooks/subscriptions declares max_retries with default 10, exclusiveMinimum 0 and maximum 10. The narrative and the machine-readable contract disagree on the default. Report upstream. give_up_behaviour: >- After the maximum attempts the notification is dropped. "You will be responsible for reconciling your data by querying our API endpoints for potentially missed events." at_least_once: true consumer_requirement: >- "We recommend you design idempotent event processing because you might receive the same event more than once." positioning: provider_guidance: >- ThreatDown actively steers integrators off polling: "The alternative to registering webhooks is performing polling, but this is deeply discouraged: besides involving more complicated code on your side, you may exceed your quota." Webhooks are also listed first among the remedies for a 429. gaps: - >- No AsyncAPI document. The event contract is prose in an OpenAPI tag description, so no event-driven tooling can generate consumers or validate payloads. - >- Only the job_status_change payload model is fully specified. The other 17 event types are named with a one-line description and no payload schema. - >- No published event catalog page outside the API reference, and no event versioning or schema-evolution policy. - No replay/redelivery API for events dropped after max retries — reconciliation is manual. - No timestamp or delivery-id field documented in the envelope for ordering or dedup.