generated: '2026-08-04' method: derived source: >- openapi/malwarebytes-threatdown-nebula-openapi.json, openapi/malwarebytes-threatdown-oneview-openapi.json, and live probes of api.threatdown.com summary: >- Cross-cutting runtime semantics for the ThreatDown Nebula and OneView APIs, derived from the two harvested OpenAPI definitions and the prose in their info.description and tag descriptions. Both APIs share one host, one auth model, one pagination style and one error envelope; they differ in tenancy — Nebula addresses a single account via an `accountid` header, OneView addresses many via an `account_id` path parameter plus an `on-behalf-of` header. base_url: https://api.threatdown.com media_type: application/json authentication: style: OAuth2 client credentials, bearer token token_endpoints: - api: nebula operation: POST /oauth2/token operationId: api.oauth2.token - api: oneview operation: POST /oneview/oauth2/token operationId: api.oneview.oauth2.token token_response: fields: [access_token, token_type, expires_in, scope] layered_authorization: >- Two independent gates. The OAuth2 SCOPE (read / write / execute) is fixed when the application is created in the console Integrate page. On top of it, the USER who created the application must hold the granular permission the operation names in its `user_permissions` requirement (86 distinct permissions on Nebula, 84 on OneView). Either gate failing produces a 403. credential_provisioning: >- Console-only. A Super Admin creates the OAuth2 client in Nebula/OneView → Integrate → Add, selecting read/write/execute. There is no programmatic credential-issuance API, so scope changes require a new client. see_also: - authentication/malwarebytes-authentication.yml - scopes/malwarebytes-scopes.yml tenancy: - api: nebula mechanism: request header header: accountid format: UUID (pattern-validated) required_on: 433 of 440 operations - api: oneview mechanism: path parameter and/or header path_param: account_id used_on: 141 operations delegation_header: on-behalf-of delegation_used_on: 7 operations note: >- OneView also exposes parent_account_id for MSP hierarchy traversal. The account identifier is the tenant boundary; a resource outside the addressed account reads as 404, not 403. headers: request: - name: authorization required: true note: >- Declared as an ordinary required string parameter on 439/440 Nebula and 400/401 OneView operations, in ADDITION to being expressed by the securityScheme. Generated clients commonly emit it twice. Value is the bearer token. - name: accountid required: true scope: nebula - name: on-behalf-of required: false scope: oneview response: - name: X-MWB-Signature direction: outbound to your webhook endpoint note: HMAC-SHA256 of the payload keyed by your subscription secret_token. documented_response_headers: [] idempotency: supported: false request_header: null note: >- No Idempotency-Key (or equivalent) header exists on any of the 841 operations, and no idempotency guarantee is documented for any write. Job issuance — POST /nebula/v1/jobs and POST /nebula/v1/jobs/bulk, which trigger real endpoint actions such as Scan, Isolate, Remediate and Reboot — is NOT safely retryable: a retried request after an ambiguous timeout may issue a second job. The only mitigation the platform offers is read-back, using correlation_id to search parent jobs before reissuing. consumer_side_guidance: >- The Webhooks documentation does advise "We recommend you design idempotent event processing because you might receive the same event more than once" — that is guidance for YOUR webhook handler, not a request-idempotency feature of the API. No Idempotency pointer is wired into apis.yml, and none should be until ThreatDown ships an idempotency key. correlation: field: correlation_id purpose: >- Groups children jobs under a parent job. A grouping/tracing key for bulk job fan-out, not a client-supplied deduplication key. pagination: style: opaque cursor request_params: - name: next_cursor in: query description: opaque cursor returned by the previous page - name: page_size in: query response_fields: - next_cursor - total_count required_in_response: [next_cursor, total_count] termination: iterate until next_cursor is absent or empty legacy: >- A small number of OneView operations still take `per_page` instead of `page_size` — the pagination contract is not perfectly uniform across the surface. note: >- Search is expressed as POST with a body rather than GET with a query string on the large collections (POST /nebula/v1/endpoints "Search endpoints", POST /nebula/v1/detections "Search detections"), so list operations are non-cacheable and non-idempotent by HTTP semantics even though they only read. filtering_and_sorting: params: [since, until, search_string, sort_by, sort_order, sort_field, sort_direction, type, category, risk] note: >- Sort parameter naming diverges between the two APIs (sort_by/sort_order on Nebula, sort_field/sort_direction on OneView) for the same concept. grouping: >- A parallel "search-groupby" operation exists on the major collections (endpoints, detections, jobs, cve) returning aggregations alongside results. field_expansion: params: [populate, populate_users, populate_risk] style: boolean/named expansion flags, not a sparse-fieldset or GraphQL-style selection bulk_and_async: bulk_operations: >- Bulk variants exist for the heavy paths — DELETE /nebula/v1/endpoints, POST /nebula/v1/jobs/bulk, DELETE /nebula/v1/jobs/bulk, POST /nebula/v1/cve/bulk, DELETE /nebula/v1/reports. async_exports: >- Export operations come in synchronous and asynchronous pairs (e.g. POST /nebula/v1/detections/export and POST /nebula/v1/detections/export/async). Async variants return 202 and complete out of band — subscribe to webhooks rather than poll. status_codes: [200, 201, 202] versioning: style: path prefix current: /nebula/v1, /oneview/v1 spec_version: 1.0.0 (static; does not move with releases) see_also: lifecycle/malwarebytes-lifecycle.yml errors: envelope: '{statusCode, error, message}' rfc9457: false see_also: errors/malwarebytes-problem-types.yml rate_limiting: default: 360 requests per minute per application algorithm: leaky bucket exceeded: 429 headers_documented: false see_also: rate-limits/malwarebytes-rate-limits.yml events: mechanism: webhooks subscription: API-only (POST /nebula/v1/webhooks/subscriptions); no console UI verification: HMAC-SHA256 in X-MWB-Signature see_also: asyncapi/malwarebytes-threatdown-webhooks.yml cors: enabled: true policy: wildcard same-origin on all responses quoted: >- "All responses have a wildcard same-origin which makes them completely public and accessible to everyone, including any code on any site." caching: conditional_requests: false etag: false last_modified: false note: No ETag / If-None-Match / Cache-Control semantics documented or declared. gaps: - No request idempotency key on destructive or job-issuing writes. - No request-id / trace-id returned to the caller for support correlation. - No rate-limit or deprecation response headers. - Inconsistent sort and page-size parameter naming between the two APIs. - Read operations modelled as POST, forfeiting HTTP caching and safe-retry semantics.