{
    "openapi": "3.1.0",
    "info": {
        "title": "InternetSecure API",
        "version": "1.0.0-draft",
        "description": "Manage your monitors and run security scans from scripts, CI, and AI assistants.\n\nEvery request needs an API key from Settings → API keys. Free accounts get keys too; plans differ in limits, not access.",
        "contact": {
            "url": "https://internetsecure.org/contact"
        }
    },
    "servers": [
        {
            "url": "https://internetsecure.org/api/v1"
        }
    ],
    "security": [
        {
            "apiKey": []
        }
    ],
    "tags": [
        {
            "name": "Account",
            "description": "Who the key belongs to, the plan, and what is left of today's limits."
        },
        {
            "name": "Monitors",
            "description": "The domains on your account: uptime checks and scheduled security scans. Same plan limits as the website."
        },
        {
            "name": "Scans",
            "description": "Run a security scan now, e.g. as a deploy check in CI. Counts against the daily scan quota."
        }
    ],
    "paths": {
        "/me": {
            "get": {
                "tags": [
                    "Account"
                ],
                "operationId": "getAccount",
                "summary": "Account, plan and remaining limits",
                "description": "Handy first call for an AI assistant: tells it what it can do before it tries.",
                "responses": {
                    "200": {
                        "description": "The account behind this key.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Account"
                                },
                                "example": {
                                    "account": {
                                        "id": 1042,
                                        "email": "ops@example.com",
                                        "plan": "pro"
                                    },
                                    "limits": {
                                        "monitors": {
                                            "used": 37,
                                            "max": 100
                                        },
                                        "requests_per_minute": 300,
                                        "scans_per_day": {
                                            "used": 12,
                                            "max": 500,
                                            "resets_at": "2026-11-07T00:00:00Z"
                                        }
                                    },
                                    "key": {
                                        "name": "GitHub Actions",
                                        "read_only": false,
                                        "created_at": "2026-11-01T09:00:00Z",
                                        "last_used_at": "2026-11-06T17:44:58Z"
                                    },
                                    "integrations": {
                                        "email": {
                                            "connected": true,
                                            "enabled": true,
                                            "settings_url": "https://internetsecure.org/account/profile?panel=email"
                                        },
                                        "slack": {
                                            "connected": true,
                                            "uptime_alerts": true,
                                            "security_alerts": true,
                                            "settings_url": "https://internetsecure.org/account/profile?panel=slack"
                                        },
                                        "teams": {
                                            "connected": false,
                                            "uptime_alerts": false,
                                            "security_alerts": false,
                                            "settings_url": "https://internetsecure.org/account/profile?panel=teams"
                                        },
                                        "jira": {
                                            "connected": false,
                                            "uptime_alerts": false,
                                            "security_alerts": false,
                                            "settings_url": "https://internetsecure.org/account/profile?panel=jira"
                                        },
                                        "linear": {
                                            "connected": false,
                                            "uptime_alerts": false,
                                            "security_alerts": false,
                                            "settings_url": "https://internetsecure.org/account/profile?panel=linear"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                }
            }
        },
        "/monitors": {
            "get": {
                "tags": [
                    "Monitors"
                ],
                "operationId": "listMonitors",
                "summary": "List monitors",
                "parameters": [
                    {
                        "name": "status",
                        "in": "query",
                        "description": "Only monitors in this uptime state.",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "up",
                                "down",
                                "degraded",
                                "paused"
                            ]
                        }
                    },
                    {
                        "name": "search",
                        "in": "query",
                        "description": "Part of a domain name.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "limit",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "default": 50,
                            "maximum": 100
                        }
                    },
                    {
                        "name": "cursor",
                        "in": "query",
                        "description": "next_cursor from the previous page.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "A page of monitors.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/Monitor"
                                            }
                                        },
                                        "next_cursor": {
                                            "type": [
                                                "string",
                                                "null"
                                            ]
                                        }
                                    }
                                },
                                "example": {
                                    "data": [
                                        {
                                            "id": 48213,
                                            "domain": "example.com",
                                            "uptime": {
                                                "enabled": true,
                                                "status": "up",
                                                "last_checked_at": "2026-11-06T17:42:10Z",
                                                "response_ms": 212,
                                                "uptime_24h": 100,
                                                "uptime_30d": 99.97,
                                                "open_incident": null
                                            },
                                            "security": {
                                                "enabled": true,
                                                "schedule": "daily",
                                                "live": true,
                                                "grade": "B",
                                                "exposure_grade": "A",
                                                "last_scan_at": "2026-11-06T06:03:44Z"
                                            },
                                            "report_url": "https://internetsecure.org/account/uptime/48213"
                                        }
                                    ],
                                    "next_cursor": null
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            },
            "post": {
                "tags": [
                    "Monitors"
                ],
                "operationId": "createMonitor",
                "summary": "Add a monitor",
                "description": "Adds a domain to the account, exactly like Monitors → Add on the website. New domains get daily security scans; paid plans also get the 2-hour live exposure check.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/MonitorCreate"
                            },
                            "example": {
                                "domain": "example.com",
                                "uptime": true,
                                "security_scans": true
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Monitor"
                                },
                                "example": {
                                    "id": 48213,
                                    "domain": "example.com",
                                    "uptime": {
                                        "enabled": true,
                                        "status": "up",
                                        "last_checked_at": "2026-11-06T17:42:10Z",
                                        "response_ms": 212,
                                        "uptime_24h": 100,
                                        "uptime_30d": 99.97,
                                        "open_incident": null
                                    },
                                    "security": {
                                        "enabled": true,
                                        "schedule": "daily",
                                        "live": true,
                                        "grade": "B",
                                        "exposure_grade": "A",
                                        "last_scan_at": "2026-11-06T06:03:44Z"
                                    },
                                    "report_url": "https://internetsecure.org/account/uptime/48213"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Plan limit reached (plan_limit), or the key is read-only (read_only_key).",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "code": "plan_limit",
                                        "message": "Your Free plan allows 1 monitor. Upgrade at https://internetsecure.org/pricing",
                                        "docs": "https://internetsecure.org/docs/api#errors"
                                    }
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Already on the account.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "code": "already_exists",
                                        "message": "example.com is already monitor 48213.",
                                        "docs": "https://internetsecure.org/docs/api#errors"
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/InvalidDomain"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        },
        "/monitors/{id}": {
            "parameters": [
                {
                    "name": "id",
                    "in": "path",
                    "required": true,
                    "description": "Monitor id (from GET /monitors).",
                    "schema": {
                        "type": "integer"
                    }
                }
            ],
            "get": {
                "tags": [
                    "Monitors"
                ],
                "operationId": "getMonitor",
                "summary": "Get one monitor",
                "responses": {
                    "200": {
                        "description": "The monitor.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Monitor"
                                },
                                "example": {
                                    "id": 48213,
                                    "domain": "example.com",
                                    "uptime": {
                                        "enabled": true,
                                        "status": "up",
                                        "last_checked_at": "2026-11-06T17:42:10Z",
                                        "response_ms": 212,
                                        "uptime_24h": 100,
                                        "uptime_30d": 99.97,
                                        "open_incident": null
                                    },
                                    "security": {
                                        "enabled": true,
                                        "schedule": "daily",
                                        "live": true,
                                        "grade": "B",
                                        "exposure_grade": "A",
                                        "last_scan_at": "2026-11-06T06:03:44Z"
                                    },
                                    "report_url": "https://internetsecure.org/account/uptime/48213"
                                }
                            }
                        }
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    }
                }
            },
            "patch": {
                "tags": [
                    "Monitors"
                ],
                "operationId": "updateMonitor",
                "summary": "Change or pause a monitor",
                "description": "Send only the fields you want to change. To pause uptime checks during a deploy: {\"uptime\": false}, then {\"uptime\": true} after. Pausing keeps the check and its history. alerts applies to every check on the monitor. Every change shows in the account's Activity Log, where it can be undone. Monitors can only be removed on the website.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/MonitorUpdate"
                            },
                            "example": {
                                "uptime": false
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Updated.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Monitor"
                                },
                                "example": {
                                    "id": 48213,
                                    "domain": "example.com",
                                    "uptime": {
                                        "enabled": false,
                                        "status": "paused",
                                        "last_checked_at": "2026-11-06T17:42:10Z",
                                        "response_ms": 212,
                                        "uptime_24h": 100,
                                        "uptime_30d": 99.97,
                                        "open_incident": null
                                    },
                                    "security": {
                                        "enabled": true,
                                        "schedule": "daily",
                                        "live": true,
                                        "grade": "B",
                                        "exposure_grade": "A",
                                        "last_scan_at": "2026-11-06T06:03:44Z"
                                    },
                                    "report_url": "https://internetsecure.org/account/uptime/48213"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "The key is read-only.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "code": "read_only_key",
                                        "message": "This key is read-only: it can read monitors and run scans, but not add or change monitors.",
                                        "docs": "https://internetsecure.org/docs/api#errors"
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "422": {
                        "description": "Bad field.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "code": "invalid_field",
                                        "message": "check_path must start with /",
                                        "docs": "https://internetsecure.org/docs/api#errors"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/monitors/{id}/security": {
            "get": {
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Monitor id (from GET /monitors).",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "tags": [
                    "Monitors"
                ],
                "operationId": "getMonitorSecurity",
                "summary": "Latest security result for a monitor",
                "description": "The most recent scheduled or live scan, with every check. Does not run a new scan and does not use scan quota.",
                "responses": {
                    "200": {
                        "description": "Latest result.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ScanResult"
                                },
                                "example": {
                                    "id": 9918231,
                                    "domain": "example.com",
                                    "scanned_at": "2026-11-06T17:45:02Z",
                                    "duration_ms": 3810,
                                    "security": {
                                        "grade": "B",
                                        "score": 78,
                                        "passed": 8,
                                        "warnings": 3,
                                        "failed": 1
                                    },
                                    "exposure": {
                                        "grade": "A",
                                        "findings": []
                                    },
                                    "checks": [
                                        {
                                            "id": "ssl",
                                            "name": "SSL certificate",
                                            "status": "pass",
                                            "detail": "Valid, expires 2027-01-15 (70 days)"
                                        },
                                        {
                                            "id": "mixed_content",
                                            "name": "Mixed content",
                                            "status": "pass",
                                            "detail": "None found"
                                        },
                                        {
                                            "id": "hsts",
                                            "name": "HSTS",
                                            "status": "pass",
                                            "detail": "max-age=31536000"
                                        },
                                        {
                                            "id": "x_frame_options",
                                            "name": "X-Frame-Options",
                                            "status": "fail",
                                            "detail": "Missing"
                                        },
                                        {
                                            "id": "csp",
                                            "name": "Content-Security-Policy",
                                            "status": "warn",
                                            "detail": "Missing"
                                        },
                                        {
                                            "id": "tls",
                                            "name": "TLS",
                                            "status": "pass",
                                            "detail": "TLS 1.3, strong cipher"
                                        },
                                        {
                                            "id": "security_headers",
                                            "name": "Security headers",
                                            "status": "pass",
                                            "detail": "4 of 6 present"
                                        },
                                        {
                                            "id": "cookies",
                                            "name": "Cookies",
                                            "status": "pass",
                                            "detail": "Secure and HttpOnly"
                                        },
                                        {
                                            "id": "dmarc",
                                            "name": "DMARC",
                                            "status": "pass",
                                            "detail": "p=reject"
                                        },
                                        {
                                            "id": "dkim",
                                            "name": "DKIM",
                                            "status": "warn",
                                            "detail": "No record on common selectors"
                                        },
                                        {
                                            "id": "dnssec",
                                            "name": "DNSSEC",
                                            "status": "warn",
                                            "detail": "Not enabled"
                                        },
                                        {
                                            "id": "server_version",
                                            "name": "Server version",
                                            "status": "pass",
                                            "detail": "Not announced"
                                        },
                                        {
                                            "id": "waf",
                                            "name": "WAF",
                                            "status": "info",
                                            "detail": "None announced (not scored)"
                                        }
                                    ],
                                    "previous": {
                                        "scan_id": 9917004,
                                        "grade": "A",
                                        "exposure_grade": "A",
                                        "scanned_at": "2026-11-06T06:02:11Z"
                                    },
                                    "changes": [
                                        {
                                            "check": "csp",
                                            "name": "Content-Security-Policy",
                                            "from": "pass",
                                            "to": "warn",
                                            "worse": true
                                        }
                                    ],
                                    "report_url": "https://internetsecure.org/account/scan/9918231",
                                    "monitor_id": 48213
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "No such monitor on this account, or it has no scan yet (code not_scanned).",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "code": "not_found",
                                        "message": "Monitor 48213 was not found on this account.",
                                        "docs": "https://internetsecure.org/docs/api#errors"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/scans": {
            "post": {
                "tags": [
                    "Scans"
                ],
                "operationId": "runScan",
                "summary": "Run a security scan now",
                "description": "Scans any public domain (it does not have to be a monitor) and waits for the result, usually 2–10 seconds. Uses 1 from the daily scan quota.\n\nFor CI: pass min_grade and/or no_drop and check gate.passed (fail the build when false); gate.reason is a one-line explanation for the build log. Every result also says what changed since your previous scan of the domain.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/ScanRequest"
                            },
                            "example": {
                                "domain": "example.com",
                                "min_grade": "B"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Scan finished.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ScanResult"
                                },
                                "example": {
                                    "id": 9918231,
                                    "domain": "example.com",
                                    "scanned_at": "2026-11-06T17:45:02Z",
                                    "duration_ms": 3810,
                                    "security": {
                                        "grade": "B",
                                        "score": 78,
                                        "passed": 8,
                                        "warnings": 3,
                                        "failed": 1
                                    },
                                    "exposure": {
                                        "grade": "A",
                                        "findings": []
                                    },
                                    "checks": [
                                        {
                                            "id": "ssl",
                                            "name": "SSL certificate",
                                            "status": "pass",
                                            "detail": "Valid, expires 2027-01-15 (70 days)"
                                        },
                                        {
                                            "id": "mixed_content",
                                            "name": "Mixed content",
                                            "status": "pass",
                                            "detail": "None found"
                                        },
                                        {
                                            "id": "hsts",
                                            "name": "HSTS",
                                            "status": "pass",
                                            "detail": "max-age=31536000"
                                        },
                                        {
                                            "id": "x_frame_options",
                                            "name": "X-Frame-Options",
                                            "status": "fail",
                                            "detail": "Missing"
                                        },
                                        {
                                            "id": "csp",
                                            "name": "Content-Security-Policy",
                                            "status": "warn",
                                            "detail": "Missing"
                                        },
                                        {
                                            "id": "tls",
                                            "name": "TLS",
                                            "status": "pass",
                                            "detail": "TLS 1.3, strong cipher"
                                        },
                                        {
                                            "id": "security_headers",
                                            "name": "Security headers",
                                            "status": "pass",
                                            "detail": "4 of 6 present"
                                        },
                                        {
                                            "id": "cookies",
                                            "name": "Cookies",
                                            "status": "pass",
                                            "detail": "Secure and HttpOnly"
                                        },
                                        {
                                            "id": "dmarc",
                                            "name": "DMARC",
                                            "status": "pass",
                                            "detail": "p=reject"
                                        },
                                        {
                                            "id": "dkim",
                                            "name": "DKIM",
                                            "status": "warn",
                                            "detail": "No record on common selectors"
                                        },
                                        {
                                            "id": "dnssec",
                                            "name": "DNSSEC",
                                            "status": "warn",
                                            "detail": "Not enabled"
                                        },
                                        {
                                            "id": "server_version",
                                            "name": "Server version",
                                            "status": "pass",
                                            "detail": "Not announced"
                                        },
                                        {
                                            "id": "waf",
                                            "name": "WAF",
                                            "status": "info",
                                            "detail": "None announced (not scored)"
                                        }
                                    ],
                                    "previous": {
                                        "scan_id": 9917004,
                                        "grade": "A",
                                        "exposure_grade": "A",
                                        "scanned_at": "2026-11-06T06:02:11Z"
                                    },
                                    "changes": [
                                        {
                                            "check": "csp",
                                            "name": "Content-Security-Policy",
                                            "from": "pass",
                                            "to": "warn",
                                            "worse": true
                                        }
                                    ],
                                    "gate": {
                                        "min_grade": "B",
                                        "no_drop": true,
                                        "passed": false,
                                        "reason": "Grade dropped A → B: Content-Security-Policy pass → warn"
                                    },
                                    "report_url": "https://internetsecure.org/account/scan/9918231"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/InvalidDomain"
                    },
                    "429": {
                        "description": "Daily scan quota used up (or too many requests).",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "code": "scan_quota",
                                        "message": "Used 10 of 10 scans today on Free. Resets 2026-11-07T00:00:00Z.",
                                        "docs": "https://internetsecure.org/docs/api#errors"
                                    }
                                }
                            }
                        }
                    },
                    "502": {
                        "description": "The site could not be scanned (no answer, DNS failure).",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "code": "scan_failed",
                                        "message": "example.com did not respond within 20 seconds. This scan did not use quota.",
                                        "docs": "https://internetsecure.org/docs/api#errors"
                                    }
                                }
                            }
                        }
                    },
                    "503": {
                        "description": "Scanners are busy; retry after the Retry-After header.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "code": "busy",
                                        "message": "All scanners are busy. Retry in 5 seconds.",
                                        "docs": "https://internetsecure.org/docs/api#errors"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/scans/{id}": {
            "get": {
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Scan id from POST /scans.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "tags": [
                    "Scans"
                ],
                "operationId": "getScan",
                "summary": "Fetch an earlier scan",
                "description": "Only scans run by this account.",
                "responses": {
                    "200": {
                        "description": "The scan.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ScanResult"
                                },
                                "example": {
                                    "id": 9918231,
                                    "domain": "example.com",
                                    "scanned_at": "2026-11-06T17:45:02Z",
                                    "duration_ms": 3810,
                                    "security": {
                                        "grade": "B",
                                        "score": 78,
                                        "passed": 8,
                                        "warnings": 3,
                                        "failed": 1
                                    },
                                    "exposure": {
                                        "grade": "A",
                                        "findings": []
                                    },
                                    "checks": [
                                        {
                                            "id": "ssl",
                                            "name": "SSL certificate",
                                            "status": "pass",
                                            "detail": "Valid, expires 2027-01-15 (70 days)"
                                        },
                                        {
                                            "id": "mixed_content",
                                            "name": "Mixed content",
                                            "status": "pass",
                                            "detail": "None found"
                                        },
                                        {
                                            "id": "hsts",
                                            "name": "HSTS",
                                            "status": "pass",
                                            "detail": "max-age=31536000"
                                        },
                                        {
                                            "id": "x_frame_options",
                                            "name": "X-Frame-Options",
                                            "status": "fail",
                                            "detail": "Missing"
                                        },
                                        {
                                            "id": "csp",
                                            "name": "Content-Security-Policy",
                                            "status": "warn",
                                            "detail": "Missing"
                                        },
                                        {
                                            "id": "tls",
                                            "name": "TLS",
                                            "status": "pass",
                                            "detail": "TLS 1.3, strong cipher"
                                        },
                                        {
                                            "id": "security_headers",
                                            "name": "Security headers",
                                            "status": "pass",
                                            "detail": "4 of 6 present"
                                        },
                                        {
                                            "id": "cookies",
                                            "name": "Cookies",
                                            "status": "pass",
                                            "detail": "Secure and HttpOnly"
                                        },
                                        {
                                            "id": "dmarc",
                                            "name": "DMARC",
                                            "status": "pass",
                                            "detail": "p=reject"
                                        },
                                        {
                                            "id": "dkim",
                                            "name": "DKIM",
                                            "status": "warn",
                                            "detail": "No record on common selectors"
                                        },
                                        {
                                            "id": "dnssec",
                                            "name": "DNSSEC",
                                            "status": "warn",
                                            "detail": "Not enabled"
                                        },
                                        {
                                            "id": "server_version",
                                            "name": "Server version",
                                            "status": "pass",
                                            "detail": "Not announced"
                                        },
                                        {
                                            "id": "waf",
                                            "name": "WAF",
                                            "status": "info",
                                            "detail": "None announced (not scored)"
                                        }
                                    ],
                                    "previous": {
                                        "scan_id": 9917004,
                                        "grade": "A",
                                        "exposure_grade": "A",
                                        "scanned_at": "2026-11-06T06:02:11Z"
                                    },
                                    "changes": [
                                        {
                                            "check": "csp",
                                            "name": "Content-Security-Policy",
                                            "from": "pass",
                                            "to": "warn",
                                            "worse": true
                                        }
                                    ],
                                    "report_url": "https://internetsecure.org/account/scan/9918231"
                                }
                            }
                        }
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    }
                }
            }
        }
    },
    "components": {
        "securitySchemes": {
            "apiKey": {
                "type": "http",
                "scheme": "bearer",
                "description": "Authorization: Bearer isk_… — create keys in Settings → API keys. Keys are shown once and stored hashed; revoke any time."
            }
        },
        "headers": {
            "X-RateLimit-Limit": {
                "description": "Requests allowed per minute on your plan.",
                "schema": {
                    "type": "integer"
                }
            },
            "X-RateLimit-Remaining": {
                "description": "Requests left this minute.",
                "schema": {
                    "type": "integer"
                }
            },
            "X-Scans-Remaining": {
                "description": "Scans left today (UTC).",
                "schema": {
                    "type": "integer"
                }
            },
            "Retry-After": {
                "description": "Seconds to wait (on 429 and 503).",
                "schema": {
                    "type": "integer"
                }
            }
        },
        "responses": {
            "Unauthorized": {
                "description": "Missing, wrong or revoked key.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "code": "unauthorized",
                                "message": "Send Authorization: Bearer <key>. Create a key in Settings → API keys.",
                                "docs": "https://internetsecure.org/docs/api#errors"
                            }
                        }
                    }
                }
            },
            "NotFound": {
                "description": "No such id on this account.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "code": "not_found",
                                "message": "Monitor 48213 was not found on this account.",
                                "docs": "https://internetsecure.org/docs/api#errors"
                            }
                        }
                    }
                }
            },
            "InvalidDomain": {
                "description": "Not a public domain.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "code": "invalid_domain",
                                "message": "localhost is not a public domain.",
                                "docs": "https://internetsecure.org/docs/api#errors"
                            }
                        }
                    }
                }
            },
            "RateLimited": {
                "description": "Too many requests this minute.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "code": "rate_limited",
                                "message": "300 requests per minute on Pro. Retry in 12 seconds.",
                                "docs": "https://internetsecure.org/docs/api#errors"
                            }
                        }
                    }
                }
            }
        },
        "schemas": {
            "Error": {
                "type": "object",
                "required": [
                    "error"
                ],
                "properties": {
                    "error": {
                        "type": "object",
                        "properties": {
                            "code": {
                                "type": "string"
                            },
                            "message": {
                                "type": "string"
                            },
                            "docs": {
                                "type": "string"
                            }
                        }
                    }
                }
            },
            "Grade": {
                "type": [
                    "string",
                    "null"
                ],
                "enum": [
                    "A+",
                    "A",
                    "A-",
                    "B",
                    "C",
                    "D",
                    "F",
                    null
                ],
                "description": "null when there is no scan yet. A- is exposure only (versions shown, no known CVEs)."
            },
            "Account": {
                "type": "object",
                "properties": {
                    "account": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "integer"
                            },
                            "email": {
                                "type": "string"
                            },
                            "plan": {
                                "type": "string",
                                "enum": [
                                    "free",
                                    "plus",
                                    "pro",
                                    "enterprise"
                                ]
                            }
                        }
                    },
                    "limits": {
                        "type": "object"
                    },
                    "key": {
                        "type": "object"
                    },
                    "integrations": {
                        "type": "object",
                        "description": "Connected alert channels and the Settings page for each. Connecting happens on the website; secrets are never returned."
                    }
                }
            },
            "Monitor": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "domain": {
                        "type": "string"
                    },
                    "uptime": {
                        "type": "object",
                        "properties": {
                            "enabled": {
                                "type": "boolean"
                            },
                            "status": {
                                "type": "string",
                                "enum": [
                                    "up",
                                    "down",
                                    "degraded",
                                    "paused"
                                ]
                            },
                            "last_checked_at": {
                                "type": [
                                    "string",
                                    "null"
                                ]
                            },
                            "response_ms": {
                                "type": [
                                    "integer",
                                    "null"
                                ]
                            },
                            "uptime_24h": {
                                "type": [
                                    "number",
                                    "null"
                                ]
                            },
                            "uptime_30d": {
                                "type": [
                                    "number",
                                    "null"
                                ]
                            },
                            "open_incident": {
                                "type": [
                                    "object",
                                    "null"
                                ]
                            }
                        }
                    },
                    "security": {
                        "type": "object",
                        "properties": {
                            "enabled": {
                                "type": "boolean"
                            },
                            "schedule": {
                                "type": [
                                    "string",
                                    "null"
                                ],
                                "enum": [
                                    "daily",
                                    "weekly",
                                    "monthly",
                                    null
                                ]
                            },
                            "live": {
                                "type": "boolean",
                                "description": "2-hour exposure re-check (paid plans)."
                            },
                            "grade": {
                                "$ref": "#/components/schemas/Grade"
                            },
                            "exposure_grade": {
                                "$ref": "#/components/schemas/Grade"
                            },
                            "last_scan_at": {
                                "type": [
                                    "string",
                                    "null"
                                ]
                            }
                        }
                    },
                    "report_url": {
                        "type": "string"
                    }
                }
            },
            "MonitorCreate": {
                "type": "object",
                "required": [
                    "domain"
                ],
                "properties": {
                    "domain": {
                        "type": "string",
                        "description": "Root domain or subdomain, no path."
                    },
                    "uptime": {
                        "type": "boolean",
                        "default": true
                    },
                    "security_scans": {
                        "type": "boolean",
                        "default": true
                    },
                    "check_path": {
                        "type": "string",
                        "default": "/",
                        "description": "Path the uptime check requests."
                    }
                }
            },
            "MonitorUpdate": {
                "type": "object",
                "properties": {
                    "uptime": {
                        "type": "boolean"
                    },
                    "security_scans": {
                        "type": "boolean"
                    },
                    "check_path": {
                        "type": "string"
                    },
                    "alerts": {
                        "type": "object",
                        "properties": {
                            "email": {
                                "type": "boolean"
                            },
                            "slack": {
                                "type": "boolean"
                            },
                            "teams": {
                                "type": "boolean"
                            },
                            "jira": {
                                "type": "boolean"
                            },
                            "linear": {
                                "type": "boolean"
                            }
                        }
                    }
                }
            },
            "ScanRequest": {
                "type": "object",
                "required": [
                    "domain"
                ],
                "properties": {
                    "domain": {
                        "type": "string"
                    },
                    "min_grade": {
                        "$ref": "#/components/schemas/Grade",
                        "description": "Optional. Gate fails below this grade."
                    },
                    "no_drop": {
                        "type": "boolean",
                        "description": "Optional. Gate fails if the grade is lower than this account's previous scan of the domain."
                    }
                }
            },
            "Check": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string"
                    },
                    "name": {
                        "type": "string"
                    },
                    "status": {
                        "type": "string",
                        "enum": [
                            "pass",
                            "warn",
                            "fail",
                            "info"
                        ]
                    },
                    "detail": {
                        "type": "string"
                    }
                }
            },
            "ScanResult": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "domain": {
                        "type": "string"
                    },
                    "scanned_at": {
                        "type": "string",
                        "format": "date-time"
                    },
                    "duration_ms": {
                        "type": "integer"
                    },
                    "security": {
                        "type": "object",
                        "properties": {
                            "grade": {
                                "$ref": "#/components/schemas/Grade"
                            },
                            "score": {
                                "type": "integer"
                            },
                            "passed": {
                                "type": "integer"
                            },
                            "warnings": {
                                "type": "integer"
                            },
                            "failed": {
                                "type": "integer"
                            }
                        }
                    },
                    "exposure": {
                        "type": "object",
                        "properties": {
                            "grade": {
                                "$ref": "#/components/schemas/Grade"
                            },
                            "findings": {
                                "type": "array",
                                "items": {
                                    "type": "object",
                                    "properties": {
                                        "title": {
                                            "type": "string"
                                        },
                                        "product": {
                                            "type": "string"
                                        },
                                        "version": {
                                            "type": "string"
                                        },
                                        "severity": {
                                            "type": "string"
                                        },
                                        "end_of_life": {
                                            "type": "boolean"
                                        },
                                        "cves": {
                                            "type": "integer"
                                        },
                                        "known_exploited": {
                                            "type": "integer"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "checks": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Check"
                        }
                    },
                    "previous": {
                        "type": [
                            "object",
                            "null"
                        ],
                        "description": "This account's previous finished scan of the domain (scheduled or manual), or null."
                    },
                    "changes": {
                        "type": "array",
                        "description": "Checks (and the exposure grade) whose result moved since previous; worse = true when it got worse.",
                        "items": {
                            "type": "object",
                            "properties": {
                                "check": {
                                    "type": "string"
                                },
                                "name": {
                                    "type": "string"
                                },
                                "from": {
                                    "type": "string"
                                },
                                "to": {
                                    "type": "string"
                                },
                                "worse": {
                                    "type": "boolean"
                                }
                            }
                        }
                    },
                    "gate": {
                        "type": "object",
                        "description": "Only when min_grade or no_drop was sent.",
                        "properties": {
                            "min_grade": {
                                "type": [
                                    "string",
                                    "null"
                                ]
                            },
                            "no_drop": {
                                "type": "boolean"
                            },
                            "passed": {
                                "type": "boolean"
                            },
                            "reason": {
                                "type": "string",
                                "description": "One line for the build log, e.g. \"Grade dropped A → B: Content-Security-Policy pass → warn\""
                            }
                        }
                    },
                    "report_url": {
                        "type": "string"
                    }
                }
            }
        }
    }
}