{
  "openapi": "3.1.1",
  "info": {
    "title": "AegisWhite Partner API",
    "version": "v1",
    "summary": "Provision monitoring, read security findings and sign customers in — the same API the WHMCS module runs on.",
    "description": "The partner API behind the AegisWhite white-label security-monitoring platform. Everything the WHMCS module does goes through these endpoints; call them directly to integrate a different billing system or your own tooling.\n\n**Base URL.** All endpoints are relative to your portal domain — the same domain your customers use, for example `https://yourbrand.portal.aegiswhite.com` or your custom portal domain. Your API key is bound to that domain: calls to any other portal domain are rejected with `403 TENANT_MISMATCH`.\n\n**Authentication.** Send your partner API key as a bearer token (`Authorization: Bearer <key>`). Keys are generated in the portal under Settings → WHMCS Integration, shown once, and act as a root credential for your account — keep them server-side.\n\n**Response envelopes.** Provisioning, SSO, alerts, domain monitoring and sandbox endpoints reply with `{\"success\": true, \"data\": …, \"meta\": …}`. The Allowed Ports and Endpoints CRUD resources reply with Laravel's resource envelope `{\"data\": …, \"links\": …, \"meta\": …}` (no `success` key). Validation, authentication and throttle failures use `{\"status\": \"error\", \"message\": …, \"errors\": …}`. The per-operation examples below show the exact shape each endpoint returns.\n\n**Customer scoping.** Read endpoints accept (and client-area integrations must send) `whmcs_customer_id` so results are scoped to one of your customers.\n\n**Sandbox.** With sandbox mode enabled, send `X-Aegis-Sandbox: true` to simulate provisioning and scans without touching real infrastructure.\n\nStart with the [API overview guide](https://aegiswhite.com/docs/api/) for auth setup, rate limits and versioning.\n\nCompiled from the AegisWhite platform, build `50d0af4`, WHMCS module v1.8.1.",
    "contact": {
      "name": "AegisWhite Partner Support",
      "url": "https://aegiswhite.com/contact/"
    }
  },
  "externalDocs": {
    "url": "https://aegiswhite.com/docs/api/",
    "description": "API overview guide"
  },
  "x-aegis-generated": {
    "aegisCommit": "50d0af4",
    "moduleVersion": "1.8.1"
  },
  "servers": [
    {
      "url": "https://{tenantDomain}",
      "description": "Your portal domain — the same domain your customers use.",
      "variables": {
        "tenantDomain": {
          "default": "portal.example.com",
          "description": "Your AegisWhite portal domain (for example yourbrand.portal.aegiswhite.com) or your custom white-label domain. Your API key is bound to it: calls to any other domain are rejected with 403 TENANT_MISMATCH."
        }
      }
    }
  ],
  "security": [
    {
      "partnerApiKey": []
    }
  ],
  "tags": [
    {
      "name": "Provisioning",
      "description": "Create, suspend, resume and terminate monitored services for your customers. These are the endpoints the WHMCS module calls from its provisioning hooks. Call them directly if you integrate with a different billing system. All provisioning actions are idempotent: repeating a call for a service that is already in the target state succeeds with a `note` in the response.\n\n- Every endpoint in this group is also available under the `/api/v1/provisioning/*` prefix (for example `/api/v1/provisioning/provision`). The two namespaces are identical. Pick one and stay consistent.\n\n- Endpoint actions (`suspend`, `resume`, `terminate`) address a monitored server by `endpoint_id`; subscription and domain actions address the WHMCS service by `client_id` + `service_id` (+ `product_slug`).",
      "x-aegis-slug": "provisioning"
    },
    {
      "name": "Single Sign-On",
      "description": "Mint short-lived sign-in tokens so your customers can jump from your systems into their security dashboard without a second login. SSO is a two-step flow: your server calls `POST /api/v1/sso/token` to obtain a token, then redirects the customer's browser to `https://{your-portal-domain}/sso?token={token}`. The portal validates the token and starts the customer's session.\n\n- Tokens are single-purpose and short-lived. Mint one per click-through, never cache or reuse them.",
      "x-aegis-slug": "sso"
    },
    {
      "name": "Health",
      "description": "Unauthenticated service health check.",
      "x-aegis-slug": "health"
    },
    {
      "name": "Alerts",
      "description": "Read and resolve port-exposure alerts (\"security issues\"). These endpoints power the client-area alert pages: pass `whmcs_customer_id` to scope results to one customer. An alert is raised for every open port that is not covered by an Allowed Ports rule, and auto-resolves when the port closes.\n\n- Error responses in this group carry the HTTP status as `error.code` (a number) and a human-readable `error.message`, rather than the string codes used by the provisioning group.",
      "x-aegis-slug": "alerts"
    },
    {
      "name": "Allowed Ports",
      "description": "Manage Allowed Ports rules: the ports a customer expects to be open. A rule can target a single endpoint or apply globally, and covers a port range and protocol. Creating a rule that matches an open alert resolves that alert automatically. (For historical reasons the resource path is `/whitelists`.)",
      "x-aegis-slug": "allowed-ports"
    },
    {
      "name": "Endpoints",
      "description": "Manage monitored endpoints (servers identified by IP address or hostname). Endpoints created here behave exactly like endpoints provisioned through WHMCS; pass `whmcs_customer_id` to scope reads and writes to one customer.",
      "x-aegis-slug": "endpoints"
    },
    {
      "name": "Domain Monitoring",
      "description": "Read leaked-credential and dark-web findings for monitored domains, and work domain alerts (acknowledge / resolve). All reads require `whmcs_customer_id` and return only that customer's domains.",
      "x-aegis-slug": "domain-monitoring"
    },
    {
      "name": "Firewall Ingest",
      "description": "Submit raw firewall rule dumps (iptables, nftables, ufw or firewalld output) for the firewall connectivity map. This is the endpoint the downloadable collector script posts to. Call it directly from your own tooling if you prefer.",
      "x-aegis-slug": "firewall-ingest"
    },
    {
      "name": "Sandbox",
      "description": "Test your integration without real scans. With sandbox mode enabled, provisioning calls sent with the `X-Aegis-Sandbox: true` header are simulated, and the scenario endpoints let you choose which mock scan results (clean, warnings or critical) your test customers see.\n\n- Sandbox endpoints are not rate limited.\n\n- The WHMCS module drives this automatically when its Sandbox Mode setting is on. These endpoints are for custom integrations and manual testing.",
      "x-aegis-slug": "sandbox"
    }
  ],
  "paths": {
    "/api/v1/whmcs/provision": {
      "post": {
        "operationId": "post-whmcs-provision",
        "tags": [
          "Provisioning"
        ],
        "summary": "Provision endpoint monitoring",
        "description": "Start monitoring a server for a customer. Creates the monitored endpoint (and the customer's account on first use) and schedules the first scan. Idempotent: provisioning the same IP again for the same customer returns the existing endpoint with a `note`.\n\n- When the IP is already monitored for this customer, the call returns `200` with the existing `endpoint_id` and a `note` field instead of creating a duplicate.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "client_id"
                ],
                "properties": {
                  "client_id": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "WHMCS client ID of the customer."
                  },
                  "ip_address": {
                    "type": "string",
                    "description": "IPv4 or IPv6 address of the server to monitor. IPv4 or IPv6 address."
                  },
                  "service_id": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "WHMCS service ID linking this endpoint to a product instance."
                  },
                  "hostname": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Display hostname shown alongside the IP."
                  },
                  "client_name": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Customer name, used to create their account on first provision."
                  },
                  "client_email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 255,
                    "description": "Customer email, used to create their account on first provision."
                  },
                  "product_slug": {
                    "type": "string",
                    "maxLength": 50,
                    "description": "Platform product to bill this endpoint against."
                  },
                  "plan_slug": {
                    "type": "string",
                    "maxLength": 50,
                    "description": "Plan tier for the product."
                  },
                  "mrr": {
                    "type": "number",
                    "minimum": 0.01,
                    "maximum": 9999.99,
                    "description": "Monthly recurring revenue you charge for this service, for revenue reporting."
                  }
                }
              },
              "example": {
                "client_id": 1042,
                "ip_address": "203.0.113.10",
                "service_id": 2251,
                "hostname": "web01.example.com",
                "client_name": "Jane Doe",
                "client_email": "jane@example.com",
                "product_slug": "port_monitoring",
                "plan_slug": "starter",
                "mrr": 19
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                    "status": "active"
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "- `TENANT_NOT_FOUND` — The API key did not resolve to an account."
          },
          "403": {
            "description": "- `ENDPOINT_LIMIT_EXCEEDED` — The customer's plan does not allow another endpoint. Upgrade the plan or terminate an endpoint first."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/whmcs/suspend": {
      "post": {
        "operationId": "post-whmcs-suspend",
        "tags": [
          "Provisioning"
        ],
        "summary": "Suspend endpoint monitoring",
        "description": "Pause monitoring for an endpoint (for example on an unpaid invoice). Scans stop; the endpoint and its history are kept.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "endpoint_id"
                ],
                "properties": {
                  "endpoint_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The endpoint UUID returned by the provision call."
                  }
                }
              },
              "example": {
                "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                    "status": "paused"
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "- `ENDPOINT_NOT_FOUND` — No endpoint with that ID belongs to your account."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/whmcs/resume": {
      "post": {
        "operationId": "post-whmcs-resume",
        "tags": [
          "Provisioning"
        ],
        "summary": "Resume endpoint monitoring",
        "description": "Resume monitoring for a suspended endpoint. Scanning picks up on the normal schedule.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "endpoint_id"
                ],
                "properties": {
                  "endpoint_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The endpoint UUID returned by the provision call."
                  }
                }
              },
              "example": {
                "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                    "status": "active"
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "- `ENDPOINT_NOT_FOUND` — No endpoint with that ID belongs to your account."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/whmcs/terminate": {
      "post": {
        "operationId": "post-whmcs-terminate",
        "tags": [
          "Provisioning"
        ],
        "summary": "Terminate endpoint monitoring",
        "description": "Stop monitoring an endpoint permanently (soft delete). Use when the customer's service is cancelled.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "endpoint_id"
                ],
                "properties": {
                  "endpoint_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The endpoint UUID returned by the provision call."
                  }
                }
              },
              "example": {
                "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                    "status": "terminated"
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "- `ENDPOINT_NOT_FOUND` — No endpoint with that ID belongs to your account."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/whmcs/scan-endpoint": {
      "post": {
        "operationId": "post-whmcs-scan-endpoint",
        "tags": [
          "Provisioning"
        ],
        "summary": "Trigger a manual scan",
        "description": "Queue an on-demand scan for one of the customer's endpoints. Scans are asynchronous. The response confirms the scan was queued, and results appear on the endpoint once it completes. Manual scans are rate limited per endpoint with a cooldown window.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "client_id",
                  "endpoint_id"
                ],
                "properties": {
                  "client_id": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "WHMCS client ID of the customer who owns the endpoint."
                  },
                  "endpoint_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "UUID of the endpoint to scan."
                  }
                }
              },
              "example": {
                "client_id": 1042,
                "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "scan_id": "3c8d0f6b-2a1e-4c5d-9e8f-7a6b5c4d3e2f",
                    "status": "pending",
                    "cooldown_seconds": 3600
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "- `ENDPOINT_NOT_FOUND` — No endpoint with that ID belongs to the customer."
          },
          "409": {
            "description": "- `ENDPOINT_NOT_ACTIVE` — The endpoint is paused or terminated. Resume it before scanning."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "description": "- `RATE_LIMITED` — The endpoint is still in its scan cooldown. `details.retry_after_seconds` says when to retry."
          },
          "500": {
            "description": "- `SCAN_DISPATCH_FAILED` — The scan could not be queued. Try again."
          },
          "503": {
            "description": "- `SCANNING_UNAVAILABLE` — The scanning service is temporarily unavailable."
          }
        }
      }
    },
    "/api/v1/whmcs/provision-domain": {
      "post": {
        "operationId": "post-whmcs-provision-domain",
        "tags": [
          "Provisioning"
        ],
        "summary": "Provision domain monitoring",
        "description": "Start watching a domain for leaked credentials and dark-web mentions. The domain is normalized (URLs are reduced to their hostname) before validation. Idempotent: an already-monitored domain returns its existing record with a `note`, and a previously terminated domain is restored.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "client_id",
                  "domain",
                  "service_id"
                ],
                "properties": {
                  "client_id": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "WHMCS client ID of the customer."
                  },
                  "domain": {
                    "type": "string",
                    "maxLength": 253,
                    "description": "Domain to monitor. Full URLs are accepted and reduced to the hostname. Must be a valid domain name."
                  },
                  "service_id": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "WHMCS service ID linking this domain to a product instance."
                  },
                  "client_name": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Customer name, used to create their account on first provision."
                  },
                  "client_email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 255,
                    "description": "Customer email, used to create their account on first provision."
                  }
                }
              },
              "example": {
                "client_id": 1042,
                "domain": "example.com",
                "service_id": 2251,
                "client_name": "Jane Doe",
                "client_email": "jane@example.com"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "monitored_domain_id": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b",
                    "domain": "example.com",
                    "status": "active"
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "- `DOMAIN_LIMIT_EXCEEDED` — The customer has used all their domain slots. Increase the subscription quantity first."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/whmcs/suspend-domain": {
      "post": {
        "operationId": "post-whmcs-suspend-domain",
        "tags": [
          "Provisioning"
        ],
        "summary": "Suspend domain monitoring",
        "description": "Pause monitoring for a domain. Leak scanning stops; findings and history are kept.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "monitored_domain_id"
                ],
                "properties": {
                  "monitored_domain_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "UUID returned by the provision-domain call."
                  }
                }
              },
              "example": {
                "monitored_domain_id": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "monitored_domain_id": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b",
                    "status": "paused"
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "- `DOMAIN_NOT_FOUND` — No monitored domain with that ID belongs to your account."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/whmcs/resume-domain": {
      "post": {
        "operationId": "post-whmcs-resume-domain",
        "tags": [
          "Provisioning"
        ],
        "summary": "Resume domain monitoring",
        "description": "Resume monitoring for a suspended domain.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "monitored_domain_id"
                ],
                "properties": {
                  "monitored_domain_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "UUID returned by the provision-domain call."
                  }
                }
              },
              "example": {
                "monitored_domain_id": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "monitored_domain_id": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b",
                    "status": "active"
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "- `DOMAIN_NOT_FOUND` — No monitored domain with that ID belongs to your account."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/whmcs/terminate-domain": {
      "post": {
        "operationId": "post-whmcs-terminate-domain",
        "tags": [
          "Provisioning"
        ],
        "summary": "Terminate domain monitoring",
        "description": "Stop monitoring a domain permanently. Use when the customer's domain-monitoring service is cancelled.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "monitored_domain_id"
                ],
                "properties": {
                  "monitored_domain_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "UUID returned by the provision-domain call."
                  }
                }
              },
              "example": {
                "monitored_domain_id": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "monitored_domain_id": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b",
                    "status": "terminated"
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "- `DOMAIN_NOT_FOUND` — No monitored domain with that ID belongs to your account."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/whmcs/update-domain": {
      "post": {
        "operationId": "post-whmcs-update-domain",
        "tags": [
          "Provisioning"
        ],
        "summary": "Change a monitored domain",
        "description": "Point an existing domain-monitoring service at a different domain (for example after a customer rebrand). Scan history for the old domain stays attached to the record; scanning continues against the new domain.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "monitored_domain_id",
                  "domain"
                ],
                "properties": {
                  "monitored_domain_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "UUID returned by the provision-domain call."
                  },
                  "domain": {
                    "type": "string",
                    "maxLength": 253,
                    "description": "The new domain to monitor. Must be a valid domain name."
                  }
                }
              },
              "example": {
                "monitored_domain_id": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b",
                "domain": "example.net"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "monitored_domain_id": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b",
                    "domain": "example.net",
                    "status": "active"
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "- `DOMAIN_NOT_FOUND` — No monitored domain with that ID belongs to your account."
          },
          "409": {
            "description": "- `DOMAIN_ALREADY_EXISTS` — The customer already monitors the new domain."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/whmcs/provision-subscription": {
      "post": {
        "operationId": "post-whmcs-provision-subscription",
        "tags": [
          "Provisioning"
        ],
        "summary": "Provision a subscription",
        "description": "Create (or update) a standalone product subscription for a customer, used for monitoring products that are not tied to a single server, such as domain-monitoring slots. Per-plan products take `plan_slug`; per-unit products take `quantity`.\n\n- Returns `201` when the subscription is created and `200` when an existing subscription is updated.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "client_id",
                  "service_id",
                  "product_slug"
                ],
                "properties": {
                  "client_id": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "WHMCS client ID of the customer."
                  },
                  "service_id": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "WHMCS service ID for this product instance."
                  },
                  "product_slug": {
                    "type": "string",
                    "maxLength": 50,
                    "description": "Platform product to subscribe the customer to."
                  },
                  "plan_slug": {
                    "type": "string",
                    "maxLength": 50,
                    "description": "Plan tier (for per-plan products such as `port_monitoring`)."
                  },
                  "quantity": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 10000,
                    "description": "Unit count (for per-unit products such as `domain_monitoring` slots)."
                  },
                  "mrr": {
                    "type": "number",
                    "minimum": 0.01,
                    "maximum": 9999.99,
                    "description": "Monthly recurring revenue you charge, for revenue reporting."
                  },
                  "client_name": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Customer name, used to create their account on first provision."
                  },
                  "client_email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 255,
                    "description": "Customer email, used to create their account on first provision."
                  }
                }
              },
              "example": {
                "client_id": 1042,
                "service_id": 2260,
                "product_slug": "domain_monitoring",
                "plan_slug": "professional",
                "quantity": 5,
                "mrr": 25,
                "client_name": "Jane Doe",
                "client_email": "jane@example.com"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "subscription_id": "7b6a5c4d-3e2f-4a1b-8c9d-0e1f2a3b4c5d",
                    "product_slug": "domain_monitoring",
                    "plan_slug": null,
                    "quantity": 5,
                    "status": "active"
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "- `PRODUCT_NOT_FOUND` — Unknown `product_slug`."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/whmcs/suspend-subscription": {
      "post": {
        "operationId": "post-whmcs-suspend-subscription",
        "tags": [
          "Provisioning"
        ],
        "summary": "Suspend a subscription",
        "description": "Pause a customer's product subscription. Endpoints and domains under the subscription are paused with it.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "client_id",
                  "service_id",
                  "product_slug"
                ],
                "properties": {
                  "client_id": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "WHMCS client ID of the customer."
                  },
                  "service_id": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "WHMCS service ID for this product instance."
                  },
                  "product_slug": {
                    "type": "string",
                    "maxLength": 50,
                    "description": "Product of the subscription to suspend."
                  }
                }
              },
              "example": {
                "client_id": 1042,
                "service_id": 2260,
                "product_slug": "domain_monitoring"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "subscription_id": "7b6a5c4d-3e2f-4a1b-8c9d-0e1f2a3b4c5d",
                    "status": "suspended"
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "- `PRODUCT_NOT_FOUND` — Unknown `product_slug`.\n- `WHMCS_CLIENT_NOT_FOUND` — The customer has no records for this service.\n- `SUBSCRIPTION_NOT_FOUND` — The customer has no subscription for this product and service."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/whmcs/resume-subscription": {
      "post": {
        "operationId": "post-whmcs-resume-subscription",
        "tags": [
          "Provisioning"
        ],
        "summary": "Resume a subscription",
        "description": "Resume a suspended product subscription. Endpoints and domains under the subscription resume with it.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "client_id",
                  "service_id",
                  "product_slug"
                ],
                "properties": {
                  "client_id": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "WHMCS client ID of the customer."
                  },
                  "service_id": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "WHMCS service ID for this product instance."
                  },
                  "product_slug": {
                    "type": "string",
                    "maxLength": 50,
                    "description": "Product of the subscription to resume."
                  }
                }
              },
              "example": {
                "client_id": 1042,
                "service_id": 2260,
                "product_slug": "domain_monitoring"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "subscription_id": "7b6a5c4d-3e2f-4a1b-8c9d-0e1f2a3b4c5d",
                    "status": "active"
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "- `PRODUCT_NOT_FOUND` — Unknown `product_slug`.\n- `WHMCS_CLIENT_NOT_FOUND` — The customer has no records for this service.\n- `SUBSCRIPTION_NOT_FOUND` — The customer has no subscription for this product and service."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/whmcs/terminate-subscription": {
      "post": {
        "operationId": "post-whmcs-terminate-subscription",
        "tags": [
          "Provisioning"
        ],
        "summary": "Terminate a subscription",
        "description": "End a customer's product subscription permanently. Endpoints and domains under the subscription are terminated with it.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "client_id",
                  "service_id",
                  "product_slug"
                ],
                "properties": {
                  "client_id": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "WHMCS client ID of the customer."
                  },
                  "service_id": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "WHMCS service ID for this product instance."
                  },
                  "product_slug": {
                    "type": "string",
                    "maxLength": 50,
                    "description": "Product of the subscription to terminate."
                  }
                }
              },
              "example": {
                "client_id": 1042,
                "service_id": 2260,
                "product_slug": "domain_monitoring"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "subscription_id": "7b6a5c4d-3e2f-4a1b-8c9d-0e1f2a3b4c5d",
                    "status": "terminated"
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "- `PRODUCT_NOT_FOUND` — Unknown `product_slug`.\n- `WHMCS_CLIENT_NOT_FOUND` — The customer has no records for this service.\n- `SUBSCRIPTION_NOT_FOUND` — The customer has no subscription for this product and service."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/whmcs/change-subscription": {
      "post": {
        "operationId": "post-whmcs-change-subscription",
        "tags": [
          "Provisioning"
        ],
        "summary": "Change a subscription plan or quantity",
        "description": "Upgrade or downgrade a subscription: change the plan tier of a per-plan product or the unit count of a per-unit product. Downgrades are checked against current usage first, so a customer using 8 domain slots cannot drop to 5.\n\n- A no-op change (already on the requested plan and quantity) returns `changed: false` with the current plan.\n- At least one of `new_plan_slug` or `new_quantity` is required.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "client_id",
                  "service_id",
                  "product_slug"
                ],
                "properties": {
                  "client_id": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "WHMCS client ID of the customer."
                  },
                  "service_id": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "WHMCS service ID for this product instance."
                  },
                  "product_slug": {
                    "type": "string",
                    "maxLength": 50,
                    "description": "Product of the subscription to change."
                  },
                  "new_plan_slug": {
                    "type": "string",
                    "maxLength": 50,
                    "description": "Target plan tier for per-plan products."
                  },
                  "new_quantity": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 10000,
                    "description": "Target unit count for per-unit products."
                  },
                  "force": {
                    "type": "boolean",
                    "description": "Reserved for administrative overrides; leave unset in normal integrations."
                  }
                }
              },
              "example": {
                "client_id": 1042,
                "service_id": 2260,
                "product_slug": "port_monitoring",
                "new_plan_slug": "professional",
                "new_quantity": 10
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "changed": true,
                    "subscription_id": "7b6a5c4d-3e2f-4a1b-8c9d-0e1f2a3b4c5d",
                    "plan_slug": "professional",
                    "direction": "upgrade",
                    "previous_plan_slug": "starter",
                    "effective_at": "2026-07-12T09:30:00+00:00"
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "- `PRODUCT_NOT_FOUND` — Unknown `product_slug`.\n- `SUBSCRIPTION_NOT_FOUND` — The customer has no subscription for this product and service."
          },
          "422": {
            "description": "- `DOWNGRADE_USAGE_EXCEEDED` — Current usage exceeds the target plan or quantity. `details` carries `current_usage` and `new_limit`.\n- `SUBSCRIPTION_NOT_CHANGEABLE` — The subscription is in a state that cannot be changed (for example terminated)."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/whmcs/pricing": {
      "post": {
        "operationId": "post-whmcs-pricing",
        "tags": [
          "Provisioning"
        ],
        "summary": "Publish the partner pricing catalogue",
        "description": "Push the partner's own products, prices and cart URLs to the portal. The platform never sets end prices for partner-billed tenants, so the customer-facing pricing page renders exactly this catalogue: one card per plan, with the partner's cart links, plus an optional per-slot domain monitoring offer.\n\n- The payload replaces the stored catalogue wholesale. Products removed in WHMCS disappear from the portal on the next sync.\n- Sending an empty `plans` array with no `domain_slot` clears the catalogue and returns `synced: false`; the portal then falls back to the partner's billing portal or website links.\n- `plan_slug` on each plan is the join key that lets the portal highlight the customer's current plan. Send the same plan slug used with `provision-subscription` and `change-subscription`.\n- A catalogue holds at most 12 plans.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "currency",
                  "plans.*.whmcs_product_id",
                  "plans.*.name",
                  "plans.*.price",
                  "plans.*.billing_cycle",
                  "plans.*.features.*",
                  "plans.*.cart_url"
                ],
                "properties": {
                  "currency": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 3,
                    "pattern": "^[A-Za-z]+$"
                  },
                  "billing_portal_url": {
                    "type": "string",
                    "maxLength": 500,
                    "format": "uri",
                    "description": "Must be a URL using `http` or `https`."
                  },
                  "plans": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 12,
                    "description": "The key must be present, even when empty."
                  },
                  "plans.*.whmcs_product_id": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "plans.*.name": {
                    "type": "string",
                    "maxLength": 60
                  },
                  "plans.*.description": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "plans.*.price": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 99999.99
                  },
                  "plans.*.billing_cycle": {
                    "type": "string",
                    "enum": [
                      "monthly",
                      "quarterly",
                      "semiannually",
                      "annually",
                      "biennially",
                      "triennially",
                      "onetime"
                    ]
                  },
                  "plans.*.features": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 10
                  },
                  "plans.*.features.*": {
                    "type": "string",
                    "maxLength": 80
                  },
                  "plans.*.cart_url": {
                    "type": "string",
                    "maxLength": 500,
                    "format": "uri",
                    "description": "Must be a URL using `http` or `https`."
                  },
                  "plans.*.plan_slug": {
                    "type": "string",
                    "maxLength": 50
                  },
                  "plans.*.endpoints_limit": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 100000
                  },
                  "plans.*.domains_limit": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 100000
                  },
                  "domain_slot": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "domain_slot.price": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 99999.99,
                    "description": "Required together with `domain slot`."
                  },
                  "domain_slot.billing_cycle": {
                    "type": "string",
                    "enum": [
                      "monthly",
                      "quarterly",
                      "semiannually",
                      "annually",
                      "biennially",
                      "triennially",
                      "onetime"
                    ]
                  },
                  "domain_slot.cart_url": {
                    "type": "string",
                    "maxLength": 500,
                    "format": "uri",
                    "description": "Required together with `domain slot`. Must be a URL using `http` or `https`."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "synced": true,
                    "currency": "USD",
                    "plan_count": 2,
                    "has_domain_slot": true,
                    "synced_at": "2026-08-09T12:00:00+00:00"
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-08-09T12:00:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "- `TENANT_NOT_FOUND` — No tenant context found. Ensure the API key is valid."
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "- `PARTNER_NOT_FOUND` — The authenticated tenant is not a hosting partner, so it cannot publish pricing."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "delete-whmcs-pricing",
        "tags": [
          "Provisioning"
        ],
        "summary": "Clear the partner pricing catalogue",
        "description": "Remove the partner's published pricing catalogue. Customer-facing upsell links fall back to the partner's billing portal or website. Idempotent: clearing an already-empty catalogue succeeds.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "synced": false
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-08-09T12:00:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "- `TENANT_NOT_FOUND` — No tenant context found. Ensure the API key is valid."
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "- `PARTNER_NOT_FOUND` — The authenticated tenant is not a hosting partner, so it cannot publish pricing."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/sso/token": {
      "post": {
        "operationId": "post-sso-token",
        "tags": [
          "Single Sign-On"
        ],
        "summary": "Generate an SSO token",
        "description": "Mint a short-lived single-sign-on token for a customer. Redirect the customer's browser to `https://{your-portal-domain}/sso?token={token}` before it expires; the portal validates the token and signs the customer in.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "client_id",
                  "email"
                ],
                "properties": {
                  "client_id": {
                    "type": "integer",
                    "description": "WHMCS client ID of the customer signing in."
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "The customer's email address."
                  },
                  "service_id": {
                    "type": "integer",
                    "description": "Optional WHMCS service ID to land the customer on a specific service."
                  }
                }
              },
              "example": {
                "client_id": 1042,
                "email": "jane@example.com",
                "service_id": 2251
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
                    "expires_in": 300
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "- `TENANT_NOT_FOUND` — The API key did not resolve to an account."
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "- `TOKEN_GENERATION_FAILED` — The token could not be generated. Try again."
          }
        }
      }
    },
    "/api/v1/health": {
      "get": {
        "operationId": "get-health",
        "tags": [
          "Health"
        ],
        "summary": "Health check",
        "description": "Unauthenticated liveness check. Use it to verify connectivity and your base URL before wiring credentials.\n\n**Rate limit:** 100 requests / minute (platform default).",
        "x-rateLimit": "100 requests / minute (platform default)",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "status": "ok",
                  "timestamp": "2026-07-12T09:30:00+00:00",
                  "meta": {
                    "version": "v1"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/alerts": {
      "get": {
        "operationId": "get-alerts",
        "tags": [
          "Alerts"
        ],
        "summary": "List alerts",
        "description": "List port-exposure alerts, newest first. Filter by customer, endpoint, status or severity. Results are windowed with `limit`/`offset`; `has_more` tells you when to page again.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "parameters": [
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": false,
            "description": "Limit results to one customer's endpoints.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by alert status.",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "acknowledged",
                "resolved",
                "false_positive"
              ]
            },
            "example": "open"
          },
          {
            "name": "severity",
            "in": "query",
            "required": false,
            "description": "Filter by severity.",
            "schema": {
              "type": "string",
              "enum": [
                "critical",
                "high",
                "medium",
                "low"
              ]
            },
            "example": "critical"
          },
          {
            "name": "endpoint_id",
            "in": "query",
            "required": false,
            "description": "Limit results to a single endpoint.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size (default 20, max 100).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "example": 20
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Number of results to skip.",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "example": 0
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Sort column.",
            "schema": {
              "type": "string",
              "enum": [
                "created_at",
                "severity",
                "last_detected_at"
              ]
            },
            "example": "created_at"
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "description": "Sort direction.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            },
            "example": "desc"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "alerts": [
                      {
                        "id": "4d3c2b1a-0f9e-4d8c-7b6a-5f4e3d2c1b0a",
                        "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                        "ip_address": "203.0.113.10",
                        "port": 3306,
                        "protocol": "tcp",
                        "service": "mysql",
                        "severity": "critical",
                        "status": "open",
                        "risk_level": "critical",
                        "title": "Database port 3306 (mysql) exposed",
                        "explanation": "MySQL is reachable from the public internet…",
                        "created_at": "2026-07-12T08:15:00+00:00",
                        "first_detected_at": "2026-07-12T08:15:00+00:00",
                        "last_detected_at": "2026-07-12T09:15:00+00:00"
                      }
                    ],
                    "total": 1,
                    "has_more": false
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/alerts/summary": {
      "get": {
        "operationId": "get-alerts-summary",
        "tags": [
          "Alerts"
        ],
        "summary": "Alert counts by severity",
        "description": "Open-alert counts for one customer, broken down by severity: the numbers behind a dashboard's severity tiles.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "parameters": [
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": true,
            "description": "The customer to summarize.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Status to count (defaults to open).",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "acknowledged",
                "resolved",
                "false_positive"
              ]
            },
            "example": "open"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "critical": 1,
                    "high": 2,
                    "medium": 0,
                    "low": 3,
                    "total": 6
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/alerts/endpoint-summary": {
      "get": {
        "operationId": "get-alerts-endpoint-summary",
        "tags": [
          "Alerts"
        ],
        "summary": "Alert counts per endpoint",
        "description": "Open-alert counts for each of a customer's endpoints, plus an overall total, used to render a per-server issue overview.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "parameters": [
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": true,
            "description": "The customer to summarize.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "endpoints": [
                      {
                        "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                        "ip_address": "203.0.113.10",
                        "nickname": "Web server",
                        "total_open": 3,
                        "critical": 1,
                        "high": 1,
                        "medium": 0,
                        "low": 1
                      }
                    ],
                    "totals": {
                      "port_alerts_open": 3
                    }
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/alerts/{id}": {
      "get": {
        "operationId": "get-alerts-id",
        "tags": [
          "Alerts"
        ],
        "summary": "Get an alert",
        "description": "Fetch one alert with its full explanation. Pass `whmcs_customer_id` when acting on a customer's behalf. The call is refused if the alert's endpoint does not belong to that customer.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Alert UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "4d3c2b1a-0f9e-4d8c-7b6a-5f4e3d2c1b0a"
          },
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": false,
            "description": "Customer making the request, for ownership checks.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "alert": {
                      "id": "4d3c2b1a-0f9e-4d8c-7b6a-5f4e3d2c1b0a",
                      "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                      "ip_address": "203.0.113.10",
                      "port": 3306,
                      "protocol": "tcp",
                      "service": "mysql",
                      "severity": "critical",
                      "status": "open",
                      "risk_level": "critical",
                      "title": "Database port 3306 (mysql) exposed",
                      "explanation": "MySQL is reachable from the public internet…",
                      "created_at": "2026-07-12T08:15:00+00:00",
                      "first_detected_at": "2026-07-12T08:15:00+00:00",
                      "last_detected_at": "2026-07-12T09:15:00+00:00"
                    }
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "- `403` — Access denied: the alert's endpoint does not belong to the given customer."
          },
          "404": {
            "description": "- `404` — Alert not found in your account."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/alerts/{id}/remediation": {
      "get": {
        "operationId": "get-alerts-id-remediation",
        "tags": [
          "Alerts"
        ],
        "summary": "Get remediation commands",
        "description": "Ready-to-run commands that close the alerted port, for a chosen firewall platform. `platform_options` lists everything available; omit `platform` to get the default suggestion.\n\n- `warning` is set when closing the port could lock you out (for example SSH). Surface it to the user before they run the commands.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Alert UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "4d3c2b1a-0f9e-4d8c-7b6a-5f4e3d2c1b0a"
          },
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "description": "Firewall platform to generate commands for.",
            "schema": {
              "type": "string",
              "enum": [
                "ufw",
                "iptables",
                "aws_sg",
                "gcp_fw",
                "azure_nsg",
                "generic"
              ]
            },
            "example": "ufw"
          },
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": false,
            "description": "Customer making the request, for ownership checks.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "platform_options": {
                      "ufw": "UFW (Ubuntu/Debian)",
                      "iptables": "iptables",
                      "aws_sg": "AWS Security Group",
                      "gcp_fw": "GCP Firewall",
                      "azure_nsg": "Azure NSG",
                      "generic": "Generic"
                    },
                    "selected_platform": "ufw",
                    "commands": {
                      "deny_incoming_traffic": "sudo ufw deny 3306/tcp",
                      "reload_firewall": "sudo ufw reload"
                    },
                    "warning": null,
                    "docs_url": "https://help.ubuntu.com/community/UFW"
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "- `403` — Access denied: the alert's endpoint does not belong to the given customer."
          },
          "404": {
            "description": "- `404` — Alert not found in your account."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/alerts/{id}/resolve": {
      "post": {
        "operationId": "post-alerts-id-resolve",
        "tags": [
          "Alerts"
        ],
        "summary": "Resolve an alert",
        "description": "Mark an alert as resolved (\"I've fixed this\"). If the port is still open on a later scan, a new alert is raised.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Alert UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "4d3c2b1a-0f9e-4d8c-7b6a-5f4e3d2c1b0a"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "whmcs_customer_id": {
                    "type": "integer",
                    "description": "Customer making the request, for ownership checks."
                  }
                }
              },
              "example": {
                "whmcs_customer_id": 1042
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "alert": {
                      "id": "4d3c2b1a-0f9e-4d8c-7b6a-5f4e3d2c1b0a",
                      "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                      "ip_address": "203.0.113.10",
                      "port": 3306,
                      "protocol": "tcp",
                      "service": "mysql",
                      "severity": "critical",
                      "status": "resolved",
                      "risk_level": "critical",
                      "title": "Database port 3306 (mysql) exposed",
                      "explanation": "MySQL is reachable from the public internet…",
                      "created_at": "2026-07-12T08:15:00+00:00",
                      "first_detected_at": "2026-07-12T08:15:00+00:00",
                      "last_detected_at": "2026-07-12T09:15:00+00:00"
                    },
                    "message": "Alert resolved successfully"
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "- `403` — Access denied: the alert's endpoint does not belong to the given customer."
          },
          "404": {
            "description": "- `404` — Alert not found in your account."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/whitelists/endpoint-summary": {
      "get": {
        "operationId": "get-whitelists-endpoint-summary",
        "tags": [
          "Allowed Ports"
        ],
        "summary": "Rule counts per endpoint",
        "description": "How many Allowed Ports rules each of a customer's endpoints has, plus the count of global rules.\n\n- `endpoints` and `global_count` are duplicated at the top level for backwards compatibility. Read them from `data`.\n\n**Rate limit:** 100 requests / minute (platform default).",
        "x-rateLimit": "100 requests / minute (platform default)",
        "parameters": [
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": true,
            "description": "The customer to summarize.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "endpoints": [
                      {
                        "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                        "ip_address": "203.0.113.10",
                        "nickname": "Web server",
                        "rule_count": 4
                      }
                    ],
                    "global_count": 2
                  },
                  "endpoints": [
                    {
                      "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                      "ip_address": "203.0.113.10",
                      "nickname": "Web server",
                      "rule_count": 4
                    }
                  ],
                  "global_count": 2
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/whitelists": {
      "get": {
        "operationId": "get-whitelists",
        "tags": [
          "Allowed Ports"
        ],
        "summary": "List Allowed Ports rules",
        "description": "List Allowed Ports rules, 25 per page. Filter to one customer, one endpoint, a scope, or active/expired status.\n\n- This group returns standard Laravel resource envelopes (`data` / `links` / `meta`), not the `success` envelope used by the provisioning and alerts groups.\n- Each rule may also carry a nested `endpoint` object (the full endpoint resource) when its endpoint relation is loaded. Endpoint-scoped rules include it on every read here.\n\n**Rate limit:** 100 requests / minute (platform default).",
        "x-rateLimit": "100 requests / minute (platform default)",
        "parameters": [
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": false,
            "description": "Limit results to one customer's rules.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          },
          {
            "name": "endpoint_id",
            "in": "query",
            "required": false,
            "description": "Limit results to rules for one endpoint.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b"
          },
          {
            "name": "scope",
            "in": "query",
            "required": false,
            "description": "Filter by rule scope.",
            "schema": {
              "type": "string",
              "enum": [
                "global",
                "endpoint"
              ]
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by rule status.",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "expired"
              ]
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Page size (default 25).",
            "schema": {
              "type": "integer"
            },
            "example": 25
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number.",
            "schema": {
              "type": "integer"
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "id": "8a7b6c5d-4e3f-4a2b-9c8d-7e6f5a4b3c2d",
                      "tenant_id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
                      "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                      "port": 443,
                      "port_start": 443,
                      "port_end": 443,
                      "range_label": "443",
                      "protocol": "tcp",
                      "service": "https",
                      "justification": "Standard TLS traffic for the customer's web store",
                      "auto_suggested": false,
                      "expires_at": null,
                      "created_by": "3c4d5e6f-7a8b-4c9d-0e1f-2a3b4c5d6e7f",
                      "created_at": "2026-07-01T10:00:00+00:00",
                      "updated_at": "2026-07-01T10:00:00+00:00",
                      "is_active": true,
                      "scope_label": "Endpoint-Specific"
                    }
                  ],
                  "links": {
                    "first": "…",
                    "last": "…",
                    "prev": null,
                    "next": null
                  },
                  "meta": {
                    "current_page": 1,
                    "per_page": 25,
                    "total": 1
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "post-whitelists",
        "tags": [
          "Allowed Ports"
        ],
        "summary": "Create an Allowed Ports rule",
        "description": "Allow a port (or port range) so it stops raising alerts. A rule covers one endpoint (`scope: endpoint`) or every endpoint (`scope: global`), and creating it resolves any open alert it matches. Overlapping rules are rejected.\n\n- `scope` defaults from your input: sending `endpoint_id` implies `scope: endpoint`, otherwise `global`.\n- Omitting `port_end` creates a single-port rule.\n\n**Rate limit:** 100 requests / minute (platform default).",
        "x-rateLimit": "100 requests / minute (platform default)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "port_start",
                  "protocol",
                  "justification",
                  "scope"
                ],
                "properties": {
                  "port_start": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 65535,
                    "description": "First port of the range."
                  },
                  "port_end": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 65535,
                    "description": "Last port of the range; omit for a single port. Must be greater than or equal to `port_start`."
                  },
                  "protocol": {
                    "type": "string",
                    "enum": [
                      "tcp",
                      "udp",
                      "*"
                    ],
                    "description": "Protocol the rule covers; `*` means both TCP and UDP."
                  },
                  "service": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "Label for what runs on the port."
                  },
                  "justification": {
                    "type": "string",
                    "minLength": 10,
                    "maxLength": 500,
                    "description": "Why the port should stay open (10–500 characters), shown in audits."
                  },
                  "scope": {
                    "type": "string",
                    "enum": [
                      "global",
                      "endpoint"
                    ],
                    "description": "Whether the rule covers one endpoint or all of them."
                  },
                  "endpoint_id": {
                    "type": "string",
                    "description": "The endpoint the rule covers; required when scope is `endpoint`. Required when `scope` is `endpoint`. Must reference a record that belongs to your account."
                  },
                  "expires_at": {
                    "type": "string",
                    "description": "Optional expiry; the rule stops matching after this date. ISO 8601 date or date-time. Must be a future date."
                  },
                  "whmcs_customer_id": {
                    "type": "integer",
                    "description": "Customer creating the rule, for ownership checks."
                  }
                }
              },
              "example": {
                "port_start": 443,
                "port_end": 443,
                "protocol": "tcp",
                "service": "https",
                "justification": "Standard TLS traffic for the customer's web store",
                "scope": "endpoint",
                "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                "expires_at": "2026-12-31",
                "whmcs_customer_id": 1042
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": "8a7b6c5d-4e3f-4a2b-9c8d-7e6f5a4b3c2d",
                    "tenant_id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
                    "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                    "port": 443,
                    "port_start": 443,
                    "port_end": 443,
                    "range_label": "443",
                    "protocol": "tcp",
                    "service": "https",
                    "justification": "Standard TLS traffic for the customer's web store",
                    "auto_suggested": false,
                    "expires_at": null,
                    "created_by": "3c4d5e6f-7a8b-4c9d-0e1f-2a3b4c5d6e7f",
                    "created_at": "2026-07-12T09:30:00+00:00",
                    "updated_at": "2026-07-12T09:30:00+00:00",
                    "is_active": true,
                    "scope_label": "Endpoint-Specific"
                  },
                  "meta": {
                    "timestamp": "2026-07-12T09:30:00+00:00",
                    "version": "v1"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "- `403` — The endpoint does not belong to the given `whmcs_customer_id`."
          },
          "422": {
            "description": "- `422` — Validation failed, including when the port range overlaps an existing rule for the same scope."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/whitelists/{whitelist}": {
      "get": {
        "operationId": "get-whitelists-whitelist",
        "tags": [
          "Allowed Ports"
        ],
        "summary": "Get an Allowed Ports rule",
        "description": "Fetch one rule, including its endpoint when endpoint-scoped.\n\n**Rate limit:** 100 requests / minute (platform default).",
        "x-rateLimit": "100 requests / minute (platform default)",
        "parameters": [
          {
            "name": "whitelist",
            "in": "path",
            "required": true,
            "description": "Allowed Ports rule UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "8a7b6c5d-4e3f-4a2b-9c8d-7e6f5a4b3c2d"
          },
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": false,
            "description": "Customer making the request, for ownership checks.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": "8a7b6c5d-4e3f-4a2b-9c8d-7e6f5a4b3c2d",
                    "tenant_id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
                    "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                    "port": 443,
                    "port_start": 443,
                    "port_end": 443,
                    "range_label": "443",
                    "protocol": "tcp",
                    "service": "https",
                    "justification": "Standard TLS traffic for the customer's web store",
                    "auto_suggested": false,
                    "expires_at": null,
                    "created_by": "3c4d5e6f-7a8b-4c9d-0e1f-2a3b4c5d6e7f",
                    "created_at": "2026-07-01T10:00:00+00:00",
                    "updated_at": "2026-07-01T10:00:00+00:00",
                    "is_active": true,
                    "scope_label": "Endpoint-Specific"
                  },
                  "meta": {
                    "timestamp": "2026-07-12T09:30:00+00:00",
                    "version": "v1"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "description": "- `404` — No rule with that ID in your account (or it does not belong to the given customer)."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "put": {
        "operationId": "put-whitelists-whitelist",
        "tags": [
          "Allowed Ports"
        ],
        "summary": "Update an Allowed Ports rule",
        "description": "Change a rule's range, protocol, service label, justification, scope or expiry. The same overlap validation as creation applies.\n\n- `PATCH` is also accepted on this path with the same body and behaves identically.\n\n**Rate limit:** 100 requests / minute (platform default).",
        "x-rateLimit": "100 requests / minute (platform default)",
        "parameters": [
          {
            "name": "whitelist",
            "in": "path",
            "required": true,
            "description": "Allowed Ports rule UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "8a7b6c5d-4e3f-4a2b-9c8d-7e6f5a4b3c2d"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "port_start",
                  "protocol",
                  "justification",
                  "scope"
                ],
                "properties": {
                  "port_start": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 65535,
                    "description": "First port of the range."
                  },
                  "port_end": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 65535,
                    "description": "Last port of the range; omit for a single port. Must be greater than or equal to `port_start`."
                  },
                  "protocol": {
                    "type": "string",
                    "enum": [
                      "tcp",
                      "udp",
                      "*"
                    ],
                    "description": "Protocol the rule covers; `*` means both TCP and UDP."
                  },
                  "service": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "Label for what runs on the port."
                  },
                  "justification": {
                    "type": "string",
                    "minLength": 10,
                    "maxLength": 500,
                    "description": "Why the port should stay open (10–500 characters)."
                  },
                  "scope": {
                    "type": "string",
                    "enum": [
                      "global",
                      "endpoint"
                    ],
                    "description": "Whether the rule covers one endpoint or all of them."
                  },
                  "endpoint_id": {
                    "type": "string",
                    "description": "The endpoint the rule covers; required when scope is `endpoint`. Required when `scope` is `endpoint`. Must reference a record that belongs to your account."
                  },
                  "expires_at": {
                    "type": "string",
                    "description": "Optional expiry; the rule stops matching after this date. ISO 8601 date or date-time. Must be a future date."
                  },
                  "whmcs_customer_id": {
                    "type": "integer",
                    "description": "Customer making the request, for ownership checks."
                  }
                }
              },
              "example": {
                "port_start": 443,
                "port_end": 444,
                "protocol": "tcp",
                "service": "https",
                "justification": "TLS plus the staging listener on 444",
                "scope": "endpoint",
                "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                "expires_at": "2026-12-31",
                "whmcs_customer_id": 1042
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": "8a7b6c5d-4e3f-4a2b-9c8d-7e6f5a4b3c2d",
                    "tenant_id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
                    "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                    "port": 443,
                    "port_start": 443,
                    "port_end": 444,
                    "range_label": "443-444",
                    "protocol": "tcp",
                    "service": "https",
                    "justification": "TLS plus the staging listener on 444",
                    "auto_suggested": false,
                    "expires_at": null,
                    "created_by": "3c4d5e6f-7a8b-4c9d-0e1f-2a3b4c5d6e7f",
                    "created_at": "2026-07-01T10:00:00+00:00",
                    "updated_at": "2026-07-12T09:30:00+00:00",
                    "is_active": true,
                    "scope_label": "Endpoint-Specific"
                  },
                  "meta": {
                    "timestamp": "2026-07-12T09:30:00+00:00",
                    "version": "v1"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "description": "- `404` — No rule with that ID in your account (or it does not belong to the given customer)."
          },
          "422": {
            "description": "- `422` — Validation failed, including range overlap with another rule."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "delete-whitelists-whitelist",
        "tags": [
          "Allowed Ports"
        ],
        "summary": "Delete an Allowed Ports rule",
        "description": "Remove a rule. Ports it covered raise alerts again on the next scan.\n\n- The `204` response has an empty body.\n\n**Rate limit:** 100 requests / minute (platform default).",
        "x-rateLimit": "100 requests / minute (platform default)",
        "parameters": [
          {
            "name": "whitelist",
            "in": "path",
            "required": true,
            "description": "Allowed Ports rule UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "8a7b6c5d-4e3f-4a2b-9c8d-7e6f5a4b3c2d"
          },
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": false,
            "description": "Customer making the request, for ownership checks.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "description": "- `404` — No rule with that ID in your account (or it does not belong to the given customer)."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/endpoints/{endpoint}/detail": {
      "get": {
        "operationId": "get-endpoints-endpoint-detail",
        "tags": [
          "Endpoints"
        ],
        "summary": "Endpoint detail with scan data",
        "description": "Everything the client-area endpoint page shows in one call: the endpoint, its latest scan, up to 10 open alerts, currently allowed ports, and which ports appeared or disappeared between the last two scans.\n\n- `latest_scan` is `null` until the first scan completes; `new_ports`/`closed_ports` are empty until at least two scans exist.\n- `whitelisted_ports` entries are `\"port:protocol\"` strings; rules covering both protocols appear once per protocol.\n- Unlike the other reads in this group, this response has no `meta` envelope.\n\n**Rate limit:** 100 requests / minute (platform default).",
        "x-rateLimit": "100 requests / minute (platform default)",
        "parameters": [
          {
            "name": "endpoint",
            "in": "path",
            "required": true,
            "description": "Monitored endpoint UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b"
          },
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": false,
            "description": "Customer making the request, for ownership checks.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                    "ip_address": "203.0.113.10",
                    "hostname": "web01.example.com",
                    "nickname": "Web server",
                    "platform": null,
                    "status": "active",
                    "nmap_cooldown_seconds": 0,
                    "last_scan_at": "2026-07-12T06:00:00+00:00",
                    "next_scan_at": "2026-07-13T06:00:00+00:00",
                    "created_at": "2026-06-01T12:00:00+00:00",
                    "updated_at": "2026-07-12T06:00:00+00:00",
                    "whmcs_customer_id": 1042
                  },
                  "latest_scan": {
                    "open_ports": [
                      22,
                      80,
                      443
                    ],
                    "status": "completed",
                    "scan_duration_ms": 8425,
                    "scanned_at": "2026-07-12T06:00:00+00:00"
                  },
                  "alerts": [
                    {
                      "id": "4d3c2b1a-0f9e-4d8c-7b6a-5f4e3d2c1b0a",
                      "port": 22,
                      "protocol": "tcp",
                      "service": "ssh",
                      "severity": "high",
                      "risk_level": "high",
                      "status": "open",
                      "title": "SSH port 22 exposed",
                      "first_detected_at": "2026-07-10T06:00:00+00:00",
                      "last_detected_at": "2026-07-12T06:00:00+00:00"
                    }
                  ],
                  "whitelisted_ports": [
                    "80:tcp",
                    "443:tcp"
                  ],
                  "new_ports": [],
                  "closed_ports": [
                    8080
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "description": "- `404` — No endpoint with that ID in your account."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/endpoints": {
      "get": {
        "operationId": "get-endpoints",
        "tags": [
          "Endpoints"
        ],
        "summary": "List endpoints",
        "description": "List monitored endpoints as pages of endpoint resources. `meta` carries the plan's endpoint limit, current usage and per-status counts alongside the pagination fields.\n\n- This group returns standard Laravel resource envelopes (`data` / `links` / `meta`), not the `success` envelope used by the provisioning and alerts groups.\n\n**Rate limit:** 100 requests / minute (platform default).",
        "x-rateLimit": "100 requests / minute (platform default)",
        "parameters": [
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": false,
            "description": "Limit results to one customer's endpoints.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by endpoint status.",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "paused"
              ]
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Page size.",
            "schema": {
              "type": "integer"
            },
            "example": 15
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number.",
            "schema": {
              "type": "integer"
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                      "ip_address": "203.0.113.10",
                      "hostname": "web01.example.com",
                      "nickname": "Web server",
                      "platform": null,
                      "status": "active",
                      "nmap_cooldown_seconds": 0,
                      "last_scan_at": "2026-07-12T06:00:00+00:00",
                      "next_scan_at": "2026-07-13T06:00:00+00:00",
                      "created_at": "2026-06-01T12:00:00+00:00",
                      "updated_at": "2026-07-12T06:00:00+00:00",
                      "whmcs_customer_id": 1042
                    }
                  ],
                  "links": {
                    "first": "…",
                    "last": "…",
                    "prev": null,
                    "next": null
                  },
                  "meta": {
                    "current_page": 1,
                    "per_page": 15,
                    "total": 1,
                    "endpoint_limit": 10,
                    "current_endpoint_count": 1,
                    "status_counts": {
                      "active": 1
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "post-endpoints",
        "tags": [
          "Endpoints"
        ],
        "summary": "Create an endpoint",
        "description": "Add a server to monitoring by IP address or hostname (hostnames are resolved at creation). Deduplicated per customer: re-adding an existing or previously deleted IP returns the existing endpoint with `200` instead of creating a duplicate.\n\n**Rate limit:** 100 requests / minute (platform default).",
        "x-rateLimit": "100 requests / minute (platform default)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "whmcs_customer_id",
                  "ip_address"
                ],
                "properties": {
                  "whmcs_customer_id": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "The customer the endpoint belongs to."
                  },
                  "ip_address": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "IP address or hostname of the server to monitor."
                  },
                  "nickname": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Display name for the endpoint."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "paused"
                    ],
                    "description": "Initial status; defaults to active."
                  },
                  "client_name": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Customer name, used to create their account if it does not exist yet."
                  },
                  "client_email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 255,
                    "description": "Customer email, used to create their account if it does not exist yet."
                  }
                }
              },
              "example": {
                "whmcs_customer_id": 1042,
                "ip_address": "203.0.113.10",
                "nickname": "Web server",
                "status": "active",
                "client_name": "Jane Doe",
                "client_email": "jane@example.com"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                    "ip_address": "203.0.113.10",
                    "hostname": "web01.example.com",
                    "nickname": "Web server",
                    "platform": null,
                    "status": "active",
                    "nmap_cooldown_seconds": 0,
                    "last_scan_at": null,
                    "next_scan_at": "2026-07-13T06:00:00+00:00",
                    "created_at": "2026-07-12T09:30:00+00:00",
                    "updated_at": "2026-07-12T09:30:00+00:00",
                    "whmcs_customer_id": 1042
                  },
                  "meta": {
                    "timestamp": "2026-07-12T09:30:00+00:00",
                    "version": "v1"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "- `ENDPOINT_LIMIT_EXCEEDED` — The customer's plan does not allow another endpoint."
          },
          "422": {
            "description": "- `DNS_RESOLUTION_FAILED` — The hostname could not be resolved to an IP address."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/endpoints/{endpoint}": {
      "get": {
        "operationId": "get-endpoints-endpoint",
        "tags": [
          "Endpoints"
        ],
        "summary": "Get an endpoint",
        "description": "Fetch one endpoint resource.\n\n**Rate limit:** 100 requests / minute (platform default).",
        "x-rateLimit": "100 requests / minute (platform default)",
        "parameters": [
          {
            "name": "endpoint",
            "in": "path",
            "required": true,
            "description": "Monitored endpoint UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b"
          },
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": false,
            "description": "Customer making the request, for ownership checks.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                    "ip_address": "203.0.113.10",
                    "hostname": "web01.example.com",
                    "nickname": "Web server",
                    "platform": null,
                    "status": "active",
                    "nmap_cooldown_seconds": 0,
                    "last_scan_at": "2026-07-12T06:00:00+00:00",
                    "next_scan_at": "2026-07-13T06:00:00+00:00",
                    "created_at": "2026-06-01T12:00:00+00:00",
                    "updated_at": "2026-07-12T06:00:00+00:00",
                    "whmcs_customer_id": 1042
                  },
                  "meta": {
                    "timestamp": "2026-07-12T09:30:00+00:00",
                    "version": "v1"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "description": "- `404` — No endpoint with that ID in your account."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "put": {
        "operationId": "put-endpoints-endpoint",
        "tags": [
          "Endpoints"
        ],
        "summary": "Update an endpoint",
        "description": "Rename an endpoint or switch it between active and paused. Only `nickname` and `status` can be changed. The IP address is fixed at creation.\n\n- `PATCH` is also accepted on this path with the same body and behaves identically.\n\n**Rate limit:** 100 requests / minute (platform default).",
        "x-rateLimit": "100 requests / minute (platform default)",
        "parameters": [
          {
            "name": "endpoint",
            "in": "path",
            "required": true,
            "description": "Monitored endpoint UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "whmcs_customer_id"
                ],
                "properties": {
                  "whmcs_customer_id": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "The customer who owns the endpoint."
                  },
                  "nickname": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "New display name."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "paused"
                    ],
                    "description": "Set to `paused` to stop scanning, `active` to resume."
                  }
                }
              },
              "example": {
                "whmcs_customer_id": 1042,
                "nickname": "Primary web server",
                "status": "paused"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                    "ip_address": "203.0.113.10",
                    "hostname": "web01.example.com",
                    "nickname": "Primary web server",
                    "platform": null,
                    "status": "paused",
                    "nmap_cooldown_seconds": 0,
                    "last_scan_at": "2026-07-12T06:00:00+00:00",
                    "next_scan_at": null,
                    "created_at": "2026-06-01T12:00:00+00:00",
                    "updated_at": "2026-07-12T09:30:00+00:00",
                    "whmcs_customer_id": 1042
                  },
                  "meta": {
                    "timestamp": "2026-07-12T09:30:00+00:00",
                    "version": "v1"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "description": "- `404` — No endpoint with that ID in your account."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "delete-endpoints-endpoint",
        "tags": [
          "Endpoints"
        ],
        "summary": "Delete an endpoint",
        "description": "Stop monitoring an endpoint and remove it from lists (soft delete: history is retained, and re-adding the same IP restores it).\n\n- The `204` response has an empty body.\n\n**Rate limit:** 100 requests / minute (platform default).",
        "x-rateLimit": "100 requests / minute (platform default)",
        "parameters": [
          {
            "name": "endpoint",
            "in": "path",
            "required": true,
            "description": "Monitored endpoint UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b"
          },
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": false,
            "description": "Customer making the request, for ownership checks.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "description": "- `404` — No endpoint with that ID in your account."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/domains": {
      "get": {
        "operationId": "get-domains",
        "tags": [
          "Domain Monitoring"
        ],
        "summary": "List monitored domains",
        "description": "List a customer's monitored domains with their latest leak counts, plus how many domain slots the customer has used. When the customer has no domain-monitoring subscription, the response instead describes the product (`subscribed: false`).\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "parameters": [
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": true,
            "description": "The customer whose domains to list.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "subscribed": true,
                    "domains": [
                      {
                        "id": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b",
                        "domain": "example.com",
                        "status": "active",
                        "scan_status": "completed",
                        "last_scan_at": "2026-07-11T22:00:00+00:00",
                        "employees_compromised": 2,
                        "customers_compromised": 14,
                        "third_parties_compromised": 5,
                        "dark_web_mentions_count": 3,
                        "portal_detail_path": "/domains/5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b"
                      }
                    ],
                    "slots_used": 1,
                    "slots_total": 5,
                    "portal_add_path": "/domains"
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "- `TENANT_NOT_FOUND` — The API key did not resolve to an account."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/domains/{id}/summary": {
      "get": {
        "operationId": "get-domains-id-summary",
        "tags": [
          "Domain Monitoring"
        ],
        "summary": "Domain summary",
        "description": "One domain's monitoring status in a single call: scan state, compromised-credential counts per category, dark-web mention count, password-strength summary and slot usage.\n\n- `password_strength` is `null` until enough leak data exists to summarize.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Monitored domain UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b"
          },
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": true,
            "description": "The customer who owns the domain.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "id": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b",
                    "domain": "example.com",
                    "status": "active",
                    "scan_status": "completed",
                    "last_scan_at": "2026-07-11T22:00:00+00:00",
                    "dark_web_last_scan_at": "2026-07-11T22:05:00+00:00",
                    "employees_compromised": 2,
                    "customers_compromised": 14,
                    "third_parties_compromised": 5,
                    "dark_web_mentions_count": 3,
                    "password_strength": null,
                    "slots_used": 1,
                    "slots_total": 5,
                    "portal_detail_path": "/domains/5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b",
                    "created_at": "2026-06-15T10:00:00+00:00"
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "- `TENANT_NOT_FOUND` — The API key did not resolve to an account."
          },
          "404": {
            "description": "- `DOMAIN_NOT_FOUND` — No monitored domain with that ID belongs to the customer."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/domains/{id}/alerts": {
      "get": {
        "operationId": "get-domains-id-alerts",
        "tags": [
          "Domain Monitoring"
        ],
        "summary": "List domain alerts",
        "description": "Page through a domain's leak alerts. Filter by status, type (credential leak vs dark-web mention), category or severity.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Monitored domain UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b"
          },
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": true,
            "description": "The customer who owns the domain.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by alert status.",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "acknowledged",
                "resolved"
              ]
            },
            "example": "open"
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Filter by finding type.",
            "schema": {
              "type": "string",
              "enum": [
                "credential_leak",
                "dark_web_mention"
              ]
            },
            "example": "credential_leak"
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Filter by who was compromised.",
            "schema": {
              "type": "string",
              "enum": [
                "employees",
                "customers",
                "third_parties"
              ]
            },
            "example": "employees"
          },
          {
            "name": "severity",
            "in": "query",
            "required": false,
            "description": "Filter by severity.",
            "schema": {
              "type": "string",
              "enum": [
                "critical",
                "high",
                "medium"
              ]
            },
            "example": "critical"
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Page size (max 50).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50
            },
            "example": 25
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number.",
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "alerts": [
                      {
                        "id": "6f5e4d3c-2b1a-4f0e-9d8c-7b6a5f4e3d2c",
                        "monitored_domain_id": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b",
                        "domain": "example.com",
                        "type": "credential_leak",
                        "category": "employees",
                        "severity": "critical",
                        "new_count": 2,
                        "previous_count": 0,
                        "status": "open",
                        "details": null,
                        "created_at": "2026-07-11T22:10:00+00:00",
                        "acknowledged_at": null,
                        "resolved_at": null
                      }
                    ],
                    "total": 1,
                    "current_page": 1,
                    "last_page": 1,
                    "has_more": false
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "- `TENANT_NOT_FOUND` — The API key did not resolve to an account."
          },
          "404": {
            "description": "- `DOMAIN_NOT_FOUND` — No monitored domain with that ID belongs to the customer."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/domains/{id}/alerts/{alertId}": {
      "get": {
        "operationId": "get-domains-id-alerts-alertId",
        "tags": [
          "Domain Monitoring"
        ],
        "summary": "Get a domain alert",
        "description": "Fetch one domain alert with a plain-language explanation of what the finding means.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Monitored domain UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b"
          },
          {
            "name": "alertId",
            "in": "path",
            "required": true,
            "description": "Domain alert UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "6f5e4d3c-2b1a-4f0e-9d8c-7b6a5f4e3d2c"
          },
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": true,
            "description": "The customer who owns the domain.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "alert": {
                      "id": "6f5e4d3c-2b1a-4f0e-9d8c-7b6a5f4e3d2c",
                      "monitored_domain_id": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b",
                      "domain": "example.com",
                      "type": "credential_leak",
                      "category": "employees",
                      "severity": "critical",
                      "new_count": 2,
                      "previous_count": 0,
                      "status": "open",
                      "details": null,
                      "explanation": "Two staff accounts on example.com appeared in a new credential dump…",
                      "created_at": "2026-07-11T22:10:00+00:00",
                      "acknowledged_at": null,
                      "resolved_at": null
                    }
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "- `TENANT_NOT_FOUND` — The API key did not resolve to an account."
          },
          "404": {
            "description": "- `DOMAIN_NOT_FOUND` — No monitored domain with that ID belongs to the customer.\n- `ALERT_NOT_FOUND` — No alert with that ID on this domain."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/domains/{id}/dark-web-mentions": {
      "get": {
        "operationId": "get-domains-id-dark-web-mentions",
        "tags": [
          "Domain Monitoring"
        ],
        "summary": "List dark-web mentions",
        "description": "Page through dark-web posts and listings that mention the domain. Filter to only new (unseen) mentions with `is_new`.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Monitored domain UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b"
          },
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": true,
            "description": "The customer who owns the domain.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          },
          {
            "name": "is_new",
            "in": "query",
            "required": false,
            "description": "Only mentions not seen before.",
            "schema": {
              "type": "boolean"
            },
            "example": true
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Page size (max 50).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50
            },
            "example": 25
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number.",
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "mentions": [
                      {
                        "id": "7a6b5c4d-3e2f-4a1b-8c9d-0e1f2a3b4c5e",
                        "title": "Combo list mentioning example.com",
                        "content_preview": "…credentials for example.com users…",
                        "source_name": "forum",
                        "author": "unknown",
                        "published_at": "2026-07-10T00:00:00+00:00",
                        "is_new": true,
                        "created_at": "2026-07-11T22:05:00+00:00"
                      }
                    ],
                    "total": 1,
                    "current_page": 1,
                    "last_page": 1,
                    "has_more": false
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "- `TENANT_NOT_FOUND` — The API key did not resolve to an account."
          },
          "404": {
            "description": "- `DOMAIN_NOT_FOUND` — No monitored domain with that ID belongs to the customer."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/domains/{id}/credential-leaks": {
      "get": {
        "operationId": "get-domains-id-credential-leaks",
        "tags": [
          "Domain Monitoring"
        ],
        "summary": "List credential leaks",
        "description": "Page through the individual leaked-credential records behind a domain's counts, optionally filtered by category. Backed by the upstream breach dataset. Record fields vary by source dump.\n\n- `enabled: false` means the upstream credential dataset is not configured for this platform.\n- If the upstream dataset is temporarily unreachable, the endpoint returns HTTP `503` with `success: true`, empty `items` and an `error` message in `data`.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Monitored domain UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b"
          },
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": true,
            "description": "The customer who owns the domain.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Filter by who was compromised.",
            "schema": {
              "type": "string",
              "enum": [
                "employees",
                "customers",
                "third_parties"
              ]
            },
            "example": "employees"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number (50 records per page).",
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "enabled": true,
                    "items": [],
                    "total": 0,
                    "page": 1,
                    "page_size": 50
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "- `TENANT_NOT_FOUND` — The API key did not resolve to an account."
          },
          "404": {
            "description": "- `DOMAIN_NOT_FOUND` — No monitored domain with that ID belongs to the customer."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/domains/{id}/scan-history": {
      "get": {
        "operationId": "get-domains-id-scan-history",
        "tags": [
          "Domain Monitoring"
        ],
        "summary": "Domain scan history",
        "description": "The domain's last 25 leak scans, newest first, with compromised-credential counts per category at each scan.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Monitored domain UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b"
          },
          {
            "name": "whmcs_customer_id",
            "in": "query",
            "required": true,
            "description": "The customer who owns the domain.",
            "schema": {
              "type": "integer"
            },
            "example": 1042
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "scans": [
                      {
                        "id": "8b7a6c5d-4e3f-4a2b-9c8d-7e6f5a4b3c2e",
                        "status": "completed",
                        "employees_compromised": 2,
                        "customers_compromised": 14,
                        "third_parties_compromised": 5,
                        "created_at": "2026-07-11T22:00:00+00:00"
                      }
                    ]
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "- `TENANT_NOT_FOUND` — The API key did not resolve to an account."
          },
          "404": {
            "description": "- `DOMAIN_NOT_FOUND` — No monitored domain with that ID belongs to the customer."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/domains/{id}/alerts/{alertId}/acknowledge": {
      "post": {
        "operationId": "post-domains-id-alerts-alertId-acknowledge",
        "tags": [
          "Domain Monitoring"
        ],
        "summary": "Acknowledge a domain alert",
        "description": "Mark a domain alert as seen. Acknowledging is required before the alert can be resolved.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Monitored domain UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b"
          },
          {
            "name": "alertId",
            "in": "path",
            "required": true,
            "description": "Domain alert UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "6f5e4d3c-2b1a-4f0e-9d8c-7b6a5f4e3d2c"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "whmcs_customer_id"
                ],
                "properties": {
                  "whmcs_customer_id": {
                    "type": "integer",
                    "description": "The customer acknowledging the alert."
                  }
                }
              },
              "example": {
                "whmcs_customer_id": 1042
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "message": "Alert acknowledged.",
                    "alert": {
                      "id": "6f5e4d3c-2b1a-4f0e-9d8c-7b6a5f4e3d2c",
                      "status": "acknowledged",
                      "acknowledged_at": "2026-07-12T09:30:00+00:00"
                    }
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "- `TENANT_NOT_FOUND` — The API key did not resolve to an account."
          },
          "404": {
            "description": "- `DOMAIN_NOT_FOUND` — No monitored domain with that ID belongs to the customer.\n- `ALERT_NOT_FOUND` — No alert with that ID on this domain."
          },
          "422": {
            "description": "- `ALERT_ALREADY_RESOLVED` — The alert is already resolved."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "- `USER_NOT_FOUND` — The customer has no portal account yet. Provision one first."
          }
        }
      }
    },
    "/api/v1/domains/{id}/alerts/{alertId}/resolve": {
      "post": {
        "operationId": "post-domains-id-alerts-alertId-resolve",
        "tags": [
          "Domain Monitoring"
        ],
        "summary": "Resolve a domain alert",
        "description": "Mark an acknowledged domain alert as resolved, typically after forcing password resets for the affected accounts.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Monitored domain UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b"
          },
          {
            "name": "alertId",
            "in": "path",
            "required": true,
            "description": "Domain alert UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "6f5e4d3c-2b1a-4f0e-9d8c-7b6a5f4e3d2c"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "whmcs_customer_id"
                ],
                "properties": {
                  "whmcs_customer_id": {
                    "type": "integer",
                    "description": "The customer resolving the alert."
                  }
                }
              },
              "example": {
                "whmcs_customer_id": 1042
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "message": "Alert resolved.",
                    "alert": {
                      "id": "6f5e4d3c-2b1a-4f0e-9d8c-7b6a5f4e3d2c",
                      "status": "resolved",
                      "resolved_at": "2026-07-12T09:30:00+00:00"
                    }
                  },
                  "meta": {
                    "version": "v1",
                    "timestamp": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "- `TENANT_NOT_FOUND` — The API key did not resolve to an account."
          },
          "404": {
            "description": "- `DOMAIN_NOT_FOUND` — No monitored domain with that ID belongs to the customer.\n- `ALERT_NOT_FOUND` — No alert with that ID on this domain."
          },
          "422": {
            "description": "- `ALERT_NOT_ACKNOWLEDGED` — The alert must be acknowledged before it can be resolved."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "- `USER_NOT_FOUND` — The customer has no portal account yet. Provision one first."
          }
        }
      }
    },
    "/api/v1/firewall/ingest": {
      "post": {
        "operationId": "post-firewall-ingest",
        "tags": [
          "Firewall Ingest"
        ],
        "summary": "Ingest a firewall snapshot",
        "description": "Submit a raw firewall configuration dump for parsing into the firewall connectivity map. Accepts the output of `iptables-save`, `nft list ruleset`, `ufw status verbose` or `firewall-cmd --list-all`; the format is auto-detected when not supplied. Provide `ips` or `endpoint_id` so the snapshot can be matched to a monitored endpoint. Parsing is asynchronous. `202` confirms the snapshot was accepted.\n\n- The downloadable collector script (`aegis-firewall-agent.sh`, available from the portal) wraps this endpoint. Run it from cron on each server instead of integrating manually.\n\n**Rate limit:** 60 requests / minute per API key.",
        "x-rateLimit": "60 requests / minute per API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "output"
                ],
                "properties": {
                  "output": {
                    "type": "string",
                    "maxLength": 524288,
                    "description": "The raw firewall dump (up to 512 KB)."
                  },
                  "format": {
                    "type": "string",
                    "enum": [
                      "iptables",
                      "nftables",
                      "ufw",
                      "firewalld"
                    ],
                    "description": "Dump format; auto-detected when omitted."
                  },
                  "hostname": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Hostname of the server the dump came from."
                  },
                  "ips": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 32,
                    "description": "IP addresses of the server, used to match it to a monitored endpoint (max 32)."
                  },
                  "ips.*": {
                    "type": "string",
                    "description": "IPv4 or IPv6 address."
                  },
                  "collected_at": {
                    "type": "string",
                    "description": "When the dump was collected. ISO 8601 date or date-time."
                  },
                  "endpoint_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Monitored endpoint UUID, when you already know it."
                  }
                }
              },
              "example": {
                "hostname": "web01.example.com",
                "collected_at": "2026-07-12T09:30:00+00:00",
                "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "snapshot_id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
                  "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/sandbox/status": {
      "get": {
        "operationId": "get-sandbox-status",
        "tags": [
          "Sandbox"
        ],
        "summary": "Sandbox status",
        "description": "Whether sandbox mode is on for your account, the active mock scenario, and how many sandbox endpoints exist.\n\n**Rate limit:** Not rate limited (sandbox).",
        "x-rateLimit": "Not rate limited (sandbox)",
        "parameters": [
          {
            "name": "X-Aegis-Sandbox",
            "in": "header",
            "required": false,
            "description": "Optional here. Sandbox lifecycle calls (provision, suspend, unsuspend, terminate) require it.",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            },
            "example": "true"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": {
                    "sandbox_enabled": true,
                    "sandbox_scenario": "warnings",
                    "sandbox_disabled_at": null,
                    "onboarding_completed_at": null,
                    "sandbox_endpoint_count": 2,
                    "can_re_enable": true
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "description": "- `404` — Your account has no hosting-partner profile."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/sandbox/scenario": {
      "post": {
        "operationId": "post-sandbox-scenario",
        "tags": [
          "Sandbox"
        ],
        "summary": "Set the sandbox scenario",
        "description": "Choose which mock scan results sandbox endpoints report: `clean` (no issues), `warnings` (web ports) or `critical` (database ports exposed).\n\n**Rate limit:** Not rate limited (sandbox).",
        "x-rateLimit": "Not rate limited (sandbox)",
        "parameters": [
          {
            "name": "X-Aegis-Sandbox",
            "in": "header",
            "required": false,
            "description": "Optional here. Sandbox lifecycle calls (provision, suspend, unsuspend, terminate) require it.",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            },
            "example": "true"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "scenario"
                ],
                "properties": {
                  "scenario": {
                    "type": "string",
                    "enum": [
                      "clean",
                      "warnings",
                      "critical"
                    ],
                    "description": "The mock scenario to activate."
                  }
                }
              },
              "example": {
                "scenario": "critical"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "message": "Sandbox scenario updated",
                  "scenario": "critical"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "- `403` — Sandbox mode is not enabled for your account."
          },
          "404": {
            "description": "- `404` — Your account has no hosting-partner profile."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/sandbox/logs": {
      "get": {
        "operationId": "get-sandbox-logs",
        "tags": [
          "Sandbox"
        ],
        "summary": "Sandbox activity log",
        "description": "Recent simulated provisioning calls with their request and response payloads, useful when debugging a module or integration in sandbox mode.\n\n**Rate limit:** Not rate limited (sandbox).",
        "x-rateLimit": "Not rate limited (sandbox)",
        "parameters": [
          {
            "name": "X-Aegis-Sandbox",
            "in": "header",
            "required": false,
            "description": "Optional here. Sandbox lifecycle calls (provision, suspend, unsuspend, terminate) require it.",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            },
            "example": "true"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many log entries to return.",
            "schema": {
              "type": "integer",
              "maximum": 100
            },
            "example": 25
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "data": [
                    {
                      "id": 118,
                      "action": "provision",
                      "request_data": {
                        "whmcs_product_id": 12,
                        "nickname": "Test endpoint"
                      },
                      "response_data": {
                        "success": true,
                        "sandbox": true
                      },
                      "created_at": "2026-07-12T09:20:00+00:00"
                    }
                  ],
                  "count": 1
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "description": "- `404` — Your account has no hosting-partner profile."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/sandbox/scan-result": {
      "get": {
        "operationId": "get-sandbox-scan-result",
        "tags": [
          "Sandbox"
        ],
        "summary": "Preview a mock scan result",
        "description": "See the mock scan payload the active (or a chosen) scenario produces, without creating anything.\n\n**Rate limit:** Not rate limited (sandbox).",
        "x-rateLimit": "Not rate limited (sandbox)",
        "parameters": [
          {
            "name": "X-Aegis-Sandbox",
            "in": "header",
            "required": false,
            "description": "Optional here. Sandbox lifecycle calls (provision, suspend, unsuspend, terminate) require it.",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            },
            "example": "true"
          },
          {
            "name": "scenario",
            "in": "query",
            "required": false,
            "description": "Preview a specific scenario instead of the active one.",
            "schema": {
              "type": "string",
              "enum": [
                "clean",
                "warnings",
                "critical"
              ]
            },
            "example": "warnings"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "sandbox": true,
                  "data": {
                    "status": "warnings",
                    "open_ports": [
                      {
                        "port": 80,
                        "service": "http",
                        "risk": "low"
                      },
                      {
                        "port": 443,
                        "service": "https",
                        "risk": "low"
                      }
                    ],
                    "risk_score": 20,
                    "risk_level": "medium",
                    "message": "Standard web server ports detected (sandbox mode)",
                    "sandbox": true,
                    "scanned_at": "2026-07-12T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "- `403` — Sandbox mode is not enabled for your account."
          },
          "404": {
            "description": "- `404` — Your account has no hosting-partner profile."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/sandbox/provision": {
      "post": {
        "operationId": "post-sandbox-provision",
        "tags": [
          "Sandbox"
        ],
        "summary": "Create a sandbox endpoint",
        "description": "Create a simulated endpoint that reports mock scan data and never triggers real scans. Requires the `X-Aegis-Sandbox: true` header.\n\n**Rate limit:** Not rate limited (sandbox).",
        "x-rateLimit": "Not rate limited (sandbox)",
        "parameters": [
          {
            "name": "X-Aegis-Sandbox",
            "in": "header",
            "required": true,
            "description": "Must be `true` — the call is refused with `400` when the header is missing.",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            },
            "example": "true"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "whmcs_product_id": {
                    "type": "integer",
                    "description": "WHMCS product to associate with the mock endpoint."
                  },
                  "nickname": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Display name for the mock endpoint."
                  }
                }
              },
              "example": {
                "whmcs_product_id": 12,
                "nickname": "Test endpoint"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "sandbox": true,
                  "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b",
                  "ip_address": "192.0.2.24",
                  "nickname": "Test endpoint",
                  "message": "Sandbox endpoint created successfully (no real scans)"
                }
              }
            }
          },
          "400": {
            "description": "- `400` — The `X-Aegis-Sandbox: true` header is missing."
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "- `SANDBOX_DISABLED` — Sandbox mode is not enabled for your account (it auto-disables after onboarding completes)."
          },
          "404": {
            "description": "- `404` — Your account has no hosting-partner profile."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/sandbox/suspend": {
      "post": {
        "operationId": "post-sandbox-suspend",
        "tags": [
          "Sandbox"
        ],
        "summary": "Suspend a sandbox endpoint",
        "description": "Simulate suspending a sandbox endpoint. Requires the `X-Aegis-Sandbox: true` header.\n\n**Rate limit:** Not rate limited (sandbox).",
        "x-rateLimit": "Not rate limited (sandbox)",
        "parameters": [
          {
            "name": "X-Aegis-Sandbox",
            "in": "header",
            "required": true,
            "description": "Must be `true` — the call is refused with `400` when the header is missing.",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            },
            "example": "true"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "endpoint_id"
                ],
                "properties": {
                  "endpoint_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The sandbox endpoint UUID."
                  }
                }
              },
              "example": {
                "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "sandbox": true,
                  "message": "Sandbox endpoint suspended successfully (simulated)"
                }
              }
            }
          },
          "400": {
            "description": "- `400` — The `X-Aegis-Sandbox: true` header is missing."
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "- `SANDBOX_DISABLED` — Sandbox mode is not enabled for your account."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/sandbox/unsuspend": {
      "post": {
        "operationId": "post-sandbox-unsuspend",
        "tags": [
          "Sandbox"
        ],
        "summary": "Unsuspend a sandbox endpoint",
        "description": "Simulate unsuspending a sandbox endpoint. Requires the `X-Aegis-Sandbox: true` header.\n\n**Rate limit:** Not rate limited (sandbox).",
        "x-rateLimit": "Not rate limited (sandbox)",
        "parameters": [
          {
            "name": "X-Aegis-Sandbox",
            "in": "header",
            "required": true,
            "description": "Must be `true` — the call is refused with `400` when the header is missing.",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            },
            "example": "true"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "endpoint_id"
                ],
                "properties": {
                  "endpoint_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The sandbox endpoint UUID."
                  }
                }
              },
              "example": {
                "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "sandbox": true,
                  "message": "Sandbox endpoint unsuspended successfully (simulated)"
                }
              }
            }
          },
          "400": {
            "description": "- `400` — The `X-Aegis-Sandbox: true` header is missing."
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "- `SANDBOX_DISABLED` — Sandbox mode is not enabled for your account."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/sandbox/terminate": {
      "post": {
        "operationId": "post-sandbox-terminate",
        "tags": [
          "Sandbox"
        ],
        "summary": "Terminate a sandbox endpoint",
        "description": "Simulate terminating a sandbox endpoint (the mock endpoint is deleted). Requires the `X-Aegis-Sandbox: true` header.\n\n**Rate limit:** Not rate limited (sandbox).",
        "x-rateLimit": "Not rate limited (sandbox)",
        "parameters": [
          {
            "name": "X-Aegis-Sandbox",
            "in": "header",
            "required": true,
            "description": "Must be `true` — the call is refused with `400` when the header is missing.",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            },
            "example": "true"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "endpoint_id"
                ],
                "properties": {
                  "endpoint_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The sandbox endpoint UUID."
                  }
                }
              },
              "example": {
                "endpoint_id": "9f2c1e4a-5b6d-4e7f-8a9b-0c1d2e3f4a5b"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "success": true,
                  "sandbox": true,
                  "message": "Sandbox endpoint terminated successfully (simulated)"
                }
              }
            }
          },
          "400": {
            "description": "- `400` — The `X-Aegis-Sandbox: true` header is missing."
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "- `SANDBOX_DISABLED` — Sandbox mode is not enabled for your account."
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "partnerApiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your partner API key (portal → Settings → WHMCS Integration). Shown once at generation; treat it as a root credential and keep it server-side."
      }
    },
    "responses": {
      "Unauthenticated": {
        "description": "Missing or invalid API key.",
        "content": {
          "application/json": {
            "example": {
              "status": "error",
              "message": "Unauthenticated.",
              "meta": {
                "timestamp": "2026-07-12T09:30:00+00:00",
                "version": "v1"
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "`TENANT_MISMATCH` — the API key does not belong to the portal domain you called.\n\n`ACCOUNT_SUSPENDED` — your partner account is suspended.",
        "content": {
          "application/json": {
            "examples": {
              "TENANT_MISMATCH": {
                "summary": "API key called on the wrong portal domain",
                "value": {
                  "error": {
                    "code": "TENANT_MISMATCH",
                    "message": "API key does not belong to this domain."
                  }
                }
              },
              "ACCOUNT_SUSPENDED": {
                "summary": "Partner account suspended",
                "value": {
                  "success": false,
                  "error": {
                    "code": "ACCOUNT_SUSPENDED",
                    "message": "Your account is suspended. Please contact support."
                  }
                }
              }
            }
          }
        }
      },
      "ValidationFailed": {
        "description": "Request validation failed. The `errors` map lists the offending fields.",
        "content": {
          "application/json": {
            "example": {
              "status": "error",
              "message": "The given data was invalid.",
              "meta": {
                "timestamp": "2026-07-12T09:30:00+00:00",
                "version": "v1"
              },
              "errors": {
                "ip_address": [
                  "The ip address field must be a valid IP address."
                ]
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded. Retry after the number of seconds in the `Retry-After` response header.",
        "content": {
          "application/json": {
            "example": {
              "status": "error",
              "message": "Too Many Attempts.",
              "meta": {
                "timestamp": "2026-07-12T09:30:00+00:00",
                "version": "v1"
              }
            }
          }
        }
      }
    }
  }
}
