{
  "openapi": "3.1.0",
  "info": {
    "title": "Approximated Public API",
    "version": "2026-08-05",
    "summary": "Manage custom domains and inspect DNS, TLS, and request traffic.",
    "description": "Authoritative machine-readable contract for the customer-facing Approximated API. Authenticated operations use the api-key header and are scoped to the proxy cluster attached to that key. Supplying api_key in a request body is unsupported legacy behavior and is intentionally not part of this contract. The general authenticated API budget is 240 requests per minute per key. Analytics and request-log reads each have an additional independent 60 requests per minute per-key budget. Public free tools use separate per-IP limits. This contract intentionally excludes browser/dashboard routes, Stripe callbacks, self-hosted node check-in/downloads, proxy configuration distribution, trace/analytics ingestion, and other cluster-internal endpoints.",
    "contact": {
      "name": "Approximated Support",
      "url": "https://approximated.app/company/",
      "email": "support@approximated.app"
    },
    "license": {
      "name": "All rights reserved"
    },
    "x-source-of-truth": [
      "Approximated Phoenix router, controllers, views, schemas, and controller tests",
      "Approximated marketing API documentation"
    ],
    "x-intentionally-excluded-route-groups": [
      "dashboard and browser-session routes",
      "Stripe webhook receiver",
      "self-hosted node check-in and image downloads",
      "proxy config, mTLS bundle, trace-event, and analytics-ingest routes",
      "OAuth and MCP routes",
      "Caddy on-demand-TLS ask endpoint"
    ]
  },
  "externalDocs": {
    "description": "Human-readable Approximated documentation",
    "url": "https://approximated.app/docs/"
  },
  "servers": [
    {
      "url": "https://cloud.approximated.app",
      "description": "Production"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {"name": "Virtual Hosts", "description": "Create and manage custom-domain proxy routes."},
    {"name": "DNS Checks", "description": "Compare live DNS answers with expected records."},
    {"name": "DNS Widget", "description": "Mint and use short-lived tokens for the embeddable DNS setup widget."},
    {"name": "Edge Verify", "description": "Manage browser-verification rules at virtual-host or cluster scope."},
    {"name": "L4 Rules", "description": "List, find, create, and revoke customer-visible network block rules."},
    {"name": "Analytics", "description": "Read request-volume and bandwidth aggregates."},
    {"name": "Request Logs", "description": "Read feature-gated recent request logs."},
    {"name": "Free Tools", "description": "Public, no-auth DNS, domain, HTTP, and TLS inspection tools."}
  ],
  "paths": {
    "/api/vhosts": {
      "post": {
        "tags": ["Virtual Hosts"],
        "operationId": "createVirtualHost",
        "summary": "Create a virtual host",
        "description": "Creates a custom-domain route on the API key's cluster. Supplying redirect_www=true also creates a non-billable www redirect. Rule/config propagation is asynchronous.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {"$ref": "#/components/schemas/VirtualHostCreate"},
              "example": {
                "incoming_address": "customer.example.com",
                "target_address": "origin.example.net",
                "target_ports": "443",
                "keep_host": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Virtual host created.",
            "headers": {"Location": {"schema": {"type": "string"}, "description": "Relative URL of the new virtual host."}},
            "content": {"application/json": {"schema": {"$ref": "#/components/schemas/VirtualHostEnvelope"}}}
          },
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "422": {"$ref": "#/components/responses/ValidationError"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      },
      "delete": {
        "tags": ["Virtual Hosts"],
        "operationId": "deleteVirtualHostByIncomingBody",
        "summary": "Delete a virtual host by incoming address",
        "description": "Compatibility endpoint. Prefer DELETE /api/vhosts/by/incoming/{incoming_address}.",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncomingAddressRequest"}}}},
        "responses": {
          "200": {"$ref": "#/components/responses/DeletingVirtualHost"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"$ref": "#/components/responses/SuspendedVirtualHost"},
          "404": {"$ref": "#/components/responses/VirtualHostNotFound"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/vhosts/{id}": {
      "parameters": [{"$ref": "#/components/parameters/VirtualHostId"}],
      "get": {
        "tags": ["Virtual Hosts"],
        "operationId": "getVirtualHost",
        "summary": "Get a virtual host by ID",
        "description": "Returns the most recently stored monitoring result; it does not start a new check.",
        "responses": {
          "200": {"$ref": "#/components/responses/VirtualHost"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/VirtualHostNotFound"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      },
      "delete": {
        "tags": ["Virtual Hosts"],
        "operationId": "deleteVirtualHost",
        "summary": "Delete a virtual host by ID",
        "responses": {
          "200": {"$ref": "#/components/responses/DeletingVirtualHost"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"$ref": "#/components/responses/SuspendedVirtualHost"},
          "404": {"$ref": "#/components/responses/VirtualHostNotFound"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/vhosts/{id}/update": {
      "parameters": [{"$ref": "#/components/parameters/VirtualHostId"}],
      "post": {
        "tags": ["Virtual Hosts"],
        "operationId": "updateVirtualHost",
        "summary": "Update a virtual host by ID",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/VirtualHostUpdate"}}}},
        "responses": {
          "200": {"$ref": "#/components/responses/VirtualHost"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"$ref": "#/components/responses/SuspendedVirtualHost"},
          "404": {"$ref": "#/components/responses/VirtualHostNotFound"},
          "422": {"$ref": "#/components/responses/ValidationError"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/vhosts/{id}/force-check": {
      "parameters": [{"$ref": "#/components/parameters/VirtualHostId"}],
      "get": {
        "tags": ["Virtual Hosts"],
        "operationId": "forceCheckVirtualHost",
        "summary": "Refresh and return a virtual host monitor result",
        "description": "At most 6 fresh checks per minute per API key. After that limit, the endpoint still returns 200 with the stored result and does not identify that a fresh check was skipped.",
        "responses": {
          "200": {"$ref": "#/components/responses/VirtualHost"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/VirtualHostNotFound"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/vhosts/offset/{offset}": {
      "get": {
        "tags": ["Virtual Hosts"],
        "operationId": "listVirtualHostsByOffset",
        "summary": "List virtual hosts using legacy offset pagination",
        "deprecated": true,
        "parameters": [{"name": "offset", "in": "path", "required": true, "schema": {"type": "integer", "minimum": 0}}],
        "responses": {
          "200": {"description": "Virtual hosts.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/VirtualHostListEnvelope"}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/cursor/vhosts/": {
      "get": {
        "tags": ["Virtual Hosts"],
        "operationId": "listVirtualHosts",
        "summary": "List the first cursor-paginated page of virtual hosts",
        "description": "Returns up to 100 virtual hosts. Pass a returned before_cursor or after_cursor to the directional cursor route.",
        "responses": {
          "200": {"description": "Cursor page.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/VirtualHostCursorEnvelope"}, "example": {"data": [{"id": 405455, "incoming_address": "customer.example.com", "target_address": "origin.example.net", "target_ports": "443", "keep_host": null, "lb_policy": "first", "lb_enabled": false, "lb_sticky": true, "rate_limit": null, "coraza_enabled": null, "mtls_client_cert": null, "upstreams": []}], "before_cursor": null, "after_cursor": "g3QAAAACZAACaWQ..."}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/cursor/vhosts/{cursor_direction}/{cursor}": {
      "get": {
        "tags": ["Virtual Hosts"],
        "operationId": "listVirtualHostsFromCursor",
        "summary": "List a directional cursor page of virtual hosts",
        "parameters": [
          {"$ref": "#/components/parameters/CursorDirection"},
          {"$ref": "#/components/parameters/Cursor"}
        ],
        "responses": {
          "200": {"description": "Cursor page.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/VirtualHostCursorEnvelope"}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/vhosts/by/incoming/{incoming_address}": {
      "parameters": [{"$ref": "#/components/parameters/IncomingAddress"}],
      "get": {
        "tags": ["Virtual Hosts"],
        "operationId": "getVirtualHostByIncomingAddress",
        "summary": "Get a virtual host by incoming address",
        "description": "Use the POST alternative when the stored incoming address contains a scheme, path, or query string.",
        "responses": {
          "200": {"$ref": "#/components/responses/VirtualHost"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/VirtualHostNotFound"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      },
      "delete": {
        "tags": ["Virtual Hosts"],
        "operationId": "deleteVirtualHostByIncomingAddress",
        "summary": "Delete a virtual host by incoming address",
        "responses": {
          "200": {"$ref": "#/components/responses/DeletingVirtualHost"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"$ref": "#/components/responses/SuspendedVirtualHost"},
          "404": {"$ref": "#/components/responses/VirtualHostNotFound"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/vhosts/by/incoming/{incoming_address}/force-check": {
      "parameters": [{"$ref": "#/components/parameters/IncomingAddress"}],
      "get": {
        "tags": ["Virtual Hosts"],
        "operationId": "forceCheckVirtualHostByIncomingAddress",
        "summary": "Refresh and return monitoring by incoming address",
        "description": "At most 6 fresh checks per minute per key; when exhausted, the stored result is returned with 200.",
        "responses": {
          "200": {"$ref": "#/components/responses/VirtualHost"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/VirtualHostNotFound"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/vhosts/by/incoming": {
      "post": {
        "tags": ["Virtual Hosts"],
        "operationId": "getVirtualHostByIncomingAddressBody",
        "summary": "Get a virtual host by incoming address in JSON",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncomingAddressRequest"}}}},
        "responses": {
          "200": {"$ref": "#/components/responses/VirtualHost"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/VirtualHostNotFound"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/vhosts/by/incoming/force-check": {
      "post": {
        "tags": ["Virtual Hosts"],
        "operationId": "forceCheckVirtualHostByIncomingAddressBody",
        "summary": "Refresh monitoring using an incoming address in JSON",
        "description": "At most 6 fresh checks per minute per key; when exhausted, the stored result is returned with 200.",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncomingAddressRequest"}}}},
        "responses": {
          "200": {"$ref": "#/components/responses/VirtualHost"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/VirtualHostNotFound"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/vhosts/update/by/incoming": {
      "post": {
        "tags": ["Virtual Hosts"],
        "operationId": "updateVirtualHostByIncomingAddress",
        "summary": "Update a virtual host by incoming address",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/VirtualHostUpdateByIncoming"}}}},
        "responses": {
          "200": {"$ref": "#/components/responses/VirtualHost"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"$ref": "#/components/responses/SuspendedVirtualHost"},
          "404": {"$ref": "#/components/responses/VirtualHostNotFound"},
          "422": {"$ref": "#/components/responses/ValidationError"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/vhosts/bulk/update/by/incoming": {
      "post": {
        "tags": ["Virtual Hosts"],
        "operationId": "bulkUpdateVirtualHostsByIncomingAddress",
        "summary": "Apply one update to up to 100 virtual hosts",
        "description": "Input addresses are de-duplicated and only the first 100 unique values are processed. incoming_address inside updates is ignored.",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/VirtualHostBulkUpdate"}}}},
        "responses": {
          "200": {"description": "Per-address update results.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/VirtualHostBulkUpdateEnvelope"}}}},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "422": {"$ref": "#/components/responses/ValidationError"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/vhosts/delete/by/incoming": {
      "post": {
        "tags": ["Virtual Hosts"],
        "operationId": "deleteVirtualHostByIncomingAddressPost",
        "summary": "Delete a virtual host by incoming address",
        "description": "Compatibility POST form of the by-incoming delete operation.",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncomingAddressRequest"}}}},
        "responses": {
          "200": {"$ref": "#/components/responses/DeletingVirtualHost"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"$ref": "#/components/responses/SuspendedVirtualHost"},
          "404": {"$ref": "#/components/responses/VirtualHostNotFound"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/dns/check-records-match-exactly": {
      "post": {
        "tags": ["DNS Checks"],
        "operationId": "checkDnsRecordsMatchExactly",
        "summary": "Check that each DNS answer has exactly one expected value",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DnsCheckRequest"}, "example": {"records": [{"address": "customer.example.com", "type": "cname", "match_against": "proxy.example.net"}]}}}},
        "responses": {
          "200": {"description": "DNS comparison results.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DnsCheckResponse"}, "example": {"records": [{"address": "customer.example.com", "type": "cname", "match_against": "proxy.example.net", "actual_values": ["proxy.example.net"], "match": true}]}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/dns/check-records-exist": {
      "post": {
        "tags": ["DNS Checks"],
        "operationId": "checkDnsRecordsExist",
        "summary": "Check that each expected value exists among the DNS answers",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DnsCheckRequest"}}}},
        "responses": {
          "200": {"description": "DNS comparison results.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DnsCheckResponse"}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/dns/token": {
      "get": {
        "tags": ["DNS Widget"],
        "operationId": "mintDnsWidgetToken",
        "summary": "Mint a short-lived DNS widget token",
        "description": "Call this server-side using the cluster API key, then pass the returned token to the browser widget. Tokens are accepted for about 10 minutes.",
        "security": [{"apiKey": []}],
        "responses": {
          "200": {"description": "Widget token.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DnsWidgetTokenEnvelope"}, "example": {"token": "SFMyNTY.g3QAAAAB...signature"}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/dns/get-provider-instructions": {
      "post": {
        "tags": ["DNS Widget"],
        "operationId": "getDnsWidgetProviderInstructions",
        "summary": "Get provider-specific DNS setup instructions",
        "description": "The official browser widget sends the short-lived token in the JSON body.",
        "security": [],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DnsWidgetInstructionsRequest"}, "example": {"token": "SFMyNTY.g3QAAAAB...signature", "records": [{"domain": "customer.example.com", "type": "CNAME", "host": "@", "value": "proxy.example.net", "ttl": 3600}]}}}},
        "responses": {
          "200": {"description": "Instructions grouped by apex domain.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DnsWidgetInstructionsResponse"}, "example": {"domain_results": {"example.com": {"steps": [{"html": "<div class=\"apxdns-instruction\">Add the CNAME record</div>"}], "records": [{"domain": "customer.example.com", "type": "CNAME", "host": "@", "value": "proxy.example.net", "ttl": 3600, "provider_domain": "generic", "apex_domain": "example.com", "subdomain": "customer", "combined_host": "customer"}]}}, "verify_section": {"html": "<div id=\"apxdnswidget-verify-section\"></div>"}}}}},
          "400": {"description": "Missing or malformed required JSON body parameters."},
          "403": {"$ref": "#/components/responses/DnsWidgetTokenRejected"}
        }
      }
    },
    "/api/dns/token/renew": {
      "post": {
        "tags": ["DNS Widget"],
        "operationId": "renewDnsWidgetToken",
        "summary": "Renew an unexpired DNS widget token",
        "security": [],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DnsWidgetTokenRequest"}, "example": {"token": "SFMyNTY.g3QAAAAB...signature"}}}},
        "responses": {
          "200": {"description": "Replacement widget token.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DnsWidgetTokenEnvelope"}, "example": {"token": "SFMyNTY.g3QAAAAC...signature"}}}},
          "403": {"$ref": "#/components/responses/DnsWidgetTokenRejected"}
        }
      }
    },
    "/api/dns/token/check-records-match-exactly": {
      "post": {
        "tags": ["DNS Widget"],
        "operationId": "checkDnsWidgetRecordsMatchExactly",
        "summary": "Check widget records for exact DNS answers",
        "security": [],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DnsWidgetExactCheckRequest"}, "example": {"token": "SFMyNTY.g3QAAAAB...signature", "records": [{"domain": "customer.example.com", "host": "@", "type": "cname", "match_against": "proxy.example.net", "value": "proxy.example.net"}]}}}},
        "responses": {
          "200": {"description": "Enriched widget record results.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DnsWidgetExactCheckResponse"}, "example": {"records": [{"domain": "customer.example.com", "host": "@", "type": "cname", "match_against": "proxy.example.net", "value": "proxy.example.net", "actual_values": ["proxy.example.net"], "match": true, "combined_host": "customer", "apex": "example", "tld": "com", "full": "customer.example.com", "non_tld": "customer.example", "subdomain": "customer"}]}}}},
          "400": {"description": "Missing or malformed required JSON body parameters."},
          "403": {"$ref": "#/components/responses/DnsWidgetTokenRejected"}
        }
      }
    },
    "/api/dns/token/check-records-exist": {
      "post": {
        "tags": ["DNS Widget"],
        "operationId": "checkDnsWidgetRecordsExist",
        "summary": "Check widget records for any matching DNS answer",
        "security": [],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DnsWidgetExistCheckRequest"}, "example": {"token": "SFMyNTY.g3QAAAAB...signature", "records": [{"address": "customer.example.com", "type": "cname", "match_against": "proxy.example.net"}]}}}},
        "responses": {
          "200": {"description": "Record results.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DnsCheckResponse"}, "example": {"records": [{"address": "customer.example.com", "type": "cname", "match_against": "proxy.example.net", "actual_values": false, "match": false}]}}}},
          "400": {"description": "Missing or malformed required JSON body parameters."},
          "403": {"$ref": "#/components/responses/DnsWidgetTokenRejected"}
        }
      }
    },
    "/api/edge-verify/rules/by/incoming": {
      "post": {
        "tags": ["Edge Verify"],
        "operationId": "listEdgeVerifyRulesByIncomingAddress",
        "summary": "List rules for one virtual host",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncomingAddressRequest"}}}},
        "responses": {
          "200": {"description": "Rules.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/EdgeVerifyRuleListEnvelope"}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/JsonNotFound"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/edge-verify/rules/create/by/incoming": {
      "post": {
        "tags": ["Edge Verify"],
        "operationId": "createEdgeVerifyRuleByIncomingAddress",
        "summary": "Create a rule for one virtual host",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/EdgeVerifyVhostCreate"}, "example": {"incoming_address": "customer.example.com", "path_pattern": "/contact", "methods": ["POST"], "mode": "monitor"}}}},
        "responses": {
          "201": {"description": "Rule created.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/EdgeVerifyRuleEnvelope"}, "example": {"data": {"id": 91, "virtual_host_id": 405455, "proxy_server_id": null, "path_pattern": "/contact", "methods": ["POST"], "mode": "monitor", "inserted_at": "2026-08-05T16:30:00", "updated_at": "2026-08-05T16:30:00"}}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/JsonNotFound"},
          "422": {"$ref": "#/components/responses/ValidationError"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/edge-verify/rules/update/by/incoming": {
      "post": {
        "tags": ["Edge Verify"],
        "operationId": "updateEdgeVerifyRuleByIncomingAddress",
        "summary": "Update a rule for one virtual host",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/EdgeVerifyVhostUpdate"}}}},
        "responses": {
          "200": {"description": "Rule updated.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/EdgeVerifyRuleEnvelope"}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/JsonNotFound"},
          "422": {"$ref": "#/components/responses/ValidationError"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/edge-verify/rules/delete/by/incoming": {
      "post": {
        "tags": ["Edge Verify"],
        "operationId": "deleteEdgeVerifyRuleByIncomingAddress",
        "summary": "Delete a rule for one virtual host",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/EdgeVerifyVhostDelete"}}}},
        "responses": {
          "204": {"description": "Rule deleted."},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/JsonNotFound"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/edge-verify/cluster-rules": {
      "get": {
        "tags": ["Edge Verify"],
        "operationId": "listClusterEdgeVerifyRules",
        "summary": "List cluster-wide rules",
        "responses": {
          "200": {"description": "Rules.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/EdgeVerifyRuleListEnvelope"}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      },
      "post": {
        "tags": ["Edge Verify"],
        "operationId": "createClusterEdgeVerifyRule",
        "summary": "Create a cluster-wide rule",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/EdgeVerifyRuleWrite"}}}},
        "responses": {
          "201": {"description": "Rule created.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/EdgeVerifyRuleEnvelope"}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "422": {"$ref": "#/components/responses/ValidationError"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/edge-verify/cluster-rules/{id}/update": {
      "parameters": [{"$ref": "#/components/parameters/RuleId"}],
      "post": {
        "tags": ["Edge Verify"],
        "operationId": "updateClusterEdgeVerifyRule",
        "summary": "Update a cluster-wide rule",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/EdgeVerifyRulePatch"}}}},
        "responses": {
          "200": {"description": "Rule updated.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/EdgeVerifyRuleEnvelope"}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/JsonNotFound"},
          "422": {"$ref": "#/components/responses/ValidationError"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/edge-verify/cluster-rules/{id}": {
      "parameters": [{"$ref": "#/components/parameters/RuleId"}],
      "delete": {
        "tags": ["Edge Verify"],
        "operationId": "deleteClusterEdgeVerifyRule",
        "summary": "Delete a cluster-wide rule",
        "responses": {
          "204": {"description": "Rule deleted."},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/JsonNotFound"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/cursor/l4-rules": {
      "get": {
        "tags": ["L4 Rules"],
        "operationId": "listL4Rules",
        "summary": "List live L4 rules for the API key's cluster",
        "description": "Returns up to 100 non-revoked, non-expired rules.",
        "responses": {
          "200": {"description": "Cursor page.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/L4RuleListEnvelope"}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/cursor/l4-rules/{cursor_direction}/{cursor}": {
      "get": {
        "tags": ["L4 Rules"],
        "operationId": "listL4RulesFromCursor",
        "summary": "List a directional cursor page of L4 rules",
        "parameters": [{"$ref": "#/components/parameters/CursorDirection"}, {"$ref": "#/components/parameters/Cursor"}],
        "responses": {
          "200": {"description": "Cursor page.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/L4RuleListEnvelope"}}}},
          "400": {"description": "Invalid cursor.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SimpleError"}, "example": {"error": "invalid cursor"}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/l4-rules/lookup": {
      "post": {
        "tags": ["L4 Rules"],
        "operationId": "lookupL4Rule",
        "summary": "Find a live rule by match type and value",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/L4RuleLookup"}}}},
        "responses": {
          "200": {"description": "Matching rule.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/L4RuleEnvelope"}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/SimpleJsonNotFound"},
          "422": {"$ref": "#/components/responses/ValidationError"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/l4-rules": {
      "post": {
        "tags": ["L4 Rules"],
        "operationId": "createL4Rule",
        "summary": "Create a cluster-scoped block rule",
        "description": "Customer API callers may only create block rules. IP values are normalized to CIDR form.",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/L4RuleCreate"}, "example": {"match_type": "ip", "value": "203.0.113.8", "action": "block", "reason": "abusive scraper"}}}},
        "responses": {
          "201": {"description": "Rule created.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/L4RuleEnvelope"}, "example": {"data": {"id": 314, "proxy_server_id": 42, "match_type": "ip", "value": "203.0.113.8/32", "action": "block", "reason": "abusive scraper", "source": "customer", "expires_at": null, "inserted_at": "2026-08-05T16:30:00", "updated_at": "2026-08-05T16:30:00"}}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "409": {"description": "A non-customer-revokable rule conflicts with the requested decision.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/L4Conflict"}}}},
          "422": {"$ref": "#/components/responses/ValidationError"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/l4-rules/{id}": {
      "parameters": [{"$ref": "#/components/parameters/RuleId"}],
      "delete": {
        "tags": ["L4 Rules"],
        "operationId": "deleteL4Rule",
        "summary": "Revoke a customer-revokable L4 rule",
        "responses": {
          "204": {"description": "Rule revoked."},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"description": "The rule was created by an operator and cannot be revoked by a customer.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SimpleError"}, "example": {"error": "not_revokable_by_customer"}}}},
          "404": {"$ref": "#/components/responses/SimpleJsonNotFound"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/monitoring/health-check": {
      "get": {
        "tags": ["Analytics"],
        "operationId": "getLatestClusterHealthCheck",
        "summary": "Get the latest proxy cluster health check",
        "description": "Returns the most recent regional health result for the cluster attached to the API key. data is null when no health check exists yet.",
        "security": [{"apiKey": []}],
        "responses": {
          "200": {"description": "Latest health check.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HealthCheckEnvelope"}, "examples": {"available": {"value": {"data": {"id": 42, "results": {"iad": true, "lhr": false}, "human_readable_results": {"Ashburn, Virginia (US)": "Healthy", "London, United Kingdom": "Repairing"}, "utc_last_updated": "2026-08-05T16:30:00", "human_readable_last_updated": "2 minutes ago"}}}, "not_yet_checked": {"value": {"data": null}}}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "429": {"$ref": "#/components/responses/ApiRateLimited"}
        }
      }
    },
    "/api/vhosts/{id}/analytics": {
      "parameters": [{"$ref": "#/components/parameters/VirtualHostId"}],
      "get": {
        "tags": ["Analytics"],
        "operationId": "getVirtualHostAnalyticsSeries",
        "summary": "Get a virtual host request series",
        "description": "Bucket granularity is selected automatically: up to 6 hours uses raw_1m, up to 30 days uses h_1h, and longer windows use d_1d.",
        "parameters": [{"$ref": "#/components/parameters/AnalyticsStart"}, {"$ref": "#/components/parameters/AnalyticsEnd"}],
        "responses": {
          "200": {"description": "Time series.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AnalyticsSeriesEnvelope"}}}},
          "400": {"$ref": "#/components/responses/AnalyticsBadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/SimpleJsonNotFound"},
          "429": {"$ref": "#/components/responses/AnalyticsRateLimited"},
          "500": {"$ref": "#/components/responses/InternalJsonError"}
        }
      }
    },
    "/api/vhosts/{id}/analytics/top": {
      "parameters": [{"$ref": "#/components/parameters/VirtualHostId"}],
      "get": {
        "tags": ["Analytics"],
        "operationId": "getVirtualHostAnalyticsTop",
        "summary": "Get top dimensions for a virtual host",
        "parameters": [{"$ref": "#/components/parameters/AnalyticsStart"}, {"$ref": "#/components/parameters/AnalyticsEnd"}, {"$ref": "#/components/parameters/AnalyticsDimension"}, {"$ref": "#/components/parameters/AnalyticsLimit"}],
        "responses": {
          "200": {"description": "Top values.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AnalyticsTopEnvelope"}}}},
          "400": {"$ref": "#/components/responses/AnalyticsBadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/SimpleJsonNotFound"},
          "429": {"$ref": "#/components/responses/AnalyticsRateLimited"},
          "500": {"$ref": "#/components/responses/InternalJsonError"}
        }
      }
    },
    "/api/proxy-servers/{id}/analytics": {
      "parameters": [{"$ref": "#/components/parameters/ProxyServerId"}],
      "get": {
        "tags": ["Analytics"],
        "operationId": "getClusterAnalyticsSeries",
        "summary": "Get a proxy cluster request series",
        "parameters": [{"$ref": "#/components/parameters/AnalyticsStart"}, {"$ref": "#/components/parameters/AnalyticsEnd"}],
        "responses": {
          "200": {"description": "Time series.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AnalyticsSeriesEnvelope"}}}},
          "400": {"$ref": "#/components/responses/AnalyticsBadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/SimpleJsonNotFound"},
          "429": {"$ref": "#/components/responses/AnalyticsRateLimited"},
          "500": {"$ref": "#/components/responses/InternalJsonError"}
        }
      }
    },
    "/api/proxy-servers/{id}/analytics/top": {
      "parameters": [{"$ref": "#/components/parameters/ProxyServerId"}],
      "get": {
        "tags": ["Analytics"],
        "operationId": "getClusterAnalyticsTop",
        "summary": "Get top dimensions for a proxy cluster",
        "parameters": [{"$ref": "#/components/parameters/AnalyticsStart"}, {"$ref": "#/components/parameters/AnalyticsEnd"}, {"$ref": "#/components/parameters/AnalyticsDimension"}, {"$ref": "#/components/parameters/AnalyticsLimit"}],
        "responses": {
          "200": {"description": "Top values.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AnalyticsTopEnvelope"}}}},
          "400": {"$ref": "#/components/responses/AnalyticsBadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/SimpleJsonNotFound"},
          "429": {"$ref": "#/components/responses/AnalyticsRateLimited"},
          "500": {"$ref": "#/components/responses/InternalJsonError"}
        }
      }
    },
    "/api/proxy-servers/{id}/logs": {
      "parameters": [{"$ref": "#/components/parameters/ProxyServerId"}],
      "get": {
        "tags": ["Request Logs"],
        "operationId": "getClusterRequestLogs",
        "summary": "Get recent request logs for a proxy cluster",
        "description": "Available only when customer-facing request logs are enabled for the cluster. A disabled cluster returns the same 404 as an unknown cluster.",
        "parameters": [{"$ref": "#/components/parameters/LogCursor"}, {"$ref": "#/components/parameters/LogLimit"}, {"$ref": "#/components/parameters/LogWindow"}, {"$ref": "#/components/parameters/LogHost"}, {"$ref": "#/components/parameters/LogStatus"}, {"$ref": "#/components/parameters/LogStatusClass"}, {"$ref": "#/components/parameters/LogMethod"}, {"$ref": "#/components/parameters/LogDisposition"}, {"$ref": "#/components/parameters/LogPathPrefix"}, {"$ref": "#/components/parameters/LogClientIp"}],
        "responses": {
          "200": {"description": "Request log page.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RequestLogEnvelope"}}}},
          "400": {"$ref": "#/components/responses/LogsBadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/SimpleJsonNotFound"},
          "429": {"$ref": "#/components/responses/LogsRateLimited"},
          "503": {"description": "Log storage is unavailable.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SimpleError"}, "example": {"error": "logs_unavailable"}}}}
        }
      }
    },
    "/api/vhosts/{id}/logs": {
      "parameters": [{"$ref": "#/components/parameters/VirtualHostId"}],
      "get": {
        "tags": ["Request Logs"],
        "operationId": "getVirtualHostRequestLogs",
        "summary": "Get recent request logs for one virtual host",
        "description": "Available only when customer-facing request logs are enabled for the owning cluster.",
        "parameters": [{"$ref": "#/components/parameters/LogCursor"}, {"$ref": "#/components/parameters/LogLimit"}, {"$ref": "#/components/parameters/LogWindow"}, {"$ref": "#/components/parameters/LogHost"}, {"$ref": "#/components/parameters/LogStatus"}, {"$ref": "#/components/parameters/LogStatusClass"}, {"$ref": "#/components/parameters/LogMethod"}, {"$ref": "#/components/parameters/LogDisposition"}, {"$ref": "#/components/parameters/LogPathPrefix"}, {"$ref": "#/components/parameters/LogClientIp"}],
        "responses": {
          "200": {"description": "Request log page.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RequestLogEnvelope"}}}},
          "400": {"$ref": "#/components/responses/LogsBadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/SimpleJsonNotFound"},
          "429": {"$ref": "#/components/responses/LogsRateLimited"},
          "503": {"description": "Log storage is unavailable.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SimpleError"}, "example": {"error": "logs_unavailable"}}}}
        }
      }
    },
    "/api/tools/ping": {
      "get": {
        "tags": ["Free Tools"],
        "operationId": "pingFreeTools",
        "summary": "Check free-tools API availability",
        "description": "Public and intentionally not rate-limited.",
        "security": [],
        "responses": {
          "200": {"description": "Service is available.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolPingEnvelope"}, "example": {"ok": true, "data": {"pong": true}}}}}
        }
      }
    },
    "/api/tools/dns-records": {
      "get": {
        "tags": ["Free Tools"],
        "operationId": "lookupDnsRecords",
        "summary": "Look up multiple DNS record types",
        "security": [],
        "parameters": [
          {"$ref": "#/components/parameters/ToolDomain"},
          {"name": "types", "in": "query", "description": "Comma-separated subset. Omit for A, AAAA, CNAME, MX, TXT, NS, and SOA.", "schema": {"type": "string", "example": "a,aaaa,cname,mx,txt,ns,soa"}}
        ],
        "responses": {
          "200": {"description": "Record map. Each requested type is present. A partial resolver failure degrades that type to an empty array and still returns 200; only failure of every requested type returns 502. Therefore an empty array can mean either an authoritative empty answer or a degraded per-type lookup.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolDnsRecordsEnvelope"}}}},
          "400": {"$ref": "#/components/responses/ToolInvalidInput"},
          "429": {"$ref": "#/components/responses/ToolRateLimited"},
          "502": {"$ref": "#/components/responses/ToolUpstreamError"}
        }
      }
    },
    "/api/tools/txt": {
      "get": {
        "tags": ["Free Tools"], "operationId": "lookupTxtRecords", "summary": "Look up and categorize TXT records", "security": [],
        "parameters": [{"$ref": "#/components/parameters/ToolDomain"}],
        "responses": {
          "200": {"description": "TXT records.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolTxtEnvelope"}}}},
          "400": {"$ref": "#/components/responses/ToolInvalidInput"}, "429": {"$ref": "#/components/responses/ToolRateLimited"}, "502": {"$ref": "#/components/responses/ToolUpstreamError"}
        }
      }
    },
    "/api/tools/spf": {
      "get": {
        "tags": ["Free Tools"], "operationId": "checkSpfRecord", "summary": "Parse a domain's SPF record", "security": [],
        "parameters": [{"$ref": "#/components/parameters/ToolDomain"}],
        "responses": {
          "200": {"description": "SPF result. No record is a successful negative result.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolSpfEnvelope"}}}},
          "400": {"$ref": "#/components/responses/ToolInvalidInput"}, "429": {"$ref": "#/components/responses/ToolRateLimited"}, "502": {"$ref": "#/components/responses/ToolUpstreamError"}
        }
      }
    },
    "/api/tools/dmarc": {
      "get": {
        "tags": ["Free Tools"], "operationId": "checkDmarcRecord", "summary": "Parse a domain's DMARC record", "security": [],
        "parameters": [{"$ref": "#/components/parameters/ToolDomain"}],
        "responses": {
          "200": {"description": "DMARC result. No record is a successful negative result.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolDmarcEnvelope"}}}},
          "400": {"$ref": "#/components/responses/ToolInvalidInput"}, "429": {"$ref": "#/components/responses/ToolRateLimited"}, "502": {"$ref": "#/components/responses/ToolUpstreamError"}
        }
      }
    },
    "/api/tools/dkim": {
      "get": {
        "tags": ["Free Tools"], "operationId": "checkDkimRecord", "summary": "Parse a DKIM selector record", "security": [],
        "parameters": [
          {"$ref": "#/components/parameters/ToolDomain"},
          {"name": "selector", "in": "query", "required": true, "schema": {"type": "string", "minLength": 1, "maxLength": 63, "pattern": "^[A-Za-z0-9._-]+$", "example": "google"}}
        ],
        "responses": {
          "200": {"description": "DKIM result. No record is a successful negative result.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolDkimEnvelope"}}}},
          "400": {"$ref": "#/components/responses/ToolInvalidInput"}, "429": {"$ref": "#/components/responses/ToolRateLimited"}, "502": {"$ref": "#/components/responses/ToolUpstreamError"}
        }
      }
    },
    "/api/tools/cname": {
      "get": {
        "tags": ["Free Tools"], "operationId": "lookupCname", "summary": "Resolve a CNAME chain and optionally generate setup instructions", "security": [],
        "parameters": [
          {"$ref": "#/components/parameters/ToolDomain"},
          {"name": "target", "in": "query", "description": "When present, adds provider detection and instructions for pointing domain to target.", "schema": {"type": "string", "format": "hostname", "example": "target.example.net"}}
        ],
        "responses": {
          "200": {"description": "CNAME lookup or generator result.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolCnameEnvelope"}}}},
          "400": {"$ref": "#/components/responses/ToolInvalidInput"}, "429": {"$ref": "#/components/responses/ToolRateLimited"}, "502": {"$ref": "#/components/responses/ToolUpstreamError"}
        }
      }
    },
    "/api/tools/reverse-ip": {
      "get": {
        "tags": ["Free Tools"], "operationId": "reverseIpLookup", "summary": "Perform a PTR lookup", "security": [],
        "parameters": [{"name": "ip", "in": "query", "required": true, "schema": {"type": "string", "example": "8.8.8.8"}}],
        "responses": {
          "200": {"description": "PTR answers.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolReverseIpEnvelope"}}}},
          "400": {"$ref": "#/components/responses/ToolInvalidInput"}, "429": {"$ref": "#/components/responses/ToolRateLimited"}, "502": {"$ref": "#/components/responses/ToolUpstreamError"}
        }
      }
    },
    "/api/tools/propagation": {
      "get": {
        "tags": ["Free Tools"], "operationId": "checkDnsPropagation", "summary": "Compare A records across public resolvers", "security": [],
        "description": "Per-resolver failures are returned inside a successful result. consistent is true when all successful resolvers agree; zero successful resolvers currently also yields true.",
        "parameters": [{"$ref": "#/components/parameters/ToolDomain"}],
        "responses": {
          "200": {"description": "Resolver results.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolPropagationEnvelope"}}}},
          "400": {"$ref": "#/components/responses/ToolInvalidInput"}, "429": {"$ref": "#/components/responses/ToolRateLimited"}
        }
      }
    },
    "/api/tools/status": {
      "get": {
        "tags": ["Free Tools"], "operationId": "checkWebsiteStatus", "summary": "Check an HTTP URL without following redirects", "security": [],
        "description": "SSRF blocks and transport failures are successful HTTP 200 responses with data.up=false and data.error set.",
        "parameters": [{"$ref": "#/components/parameters/ToolUrl"}],
        "responses": {
          "200": {"description": "HTTP status or in-band block/failure result.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolStatusEnvelope"}, "examples": {"reachable": {"value": {"ok": true, "data": {"url": "https://example.com/", "up": true, "status": 200, "response_time_ms": 87, "final_url": "https://example.com/", "error": null}}}, "blocked_private_host": {"value": {"ok": true, "data": {"url": "http://10.0.0.1/", "up": false, "status": null, "response_time_ms": null, "final_url": "http://10.0.0.1/", "error": "blocked_private_address"}}}}}}},
          "400": {"$ref": "#/components/responses/ToolInvalidInput"}, "429": {"$ref": "#/components/responses/ToolRateLimited"}
        }
      }
    },
    "/api/tools/headers": {
      "get": {
        "tags": ["Free Tools"], "operationId": "inspectHttpHeaders", "summary": "Inspect response and security headers", "security": [],
        "description": "SSRF blocks and transport failures are successful HTTP 200 responses with an empty header map and data.error set.",
        "parameters": [{"$ref": "#/components/parameters/ToolUrl"}],
        "responses": {
          "200": {"description": "HTTP headers or in-band block/failure result.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolHeadersEnvelope"}}}},
          "400": {"$ref": "#/components/responses/ToolInvalidInput"}, "429": {"$ref": "#/components/responses/ToolRateLimited"}
        }
      }
    },
    "/api/tools/redirects": {
      "get": {
        "tags": ["Free Tools"], "operationId": "inspectRedirectChain", "summary": "Follow and inspect an HTTP redirect chain", "security": [],
        "description": "Every hop is SSRF-checked. Blocks, loops, hop limits, and transport failures are represented in-band in a successful HTTP 200 response.",
        "parameters": [{"$ref": "#/components/parameters/ToolUrl"}],
        "responses": {
          "200": {"description": "Redirect chain or in-band stop result.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolRedirectsEnvelope"}}}},
          "400": {"$ref": "#/components/responses/ToolInvalidInput"}, "429": {"$ref": "#/components/responses/ToolRateLimited"}
        }
      }
    },
    "/api/tools/ssl": {
      "get": {
        "tags": ["Free Tools"], "operationId": "inspectTlsCertificate", "summary": "Inspect a host's leaf TLS certificate", "security": [],
        "parameters": [
          {"name": "host", "in": "query", "required": true, "schema": {"type": "string", "format": "hostname", "example": "example.com"}},
          {"name": "port", "in": "query", "schema": {"type": "integer", "minimum": 1, "maximum": 65535, "default": 443}}
        ],
        "responses": {
          "200": {"description": "Certificate details.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolSslEnvelope"}}}},
          "400": {"description": "Invalid input or private/unresolvable host.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolError"}}}},
          "429": {"$ref": "#/components/responses/ToolRateLimited"},
          "502": {"$ref": "#/components/responses/ToolUpstreamError"},
          "504": {"description": "TLS connection timed out.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolError"}, "example": {"ok": false, "error": "upstream_timeout", "message": "Timed out connecting to example.com:443."}}}}
        }
      }
    },
    "/api/tools/whois": {
      "get": {
        "tags": ["Free Tools"], "operationId": "lookupDomainRegistration", "summary": "Look up domain registration using RDAP", "security": [],
        "parameters": [{"$ref": "#/components/parameters/ToolDomain"}],
        "responses": {
          "200": {"description": "Registered, unregistered, or unsupported-TLD result.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolWhoisEnvelope"}}}},
          "400": {"$ref": "#/components/responses/ToolInvalidInput"}, "429": {"$ref": "#/components/responses/ToolRateLimited"}, "502": {"$ref": "#/components/responses/ToolUpstreamError"}
        }
      }
    },
    "/api/tools/subdomains": {
      "get": {
        "tags": ["Free Tools"], "operationId": "findSubdomains", "summary": "Find subdomains from certificate transparency", "security": [],
        "description": "crt.sh failures degrade to a 200 response with an empty list and data.error set.",
        "parameters": [{"$ref": "#/components/parameters/ToolDomain"}],
        "responses": {
          "200": {"description": "Subdomain result, possibly partial.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolSubdomainsEnvelope"}}}},
          "400": {"$ref": "#/components/responses/ToolInvalidInput"}, "429": {"$ref": "#/components/responses/ToolRateLimited"}
        }
      }
    },
    "/api/tools/domain-age": {
      "get": {
        "tags": ["Free Tools"], "operationId": "getDomainAge", "summary": "Calculate domain age from RDAP", "security": [],
        "parameters": [{"$ref": "#/components/parameters/ToolDomain"}],
        "responses": {
          "200": {"description": "Registered, unregistered, or unsupported-TLD result.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolDomainAgeEnvelope"}}}},
          "400": {"$ref": "#/components/responses/ToolInvalidInput"}, "429": {"$ref": "#/components/responses/ToolRateLimited"}, "502": {"$ref": "#/components/responses/ToolUpstreamError"}
        }
      }
    },
    "/api/tools/availability": {
      "get": {
        "tags": ["Free Tools"], "operationId": "checkDomainAvailability", "summary": "Check one domain or a base label across TLDs", "security": [],
        "description": "With tlds, this is a partial-result operation: available=null means either an unsupported TLD or an RDAP network failure, and the per-item cause is not distinguishable. Those items remain inside HTTP 200. The 502 response applies only to a single-domain upstream failure.",
        "parameters": [
          {"name": "domain", "in": "query", "required": true, "description": "A full domain when tlds is absent; a base label with no dot when tlds is present.", "schema": {"type": "string", "example": "example.com"}},
          {"name": "tlds", "in": "query", "description": "Comma-separated TLDs; at most 10.", "schema": {"type": "string", "example": "com,net,io"}}
        ],
        "responses": {
          "200": {"description": "Single-domain or multi-TLD result.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolAvailabilityEnvelope"}}}},
          "400": {"$ref": "#/components/responses/ToolInvalidInput"}, "429": {"$ref": "#/components/responses/ToolRateLimited"}, "502": {"$ref": "#/components/responses/ToolUpstreamError"}
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "api-key",
        "description": "Cluster-scoped Approximated API key. Header names are case-insensitive. The server's legacy request-body api_key fallback is unsupported and must not be used by clients."
      }
    },
    "parameters": {
      "VirtualHostId": {"name": "id", "in": "path", "required": true, "description": "Virtual host database ID.", "schema": {"type": "integer", "minimum": 1}},
      "ProxyServerId": {"name": "id", "in": "path", "required": true, "description": "Proxy cluster ID. Must equal the cluster attached to the API key.", "schema": {"type": "integer", "minimum": 1}},
      "RuleId": {"name": "id", "in": "path", "required": true, "description": "Rule database ID.", "schema": {"type": "integer", "minimum": 1}},
      "IncomingAddress": {"name": "incoming_address", "in": "path", "required": true, "description": "Custom domain without a scheme, path, or query string.", "schema": {"type": "string", "example": "customer.example.com"}},
      "CursorDirection": {"name": "cursor_direction", "in": "path", "required": true, "schema": {"type": "string", "enum": ["before", "after"]}},
      "Cursor": {"name": "cursor", "in": "path", "required": true, "description": "Opaque cursor returned by the corresponding list endpoint.", "schema": {"type": "string"}},
      "AnalyticsStart": {"name": "start", "in": "query", "description": "Inclusive RFC 3339 start. Defaults to 24 hours before end.", "schema": {"type": "string", "format": "date-time"}},
      "AnalyticsEnd": {"name": "end", "in": "query", "description": "Exclusive RFC 3339 end. Defaults to now and must be after start.", "schema": {"type": "string", "format": "date-time"}},
      "AnalyticsDimension": {"name": "dim", "in": "query", "schema": {"type": "string", "enum": ["country", "status", "method", "origin"], "default": "country"}},
      "AnalyticsLimit": {"name": "limit", "in": "query", "description": "Requested top-item count. Non-integer input falls back to 10; storage limits the effective count.", "schema": {"type": "integer", "minimum": 1, "maximum": 100, "default": 10}},
      "LogCursor": {"name": "cursor", "in": "query", "description": "Opaque next_cursor from a prior page. Malformed cursors are ignored.", "schema": {"type": "string", "maxLength": 200}},
      "LogLimit": {"name": "limit", "in": "query", "description": "Page size, clamped to 1..1000.", "schema": {"type": "integer", "minimum": 1, "maximum": 1000, "default": 100}},
      "LogWindow": {"name": "window", "in": "query", "description": "Lookback seconds, clamped to cluster retention.", "schema": {"type": "integer", "minimum": 1, "default": 3600}},
      "LogHost": {"name": "host", "in": "query", "description": "Exact host filter.", "schema": {"type": "string"}},
      "LogStatus": {"name": "status", "in": "query", "description": "Exact HTTP status. Invalid values are ignored.", "schema": {"type": "integer", "minimum": 100, "maximum": 599}},
      "LogStatusClass": {"name": "status_class", "in": "query", "description": "HTTP status class. Invalid values are ignored.", "schema": {"type": "integer", "enum": [2, 3, 4, 5]}},
      "LogMethod": {"name": "method", "in": "query", "description": "Exact HTTP method.", "schema": {"type": "string"}},
      "LogDisposition": {"name": "disposition", "in": "query", "description": "Invalid values are ignored.", "schema": {"type": "string", "enum": ["served", "waf_blocked", "rate_limited", "challenge_issued", "challenge_passed", "challenge_failed", "challenge_passed_prior"]}},
      "LogPathPrefix": {"name": "path_prefix", "in": "query", "description": "Literal path-prefix filter.", "schema": {"type": "string"}},
      "LogClientIp": {"name": "client_ip", "in": "query", "description": "Exact IPv4 or IPv6 address.", "schema": {"type": "string"}},
      "ToolDomain": {"name": "domain", "in": "query", "required": true, "schema": {"type": "string", "format": "hostname", "example": "example.com"}},
      "ToolUrl": {"name": "url", "in": "query", "required": true, "description": "An HTTP or HTTPS URL, at most 2048 bytes after normalization. A scheme-less host/path is accepted and normalized to https://. Other URI schemes are rejected.", "schema": {"type": "string", "minLength": 1, "maxLength": 2048, "examples": ["https://example.com/", "example.com/path"]}}
    },
    "responses": {
      "Unauthorized": {"description": "Missing, deleted, or unknown API key.", "content": {"application/json": {"schema": {"type": "string"}, "example": "Unauthorized"}}},
      "ApiRateLimited": {"description": "General 240 requests/minute API-key budget exceeded. The response is plain text and includes a dynamic wait duration.", "content": {"text/plain": {"schema": {"type": "string"}, "example": "Too many requests: 240 requests per minute are allowed. Please wait 12.0 seconds."}}},
      "AnalyticsRateLimited": {"description": "The independent 60 requests/minute analytics budget was exceeded. A request can also be stopped earlier by the general API-key budget.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SimpleError"}, "example": {"error": "rate_limit_exceeded"}}}},
      "LogsRateLimited": {"description": "The independent 60 requests/minute request-log budget was exceeded. A request can also be stopped earlier by the general API-key budget.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SimpleError"}, "example": {"error": "rate_limit_exceeded"}}}},
      "VirtualHost": {"description": "Virtual host with stored monitor data when available.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/VirtualHostEnvelope"}}}},
      "VirtualHostNotFound": {"description": "No matching virtual host belongs to this API key.", "content": {"text/plain": {"schema": {"type": "string"}, "example": "Couldn't find a virtual host that matches for the api key used."}}},
      "DeletingVirtualHost": {"description": "Deletion accepted. The response is plain text because removal/config propagation is asynchronous.", "content": {"text/plain": {"schema": {"type": "string"}, "example": "Deleting customer.example.com"}}},
      "SuspendedVirtualHost": {"description": "The virtual host is suspended pending review.", "content": {"text/plain": {"schema": {"type": "string"}, "example": "Cannot update customer.example.com as it is suspended pending review."}}},
      "ValidationError": {"description": "Request validation failed. Keys and messages depend on invalid fields; upstream entries also carry params and a zero-based index.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationErrors"}}}},
      "BadRequest": {"description": "Malformed request.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SimpleError"}}}},
      "JsonNotFound": {"description": "Resource not found, not owned, or in the wrong scope.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationErrors"}, "example": {"errors": {"base": ["not_found"]}}}}},
      "SimpleJsonNotFound": {"description": "Resource not found or not owned.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SimpleError"}, "example": {"error": "not_found"}}}},
      "AnalyticsBadRequest": {"description": "Invalid start/end window or dimension.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorWithMessage"}}}},
      "LogsBadRequest": {"description": "window was not a positive integer or limit was not an integer.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorWithMessage"}}}},
      "InternalJsonError": {"description": "Unexpected backend error.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SimpleError"}, "example": {"error": "internal_error"}}}},
      "ToolInvalidInput": {"description": "Required input is missing or invalid.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolError"}, "example": {"ok": false, "error": "invalid_input", "message": "Missing or invalid `domain`."}}}},
      "ToolRateLimited": {"description": "A per-IP free-tool bucket denied the request. Limits are 500/day across tools, 30/minute across tools, and 10/minute for this tool.", "headers": {"Retry-After": {"description": "Seconds until the denying bucket resets.", "schema": {"type": "integer", "minimum": 1}, "example": 42}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolRateLimitError"}, "example": {"ok": false, "error": "rate_limited", "scope": "tool", "message": "Too many requests. Try again later."}}}},
      "ToolUpstreamError": {"description": "An upstream DNS, RDAP, or TLS dependency failed.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ToolError"}, "example": {"ok": false, "error": "upstream_error", "message": "The upstream service is unavailable right now."}}}}
      ,
      "DnsWidgetTokenRejected": {"description": "Widget token is missing or expired.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DnsWidgetError"}, "examples": {"expired": {"value": {"error": "token_expired", "message": "Token is expired. Please generate a new token server-side."}}, "missing": {"value": {"error": "missing_token", "message": "There is no token included in your renewal request, please include one."}}}}}}
    },
    "schemas": {
      "NaiveDateTime": {
        "type": "string",
        "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,6})?$",
        "description": "Offset-less ISO 8601 timestamp serialized from an Ecto NaiveDateTime. The API currently omits a UTC offset; do not require a trailing Z."
      },
      "SimpleError": {
        "type": "object",
        "required": ["error"],
        "properties": {"error": {"type": "string"}},
        "additionalProperties": false
      },
      "ErrorWithMessage": {
        "type": "object",
        "required": ["error", "message"],
        "properties": {"error": {"type": "string"}, "message": {"type": "string"}},
        "additionalProperties": false
      },
      "ValidationErrors": {
        "type": "object",
        "required": ["errors"],
        "properties": {
          "errors": {
            "type": "object",
            "description": "Field name to error messages, with structured upstream validation entries where applicable.",
            "additionalProperties": true,
            "example": {"incoming_address": ["this incoming address has already been created on the cluster you selected"]}
          }
        },
        "additionalProperties": false
      },
      "IncomingAddressRequest": {
        "type": "object",
        "required": ["incoming_address"],
        "properties": {"incoming_address": {"type": "string", "example": "customer.example.com"}},
        "additionalProperties": false
      },
      "UpstreamWrite": {
        "type": "object",
        "required": ["host"],
        "properties": {
          "host": {"type": "string", "format": "hostname", "description": "Hostname only; no scheme, path, or port.", "example": "origin-2.example.net"},
          "port": {"type": "integer", "minimum": 1, "maximum": 65535, "default": 443, "description": "Defaults to 443 when omitted."},
          "enabled": {"type": "boolean", "default": true, "description": "Defaults to true when omitted."},
          "priority": {"type": "integer", "minimum": 0, "maximum": 999, "default": 0},
          "weight": {"type": "integer", "minimum": 0, "maximum": 999, "default": 1}
        },
        "additionalProperties": false
      },
      "Upstream": {
        "type": "object",
        "required": ["id", "host", "port", "enabled"],
        "properties": {
          "id": {"type": "integer"}, "host": {"type": "string"}, "port": {"type": "integer"}, "enabled": {"type": "boolean"}
        },
        "additionalProperties": false
      },
      "VirtualHostCreate": {
        "type": "object",
        "required": ["incoming_address", "target_address"],
        "properties": {
          "incoming_address": {"type": "string", "description": "Custom domain, optionally including a path for exact-match routing.", "example": "customer.example.com"},
          "target_address": {"type": "string", "description": "Origin hostname or URL/path. Redirect targets should include http:// or https://.", "example": "origin.example.net"},
          "target_ports": {"oneOf": [{"type": "string"}, {"type": "integer"}], "default": "443", "examples": ["443", "80", "8080"]},
          "wildcard": {"type": "boolean", "default": false},
          "redirect": {"type": "boolean", "default": false},
          "exact_match": {"type": "boolean", "default": false},
          "redirect_www": {"oneOf": [{"type": "boolean"}, {"type": "string", "enum": ["true", "false"]}], "default": false},
          "keep_host": {"type": ["boolean", "null"], "description": "null inherits the cluster default."},
          "lb_enabled": {"type": "boolean", "default": false},
          "lb_policy": {"type": "string", "enum": ["first", "random", "round_robin", "least_conn"], "default": "first"},
          "lb_sticky": {"type": "boolean", "default": true},
          "mtls_client_cert_id": {"type": ["string", "null"], "description": "UUID of an mTLS client certificate belonging to the cluster."},
          "rate_limit": {"type": ["integer", "null"], "minimum": 1, "maximum": 10000000, "description": "Optional per-vhost request-rate limit."},
          "upstreams": {"type": "array", "items": {"$ref": "#/components/schemas/UpstreamWrite"}, "default": []}
        },
        "additionalProperties": true,
        "description": "Unknown/admin-only keys are ignored by the backend rather than rejected."
      },
      "VirtualHostUpdate": {
        "type": "object",
        "minProperties": 1,
        "properties": {
          "incoming_address": {"type": "string"},
          "target_address": {"type": "string"},
          "target_ports": {"oneOf": [{"type": "string"}, {"type": "integer"}]},
          "redirect": {"type": "boolean"},
          "exact_match": {"type": "boolean"},
          "keep_host": {"type": ["boolean", "null"]},
          "lb_enabled": {"type": "boolean"},
          "lb_policy": {"type": "string", "enum": ["first", "random", "round_robin", "least_conn"]},
          "lb_sticky": {"type": "boolean"},
          "mtls_client_cert_id": {"type": ["string", "null"]},
          "rate_limit": {"type": ["integer", "null"], "minimum": 1, "maximum": 10000000},
          "upstreams": {"type": "array", "description": "When present, replaces all existing additional upstreams.", "items": {"$ref": "#/components/schemas/UpstreamWrite"}}
        },
        "additionalProperties": true,
        "description": "Omitted fields are unchanged. Unknown/admin-only keys are ignored."
      },
      "VirtualHostUpdateByIncoming": {
        "allOf": [
          {"$ref": "#/components/schemas/VirtualHostUpdate"},
          {"type": "object", "required": ["current_incoming_address"], "properties": {"current_incoming_address": {"type": "string", "example": "customer.example.com"}}}
        ]
      },
      "VirtualHostBulkUpdate": {
        "type": "object",
        "required": ["incoming_addresses", "updates"],
        "properties": {
          "incoming_addresses": {"type": "array", "items": {"type": "string"}, "minItems": 1, "description": "Addresses are de-duplicated and only the first 100 unique values are processed; additional values are silently truncated."},
          "updates": {"$ref": "#/components/schemas/VirtualHostUpdate"}
        },
        "additionalProperties": false
      },
      "MtlsClientCertReference": {
        "type": "object",
        "required": ["name", "id"],
        "properties": {"name": {"type": "string"}, "id": {"type": "string"}},
        "additionalProperties": false
      },
      "CaaDetails": {
        "type": "object",
        "properties": {
          "governing_domain": {"type": ["string", "null"]},
          "records": {"type": "array", "items": {"type": "string"}},
          "allowed_cas": {"type": "array", "items": {"type": "string"}},
          "denied_cas": {"type": "array", "items": {"type": "string"}},
          "message": {"type": ["string", "null"]}
        },
        "additionalProperties": true
      },
      "VirtualHost": {
        "type": "object",
        "required": ["id", "incoming_address", "target_address", "target_ports", "keep_host", "lb_policy", "lb_enabled", "lb_sticky", "rate_limit", "coraza_enabled", "mtls_client_cert", "upstreams"],
        "properties": {
          "id": {"type": "integer"},
          "incoming_address": {"type": "string"},
          "target_address": {"type": "string"},
          "target_ports": {"type": "string"},
          "keep_host": {"type": ["boolean", "null"]},
          "lb_policy": {"type": "string", "enum": ["first", "random", "round_robin", "least_conn"]},
          "lb_enabled": {"type": "boolean"},
          "lb_sticky": {"type": "boolean"},
          "rate_limit": {"type": ["integer", "null"]},
          "coraza_enabled": {"type": ["boolean", "null"], "readOnly": true, "description": "Admin-managed WAF override; returned for visibility but ignored on customer writes."},
          "mtls_client_cert": {"oneOf": [{"$ref": "#/components/schemas/MtlsClientCertReference"}, {"type": "null"}]},
          "upstreams": {"type": "array", "items": {"$ref": "#/components/schemas/Upstream"}},
          "created_at": {"$ref": "#/components/schemas/NaiveDateTime"},
          "has_ssl": {"type": ["boolean", "null"]},
          "is_resolving": {"type": ["boolean", "null"]},
          "ssl_active_from": {"oneOf": [{"$ref": "#/components/schemas/NaiveDateTime"}, {"type": "null"}]},
          "ssl_active_until": {"oneOf": [{"$ref": "#/components/schemas/NaiveDateTime"}, {"type": "null"}]},
          "status": {"type": "string"},
          "status_message": {"type": "string"},
          "dns_pointed_at": {"type": ["string", "null"]},
          "apx_hit": {"type": ["boolean", "null"]},
          "caa_status": {"type": ["string", "null"], "enum": ["ok", "blocked", "inconclusive", null]},
          "caa_details": {"oneOf": [{"$ref": "#/components/schemas/CaaDetails"}, {"type": "null"}]},
          "last_monitored_unix": {"type": "integer"},
          "last_monitored_humanized": {"type": "string"},
          "utc_last_updated": {"$ref": "#/components/schemas/NaiveDateTime"},
          "human_readable_last_updated": {"type": "string"},
          "user_message": {"type": "string"},
          "user_dns_instructions": {"description": "Provider-aware DNS guidance generated for this virtual host."}
        },
        "additionalProperties": false
      },
      "VirtualHostEnvelope": {"type": "object", "required": ["data"], "properties": {"data": {"$ref": "#/components/schemas/VirtualHost"}}, "additionalProperties": false},
      "VirtualHostListEnvelope": {"type": "object", "required": ["data"], "properties": {"data": {"type": "array", "items": {"$ref": "#/components/schemas/VirtualHost"}}}, "additionalProperties": false},
      "VirtualHostCursorEnvelope": {
        "type": "object", "required": ["data", "before_cursor", "after_cursor"],
        "properties": {"data": {"type": "array", "items": {"$ref": "#/components/schemas/VirtualHost"}}, "before_cursor": {"type": ["string", "null"]}, "after_cursor": {"type": ["string", "null"]}},
        "additionalProperties": false
      },
      "VirtualHostBulkUpdateEnvelope": {
        "type": "object", "required": ["data"],
        "properties": {"data": {"type": "object", "required": ["summary", "successful", "failed", "not_found"], "properties": {
          "summary": {"type": "object", "required": ["total_requested", "successful", "failed", "not_found", "suspended"], "properties": {"total_requested": {"type": "integer"}, "successful": {"type": "integer"}, "failed": {"type": "integer"}, "not_found": {"type": "integer"}, "suspended": {"type": "integer"}}, "additionalProperties": false},
          "successful": {"type": "array", "items": {"type": "string"}},
          "failed": {"type": "array", "items": {"type": "object", "required": ["current_incoming_address", "errors"], "properties": {"current_incoming_address": {"type": "string"}, "errors": {"type": "object", "additionalProperties": true}}}},
          "not_found": {"type": "array", "items": {"type": "string"}}
        }, "additionalProperties": false}},
        "additionalProperties": false
      },
      "DnsCheckRecord": {
        "type": "object", "required": ["address", "type", "match_against"],
        "properties": {"address": {"type": "string", "example": "example.com"}, "type": {"type": "string", "description": "Lowercase DNS record type.", "example": "a"}, "match_against": {"type": "string", "example": "93.184.216.34"}},
        "additionalProperties": true
      },
      "DnsCheckRequest": {"type": "object", "required": ["records"], "properties": {"records": {"oneOf": [{"$ref": "#/components/schemas/DnsCheckRecord"}, {"type": "array", "items": {"$ref": "#/components/schemas/DnsCheckRecord"}}]}}, "additionalProperties": false},
      "DnsCheckResult": {
        "allOf": [
          {"$ref": "#/components/schemas/DnsCheckRecord"},
          {"type": "object", "required": ["actual_values", "match"], "properties": {"actual_values": {"oneOf": [{"type": "array", "items": {"type": "string"}}, {"const": false}], "description": "false when DNS resolution or the record-type lookup failed."}, "match": {"type": "boolean"}}}
        ]
      },
      "DnsCheckResponse": {"type": "object", "required": ["records"], "properties": {"records": {"type": "array", "items": {"$ref": "#/components/schemas/DnsCheckResult"}}}, "additionalProperties": false},
      "DnsWidgetTokenEnvelope": {"type": "object", "required": ["token"], "properties": {"token": {"type": "string", "description": "Phoenix-signed token accepted for approximately 10 minutes."}}, "additionalProperties": false},
      "DnsWidgetTokenRequest": {"type": "object", "required": ["token"], "properties": {"token": {"type": "string"}}, "additionalProperties": false},
      "DnsWidgetError": {"type": "object", "required": ["error", "message"], "properties": {"error": {"type": "string", "enum": ["token_expired", "missing_token"]}, "message": {"type": "string"}}, "additionalProperties": false},
      "DnsWidgetRecord": {
        "type": "object", "required": ["domain", "type", "host", "value", "ttl"],
        "properties": {"domain": {"type": "string"}, "type": {"type": "string", "description": "The widget supports A, CNAME, and TXT records."}, "host": {"type": "string", "description": "@ for apex or a relative subdomain."}, "value": {"type": "string"}, "ttl": {"oneOf": [{"type": "integer"}, {"type": "string"}]}},
        "additionalProperties": true
      },
      "DnsWidgetInstructionsRequest": {"type": "object", "required": ["token", "records"], "properties": {"token": {"type": "string"}, "records": {"type": "array", "minItems": 1, "items": {"$ref": "#/components/schemas/DnsWidgetRecord"}}}, "additionalProperties": false},
      "DnsWidgetHtmlFragment": {"type": "object", "required": ["html"], "properties": {"html": {"type": "string", "description": "Trusted widget HTML intended for the Approximated DNS widget renderer."}}, "additionalProperties": true},
      "DnsWidgetInstructionsResponse": {
        "type": "object", "required": ["domain_results", "verify_section"],
        "properties": {
          "domain_results": {"type": "object", "additionalProperties": {"type": "object", "required": ["steps", "records"], "properties": {"steps": {"type": "array", "items": {"$ref": "#/components/schemas/DnsWidgetHtmlFragment"}}, "records": {"type": "array", "items": {"type": "object", "additionalProperties": true}}}, "additionalProperties": false}},
          "verify_section": {"$ref": "#/components/schemas/DnsWidgetHtmlFragment"}
        },
        "additionalProperties": false
      },
      "DnsWidgetExactCheckRecord": {
        "type": "object", "required": ["domain", "host", "type", "match_against"],
        "properties": {"domain": {"type": "string"}, "host": {"type": "string"}, "type": {"type": "string"}, "match_against": {"type": "string"}, "value": {"type": "string"}, "apex": {"type": "string"}, "tld": {"type": "string"}},
        "additionalProperties": true
      },
      "DnsWidgetExactCheckRequest": {"type": "object", "required": ["token", "records"], "properties": {"token": {"type": "string"}, "records": {"oneOf": [{"$ref": "#/components/schemas/DnsWidgetExactCheckRecord"}, {"type": "array", "items": {"$ref": "#/components/schemas/DnsWidgetExactCheckRecord"}}]}}, "additionalProperties": false},
      "DnsWidgetExactCheckResult": {
        "type": "object", "required": ["domain", "host", "type", "match_against", "actual_values", "match", "combined_host", "apex", "tld", "full", "non_tld", "subdomain"],
        "properties": {"domain": {"type": "string"}, "host": {"type": "string"}, "type": {"type": "string"}, "match_against": {"type": "string"}, "value": {"type": "string"}, "actual_values": {"oneOf": [{"type": "array", "items": {"type": "string"}}, {"const": false}]}, "match": {"type": "boolean"}, "combined_host": {"type": "string"}, "apex": {"type": "string"}, "tld": {"type": "string"}, "full": {"type": "string"}, "non_tld": {"type": "string"}, "subdomain": {"type": "string"}},
        "additionalProperties": true
      },
      "DnsWidgetExactCheckResponse": {"type": "object", "required": ["records"], "properties": {"records": {"type": "array", "items": {"$ref": "#/components/schemas/DnsWidgetExactCheckResult"}}}, "additionalProperties": false},
      "DnsWidgetExistCheckRequest": {"type": "object", "required": ["token", "records"], "properties": {"token": {"type": "string"}, "records": {"oneOf": [{"$ref": "#/components/schemas/DnsCheckRecord"}, {"type": "array", "items": {"$ref": "#/components/schemas/DnsCheckRecord"}}]}}, "additionalProperties": false},
      "HealthCheck": {
        "type": "object", "required": ["id", "results", "human_readable_results", "utc_last_updated", "human_readable_last_updated"],
        "properties": {"id": {"type": "integer"}, "results": {"type": "object", "description": "Region code to healthy boolean.", "additionalProperties": {"type": "boolean"}}, "human_readable_results": {"type": "object", "description": "Region display name to Healthy or Repairing.", "additionalProperties": {"type": "string", "enum": ["Healthy", "Repairing"]}}, "utc_last_updated": {"$ref": "#/components/schemas/NaiveDateTime"}, "human_readable_last_updated": {"type": "string"}},
        "additionalProperties": false
      },
      "HealthCheckEnvelope": {"type": "object", "required": ["data"], "properties": {"data": {"oneOf": [{"$ref": "#/components/schemas/HealthCheck"}, {"type": "null"}]}}, "additionalProperties": false},
      "EdgeVerifyRuleWrite": {
        "type": "object", "required": ["path_pattern", "methods", "mode"],
        "properties": {
          "path_pattern": {"type": "string", "pattern": "^/", "description": "Matches this path and its descendants; a trailing slash is removed except for root.", "example": "/contact"},
          "methods": {"type": "array", "minItems": 1, "items": {"type": "string", "enum": ["POST", "PUT", "PATCH", "DELETE"]}, "example": ["POST"]},
          "mode": {"type": "string", "enum": ["monitor", "enforce"]}
        }, "additionalProperties": false
      },
      "EdgeVerifyRulePatch": {
        "type": "object", "minProperties": 1,
        "properties": {"path_pattern": {"type": "string", "pattern": "^/"}, "methods": {"type": "array", "minItems": 1, "items": {"type": "string", "enum": ["POST", "PUT", "PATCH", "DELETE"]}}, "mode": {"type": "string", "enum": ["monitor", "enforce"]}},
        "additionalProperties": false
      },
      "EdgeVerifyVhostCreate": {
        "type": "object", "required": ["incoming_address", "path_pattern", "methods", "mode"],
        "properties": {"incoming_address": {"type": "string"}, "path_pattern": {"type": "string", "pattern": "^/"}, "methods": {"type": "array", "minItems": 1, "items": {"type": "string", "enum": ["POST", "PUT", "PATCH", "DELETE"]}}, "mode": {"type": "string", "enum": ["monitor", "enforce"]}},
        "additionalProperties": false
      },
      "EdgeVerifyVhostUpdate": {
        "type": "object", "required": ["incoming_address", "id"], "minProperties": 3,
        "properties": {"incoming_address": {"type": "string"}, "id": {"type": "integer", "minimum": 1}, "path_pattern": {"type": "string", "pattern": "^/"}, "methods": {"type": "array", "minItems": 1, "items": {"type": "string", "enum": ["POST", "PUT", "PATCH", "DELETE"]}}, "mode": {"type": "string", "enum": ["monitor", "enforce"]}},
        "additionalProperties": false
      },
      "EdgeVerifyVhostDelete": {"type": "object", "required": ["incoming_address", "id"], "properties": {"incoming_address": {"type": "string"}, "id": {"type": "integer", "minimum": 1}}, "additionalProperties": false},
      "EdgeVerifyRule": {
        "type": "object", "required": ["id", "virtual_host_id", "proxy_server_id", "path_pattern", "methods", "mode", "inserted_at", "updated_at"],
        "properties": {"id": {"type": "integer"}, "virtual_host_id": {"type": ["integer", "null"]}, "proxy_server_id": {"type": ["integer", "null"]}, "path_pattern": {"type": "string"}, "methods": {"type": "array", "items": {"type": "string"}}, "mode": {"type": "string", "enum": ["monitor", "enforce"]}, "inserted_at": {"$ref": "#/components/schemas/NaiveDateTime"}, "updated_at": {"$ref": "#/components/schemas/NaiveDateTime"}},
        "additionalProperties": false
      },
      "EdgeVerifyRuleEnvelope": {"type": "object", "required": ["data"], "properties": {"data": {"$ref": "#/components/schemas/EdgeVerifyRule"}}, "additionalProperties": false},
      "EdgeVerifyRuleListEnvelope": {"type": "object", "required": ["data"], "properties": {"data": {"type": "array", "items": {"$ref": "#/components/schemas/EdgeVerifyRule"}}}, "additionalProperties": false},
      "L4RuleLookup": {"type": "object", "required": ["match_type", "value"], "properties": {"match_type": {"type": "string", "enum": ["ip", "sni", "ja3", "ja4"]}, "value": {"type": "string"}}, "additionalProperties": false},
      "L4RuleCreate": {
        "type": "object", "required": ["match_type", "value"],
        "properties": {"match_type": {"type": "string", "enum": ["ip", "sni", "ja3", "ja4"]}, "value": {"type": "string"}, "action": {"type": "string", "enum": ["block"], "default": "block"}, "reason": {"type": "string"}, "expires_at": {"type": ["string", "null"], "format": "date-time", "description": "Must be in the future when present."}},
        "additionalProperties": true,
        "description": "proxy_server_id is ignored if supplied; cluster scope always comes from the API key."
      },
      "L4Rule": {
        "type": "object", "required": ["id", "proxy_server_id", "match_type", "value", "action", "source", "expires_at", "inserted_at", "updated_at"],
        "properties": {"id": {"type": "integer"}, "proxy_server_id": {"type": "integer"}, "match_type": {"type": "string"}, "value": {"type": "string"}, "action": {"type": "string", "enum": ["block", "allow", "challenge"]}, "reason": {"type": ["string", "null"]}, "source": {"type": "string", "enum": ["worker", "admin", "customer", "system", "fleet"]}, "expires_at": {"type": ["string", "null"], "format": "date-time"}, "inserted_at": {"$ref": "#/components/schemas/NaiveDateTime"}, "updated_at": {"$ref": "#/components/schemas/NaiveDateTime"}},
        "additionalProperties": false
      },
      "L4RuleEnvelope": {"type": "object", "required": ["data"], "properties": {"data": {"$ref": "#/components/schemas/L4Rule"}}, "additionalProperties": false},
      "L4RuleListEnvelope": {"type": "object", "required": ["data", "cursors"], "properties": {"data": {"type": "array", "items": {"$ref": "#/components/schemas/L4Rule"}}, "cursors": {"type": "object", "required": ["before", "after"], "properties": {"before": {"type": ["string", "null"]}, "after": {"type": ["string", "null"]}}, "additionalProperties": false}}, "additionalProperties": false},
      "L4Conflict": {
        "type": "object", "required": ["error", "message", "blocker"],
        "properties": {"error": {"const": "conflict_not_revokable"}, "message": {"type": "string"}, "blocker": {"type": "object", "required": ["id", "source", "kind"], "properties": {"id": {"type": "integer"}, "source": {"type": "string"}, "kind": {"type": "string", "enum": ["auto_block_exemption", "l4_rule"]}}, "additionalProperties": false}},
        "additionalProperties": false
      },
      "AnalyticsWindow": {"type": "object", "required": ["start", "end"], "properties": {"start": {"type": "string", "format": "date-time"}, "end": {"type": "string", "format": "date-time"}}, "additionalProperties": false},
      "AnalyticsBucket": {
        "type": "object", "required": ["ts", "request_count", "bytes_in", "bytes_out", "bytes_egress", "by_origin", "by_status_class"],
        "properties": {"ts": {"type": "string", "format": "date-time"}, "request_count": {"type": "integer", "minimum": 0}, "bytes_in": {"type": "integer", "minimum": 0}, "bytes_out": {"type": "integer", "minimum": 0}, "bytes_egress": {"type": "integer", "minimum": 0}, "by_origin": {"type": "object", "additionalProperties": {"type": "integer"}}, "by_status_class": {"type": "object", "additionalProperties": {"type": "integer"}}},
        "additionalProperties": false
      },
      "AnalyticsSeriesEnvelope": {
        "type": "object", "required": ["data"], "properties": {"data": {"type": "object", "required": ["proxy_server_id", "window", "granularity", "buckets"], "properties": {"proxy_server_id": {"type": "integer"}, "vhost_id": {"type": "integer"}, "window": {"$ref": "#/components/schemas/AnalyticsWindow"}, "granularity": {"type": "string", "enum": ["raw_1m", "h_1h", "d_1d"]}, "buckets": {"type": "array", "items": {"$ref": "#/components/schemas/AnalyticsBucket"}}}, "additionalProperties": false}},
        "additionalProperties": false
      },
      "AnalyticsTopEnvelope": {
        "type": "object", "required": ["data"], "properties": {"data": {"type": "object", "required": ["proxy_server_id", "window", "granularity", "dim", "items"], "properties": {"proxy_server_id": {"type": "integer"}, "vhost_id": {"type": "integer"}, "window": {"$ref": "#/components/schemas/AnalyticsWindow"}, "granularity": {"type": "string", "enum": ["raw_1m", "h_1h", "d_1d"]}, "dim": {"type": "string", "enum": ["country", "status", "method", "origin"]}, "items": {"type": "array", "items": {"type": "object", "required": ["value", "request_count", "bytes_in", "bytes_out"], "properties": {"value": {"oneOf": [{"type": "string"}, {"type": "integer"}]}, "request_count": {"type": "integer"}, "bytes_in": {"type": "integer"}, "bytes_out": {"type": "integer"}}, "additionalProperties": false}}}, "additionalProperties": false}},
        "additionalProperties": false
      },
      "RequestLog": {
        "type": "object", "required": ["ts", "vhost_id", "host", "method", "path", "status", "http_version", "client_ip", "country", "ua", "bytes_in", "bytes_out", "duration_us", "disposition", "sample_rate"],
        "properties": {"ts": {"type": "string", "format": "date-time"}, "vhost_id": {"type": "integer"}, "host": {"type": "string"}, "method": {"type": "string"}, "path": {"type": "string"}, "status": {"type": "integer"}, "http_version": {"type": "string"}, "client_ip": {"type": ["string", "null"]}, "country": {"type": ["string", "null"]}, "ua": {"type": "string"}, "bytes_in": {"type": "integer", "minimum": 0}, "bytes_out": {"type": "integer", "minimum": 0}, "duration_us": {"type": "integer", "minimum": 0}, "disposition": {"type": "string"}, "sample_rate": {"type": "integer", "minimum": 1}},
        "additionalProperties": false
      },
      "RequestLogEnvelope": {"type": "object", "required": ["logs", "next_cursor"], "properties": {"logs": {"type": "array", "items": {"$ref": "#/components/schemas/RequestLog"}}, "next_cursor": {"type": ["string", "null"]}}, "additionalProperties": false},
      "ToolError": {
        "type": "object", "required": ["ok", "error", "message"],
        "properties": {"ok": {"const": false}, "error": {"type": "string", "enum": ["invalid_input", "blocked_host", "rate_limited", "upstream_error", "upstream_timeout"]}, "message": {"type": "string"}},
        "additionalProperties": false
      },
      "ToolRateLimitError": {
        "type": "object", "required": ["ok", "error", "message", "scope"],
        "properties": {"ok": {"const": false}, "error": {"const": "rate_limited"}, "message": {"type": "string"}, "scope": {"type": "string", "enum": ["tool", "global", "daily"]}},
        "additionalProperties": false
      },
      "ToolPingEnvelope": {
        "type": "object", "required": ["ok", "data"],
        "properties": {"ok": {"const": true}, "data": {"type": "object", "required": ["pong"], "properties": {"pong": {"const": true}}, "additionalProperties": false}},
        "additionalProperties": false
      },
      "ToolSuccess": {
        "type": "object", "required": ["ok", "data"],
        "properties": {"ok": {"const": true}, "data": {"type": "object"}},
        "additionalProperties": false
      },
      "StringList": {"type": "array", "items": {"type": "string"}},
      "ToolDnsRecordsEnvelope": {
        "type": "object", "required": ["ok", "data"],
        "properties": {"ok": {"const": true}, "data": {"type": "object", "required": ["domain", "records"], "properties": {"domain": {"type": "string"}, "records": {"type": "object", "additionalProperties": {"$ref": "#/components/schemas/StringList"}, "example": {"a": ["93.184.216.34"], "aaaa": [], "cname": []}}}, "additionalProperties": false}},
        "additionalProperties": false
      },
      "ToolTxtEnvelope": {
        "type": "object", "required": ["ok", "data"],
        "properties": {"ok": {"const": true}, "data": {"type": "object", "required": ["domain", "records", "categorized"], "properties": {"domain": {"type": "string"}, "records": {"$ref": "#/components/schemas/StringList"}, "categorized": {"type": "object", "required": ["spf", "dkim", "dmarc", "verification", "other"], "properties": {"spf": {"$ref": "#/components/schemas/StringList"}, "dkim": {"$ref": "#/components/schemas/StringList"}, "dmarc": {"$ref": "#/components/schemas/StringList"}, "verification": {"$ref": "#/components/schemas/StringList"}, "other": {"$ref": "#/components/schemas/StringList"}}, "additionalProperties": false}}, "additionalProperties": false}},
        "additionalProperties": false
      },
      "ToolSpfEnvelope": {
        "type": "object", "required": ["ok", "data"],
        "properties": {"ok": {"const": true}, "data": {"type": "object", "required": ["domain", "found", "record", "mechanisms", "all_qualifier", "dns_lookups", "warnings"], "properties": {"domain": {"type": "string"}, "found": {"type": "boolean"}, "record": {"type": ["string", "null"]}, "mechanisms": {"$ref": "#/components/schemas/StringList"}, "all_qualifier": {"type": ["string", "null"], "enum": ["+", "-", "~", "?", null]}, "dns_lookups": {"type": "integer", "minimum": 0}, "warnings": {"$ref": "#/components/schemas/StringList"}}, "additionalProperties": false}},
        "additionalProperties": false
      },
      "ToolDmarcEnvelope": {
        "type": "object", "required": ["ok", "data"],
        "properties": {"ok": {"const": true}, "data": {"type": "object", "required": ["domain", "found", "record", "policy", "subdomain_policy", "pct", "rua", "warnings"], "properties": {"domain": {"type": "string"}, "found": {"type": "boolean"}, "record": {"type": ["string", "null"]}, "policy": {"type": ["string", "null"]}, "subdomain_policy": {"type": ["string", "null"]}, "pct": {"type": ["integer", "null"]}, "rua": {"$ref": "#/components/schemas/StringList"}, "warnings": {"$ref": "#/components/schemas/StringList"}}, "additionalProperties": false}},
        "additionalProperties": false
      },
      "ToolDkimEnvelope": {
        "type": "object", "required": ["ok", "data"],
        "properties": {"ok": {"const": true}, "data": {"type": "object", "required": ["domain", "selector", "found", "record", "key_type", "public_key_present", "warnings"], "properties": {"domain": {"type": "string"}, "selector": {"type": "string"}, "found": {"type": "boolean"}, "record": {"type": ["string", "null"]}, "key_type": {"type": ["string", "null"]}, "public_key_present": {"type": "boolean"}, "warnings": {"$ref": "#/components/schemas/StringList"}}, "additionalProperties": false}},
        "additionalProperties": false
      },
      "CnameProvider": {
        "type": "object", "required": ["name", "provider_domain", "nameservers"],
        "properties": {"name": {"type": "string"}, "provider_domain": {"type": "string"}, "nameservers": {"$ref": "#/components/schemas/StringList"}},
        "additionalProperties": false
      },
      "DnsInstructionRecord": {
        "type": "object", "required": ["type", "host", "domain", "value", "ttl", "subdomain", "combined_host", "apex_domain"],
        "properties": {"type": {"type": "string"}, "host": {"type": "string"}, "domain": {"type": "string"}, "value": {"type": "string"}, "ttl": {"oneOf": [{"type": "string"}, {"type": "integer"}]}, "subdomain": {"type": "string"}, "combined_host": {"type": "string"}, "apex_domain": {"type": "string"}},
        "additionalProperties": true
      },
      "CnameInstructions": {
        "type": "object", "required": ["domain_results"],
        "properties": {"domain_results": {"type": "object", "additionalProperties": {"type": "object", "required": ["steps", "records"], "properties": {"steps": {"description": "Plain text with server-side HTML stripped.", "$ref": "#/components/schemas/StringList"}, "records": {"type": "array", "items": {"$ref": "#/components/schemas/DnsInstructionRecord"}}}, "additionalProperties": false}}},
        "additionalProperties": false
      },
      "ToolCnameEnvelope": {
        "type": "object", "required": ["ok", "data"],
        "properties": {"ok": {"const": true}, "data": {"oneOf": [
          {"type": "object", "required": ["domain", "target", "chain"], "properties": {"domain": {"type": "string"}, "target": {"type": ["string", "null"]}, "chain": {"$ref": "#/components/schemas/StringList"}}, "additionalProperties": false},
          {"type": "object", "required": ["domain", "target", "chain", "requested_target", "provider", "instructions"], "properties": {"domain": {"type": "string"}, "target": {"type": ["string", "null"]}, "chain": {"$ref": "#/components/schemas/StringList"}, "requested_target": {"type": "string"}, "provider": {"$ref": "#/components/schemas/CnameProvider"}, "instructions": {"oneOf": [{"$ref": "#/components/schemas/CnameInstructions"}, {"type": "null"}]}}, "additionalProperties": false}
        ]}},
        "additionalProperties": false
      },
      "ToolReverseIpEnvelope": {
        "type": "object", "required": ["ok", "data"],
        "properties": {"ok": {"const": true}, "data": {"type": "object", "required": ["ip", "hostnames"], "properties": {"ip": {"type": "string"}, "hostnames": {"$ref": "#/components/schemas/StringList"}}, "additionalProperties": false}},
        "additionalProperties": false
      },
      "PropagationResolverResult": {
        "type": "object", "required": ["resolver", "status", "ips", "error", "duration_ms"],
        "properties": {"resolver": {"type": "string"}, "status": {"type": "string", "enum": ["ok", "error"]}, "ips": {"$ref": "#/components/schemas/StringList"}, "error": {"type": ["string", "null"]}, "duration_ms": {"type": "integer", "minimum": 0}},
        "additionalProperties": false
      },
      "ToolPropagationEnvelope": {
        "type": "object", "required": ["ok", "data"],
        "properties": {"ok": {"const": true}, "data": {"type": "object", "required": ["domain", "consistent", "results"], "properties": {"domain": {"type": "string"}, "consistent": {"type": "boolean"}, "results": {"type": "array", "items": {"$ref": "#/components/schemas/PropagationResolverResult"}}}, "additionalProperties": false}},
        "additionalProperties": false
      },
      "ToolStatusEnvelope": {
        "type": "object", "required": ["ok", "data"],
        "properties": {"ok": {"const": true}, "data": {"type": "object", "required": ["url", "up", "status", "response_time_ms", "final_url", "error"], "properties": {"url": {"type": "string", "format": "uri"}, "up": {"type": "boolean"}, "status": {"type": ["integer", "null"]}, "response_time_ms": {"type": ["integer", "null"]}, "final_url": {"type": "string", "format": "uri"}, "error": {"type": ["string", "null"]}}, "additionalProperties": false}},
        "additionalProperties": false
      },
      "SecurityHeaderGrade": {
        "type": "object", "required": ["grade", "present", "missing"],
        "properties": {"grade": {"type": "string", "enum": ["A", "B", "C", "D", "E", "F"]}, "present": {"$ref": "#/components/schemas/StringList"}, "missing": {"$ref": "#/components/schemas/StringList"}},
        "additionalProperties": false
      },
      "ToolHeadersEnvelope": {
        "type": "object", "required": ["ok", "data"],
        "properties": {"ok": {"const": true}, "data": {"type": "object", "required": ["url", "status", "headers", "security", "error"], "properties": {"url": {"type": "string", "format": "uri"}, "status": {"type": ["integer", "null"]}, "headers": {"type": "object", "additionalProperties": {"type": "string"}}, "security": {"$ref": "#/components/schemas/SecurityHeaderGrade"}, "error": {"type": ["string", "null"]}}, "additionalProperties": false}},
        "additionalProperties": false
      },
      "RedirectHop": {"type": "object", "required": ["url", "status", "location"], "properties": {"url": {"type": "string", "format": "uri"}, "status": {"type": ["integer", "null"]}, "location": {"type": ["string", "null"]}}, "additionalProperties": false},
      "ToolRedirectsEnvelope": {
        "type": "object", "required": ["ok", "data"],
        "properties": {"ok": {"const": true}, "data": {"type": "object", "required": ["url", "hops", "final_url", "final_status", "count", "looped", "error"], "properties": {"url": {"type": "string", "format": "uri"}, "hops": {"type": "array", "items": {"$ref": "#/components/schemas/RedirectHop"}}, "final_url": {"type": "string", "format": "uri"}, "final_status": {"type": ["integer", "null"]}, "count": {"type": "integer", "minimum": 0}, "looped": {"type": "boolean"}, "error": {"type": ["string", "null"]}}, "additionalProperties": false}},
        "additionalProperties": false
      },
      "ToolSslEnvelope": {
        "type": "object", "required": ["ok", "data"],
        "properties": {"ok": {"const": true}, "data": {"type": "object", "required": ["host", "port", "subject", "issuer", "sans", "not_before", "not_after", "days_remaining", "serial", "signature_algorithm", "self_signed"], "properties": {"host": {"type": "string"}, "port": {"type": "integer"}, "subject": {"type": "string"}, "issuer": {"type": "string"}, "sans": {"$ref": "#/components/schemas/StringList"}, "not_before": {"type": "string", "format": "date-time"}, "not_after": {"type": "string", "format": "date-time"}, "days_remaining": {"type": "integer"}, "serial": {"type": "string"}, "signature_algorithm": {"type": "string"}, "self_signed": {"type": "boolean"}}, "additionalProperties": false}},
        "additionalProperties": false
      },
      "WhoisRegistered": {
        "type": "object", "required": ["domain", "registered", "registrar", "created", "updated", "expires", "nameservers", "statuses", "registrant_country"],
        "properties": {"domain": {"type": "string"}, "registered": {"const": true}, "registrar": {"type": ["string", "null"]}, "created": {"type": ["string", "null"], "format": "date-time"}, "updated": {"type": ["string", "null"], "format": "date-time"}, "expires": {"type": ["string", "null"], "format": "date-time"}, "nameservers": {"$ref": "#/components/schemas/StringList"}, "statuses": {"$ref": "#/components/schemas/StringList"}, "registrant_country": {"type": ["string", "null"]}},
        "additionalProperties": false
      },
      "RegistrationNegative": {"type": "object", "required": ["domain", "registered"], "properties": {"domain": {"type": "string"}, "registered": {"const": false}}, "additionalProperties": false},
      "RegistrationUnknown": {"type": "object", "required": ["domain", "registered", "note"], "properties": {"domain": {"type": "string"}, "registered": {"type": "null"}, "note": {"type": "string"}}, "additionalProperties": false},
      "ToolWhoisEnvelope": {
        "type": "object", "required": ["ok", "data"],
        "properties": {"ok": {"const": true}, "data": {"oneOf": [{"$ref": "#/components/schemas/WhoisRegistered"}, {"$ref": "#/components/schemas/RegistrationNegative"}, {"$ref": "#/components/schemas/RegistrationUnknown"}]}},
        "additionalProperties": false
      },
      "ToolSubdomainsEnvelope": {
        "type": "object", "required": ["ok", "data"],
        "properties": {"ok": {"const": true}, "data": {"type": "object", "required": ["domain", "subdomains", "count", "truncated", "source"], "properties": {"domain": {"type": "string"}, "subdomains": {"$ref": "#/components/schemas/StringList"}, "count": {"type": "integer", "minimum": 0, "maximum": 500}, "truncated": {"type": "boolean"}, "source": {"const": "crt.sh"}, "error": {"type": "string", "description": "Present only when crt.sh returned a partial/failure result."}}, "additionalProperties": false}},
        "additionalProperties": false
      },
      "DomainAgeRegistered": {
        "type": "object", "required": ["domain", "registered", "created", "expires", "age_days", "age_years"],
        "properties": {"domain": {"type": "string"}, "registered": {"const": true}, "created": {"type": ["string", "null"], "format": "date-time"}, "expires": {"type": ["string", "null"], "format": "date-time"}, "age_days": {"type": ["integer", "null"]}, "age_years": {"type": ["integer", "null"]}},
        "additionalProperties": false
      },
      "ToolDomainAgeEnvelope": {
        "type": "object", "required": ["ok", "data"],
        "properties": {"ok": {"const": true}, "data": {"oneOf": [{"$ref": "#/components/schemas/DomainAgeRegistered"}, {"$ref": "#/components/schemas/RegistrationNegative"}, {"$ref": "#/components/schemas/RegistrationUnknown"}]}},
        "additionalProperties": false
      },
      "AvailabilitySingle": {
        "oneOf": [
          {"type": "object", "required": ["domain", "available"], "properties": {"domain": {"type": "string"}, "available": {"const": true}}, "additionalProperties": false},
          {"type": "object", "required": ["domain", "available", "expires"], "properties": {"domain": {"type": "string"}, "available": {"const": false}, "expires": {"type": ["string", "null"], "format": "date-time"}}, "additionalProperties": false},
          {"type": "object", "required": ["domain", "available", "note"], "properties": {"domain": {"type": "string"}, "available": {"type": "null"}, "note": {"type": "string"}}, "additionalProperties": false}
        ]
      },
      "AvailabilityMulti": {
        "type": "object", "required": ["base", "results"],
        "properties": {"base": {"type": "string"}, "results": {"type": "array", "maxItems": 10, "items": {"type": "object", "required": ["domain", "available"], "properties": {"domain": {"type": "string"}, "available": {"type": ["boolean", "null"]}}, "additionalProperties": false}}},
        "additionalProperties": false
      },
      "ToolAvailabilityEnvelope": {
        "type": "object", "required": ["ok", "data"],
        "properties": {"ok": {"const": true}, "data": {"oneOf": [{"$ref": "#/components/schemas/AvailabilitySingle"}, {"$ref": "#/components/schemas/AvailabilityMulti"}]}},
        "additionalProperties": false
      }
    }
  },
  "webhooks": {}
}
