{
  "openapi": "3.1.1",
  "info": {
    "title": "imsu.io API",
    "description": "Create, list, change, pause and delete the uptime monitors of an imsu.io account. Authenticate with an API key from Settings → API keys, sent as Authorization: Bearer imsu_pat_…. Errors are RFC 9457 problem details whose type links to their explanation. Every response has an X-Request-Id header. Guide: https://imsu.io/docs/api",
    "version": "v1"
  },
  "servers": [
    {
      "url": "https://imsu.io"
    }
  ],
  "paths": {
    "/api/v1/me": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Check the API key",
        "description": "Returns the user the key acts as, its account, and the key's name, scopes and expiry. Any key may call it.",
        "operationId": "getMe",
        "responses": {
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiMeResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/limits": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Get the plan's limits",
        "description": "How many monitors the account may have and has, and the shortest check interval its plan allows. Needs monitors:read.",
        "operationId": "getLimits",
        "responses": {
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiLimitsResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/monitors": {
      "get": {
        "tags": [
          "Monitors"
        ],
        "summary": "List monitors",
        "description": "Monitors of the account, oldest first, with counts by status. Filters combine. Pages hold up to `limit` monitors; when nextCursor is not null, pass it as `cursor` to get the next page. Needs monitors:read.",
        "operationId": "listMonitors",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Only monitors in this state: up, down, unknown (not checked yet) or paused.",
            "schema": {
              "enum": [
                "up",
                "down",
                "unknown",
                "paused"
              ],
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Only monitors of this type: http, port or heartbeat.",
            "schema": {
              "enum": [
                "http",
                "port",
                "heartbeat"
              ],
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "description": "Text to find in the name or the target (URL or host:port), ignoring case.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Monitors per page, 1 to 500. Default 100.",
            "schema": {
              "maximum": 500,
              "minimum": 1,
              "type": "integer",
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The nextCursor of the previous page.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sslExpiringWithinDays",
            "in": "query",
            "description": "Only monitors whose SSL certificate expires within this many days, or has expired.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MonitorListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "Monitors"
        ],
        "summary": "Create a monitor",
        "description": "Creates a monitor; omitted fields get their defaults, and the first check runs within a minute. Heartbeats come back with the pingUrl their job must request. Send an Idempotency-Key header (e.g. a UUID) to retry safely: within 24 hours a retry with the same key and body gets the first response, with Idempotent-Replayed: true. Fields: name; type (http, port or heartbeat; default http; can't change later); url (http); host and port (port); intervalSeconds (seconds between checks, or for heartbeats the time expected between pings; default and minimum: the plan's minIntervalSeconds from GET /api/v1/limits); timeoutSeconds (default 10); method (GET or HEAD, default GET); expectedStatus (status codes that count as up, e.g. \"200-299\" or \"200,204\"; default 200-299); keyword (text the response body must contain); followRedirects (default true); failureThreshold (failed checks in a row before the monitor is down, default 2); expectClosed (port: alert when the port accepts connections); graceSeconds and runTimeoutSeconds (heartbeat); sslExpiryAlerts (http with an https URL: warn before the certificate expires; default on for https URLs) and sslAlertDays (days before expiry of the first warning: 1, 3, 7, 14 or 30; default 14). Responses also carry ssl (the certificate last seen: expiresAt, daysLeft, issuer, subject, checkedAt) and downReason (ssl while down for an SSL error; status stays down). Fields of other types are ignored. Creating and replacing need name, plus url (http) or host and port (port). Needs monitors:write.",
        "operationId": "createMonitor",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Any unique value, e.g. a UUID. A retry with the same key and body within 24 hours gets the first response (with Idempotent-Replayed: true) instead of creating a second monitor.",
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MonitorRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MonitorResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable Entity",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "413": {
            "description": "Payload Too Large",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/monitors/{id}": {
      "get": {
        "tags": [
          "Monitors"
        ],
        "summary": "Get a monitor",
        "description": "All settings and the current state of one monitor, its uptime over the last 24 hours, and pingUrl for heartbeats. Needs monitors:read.",
        "operationId": "getMonitor",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MonitorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": [ ]
          }
        ]
      },
      "put": {
        "tags": [
          "Monitors"
        ],
        "summary": "Replace a monitor's settings",
        "description": "A full update: fields left out go back to their defaults. To change only some fields, use PATCH. The type can't change. Fields: name; type (http, port or heartbeat; default http; can't change later); url (http); host and port (port); intervalSeconds (seconds between checks, or for heartbeats the time expected between pings; default and minimum: the plan's minIntervalSeconds from GET /api/v1/limits); timeoutSeconds (default 10); method (GET or HEAD, default GET); expectedStatus (status codes that count as up, e.g. \"200-299\" or \"200,204\"; default 200-299); keyword (text the response body must contain); followRedirects (default true); failureThreshold (failed checks in a row before the monitor is down, default 2); expectClosed (port: alert when the port accepts connections); graceSeconds and runTimeoutSeconds (heartbeat); sslExpiryAlerts (http with an https URL: warn before the certificate expires; default on for https URLs) and sslAlertDays (days before expiry of the first warning: 1, 3, 7, 14 or 30; default 14). Responses also carry ssl (the certificate last seen: expiresAt, daysLeft, issuer, subject, checkedAt) and downReason (ssl while down for an SSL error; status stays down). Fields of other types are ignored. Creating and replacing need name, plus url (http) or host and port (port). Needs monitors:write.",
        "operationId": "replaceMonitor",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MonitorRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MonitorResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "413": {
            "description": "Payload Too Large",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": [ ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Monitors"
        ],
        "summary": "Change some of a monitor's settings",
        "description": "A partial update: fields left out keep their values, and \"keyword\": null removes the keyword. The type can't change. Fields: name; type (http, port or heartbeat; default http; can't change later); url (http); host and port (port); intervalSeconds (seconds between checks, or for heartbeats the time expected between pings; default and minimum: the plan's minIntervalSeconds from GET /api/v1/limits); timeoutSeconds (default 10); method (GET or HEAD, default GET); expectedStatus (status codes that count as up, e.g. \"200-299\" or \"200,204\"; default 200-299); keyword (text the response body must contain); followRedirects (default true); failureThreshold (failed checks in a row before the monitor is down, default 2); expectClosed (port: alert when the port accepts connections); graceSeconds and runTimeoutSeconds (heartbeat); sslExpiryAlerts (http with an https URL: warn before the certificate expires; default on for https URLs) and sslAlertDays (days before expiry of the first warning: 1, 3, 7, 14 or 30; default 14). Responses also carry ssl (the certificate last seen: expiresAt, daysLeft, issuer, subject, checkedAt) and downReason (ssl while down for an SSL error; status stays down). Fields of other types are ignored. Creating and replacing need name, plus url (http) or host and port (port). Needs monitors:write.",
        "operationId": "updateMonitor",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MonitorRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MonitorResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "413": {
            "description": "Payload Too Large",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Monitors"
        ],
        "summary": "Delete a monitor",
        "description": "Deletes the monitor: checks and alerts stop, and its history is no longer shown. Needs monitors:write.",
        "operationId": "deleteMonitor",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "204": {
            "description": "No Content"
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/monitors/{id}/pause": {
      "post": {
        "tags": [
          "Monitors"
        ],
        "summary": "Pause a monitor",
        "description": "Stops checks and alerts until the monitor is resumed. Pausing a paused monitor changes nothing. Needs monitors:write.",
        "operationId": "pauseMonitor",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MonitorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/monitors/{id}/resume": {
      "post": {
        "tags": [
          "Monitors"
        ],
        "summary": "Resume a monitor",
        "description": "Restarts checks: an http or port monitor is checked right away; a heartbeat gets its interval plus grace for the next ping. Resuming an active monitor changes nothing. Needs monitors:write.",
        "operationId": "resumeMonitor",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MonitorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": [ ]
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "ApiCredentialResponse": {
        "required": [
          "kind",
          "id",
          "name",
          "scopes",
          "expiresAt"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "type": "string"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "expiresAt": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          }
        }
      },
      "ApiLimitsResponse": {
        "required": [
          "maxMonitors",
          "minIntervalSeconds",
          "monitorsInUse"
        ],
        "type": "object",
        "properties": {
          "maxMonitors": {
            "type": "integer",
            "format": "int32"
          },
          "minIntervalSeconds": {
            "type": "integer",
            "format": "int32"
          },
          "monitorsInUse": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "ApiMeResponse": {
        "required": [
          "user",
          "accountId",
          "credential"
        ],
        "type": "object",
        "properties": {
          "user": {
            "$ref": "#/components/schemas/ApiUserResponse"
          },
          "accountId": {
            "type": "string",
            "format": "uuid"
          },
          "credential": {
            "$ref": "#/components/schemas/ApiCredentialResponse"
          }
        }
      },
      "ApiUserResponse": {
        "required": [
          "id",
          "email",
          "displayName"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          }
        }
      },
      "DownReason": {
        "enum": [
          "ssl",
          null
        ]
      },
      "HttpValidationProblemDetails": {
        "type": "object",
        "properties": {
          "type": {
            "type": [
              "null",
              "string"
            ]
          },
          "title": {
            "type": [
              "null",
              "string"
            ]
          },
          "status": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "detail": {
            "type": [
              "null",
              "string"
            ]
          },
          "instance": {
            "type": [
              "null",
              "string"
            ]
          },
          "errors": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        }
      },
      "MonitorCountsResponse": {
        "required": [
          "total",
          "up",
          "down",
          "unknown",
          "paused"
        ],
        "type": "object",
        "properties": {
          "total": {
            "type": "integer",
            "format": "int32"
          },
          "up": {
            "type": "integer",
            "format": "int32"
          },
          "down": {
            "type": "integer",
            "format": "int32"
          },
          "unknown": {
            "type": "integer",
            "format": "int32"
          },
          "paused": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "MonitorListResponse": {
        "required": [
          "items",
          "counts",
          "nextCursor"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MonitorResponse"
            }
          },
          "counts": {
            "$ref": "#/components/schemas/MonitorCountsResponse"
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "MonitorRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": [
              "null",
              "string"
            ]
          },
          "url": {
            "type": [
              "null",
              "string"
            ]
          },
          "method": {
            "type": [
              "null",
              "string"
            ]
          },
          "intervalSeconds": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "timeoutSeconds": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "expectedStatus": {
            "type": [
              "null",
              "string"
            ]
          },
          "keyword": {
            "type": [
              "null",
              "string"
            ]
          },
          "followRedirects": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "failureThreshold": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "type": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/MonitorType"
              }
            ]
          },
          "host": {
            "type": [
              "null",
              "string"
            ]
          },
          "port": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "graceSeconds": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "runTimeoutSeconds": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "expectClosed": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "sslExpiryAlerts": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "sslAlertDays": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          }
        }
      },
      "MonitorResponse": {
        "required": [
          "id",
          "name",
          "type",
          "target",
          "url",
          "host",
          "port",
          "method",
          "intervalSeconds",
          "timeoutSeconds",
          "expectedStatus",
          "keyword",
          "followRedirects",
          "failureThreshold",
          "isActive",
          "status",
          "lastCheckedAt",
          "lastResponseMs",
          "nextCheckAt",
          "createdAt",
          "updatedAt",
          "uptime24h",
          "heartbeatToken",
          "graceSeconds",
          "runTimeoutSeconds",
          "lastPingAt",
          "expectClosed"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "$ref": "#/components/schemas/MonitorType"
          },
          "target": {
            "type": "string"
          },
          "url": {
            "type": [
              "null",
              "string"
            ]
          },
          "host": {
            "type": [
              "null",
              "string"
            ]
          },
          "port": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "method": {
            "type": "string"
          },
          "intervalSeconds": {
            "type": "integer",
            "format": "int32"
          },
          "timeoutSeconds": {
            "type": "integer",
            "format": "int32"
          },
          "expectedStatus": {
            "type": "string"
          },
          "keyword": {
            "type": [
              "null",
              "string"
            ]
          },
          "followRedirects": {
            "type": "boolean"
          },
          "failureThreshold": {
            "type": "integer",
            "format": "int32"
          },
          "isActive": {
            "type": "boolean"
          },
          "status": {
            "$ref": "#/components/schemas/MonitorStatus"
          },
          "lastCheckedAt": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "lastResponseMs": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "nextCheckAt": {
            "type": "string",
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "uptime24h": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "heartbeatToken": {
            "type": [
              "null",
              "string"
            ]
          },
          "graceSeconds": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "runTimeoutSeconds": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "lastPingAt": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "expectClosed": {
            "type": "boolean"
          },
          "pingUrl": {
            "type": [
              "null",
              "string"
            ]
          },
          "downReason": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/DownReason"
              }
            ]
          },
          "sslExpiryAlerts": {
            "type": "boolean",
            "default": false
          },
          "sslAlertDays": {
            "type": "integer",
            "format": "int32",
            "default": 14
          },
          "ssl": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/SslCertificateResponse"
              }
            ]
          }
        }
      },
      "MonitorStatus": {
        "enum": [
          "unknown",
          "up",
          "down"
        ]
      },
      "MonitorType": {
        "enum": [
          "http",
          "port",
          "heartbeat"
        ]
      },
      "ProblemDetails": {
        "type": "object",
        "properties": {
          "type": {
            "type": [
              "null",
              "string"
            ]
          },
          "title": {
            "type": [
              "null",
              "string"
            ]
          },
          "status": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "detail": {
            "type": [
              "null",
              "string"
            ]
          },
          "instance": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "SslCertificateResponse": {
        "required": [
          "expiresAt",
          "daysLeft",
          "issuer",
          "subject",
          "checkedAt"
        ],
        "type": "object",
        "properties": {
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "daysLeft": {
            "type": "integer",
            "format": "int32"
          },
          "issuer": {
            "type": [
              "null",
              "string"
            ]
          },
          "subject": {
            "type": [
              "null",
              "string"
            ]
          },
          "checkedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    },
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "description": "API key: imsu_pat_…. Create one in Settings → API keys.",
        "scheme": "bearer"
      }
    }
  },
  "tags": [
    {
      "name": "Account"
    },
    {
      "name": "Monitors"
    }
  ]
}