{
  "openapi": "3.1.0",
  "info": {
    "title": "Status page public API",
    "version": "v1",
    "description": "The public, read-mostly API behind the status page: its status, uptime, incidents, metrics and reports as JSON, the subscription endpoints the subscribe page uses, and the feeds and badge.\n\nNo authentication. Without a session you get exactly the public page; a signed-in session (the page's Azure AD sign-in) also sees the items its role may see. Responses are JSON with UTC RFC 3339 timestamps, cached for a short while (the `Cache-Control` is `public` for a visitor, `private` for a signed-in session, with `Vary: Cookie`).\n\nErrors are JSON `{\"error\": …, \"code\": …}`: `error` is an English sentence, `code` (when present) a stable identifier to branch on; a body that doesn't match its schema also lists `details` with each offending field. Subscribing is rate-limited per address (10 a minute).\n\nThe contract is stable within v1: fields are only ever added. A rename or removal would come as v2, alongside."
  },
  "servers": [
    {
      "url": "/",
      "description": "This deployment."
    }
  ],
  "tags": [
    {
      "name": "Status",
      "description": "The page's current state: overall, per service and per component, with the 90-day grids."
    },
    {
      "name": "Incidents",
      "description": "Incident and maintenance history, and single incidents."
    },
    {
      "name": "Metrics",
      "description": "Bucketed monitor metrics per component: response time, its phases, flow steps, uptime and incidents."
    },
    {
      "name": "Reports",
      "description": "Uptime reports for a month, quarter or year."
    },
    {
      "name": "Subscriptions",
      "description": "Subscribing, confirming and managing notifications: what the /subscribe page does. These change data, so the reference never sends them for you."
    },
    {
      "name": "Feeds and badge",
      "description": "The Atom feed, the maintenance calendar and the status badge."
    }
  ],
  "paths": {
    "/api/v1/summary.json": {
      "get": {
        "operationId": "summary",
        "tags": [
          "Status"
        ],
        "summary": "The page's status in one call",
        "description": "Everything the public index shows: the overall status, every service with its components and their current status, whether monitoring is running (monitoring: delayed when the checks have stopped, with the time of their last heartbeat), active incidents with their full update timelines, and the maintenance running now and coming up in the next 14 days. A component whose checks have stopped reporting reads no_data, never its last result.",
        "x-try-it": true,
        "responses": {
          "200": {
            "description": "The page's current state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "summary.schema.json"
                }
              }
            },
            "x-cache-seconds": 30
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v1/services/{slug}": {
      "get": {
        "operationId": "service-detail",
        "tags": [
          "Status"
        ],
        "summary": "One service with its grids and monitors",
        "description": "One service with per-component 90-day daily and 7-day hourly grids, and the public state of each monitor: its type, its target (only when the operator shows it; null otherwise), 24-hour uptime and most recent check (a failed check carries a stable error_code to branch on). Everything the service page shows.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "description": "The service's slug (from the summary). The .json suffix is optional: /api/v1/services/customer-api.json works too.",
            "schema": {
              "type": "string"
            },
            "required": true,
            "x-sample": "service.slug",
            "example": "customer-api"
          }
        ],
        "x-try-it": true,
        "responses": {
          "200": {
            "description": "The service.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "service-detail.schema.json"
                }
              }
            },
            "x-cache-seconds": 30
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v1/uptime.json": {
      "get": {
        "operationId": "uptime",
        "tags": [
          "Status"
        ],
        "summary": "90-day uptime for every component",
        "description": "The 90-day daily uptime grid and 90-day figures (uptime, average response time, incidents) for every component, keyed by component id. A day without checks is no_data with uptime_pct null, never assumed green.",
        "x-try-it": true,
        "responses": {
          "200": {
            "description": "Grids and figures per component.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "uptime.schema.json"
                }
              }
            },
            "x-cache-seconds": 60
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v1/incidents.json": {
      "get": {
        "operationId": "incidents",
        "tags": [
          "Incidents"
        ],
        "summary": "Incident history, newest first",
        "description": "Non-scheduled incidents, active and resolved, each with its full update timeline. Two modes: a window of days (the default), or the whole archive page by page with limit and before.",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "description": "Window mode: incidents that started within the last this-many days, clamped to 1-90. Default 90. Echoed in the response.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 90
            },
            "required": false,
            "example": 30
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Archive mode: page size, clamped to 1-100 (default 25). Using limit or before switches to archive mode.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false
          },
          {
            "name": "before",
            "in": "query",
            "description": "Archive mode: the opaque cursor from the previous page's next_before.",
            "schema": {
              "type": "string"
            },
            "required": false
          },
          {
            "name": "component",
            "in": "query",
            "description": "Only incidents attached to this component id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": false
          },
          {
            "name": "include",
            "in": "query",
            "description": "maintenance also returns maintenance windows that have started, marked is_scheduled: true.",
            "schema": {
              "type": "string",
              "enum": [
                "maintenance"
              ]
            },
            "required": false
          }
        ],
        "x-try-it": true,
        "responses": {
          "200": {
            "description": "A page of incidents.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "incidents.schema.json"
                }
              }
            },
            "x-cache-seconds": 60
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v1/incidents/{id}": {
      "get": {
        "operationId": "incident",
        "tags": [
          "Incidents"
        ],
        "summary": "One incident, by id",
        "description": "One incident or maintenance window with its full update timeline: the permalink's data. The .json suffix is optional.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The incident's id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "x-sample": "incident.id"
          }
        ],
        "x-try-it": true,
        "responses": {
          "200": {
            "description": "The incident.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "incident.schema.json"
                }
              }
            },
            "x-cache-seconds": 60
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v1/components/{id}/metrics.json": {
      "get": {
        "operationId": "component-metrics",
        "tags": [
          "Metrics"
        ],
        "summary": "A component's monitor metrics",
        "description": "Bucketed series for one component and each of its monitors: uptime, incidents, response time and, per type, its phases (HTTP), days to expiry (certificates, domains), the heartbeat interval (push) or each step's time (flows). Every series has one entry per bucket; null means no data in that bucket. The .json suffix is optional.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The component's id (from the summary).",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "x-sample": "component.id"
          },
          {
            "name": "window",
            "in": "query",
            "description": "The time span: day (96 × 15 minutes), week (84 × 2 hours, the default) or month (90 × 8 hours).",
            "schema": {
              "type": "string",
              "enum": [
                "day",
                "week",
                "month"
              ],
              "default": "week"
            },
            "required": false
          }
        ],
        "x-try-it": true,
        "responses": {
          "200": {
            "description": "The component's series.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "component-metrics.schema.json"
                }
              }
            },
            "x-cache-seconds": 60
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v1/report.json": {
      "get": {
        "operationId": "report",
        "tags": [
          "Reports"
        ],
        "summary": "Every service's uptime for a period",
        "description": "The uptime report for the whole page: each service's uptime, downtime, incident count and SLA verdict for the period, the page's uptime, and every incident and maintenance window overlapping it.",
        "parameters": [
          {
            "name": "period",
            "in": "query",
            "description": "A calendar month (2026-08), quarter (2026-Q3) or year (2026), in UTC. Omitted: the last full month. A period that hasn't started is a 400.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9]{4}(-[0-9]{2}|-Q[1-4])?$"
            },
            "required": false,
            "example": "2026-08"
          }
        ],
        "x-try-it": true,
        "responses": {
          "200": {
            "description": "The report. Cached 60 seconds while the period runs, 5 minutes once it's over.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "report.schema.json"
                }
              }
            },
            "x-cache-seconds": 60
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v1/services/{ref}/report.json": {
      "get": {
        "operationId": "service-report",
        "tags": [
          "Reports"
        ],
        "summary": "One service's uptime for a period",
        "description": "One service's report: its uptime and each component's, measured and maintenance minutes, the SLA verdict, and the incidents and maintenance overlapping the period.",
        "parameters": [
          {
            "name": "ref",
            "in": "path",
            "description": "The service's id (from the summary); its slug works too.",
            "schema": {
              "type": "string"
            },
            "required": true,
            "x-sample": "service.id"
          },
          {
            "name": "period",
            "in": "query",
            "description": "A calendar month (2026-08), quarter (2026-Q3) or year (2026), in UTC. Omitted: the last full month. A period that hasn't started is a 400.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9]{4}(-[0-9]{2}|-Q[1-4])?$"
            },
            "required": false,
            "example": "2026-08"
          }
        ],
        "x-try-it": true,
        "responses": {
          "200": {
            "description": "The report. Cached 60 seconds while the period runs, 5 minutes once it's over.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "service-report.schema.json"
                }
              }
            },
            "x-cache-seconds": 60
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v1/subscriptions": {
      "post": {
        "operationId": "subscription",
        "tags": [
          "Subscriptions"
        ],
        "summary": "Subscribe destinations",
        "description": "Creates one or more destinations (email, webhook, Slack, Teams) for an owner email. A new owner gets a manage key at once, which works once they click the emailed link; an existing owner needs its manage key, a management session, or to be signed in as that email. Rate-limited per address.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "subscription.schema.json"
              },
              "example": {
                "owner_email": "alice@example.com",
                "destinations": [
                  {
                    "kind": "webhook",
                    "endpoint": "https://example.com/hook"
                  }
                ],
                "min_severity": "major"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Created; see status for what still waits for a confirmation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscribeResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "description": "The email already has subscriptions and no valid key came with it (code owner_key_required).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/subscriptions/confirm": {
      "post": {
        "operationId": "subscription-confirm",
        "tags": [
          "Subscriptions"
        ],
        "summary": "Confirm an email destination",
        "description": "Confirms an email destination to an address other than the owner's, with the two values from its emailed link.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "The token from the link."
                  },
                  "confirm": {
                    "type": "string",
                    "description": "The confirm value from the link."
                  }
                },
                "required": [
                  "token",
                  "confirm"
                ],
                "description": "The link's two values."
              },
              "example": {
                "token": "<token from the link>",
                "confirm": "<confirm from the link>"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Confirmed (status confirmed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Outcome"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/subscriptions/unsubscribe": {
      "post": {
        "operationId": "subscription-unsubscribe",
        "tags": [
          "Subscriptions"
        ],
        "summary": "Unsubscribe one destination",
        "description": "Removes the one destination whose unsubscribe token this is: the link in every message, no key needed.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "The unsubscribe token from a message."
                  }
                },
                "required": [
                  "token"
                ],
                "description": "The message's unsubscribe token."
              },
              "example": {
                "token": "<token from the message>"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Removed (status unsubscribed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Outcome"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/subscriptions/owner/confirm": {
      "post": {
        "operationId": "subscription-owner-confirm",
        "tags": [
          "Subscriptions"
        ],
        "summary": "Confirm the owner email",
        "x-opens-session": true,
        "description": "Confirms the owner email with the values from its emailed link: its destinations start, its manage key starts working, and a management session opens.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "subscription-owner-confirm.schema.json"
              },
              "example": {
                "owner": "<owner id from the link>",
                "c": "<c from the link>"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "status confirmed or already_confirmed, with email; manage_key when one is issued here.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "properties": {
                    "status": {
                      "type": "string",
                      "description": "confirmed or already_confirmed.",
                      "enum": [
                        "confirmed",
                        "already_confirmed"
                      ]
                    },
                    "email": {
                      "type": "string",
                      "description": "The owner email."
                    },
                    "manage_key": {
                      "type": "string",
                      "description": "A new manage key, only for owners from before keys were issued at subscribe."
                    }
                  },
                  "required": [
                    "status",
                    "email"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/api/v1/subscriptions/manage": {
      "post": {
        "operationId": "subscription-manage",
        "tags": [
          "Subscriptions"
        ],
        "summary": "Open a management session",
        "x-opens-session": true,
        "description": "Opens a 30-minute management session for the owner with this email and manage key (the session cookie). A wrong key and an unknown email answer alike. Rate-limited per address and per email.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "subscription-manage.schema.json"
              },
              "example": {
                "email": "alice@example.com",
                "key": "7K2M-9QXD-4HNP-B6TR-2WZC-8FJV"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Opened (status ok).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Outcome"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "description": "invalid_key (wrong key or unknown email) or owner_unverified (confirm the email first).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many attempts (code rate_limited).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "subscription-manage-close",
        "tags": [
          "Subscriptions"
        ],
        "summary": "End the management session",
        "description": "Ends the management session. A signed-in session stays signed in.",
        "responses": {
          "204": {
            "description": "Ended."
          }
        }
      }
    },
    "/api/v1/subscriptions/mine": {
      "get": {
        "operationId": "subscription-mine",
        "tags": [
          "Subscriptions"
        ],
        "summary": "The destinations you may manage",
        "description": "The owner's destinations. Needs a management session (the `statuspage_session` cookie that confirming the owner email, `POST /api/v1/subscriptions/manage` or `POST /api/v1/subscriptions/new-key` opens for 30 minutes) or a signed-in session whose email owns the subscriptions. Without one it answers {\"open\": false}, never an error.",
        "security": [
          {
            "managementSession": []
          },
          {}
        ],
        "x-try-it": true,
        "responses": {
          "200": {
            "description": "The managed destinations, or open false.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MySubscriptions"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/subscriptions/{id}": {
      "put": {
        "operationId": "subscription-update",
        "tags": [
          "Subscriptions"
        ],
        "summary": "Change a destination",
        "description": "Changes one destination's components, minimum severity, maintenance notices or paused state; omitted fields stay. Components the viewer can't see are kept as they are. Needs a management session (the `statuspage_session` cookie that confirming the owner email, `POST /api/v1/subscriptions/manage` or `POST /api/v1/subscriptions/new-key` opens for 30 minutes) or a signed-in session whose email owns the subscriptions.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The destination's id (from subscriptions/mine).",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "security": [
          {
            "managementSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "subscription-update.schema.json"
              },
              "example": {
                "paused": true
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Changed."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/ManageRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "subscription-delete",
        "tags": [
          "Subscriptions"
        ],
        "summary": "Delete a destination",
        "description": "Deletes one destination. Needs a management session (the `statuspage_session` cookie that confirming the owner email, `POST /api/v1/subscriptions/manage` or `POST /api/v1/subscriptions/new-key` opens for 30 minutes) or a signed-in session whose email owns the subscriptions.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The destination's id (from subscriptions/mine).",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "security": [
          {
            "managementSession": []
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "401": {
            "$ref": "#/components/responses/ManageRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/subscriptions/{id}/test": {
      "post": {
        "operationId": "subscription-test",
        "tags": [
          "Subscriptions"
        ],
        "summary": "Send a test notification",
        "description": "Sends one clearly labelled test notification to a confirmed destination of a confirmed owner, at most once a minute. Needs a management session (the `statuspage_session` cookie that confirming the owner email, `POST /api/v1/subscriptions/manage` or `POST /api/v1/subscriptions/new-key` opens for 30 minutes) or a signed-in session whose email owns the subscriptions.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The destination's id (from subscriptions/mine).",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "security": [
          {
            "managementSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "subscription-test.schema.json"
              },
              "example": {}
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued (status queued).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Outcome"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ManageRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Not confirmed yet (code not_confirmed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "One test a minute (code rate_limited).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/subscriptions/forgot-key": {
      "post": {
        "operationId": "subscription-forgot-key",
        "tags": [
          "Subscriptions"
        ],
        "summary": "Mail a link to a new manage key",
        "description": "Mails the owner a one-time link, valid for an hour, that issues a new manage key. Always answers 202, whether or not the email has subscriptions.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "subscription-forgot-key.schema.json"
              },
              "example": {
                "email": "alice@example.com"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "status sent_if_known.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Outcome"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/api/v1/subscriptions/new-key": {
      "post": {
        "operationId": "subscription-new-key",
        "tags": [
          "Subscriptions"
        ],
        "summary": "Issue a new manage key",
        "x-opens-session": true,
        "description": "Issues a new manage key (the old one stops working) from the forgot-key link's values, or for the owner already managing (an empty body with a management session or signed in). Opens a management session.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "subscription-new-key.schema.json"
              },
              "example": {
                "owner": "<owner id from the link>",
                "e": 1790000000,
                "c": "<c from the link>"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The new key, shown once.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "properties": {
                    "manage_key": {
                      "type": "string",
                      "description": "The new manage key."
                    },
                    "email": {
                      "type": "string",
                      "description": "The owner email."
                    }
                  },
                  "required": [
                    "manage_key",
                    "email"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/ManageRequired"
          }
        }
      }
    },
    "/feed.atom": {
      "get": {
        "operationId": "feed",
        "tags": [
          "Feeds and badge"
        ],
        "summary": "Atom feed of incident updates",
        "description": "Every incident and maintenance update as an Atom 1.0 feed, newest first, for feed readers and chat integrations.",
        "x-try-it": true,
        "responses": {
          "200": {
            "description": "The feed.",
            "x-cache-seconds": 60,
            "content": {
              "application/atom+xml": {
                "schema": {
                  "type": "string",
                  "description": "An Atom 1.0 document."
                }
              }
            }
          },
          "500": {
            "description": "The server couldn't read what the answer needs: a plain-text 500 (a feed reader, calendar or image tag has no use for a JSON body). Retry later."
          }
        }
      }
    },
    "/maintenance.ics": {
      "get": {
        "operationId": "maintenance-ical",
        "tags": [
          "Feeds and badge"
        ],
        "summary": "Maintenance calendar (iCalendar)",
        "description": "Upcoming and recent maintenance windows as an iCalendar feed to subscribe to from a calendar app.",
        "x-try-it": true,
        "responses": {
          "200": {
            "description": "The calendar.",
            "x-cache-seconds": 300,
            "content": {
              "text/calendar": {
                "schema": {
                  "type": "string",
                  "description": "An iCalendar document."
                }
              }
            }
          },
          "500": {
            "description": "The server couldn't read what the answer needs: a plain-text 500 (a feed reader, calendar or image tag has no use for a JSON body). Retry later."
          }
        }
      }
    },
    "/badge.svg": {
      "get": {
        "operationId": "badge",
        "tags": [
          "Feeds and badge"
        ],
        "summary": "Status badge (SVG)",
        "description": "A small SVG badge with the page's status, or one service's with service. For READMEs and dashboards.",
        "parameters": [
          {
            "name": "service",
            "in": "query",
            "description": "A service slug for that service's badge; omitted, the whole page.",
            "schema": {
              "type": "string"
            },
            "required": false,
            "x-sample": "service.slug"
          }
        ],
        "x-try-it": true,
        "responses": {
          "200": {
            "description": "The badge.",
            "x-cache-seconds": 30,
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string",
                  "description": "An SVG image."
                }
              }
            }
          },
          "500": {
            "description": "The server couldn't read what the answer needs: a plain-text 500 (a feed reader, calendar or image tag has no use for a JSON body). Retry later."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "error": {
            "type": "string",
            "description": "What went wrong, as an English sentence for people."
          },
          "code": {
            "type": "string",
            "description": "A stable identifier for the failure, when the endpoint has several a client may want to tell apart (for example endpoint_host_slack)."
          },
          "index": {
            "type": "integer",
            "minimum": 0,
            "description": "For a subscribe request refused over one destination: which one, counting from 0 (0 for the single-destination body)."
          },
          "details": {
            "type": "array",
            "description": "For a body that doesn't match its schema: each offending field.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "path": {
                  "type": "string",
                  "description": "JSON pointer to the field."
                },
                "message": {
                  "type": "string",
                  "description": "Why it was refused."
                }
              },
              "required": [
                "path",
                "message"
              ]
            }
          }
        },
        "required": [
          "error"
        ],
        "description": "An error response."
      },
      "SubscribeResult": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "status": {
            "type": "string",
            "description": "pending_owner_confirmation (a new owner email: click the emailed link), pending_confirmation (an email destination waits for its own link) or subscribed.",
            "enum": [
              "subscribed",
              "pending_confirmation",
              "pending_owner_confirmation"
            ]
          },
          "destinations": {
            "type": "array",
            "description": "Each destination created, in request order.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "kind": {
                  "type": "string",
                  "description": "email, webhook, slack or teams.",
                  "enum": [
                    "email",
                    "webhook",
                    "slack",
                    "teams"
                  ]
                },
                "endpoint": {
                  "type": "string",
                  "description": "The endpoint, redacted for URL kinds."
                },
                "status": {
                  "type": "string",
                  "description": "This destination's own status.",
                  "enum": [
                    "subscribed",
                    "pending_confirmation",
                    "pending_owner_confirmation"
                  ]
                }
              },
              "required": [
                "kind",
                "endpoint",
                "status"
              ]
            }
          },
          "manage_key": {
            "type": "string",
            "description": "The owner's manage key, shown once when one is issued (a new owner, or one without a key). Never mailed; store it."
          }
        },
        "required": [
          "status",
          "destinations"
        ],
        "description": "The outcome of a subscribe."
      },
      "MySubscriptions": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "open": {
            "type": "boolean",
            "description": "False without a management session or a signed-in owner; nothing else is returned then."
          },
          "email": {
            "type": "string",
            "description": "The owner email being managed."
          },
          "signed_in": {
            "type": "boolean",
            "description": "True when the owner comes from the Azure AD sign-in rather than a manage-key session."
          },
          "key_issued_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the current manage key was issued."
          },
          "verified": {
            "type": "boolean",
            "description": "Whether the owner email is confirmed; nothing is sent before."
          },
          "subscriptions": {
            "type": "array",
            "description": "The owner's destinations.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Destination id.",
                  "format": "uuid"
                },
                "kind": {
                  "type": "string",
                  "description": "email, webhook, slack or teams.",
                  "enum": [
                    "email",
                    "webhook",
                    "slack",
                    "teams"
                  ]
                },
                "endpoint": {
                  "type": "string",
                  "description": "The endpoint; URL kinds are redacted (whoever holds the key may not be who typed it)."
                },
                "visibility": {
                  "type": "string",
                  "description": "The level it is notified at.",
                  "enum": [
                    "public",
                    "signed_in",
                    "admins"
                  ]
                },
                "confirmed": {
                  "type": "boolean",
                  "description": "The destination's own confirmation (email destinations to another address need one)."
                },
                "waiting": {
                  "type": "boolean",
                  "description": "True while the owner email is unconfirmed: nothing is sent yet."
                },
                "all_components": {
                  "type": "boolean",
                  "description": "True when it covers the whole page."
                },
                "components": {
                  "type": "array",
                  "description": "The covered components the viewer may see.",
                  "items": {
                    "type": "object",
                    "additionalProperties": false,
                    "properties": {
                      "id": {
                        "type": "string",
                        "description": "Component id.",
                        "format": "uuid"
                      },
                      "name": {
                        "type": "string",
                        "description": "Service · component."
                      }
                    },
                    "required": [
                      "id",
                      "name"
                    ]
                  }
                },
                "hidden_components": {
                  "type": "integer",
                  "description": "Covered components the viewer may not see, counted, never named."
                },
                "created_at": {
                  "type": "string",
                  "description": "When it was created.",
                  "format": "date-time"
                },
                "min_severity": {
                  "type": "string",
                  "description": "The least severe incident it hears about.",
                  "enum": [
                    "none",
                    "minor",
                    "major",
                    "critical"
                  ]
                },
                "include_maintenance": {
                  "type": "boolean",
                  "description": "Whether maintenance notices are sent."
                },
                "paused": {
                  "type": "boolean",
                  "description": "A paused destination keeps its settings but gets nothing."
                }
              },
              "required": [
                "id",
                "kind",
                "endpoint",
                "visibility",
                "confirmed",
                "waiting",
                "all_components",
                "components",
                "hidden_components",
                "created_at",
                "min_severity",
                "include_maintenance",
                "paused"
              ]
            }
          }
        },
        "required": [
          "open"
        ],
        "description": "The destinations the management session (or the signed-in owner) may manage."
      },
      "Outcome": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "status": {
            "type": "string",
            "description": "What happened."
          }
        },
        "required": [
          "status"
        ],
        "description": "A one-word outcome."
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request is malformed: a bad parameter, or a body that doesn't match its schema.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Nothing with that id, or it is hidden from this viewer.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ManageRequired": {
        "description": "No management session and no signed-in owner (code manage_required).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many attempts from this address (code rate_limited); retry after the Retry-After seconds.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ServerError": {
        "description": "The server couldn't read what the answer needs (it is logged). The whole answer fails rather than coming back with parts missing; retry later. The status page itself shows its last copy with a stale banner meanwhile.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "securitySchemes": {
      "managementSession": {
        "type": "apiKey",
        "in": "cookie",
        "name": "statuspage_session",
        "description": "The page's session cookie: a management session (email + manage key, or the confirmation link) or a signed-in session."
      }
    }
  }
}
