{
  "openapi": "3.1.0",
  "info": {
    "title": "Upfinity API",
    "version": "1",
    "description": "REST API för Upfinity. Scopes: read:sites, write:sites, read:checks, read:incidents, read:stats, read:probes."
  },
  "servers": [
    {
      "url": "https://api.upfinity.app/api/v1"
    }
  ],
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "API-nyckel som börjar med upf_live_"
      }
    }
  },
  "paths": {
    "/": {
      "get": {
        "operationId": "api-index",
        "summary": "Om API:et",
        "description": "Returnerar bas-adress, länk till dokumentationen, hur man autentiserar och vilka endpoints som finns.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "name": "Upfinity API",
                  "version": "v1",
                  "baseUrl": "https://api.upfinity.app/api/v1",
                  "documentation": "https://developer.upfinity.app",
                  "authentication": "Authorization: Bearer upf_...",
                  "endpoints": [
                    "GET /sites",
                    "GET /sites/:siteId",
                    "…"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Fel i det som skickats. details visar vad som är fel per fält.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Validation failed",
                  "details": {
                    "url": [
                      "URL is required for HTTP checks"
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Nyckel saknas, är fel, avstängd eller har gått ut.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Unauthorized",
                  "message": "Missing or invalid API key. Use: Authorization: Bearer upf_..."
                }
              }
            }
          },
          "402": {
            "description": "Organisationens provperiod har gått ut eller abonnemanget är inte aktivt.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Subscription required",
                  "code": "TRIAL_EXPIRED",
                  "message": "Your free trial has expired. Please subscribe to continue using Upfinity.",
                  "upgradeUrl": "/billing"
                }
              }
            }
          },
          "403": {
            "description": "Nyckeln saknar scope för endpointen, planen saknar funktionen eller planens antal checkar är uppnått.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Insufficient permissions",
                  "message": "This endpoint requires one of: write:sites",
                  "required": [
                    "write:sites"
                  ],
                  "available": [
                    "read:sites"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Checken finns inte eller tillhör en annan organisation.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Site not found"
                }
              }
            }
          },
          "429": {
            "description": "För många anrop. Vänta det antal sekunder som Retry-After anger.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Rate limit exceeded",
                  "message": "Too many requests. Try again in 42 seconds.",
                  "retryAfter": 42
                }
              }
            }
          }
        },
        "security": []
      }
    },
    "/sites": {
      "get": {
        "operationId": "list-sites",
        "summary": "Lista checkar",
        "description": "Alla checkar i organisationen, sorterade på namn, med senaste kontroll och upptid de senaste 24 timmarna.\n\nFöre version 1.13 låg listan i sites.sites. Nu ligger den direkt i sites.\n\nlastCheck är null om checken inte har kontrollerats de senaste två timmarna. uptime24h är i procent.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "sites": [
                    {
                      "id": "6f1c2b0e-8a4d-4c1e-9b7a-2d5f3e8c1a90",
                      "organizationId": "a3e5c7d9-1b2f-4a6c-8e0d-9f7b5c3a1e2d",
                      "name": "dizweb.se",
                      "url": "https://dizweb.se",
                      "method": "GET",
                      "expectedStatusCode": 200,
                      "checkInterval": 60,
                      "isActive": true,
                      "isPaused": false,
                      "resolvedIp": "46.62.255.136",
                      "lastIpChange": null,
                      "createdAt": "2026-10-01T08:12:44.000Z",
                      "checkType": "http",
                      "protocol": "https",
                      "port": null,
                      "projectId": "c0ffee00-1111-4222-8333-444455556666",
                      "folderId": null,
                      "tags": [
                        {
                          "id": "b1d2e3f4-0000-4000-8000-000000000001",
                          "name": "produktion",
                          "color": "#1D7A46"
                        }
                      ],
                      "lastCheck": {
                        "isUp": true,
                        "responseTime": 212,
                        "checkedAt": "2026-11-12T13:40:02.000Z"
                      },
                      "slowSince": null,
                      "uptime24h": 99.98,
                      "avgResponseTime24h": 208,
                      "lighthouseMobile": null,
                      "lighthouseDesktop": null
                    }
                  ],
                  "total": 1
                }
              }
            }
          },
          "400": {
            "description": "Fel i det som skickats. details visar vad som är fel per fält.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Validation failed",
                  "details": {
                    "url": [
                      "URL is required for HTTP checks"
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Nyckel saknas, är fel, avstängd eller har gått ut.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Unauthorized",
                  "message": "Missing or invalid API key. Use: Authorization: Bearer upf_..."
                }
              }
            }
          },
          "402": {
            "description": "Organisationens provperiod har gått ut eller abonnemanget är inte aktivt.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Subscription required",
                  "code": "TRIAL_EXPIRED",
                  "message": "Your free trial has expired. Please subscribe to continue using Upfinity.",
                  "upgradeUrl": "/billing"
                }
              }
            }
          },
          "403": {
            "description": "Nyckeln saknar scope för endpointen, planen saknar funktionen eller planens antal checkar är uppnått.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Insufficient permissions",
                  "message": "This endpoint requires one of: write:sites",
                  "required": [
                    "write:sites"
                  ],
                  "available": [
                    "read:sites"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Checken finns inte eller tillhör en annan organisation.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Site not found"
                }
              }
            }
          },
          "429": {
            "description": "För många anrop. Vänta det antal sekunder som Retry-After anger.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Rate limit exceeded",
                  "message": "Too many requests. Try again in 42 seconds.",
                  "retryAfter": 42
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "read:sites"
      },
      "post": {
        "operationId": "create-site",
        "summary": "Skapa en check",
        "description": "Skapar en check och börjar kontrollera den direkt. Fälten beskrivs under \"Fält för checkar\".\n\nValideringsfel ger 400 med details per fält.\n\nHeartbeat-checkar kräver plan Standard eller högre, annars 403 med requiredTier.\n\nPlanens gränser gäller: fler checkar än planen tillåter ger 403 (Site limit reached), och ett kortare intervall än planens minsta ger 400 med minAllowed.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "site": {
                    "id": "6f1c2b0e-8a4d-4c1e-9b7a-2d5f3e8c1a90",
                    "organizationId": "a3e5c7d9-1b2f-4a6c-8e0d-9f7b5c3a1e2d",
                    "name": "Kundportal",
                    "url": "https://portal.example.com/health",
                    "method": "GET",
                    "expectedStatusCode": 200,
                    "checkInterval": 60,
                    "isActive": true,
                    "isPaused": false,
                    "resolvedIp": "46.62.255.136",
                    "lastIpChange": null,
                    "createdAt": "2026-10-01T08:12:44.000Z",
                    "checkType": "http",
                    "protocol": "https",
                    "port": null,
                    "projectId": "c0ffee00-1111-4222-8333-444455556666",
                    "folderId": null,
                    "tags": [
                      {
                        "id": "b1d2e3f4-0000-4000-8000-000000000001",
                        "name": "produktion",
                        "color": "#1D7A46"
                      }
                    ],
                    "lastCheck": {
                      "isUp": true,
                      "responseTime": 212,
                      "checkedAt": "2026-11-12T13:40:02.000Z",
                      "sslValid": true,
                      "sslExpiresAt": "2027-01-03T09:37:55.000Z",
                      "sslIssuer": "Let's Encrypt"
                    },
                    "slowSince": null,
                    "uptime24h": 99.98,
                    "avgResponseTime24h": 208,
                    "lighthouseMobile": null,
                    "lighthouseDesktop": null,
                    "headers": null,
                    "body": null,
                    "timeout": 30,
                    "sslCheckEnabled": true,
                    "sslExpiryWarningDays": 14,
                    "notifyAfterMinutes": 0,
                    "notifyOnIpChange": false,
                    "responseTimeThresholdMs": 1500,
                    "responseTimeConsecutiveChecks": 3,
                    "responseTimeRecoveryChecks": 3,
                    "checkRegions": [
                      "eu-north"
                    ],
                    "contentValidationType": "none",
                    "authType": "none",
                    "hasAuthPassword": false,
                    "hasAuthToken": false,
                    "notificationMode": "inherit",
                    "notificationChannels": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "Fel i det som skickats. details visar vad som är fel per fält.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Validation failed",
                  "details": {
                    "url": [
                      "URL is required for HTTP checks"
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Nyckel saknas, är fel, avstängd eller har gått ut.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Unauthorized",
                  "message": "Missing or invalid API key. Use: Authorization: Bearer upf_..."
                }
              }
            }
          },
          "402": {
            "description": "Organisationens provperiod har gått ut eller abonnemanget är inte aktivt.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Subscription required",
                  "code": "TRIAL_EXPIRED",
                  "message": "Your free trial has expired. Please subscribe to continue using Upfinity.",
                  "upgradeUrl": "/billing"
                }
              }
            }
          },
          "403": {
            "description": "Nyckeln saknar scope för endpointen, planen saknar funktionen eller planens antal checkar är uppnått.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Insufficient permissions",
                  "message": "This endpoint requires one of: write:sites",
                  "required": [
                    "write:sites"
                  ],
                  "available": [
                    "read:sites"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Checken finns inte eller tillhör en annan organisation.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Site not found"
                }
              }
            }
          },
          "429": {
            "description": "För många anrop. Vänta det antal sekunder som Retry-After anger.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Rate limit exceeded",
                  "message": "Too many requests. Try again in 42 seconds.",
                  "retryAfter": 42
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "write:sites",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Namn som visas i listor och notiser. Max 255 tecken."
                  },
                  "checkType": {
                    "type": "string",
                    "description": "Typ av check: http, tcp, ping, ftp, ssh eller heartbeat."
                  },
                  "url": {
                    "type": "string",
                    "description": "Adressen som ska kontrolleras, t.ex. https://example.com/health. Krävs för http."
                  },
                  "host": {
                    "type": "string",
                    "description": "Värdnamn eller IP-adress. Krävs för tcp, ping, ftp och ssh."
                  },
                  "port": {
                    "type": "integer",
                    "description": "Port 1–65535. Krävs för tcp. Standard 21 för ftp, 990 för ftps och 22 för ssh."
                  },
                  "protocol": {
                    "type": "string",
                    "description": "https eller http för http-checkar, ftp eller ftps för ftp-checkar."
                  },
                  "checkInterval": {
                    "type": "integer",
                    "description": "Sekunder mellan kontroller, 30–3600."
                  },
                  "timeout": {
                    "type": "integer",
                    "description": "Sekunder innan en kontroll räknas som misslyckad, 5–60."
                  },
                  "checkRegions": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Regioner att kontrollera från: eu-north, na, asia. I dag finns probar bara i eu-north."
                  },
                  "tagIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Taggar att sätta på checken."
                  },
                  "projectId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Projekt checken ska ligga i. Se GET /projects."
                  },
                  "folderId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Mapp i projektet. En mapp räcker, projektet följer med."
                  },
                  "method": {
                    "type": "string",
                    "description": "GET, POST, PUT, PATCH, DELETE, HEAD eller OPTIONS."
                  },
                  "headers": {
                    "type": "object",
                    "description": "Egna headers, t.ex. { \"X-Api-Key\": \"…\" }. Ersätter standardvärdena, även User-Agent."
                  },
                  "body": {
                    "type": "string",
                    "description": "Body som skickas med anropet."
                  },
                  "expectedStatusCode": {
                    "type": "integer",
                    "description": "Statuskod som räknas som uppe, 100–599. Omdirigeringar följs upp till fem steg först."
                  },
                  "sslCheckEnabled": {
                    "type": "boolean",
                    "description": "Kontrollera certifikatet och varna innan det går ut."
                  },
                  "sslExpiryWarningDays": {
                    "type": "integer",
                    "description": "Dagar före utgång då varningen skickas, 1–90."
                  },
                  "contentValidationType": {
                    "type": "string",
                    "description": "none, contains_string eller json_path."
                  },
                  "contentValidationRule": {
                    "type": "string",
                    "description": "Strängen som ska finnas, eller JSON-sökvägen, t.ex. data.status."
                  },
                  "contentValidationExpected": {
                    "type": "string",
                    "description": "Värdet som JSON-sökvägen ska ha. Krävs för json_path."
                  },
                  "authType": {
                    "type": "string",
                    "description": "none, basic, bearer, api_key eller custom."
                  },
                  "authUsername": {
                    "type": "string",
                    "description": "Användarnamn för basic."
                  },
                  "authPassword": {
                    "type": "string",
                    "description": "Lösenord för basic. Sparas krypterat och returneras aldrig."
                  },
                  "authToken": {
                    "type": "string",
                    "description": "Token för bearer, api_key och custom. Sparas krypterat och returneras aldrig."
                  },
                  "authHeaderName": {
                    "type": "string",
                    "description": "Header-namn för api_key och custom, t.ex. X-Api-Key."
                  },
                  "loginEnabled": {
                    "type": "boolean",
                    "description": "Logga in via ett formulär före varje kontroll och skicka med sessionens cookies."
                  },
                  "loginUrl": {
                    "type": "string",
                    "description": "Adressen inloggningsformuläret skickas till."
                  },
                  "loginMethod": {
                    "type": "string",
                    "description": "GET eller POST."
                  },
                  "loginUsernameField": {
                    "type": "string",
                    "description": "Fältnamn för användarnamnet."
                  },
                  "loginPasswordField": {
                    "type": "string",
                    "description": "Fältnamn för lösenordet."
                  },
                  "loginUsername": {
                    "type": "string",
                    "description": "Användarnamn för inloggningen."
                  },
                  "loginPassword": {
                    "type": "string",
                    "description": "Lösenord för inloggningen. Sparas krypterat och returneras aldrig."
                  },
                  "loginExtraFields": {
                    "type": "object",
                    "description": "Övriga formulärfält, t.ex. { \"remember\": \"1\" }."
                  },
                  "tcpExpectResponse": {
                    "type": "string",
                    "description": "Text som servern ska svara med efter uppkoppling."
                  },
                  "ftpUsername": {
                    "type": "string",
                    "description": "Användarnamn för FTP-inloggningen."
                  },
                  "ftpPassword": {
                    "type": "string",
                    "description": "Lösenord. Sparas krypterat och returneras aldrig."
                  },
                  "ftpPath": {
                    "type": "string",
                    "description": "Katalog som ska listas efter inloggningen."
                  },
                  "ftpPassive": {
                    "type": "boolean",
                    "description": "Använd passivt läge."
                  },
                  "sshUsername": {
                    "type": "string",
                    "description": "Användarnamn för SSH-inloggningen."
                  },
                  "sshPassword": {
                    "type": "string",
                    "description": "Lösenord. Sparas krypterat och returneras aldrig."
                  },
                  "sshCommand": {
                    "type": "string",
                    "description": "Kommando som körs efter inloggningen."
                  },
                  "sshExpectedExitCode": {
                    "type": "integer",
                    "description": "Förväntad exit-kod för kommandot, -1–255."
                  },
                  "heartbeatIntervalSeconds": {
                    "type": "integer",
                    "description": "Förväntad tid mellan pingar, 60 sekunder–30 dagar. Ange detta eller heartbeatSchedule."
                  },
                  "heartbeatSchedule": {
                    "type": "object",
                    "description": "Schema i stället för intervall: { freq, every, weekdays, monthDays, times, cron, startDate, timezone }. freq är minute, hour, day, week, month eller cron."
                  },
                  "heartbeatGraceSeconds": {
                    "type": "integer",
                    "description": "Marginal innan larm, 0–7 dagar. Utan värde: 10 % av intervallet, minst 1 minut."
                  },
                  "lighthouseEnabled": {
                    "type": "boolean",
                    "description": "Slå på Lighthouse-mätning. Första mätningen görs direkt."
                  },
                  "lighthouseSchedule": {
                    "type": "string",
                    "description": "daily eller weekly."
                  },
                  "lighthouseStrategy": {
                    "type": "string",
                    "description": "mobile, desktop eller both."
                  },
                  "lighthouseThreshold": {
                    "type": "integer",
                    "description": "Larma när performance-poängen går under värdet, 0–100."
                  },
                  "notifyAfterMinutes": {
                    "type": "integer",
                    "description": "Minuter checken ska vara nere innan larm, 0–60."
                  },
                  "notifyOnIpChange": {
                    "type": "boolean",
                    "description": "Larma när domänen pekar om till en okänd IP-adress."
                  },
                  "responseTimeThresholdMs": {
                    "type": "integer",
                    "description": "Larma när svarstiden överstiger värdet (100–60000 ms). null stänger av."
                  },
                  "responseTimeConsecutiveChecks": {
                    "type": "integer",
                    "description": "Långsamma kontroller i rad innan larm, 1–30."
                  },
                  "responseTimeRecoveryChecks": {
                    "type": "integer",
                    "description": "Normala kontroller i rad innan det räknas som återställt, 1–30."
                  },
                  "notificationMode": {
                    "type": "string",
                    "description": "inherit följer projektets eller organisationens standardkanaler. custom använder notificationChannels."
                  },
                  "notificationChannels": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "description": "Egna kanaler: [{ channelId, notifyOnDown, notifyOnUp, notifyOnSlow, notifyOnSslExpiry }]. Händelsefälten är true om de utelämnas."
                  },
                  "reminderEnabled": {
                    "type": "boolean",
                    "description": "Skicka påminnelser medan checken är nere. Kräver plan Standard eller högre. Utelämnat = organisationens standard."
                  },
                  "reminderIntervalMinutes": {
                    "type": "integer",
                    "description": "Minuter mellan påminnelser, 5–1440. Utelämnat = organisationens standard."
                  },
                  "maxReminders": {
                    "type": "integer",
                    "description": "Högst så här många påminnelser per incident, 0 = obegränsat. null = organisationens gräns."
                  },
                  "notificationChannelIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Äldre sätt att ange egna kanaler, alla händelser. En lista som inte är tom sätter notificationMode till custom."
                  }
                },
                "required": [
                  "name"
                ]
              },
              "example": {
                "name": "Kundportal",
                "url": "https://portal.example.com/health",
                "checkInterval": 60,
                "expectedStatusCode": 200,
                "responseTimeThresholdMs": 1500,
                "projectId": "c0ffee00-1111-4222-8333-444455556666"
              }
            }
          }
        }
      }
    },
    "/sites/{siteId}": {
      "get": {
        "operationId": "get-site",
        "summary": "Hämta en check",
        "description": "Alla inställningar för checken. Lösenord och tokens returneras aldrig, bara has-fält som visar om de är satta.\n\nHeartbeat-checkar har dessutom heartbeatUrl, adressen som jobbet ska anropa.",
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "description": "Checkens id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "site": {
                    "id": "6f1c2b0e-8a4d-4c1e-9b7a-2d5f3e8c1a90",
                    "organizationId": "a3e5c7d9-1b2f-4a6c-8e0d-9f7b5c3a1e2d",
                    "name": "dizweb.se",
                    "url": "https://dizweb.se",
                    "method": "GET",
                    "expectedStatusCode": 200,
                    "checkInterval": 60,
                    "isActive": true,
                    "isPaused": false,
                    "resolvedIp": "46.62.255.136",
                    "lastIpChange": null,
                    "createdAt": "2026-10-01T08:12:44.000Z",
                    "checkType": "http",
                    "protocol": "https",
                    "port": null,
                    "projectId": "c0ffee00-1111-4222-8333-444455556666",
                    "folderId": null,
                    "tags": [
                      {
                        "id": "b1d2e3f4-0000-4000-8000-000000000001",
                        "name": "produktion",
                        "color": "#1D7A46"
                      }
                    ],
                    "lastCheck": {
                      "isUp": true,
                      "responseTime": 212,
                      "checkedAt": "2026-11-12T13:40:02.000Z",
                      "sslValid": true,
                      "sslExpiresAt": "2027-01-03T09:37:55.000Z",
                      "sslIssuer": "Let's Encrypt"
                    },
                    "slowSince": null,
                    "uptime24h": 99.98,
                    "avgResponseTime24h": 208,
                    "lighthouseMobile": null,
                    "lighthouseDesktop": null,
                    "headers": null,
                    "body": null,
                    "timeout": 30,
                    "sslCheckEnabled": true,
                    "sslExpiryWarningDays": 14,
                    "notifyAfterMinutes": 0,
                    "notifyOnIpChange": false,
                    "responseTimeThresholdMs": 1500,
                    "responseTimeConsecutiveChecks": 3,
                    "responseTimeRecoveryChecks": 3,
                    "checkRegions": [
                      "eu-north"
                    ],
                    "contentValidationType": "none",
                    "authType": "none",
                    "hasAuthPassword": false,
                    "hasAuthToken": false,
                    "notificationMode": "inherit",
                    "notificationChannels": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "Fel i det som skickats. details visar vad som är fel per fält.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Validation failed",
                  "details": {
                    "url": [
                      "URL is required for HTTP checks"
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Nyckel saknas, är fel, avstängd eller har gått ut.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Unauthorized",
                  "message": "Missing or invalid API key. Use: Authorization: Bearer upf_..."
                }
              }
            }
          },
          "402": {
            "description": "Organisationens provperiod har gått ut eller abonnemanget är inte aktivt.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Subscription required",
                  "code": "TRIAL_EXPIRED",
                  "message": "Your free trial has expired. Please subscribe to continue using Upfinity.",
                  "upgradeUrl": "/billing"
                }
              }
            }
          },
          "403": {
            "description": "Nyckeln saknar scope för endpointen, planen saknar funktionen eller planens antal checkar är uppnått.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Insufficient permissions",
                  "message": "This endpoint requires one of: write:sites",
                  "required": [
                    "write:sites"
                  ],
                  "available": [
                    "read:sites"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Checken finns inte eller tillhör en annan organisation.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Site not found"
                }
              }
            }
          },
          "429": {
            "description": "För många anrop. Vänta det antal sekunder som Retry-After anger.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Rate limit exceeded",
                  "message": "Too many requests. Try again in 42 seconds.",
                  "retryAfter": 42
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "read:sites"
      },
      "put": {
        "operationId": "update-site",
        "summary": "Ändra en check",
        "description": "Ändrar de fält som skickas med. Övriga fält behåller sina värden. Tomma lösenordsfält betyder oförändrat.\n\nEtt kortare intervall än planens minsta ger 400 med minAllowed.",
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "description": "Checkens id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "site": {
                    "id": "6f1c2b0e-8a4d-4c1e-9b7a-2d5f3e8c1a90",
                    "organizationId": "a3e5c7d9-1b2f-4a6c-8e0d-9f7b5c3a1e2d",
                    "name": "Kundportal (produktion)",
                    "url": "https://dizweb.se",
                    "method": "GET",
                    "expectedStatusCode": 200,
                    "checkInterval": 30,
                    "isActive": true,
                    "isPaused": false,
                    "resolvedIp": "46.62.255.136",
                    "lastIpChange": null,
                    "createdAt": "2026-10-01T08:12:44.000Z",
                    "checkType": "http",
                    "protocol": "https",
                    "port": null,
                    "projectId": "c0ffee00-1111-4222-8333-444455556666",
                    "folderId": null,
                    "tags": [
                      {
                        "id": "b1d2e3f4-0000-4000-8000-000000000001",
                        "name": "produktion",
                        "color": "#1D7A46"
                      }
                    ],
                    "lastCheck": {
                      "isUp": true,
                      "responseTime": 212,
                      "checkedAt": "2026-11-12T13:40:02.000Z",
                      "sslValid": true,
                      "sslExpiresAt": "2027-01-03T09:37:55.000Z",
                      "sslIssuer": "Let's Encrypt"
                    },
                    "slowSince": null,
                    "uptime24h": 99.98,
                    "avgResponseTime24h": 208,
                    "lighthouseMobile": null,
                    "lighthouseDesktop": null,
                    "headers": null,
                    "body": null,
                    "timeout": 30,
                    "sslCheckEnabled": true,
                    "sslExpiryWarningDays": 14,
                    "notifyAfterMinutes": 0,
                    "notifyOnIpChange": false,
                    "responseTimeThresholdMs": 1500,
                    "responseTimeConsecutiveChecks": 3,
                    "responseTimeRecoveryChecks": 3,
                    "checkRegions": [
                      "eu-north"
                    ],
                    "contentValidationType": "none",
                    "authType": "none",
                    "hasAuthPassword": false,
                    "hasAuthToken": false,
                    "notificationMode": "inherit",
                    "notificationChannels": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "Fel i det som skickats. details visar vad som är fel per fält.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Validation failed",
                  "details": {
                    "url": [
                      "URL is required for HTTP checks"
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Nyckel saknas, är fel, avstängd eller har gått ut.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Unauthorized",
                  "message": "Missing or invalid API key. Use: Authorization: Bearer upf_..."
                }
              }
            }
          },
          "402": {
            "description": "Organisationens provperiod har gått ut eller abonnemanget är inte aktivt.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Subscription required",
                  "code": "TRIAL_EXPIRED",
                  "message": "Your free trial has expired. Please subscribe to continue using Upfinity.",
                  "upgradeUrl": "/billing"
                }
              }
            }
          },
          "403": {
            "description": "Nyckeln saknar scope för endpointen, planen saknar funktionen eller planens antal checkar är uppnått.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Insufficient permissions",
                  "message": "This endpoint requires one of: write:sites",
                  "required": [
                    "write:sites"
                  ],
                  "available": [
                    "read:sites"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Checken finns inte eller tillhör en annan organisation.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Site not found"
                }
              }
            }
          },
          "429": {
            "description": "För många anrop. Vänta det antal sekunder som Retry-After anger.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Rate limit exceeded",
                  "message": "Too many requests. Try again in 42 seconds.",
                  "retryAfter": 42
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "write:sites",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Namn som visas i listor och notiser. Max 255 tecken."
                  },
                  "checkType": {
                    "type": "string",
                    "description": "Typ av check: http, tcp, ping, ftp, ssh eller heartbeat."
                  },
                  "url": {
                    "type": "string",
                    "description": "Adressen som ska kontrolleras, t.ex. https://example.com/health. Krävs för http."
                  },
                  "host": {
                    "type": "string",
                    "description": "Värdnamn eller IP-adress. Krävs för tcp, ping, ftp och ssh."
                  },
                  "port": {
                    "type": "integer",
                    "description": "Port 1–65535. Krävs för tcp. Standard 21 för ftp, 990 för ftps och 22 för ssh."
                  },
                  "protocol": {
                    "type": "string",
                    "description": "https eller http för http-checkar, ftp eller ftps för ftp-checkar."
                  },
                  "checkInterval": {
                    "type": "integer",
                    "description": "Sekunder mellan kontroller, 30–3600."
                  },
                  "timeout": {
                    "type": "integer",
                    "description": "Sekunder innan en kontroll räknas som misslyckad, 5–60."
                  },
                  "checkRegions": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Regioner att kontrollera från: eu-north, na, asia. I dag finns probar bara i eu-north."
                  },
                  "tagIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Taggar att sätta på checken."
                  },
                  "projectId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Projekt checken ska ligga i. Se GET /projects."
                  },
                  "folderId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Mapp i projektet. En mapp räcker, projektet följer med."
                  },
                  "method": {
                    "type": "string",
                    "description": "GET, POST, PUT, PATCH, DELETE, HEAD eller OPTIONS."
                  },
                  "headers": {
                    "type": "object",
                    "description": "Egna headers, t.ex. { \"X-Api-Key\": \"…\" }. Ersätter standardvärdena, även User-Agent."
                  },
                  "body": {
                    "type": "string",
                    "description": "Body som skickas med anropet."
                  },
                  "expectedStatusCode": {
                    "type": "integer",
                    "description": "Statuskod som räknas som uppe, 100–599. Omdirigeringar följs upp till fem steg först."
                  },
                  "sslCheckEnabled": {
                    "type": "boolean",
                    "description": "Kontrollera certifikatet och varna innan det går ut."
                  },
                  "sslExpiryWarningDays": {
                    "type": "integer",
                    "description": "Dagar före utgång då varningen skickas, 1–90."
                  },
                  "contentValidationType": {
                    "type": "string",
                    "description": "none, contains_string eller json_path."
                  },
                  "contentValidationRule": {
                    "type": "string",
                    "description": "Strängen som ska finnas, eller JSON-sökvägen, t.ex. data.status."
                  },
                  "contentValidationExpected": {
                    "type": "string",
                    "description": "Värdet som JSON-sökvägen ska ha. Krävs för json_path."
                  },
                  "authType": {
                    "type": "string",
                    "description": "none, basic, bearer, api_key eller custom."
                  },
                  "authUsername": {
                    "type": "string",
                    "description": "Användarnamn för basic."
                  },
                  "authPassword": {
                    "type": "string",
                    "description": "Lösenord för basic. Sparas krypterat och returneras aldrig."
                  },
                  "authToken": {
                    "type": "string",
                    "description": "Token för bearer, api_key och custom. Sparas krypterat och returneras aldrig."
                  },
                  "authHeaderName": {
                    "type": "string",
                    "description": "Header-namn för api_key och custom, t.ex. X-Api-Key."
                  },
                  "loginEnabled": {
                    "type": "boolean",
                    "description": "Logga in via ett formulär före varje kontroll och skicka med sessionens cookies."
                  },
                  "loginUrl": {
                    "type": "string",
                    "description": "Adressen inloggningsformuläret skickas till."
                  },
                  "loginMethod": {
                    "type": "string",
                    "description": "GET eller POST."
                  },
                  "loginUsernameField": {
                    "type": "string",
                    "description": "Fältnamn för användarnamnet."
                  },
                  "loginPasswordField": {
                    "type": "string",
                    "description": "Fältnamn för lösenordet."
                  },
                  "loginUsername": {
                    "type": "string",
                    "description": "Användarnamn för inloggningen."
                  },
                  "loginPassword": {
                    "type": "string",
                    "description": "Lösenord för inloggningen. Sparas krypterat och returneras aldrig."
                  },
                  "loginExtraFields": {
                    "type": "object",
                    "description": "Övriga formulärfält, t.ex. { \"remember\": \"1\" }."
                  },
                  "tcpExpectResponse": {
                    "type": "string",
                    "description": "Text som servern ska svara med efter uppkoppling."
                  },
                  "ftpUsername": {
                    "type": "string",
                    "description": "Användarnamn för FTP-inloggningen."
                  },
                  "ftpPassword": {
                    "type": "string",
                    "description": "Lösenord. Sparas krypterat och returneras aldrig."
                  },
                  "ftpPath": {
                    "type": "string",
                    "description": "Katalog som ska listas efter inloggningen."
                  },
                  "ftpPassive": {
                    "type": "boolean",
                    "description": "Använd passivt läge."
                  },
                  "sshUsername": {
                    "type": "string",
                    "description": "Användarnamn för SSH-inloggningen."
                  },
                  "sshPassword": {
                    "type": "string",
                    "description": "Lösenord. Sparas krypterat och returneras aldrig."
                  },
                  "sshCommand": {
                    "type": "string",
                    "description": "Kommando som körs efter inloggningen."
                  },
                  "sshExpectedExitCode": {
                    "type": "integer",
                    "description": "Förväntad exit-kod för kommandot, -1–255."
                  },
                  "heartbeatIntervalSeconds": {
                    "type": "integer",
                    "description": "Förväntad tid mellan pingar, 60 sekunder–30 dagar. Ange detta eller heartbeatSchedule."
                  },
                  "heartbeatSchedule": {
                    "type": "object",
                    "description": "Schema i stället för intervall: { freq, every, weekdays, monthDays, times, cron, startDate, timezone }. freq är minute, hour, day, week, month eller cron."
                  },
                  "heartbeatGraceSeconds": {
                    "type": "integer",
                    "description": "Marginal innan larm, 0–7 dagar. Utan värde: 10 % av intervallet, minst 1 minut."
                  },
                  "lighthouseEnabled": {
                    "type": "boolean",
                    "description": "Slå på Lighthouse-mätning. Första mätningen görs direkt."
                  },
                  "lighthouseSchedule": {
                    "type": "string",
                    "description": "daily eller weekly."
                  },
                  "lighthouseStrategy": {
                    "type": "string",
                    "description": "mobile, desktop eller both."
                  },
                  "lighthouseThreshold": {
                    "type": "integer",
                    "description": "Larma när performance-poängen går under värdet, 0–100."
                  },
                  "notifyAfterMinutes": {
                    "type": "integer",
                    "description": "Minuter checken ska vara nere innan larm, 0–60."
                  },
                  "notifyOnIpChange": {
                    "type": "boolean",
                    "description": "Larma när domänen pekar om till en okänd IP-adress."
                  },
                  "responseTimeThresholdMs": {
                    "type": "integer",
                    "description": "Larma när svarstiden överstiger värdet (100–60000 ms). null stänger av."
                  },
                  "responseTimeConsecutiveChecks": {
                    "type": "integer",
                    "description": "Långsamma kontroller i rad innan larm, 1–30."
                  },
                  "responseTimeRecoveryChecks": {
                    "type": "integer",
                    "description": "Normala kontroller i rad innan det räknas som återställt, 1–30."
                  },
                  "notificationMode": {
                    "type": "string",
                    "description": "inherit följer projektets eller organisationens standardkanaler. custom använder notificationChannels."
                  },
                  "notificationChannels": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "description": "Egna kanaler: [{ channelId, notifyOnDown, notifyOnUp, notifyOnSlow, notifyOnSslExpiry }]. Händelsefälten är true om de utelämnas."
                  },
                  "reminderEnabled": {
                    "type": "boolean",
                    "description": "Skicka påminnelser medan checken är nere. Kräver plan Standard eller högre. Utelämnat = organisationens standard."
                  },
                  "reminderIntervalMinutes": {
                    "type": "integer",
                    "description": "Minuter mellan påminnelser, 5–1440. Utelämnat = organisationens standard."
                  },
                  "maxReminders": {
                    "type": "integer",
                    "description": "Högst så här många påminnelser per incident, 0 = obegränsat. null = organisationens gräns."
                  },
                  "notificationChannelIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Äldre sätt att ange egna kanaler, alla händelser. En lista som inte är tom sätter notificationMode till custom."
                  }
                }
              },
              "example": {
                "name": "Kundportal (produktion)",
                "checkInterval": 30
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete-site",
        "summary": "Ta bort en check",
        "description": "Tar bort checken med kontroller, incidenter och statistik. Går inte att ångra.",
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "description": "Checkens id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Inget innehåll"
          },
          "400": {
            "description": "Fel i det som skickats. details visar vad som är fel per fält.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Validation failed",
                  "details": {
                    "url": [
                      "URL is required for HTTP checks"
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Nyckel saknas, är fel, avstängd eller har gått ut.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Unauthorized",
                  "message": "Missing or invalid API key. Use: Authorization: Bearer upf_..."
                }
              }
            }
          },
          "402": {
            "description": "Organisationens provperiod har gått ut eller abonnemanget är inte aktivt.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Subscription required",
                  "code": "TRIAL_EXPIRED",
                  "message": "Your free trial has expired. Please subscribe to continue using Upfinity.",
                  "upgradeUrl": "/billing"
                }
              }
            }
          },
          "403": {
            "description": "Nyckeln saknar scope för endpointen, planen saknar funktionen eller planens antal checkar är uppnått.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Insufficient permissions",
                  "message": "This endpoint requires one of: write:sites",
                  "required": [
                    "write:sites"
                  ],
                  "available": [
                    "read:sites"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Checken finns inte eller tillhör en annan organisation.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Site not found"
                }
              }
            }
          },
          "429": {
            "description": "För många anrop. Vänta det antal sekunder som Retry-After anger.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Rate limit exceeded",
                  "message": "Too many requests. Try again in 42 seconds.",
                  "retryAfter": 42
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "write:sites"
      }
    },
    "/sites/{siteId}/pause": {
      "post": {
        "operationId": "pause-site",
        "summary": "Pausa en check",
        "description": "Stoppar kontrollerna och larmen tills checken återupptas.",
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "description": "Checkens id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "site": {
                    "id": "6f1c2b0e-8a4d-4c1e-9b7a-2d5f3e8c1a90",
                    "organizationId": "a3e5c7d9-1b2f-4a6c-8e0d-9f7b5c3a1e2d",
                    "name": "dizweb.se",
                    "url": "https://dizweb.se",
                    "method": "GET",
                    "expectedStatusCode": 200,
                    "checkInterval": 60,
                    "isActive": true,
                    "isPaused": true,
                    "resolvedIp": "46.62.255.136",
                    "lastIpChange": null,
                    "createdAt": "2026-10-01T08:12:44.000Z",
                    "checkType": "http",
                    "protocol": "https",
                    "port": null,
                    "projectId": "c0ffee00-1111-4222-8333-444455556666",
                    "folderId": null,
                    "tags": [
                      {
                        "id": "b1d2e3f4-0000-4000-8000-000000000001",
                        "name": "produktion",
                        "color": "#1D7A46"
                      }
                    ],
                    "lastCheck": {
                      "isUp": true,
                      "responseTime": 212,
                      "checkedAt": "2026-11-12T13:40:02.000Z",
                      "sslValid": true,
                      "sslExpiresAt": "2027-01-03T09:37:55.000Z",
                      "sslIssuer": "Let's Encrypt"
                    },
                    "slowSince": null,
                    "uptime24h": 99.98,
                    "avgResponseTime24h": 208,
                    "lighthouseMobile": null,
                    "lighthouseDesktop": null,
                    "headers": null,
                    "body": null,
                    "timeout": 30,
                    "sslCheckEnabled": true,
                    "sslExpiryWarningDays": 14,
                    "notifyAfterMinutes": 0,
                    "notifyOnIpChange": false,
                    "responseTimeThresholdMs": 1500,
                    "responseTimeConsecutiveChecks": 3,
                    "responseTimeRecoveryChecks": 3,
                    "checkRegions": [
                      "eu-north"
                    ],
                    "contentValidationType": "none",
                    "authType": "none",
                    "hasAuthPassword": false,
                    "hasAuthToken": false,
                    "notificationMode": "inherit",
                    "notificationChannels": []
                  },
                  "message": "Site monitoring paused"
                }
              }
            }
          },
          "400": {
            "description": "Fel i det som skickats. details visar vad som är fel per fält.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Validation failed",
                  "details": {
                    "url": [
                      "URL is required for HTTP checks"
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Nyckel saknas, är fel, avstängd eller har gått ut.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Unauthorized",
                  "message": "Missing or invalid API key. Use: Authorization: Bearer upf_..."
                }
              }
            }
          },
          "402": {
            "description": "Organisationens provperiod har gått ut eller abonnemanget är inte aktivt.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Subscription required",
                  "code": "TRIAL_EXPIRED",
                  "message": "Your free trial has expired. Please subscribe to continue using Upfinity.",
                  "upgradeUrl": "/billing"
                }
              }
            }
          },
          "403": {
            "description": "Nyckeln saknar scope för endpointen, planen saknar funktionen eller planens antal checkar är uppnått.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Insufficient permissions",
                  "message": "This endpoint requires one of: write:sites",
                  "required": [
                    "write:sites"
                  ],
                  "available": [
                    "read:sites"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Checken finns inte eller tillhör en annan organisation.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Site not found"
                }
              }
            }
          },
          "429": {
            "description": "För många anrop. Vänta det antal sekunder som Retry-After anger.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Rate limit exceeded",
                  "message": "Too many requests. Try again in 42 seconds.",
                  "retryAfter": 42
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "write:sites"
      }
    },
    "/sites/{siteId}/resume": {
      "post": {
        "operationId": "resume-site",
        "summary": "Återuppta en check",
        "description": "Startar kontrollerna igen.",
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "description": "Checkens id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "site": {
                    "id": "6f1c2b0e-8a4d-4c1e-9b7a-2d5f3e8c1a90",
                    "organizationId": "a3e5c7d9-1b2f-4a6c-8e0d-9f7b5c3a1e2d",
                    "name": "dizweb.se",
                    "url": "https://dizweb.se",
                    "method": "GET",
                    "expectedStatusCode": 200,
                    "checkInterval": 60,
                    "isActive": true,
                    "isPaused": false,
                    "resolvedIp": "46.62.255.136",
                    "lastIpChange": null,
                    "createdAt": "2026-10-01T08:12:44.000Z",
                    "checkType": "http",
                    "protocol": "https",
                    "port": null,
                    "projectId": "c0ffee00-1111-4222-8333-444455556666",
                    "folderId": null,
                    "tags": [
                      {
                        "id": "b1d2e3f4-0000-4000-8000-000000000001",
                        "name": "produktion",
                        "color": "#1D7A46"
                      }
                    ],
                    "lastCheck": {
                      "isUp": true,
                      "responseTime": 212,
                      "checkedAt": "2026-11-12T13:40:02.000Z",
                      "sslValid": true,
                      "sslExpiresAt": "2027-01-03T09:37:55.000Z",
                      "sslIssuer": "Let's Encrypt"
                    },
                    "slowSince": null,
                    "uptime24h": 99.98,
                    "avgResponseTime24h": 208,
                    "lighthouseMobile": null,
                    "lighthouseDesktop": null,
                    "headers": null,
                    "body": null,
                    "timeout": 30,
                    "sslCheckEnabled": true,
                    "sslExpiryWarningDays": 14,
                    "notifyAfterMinutes": 0,
                    "notifyOnIpChange": false,
                    "responseTimeThresholdMs": 1500,
                    "responseTimeConsecutiveChecks": 3,
                    "responseTimeRecoveryChecks": 3,
                    "checkRegions": [
                      "eu-north"
                    ],
                    "contentValidationType": "none",
                    "authType": "none",
                    "hasAuthPassword": false,
                    "hasAuthToken": false,
                    "notificationMode": "inherit",
                    "notificationChannels": []
                  },
                  "message": "Site monitoring resumed"
                }
              }
            }
          },
          "400": {
            "description": "Fel i det som skickats. details visar vad som är fel per fält.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Validation failed",
                  "details": {
                    "url": [
                      "URL is required for HTTP checks"
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Nyckel saknas, är fel, avstängd eller har gått ut.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Unauthorized",
                  "message": "Missing or invalid API key. Use: Authorization: Bearer upf_..."
                }
              }
            }
          },
          "402": {
            "description": "Organisationens provperiod har gått ut eller abonnemanget är inte aktivt.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Subscription required",
                  "code": "TRIAL_EXPIRED",
                  "message": "Your free trial has expired. Please subscribe to continue using Upfinity.",
                  "upgradeUrl": "/billing"
                }
              }
            }
          },
          "403": {
            "description": "Nyckeln saknar scope för endpointen, planen saknar funktionen eller planens antal checkar är uppnått.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Insufficient permissions",
                  "message": "This endpoint requires one of: write:sites",
                  "required": [
                    "write:sites"
                  ],
                  "available": [
                    "read:sites"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Checken finns inte eller tillhör en annan organisation.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Site not found"
                }
              }
            }
          },
          "429": {
            "description": "För många anrop. Vänta det antal sekunder som Retry-After anger.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Rate limit exceeded",
                  "message": "Too many requests. Try again in 42 seconds.",
                  "retryAfter": 42
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "write:sites"
      }
    },
    "/projects": {
      "get": {
        "operationId": "list-projects",
        "summary": "Lista projekt",
        "description": "Alla projekt med mappar och antal checkar.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "projects": [
                    {
                      "id": "c0ffee00-1111-4222-8333-444455556666",
                      "organizationId": "a3e5c7d9-1b2f-4a6c-8e0d-9f7b5c3a1e2d",
                      "name": "DizWeb",
                      "color": "#2563eb",
                      "description": null,
                      "sortOrder": 0,
                      "createdAt": "2026-10-27T10:00:00.000Z",
                      "updatedAt": "2026-10-27T10:00:00.000Z",
                      "defaultChannelIds": [],
                      "checkCount": 6,
                      "folders": [
                        {
                          "id": "f01de700-aaaa-4bbb-8ccc-ddddeeeeffff",
                          "projectId": "c0ffee00-1111-4222-8333-444455556666",
                          "name": "API",
                          "sortOrder": 0,
                          "createdAt": "2026-10-27T10:00:00.000Z",
                          "checkCount": 2
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Fel i det som skickats. details visar vad som är fel per fält.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Validation failed",
                  "details": {
                    "url": [
                      "URL is required for HTTP checks"
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Nyckel saknas, är fel, avstängd eller har gått ut.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Unauthorized",
                  "message": "Missing or invalid API key. Use: Authorization: Bearer upf_..."
                }
              }
            }
          },
          "402": {
            "description": "Organisationens provperiod har gått ut eller abonnemanget är inte aktivt.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Subscription required",
                  "code": "TRIAL_EXPIRED",
                  "message": "Your free trial has expired. Please subscribe to continue using Upfinity.",
                  "upgradeUrl": "/billing"
                }
              }
            }
          },
          "403": {
            "description": "Nyckeln saknar scope för endpointen, planen saknar funktionen eller planens antal checkar är uppnått.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Insufficient permissions",
                  "message": "This endpoint requires one of: write:sites",
                  "required": [
                    "write:sites"
                  ],
                  "available": [
                    "read:sites"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Checken finns inte eller tillhör en annan organisation.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Site not found"
                }
              }
            }
          },
          "429": {
            "description": "För många anrop. Vänta det antal sekunder som Retry-After anger.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Rate limit exceeded",
                  "message": "Too many requests. Try again in 42 seconds.",
                  "retryAfter": 42
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "read:sites"
      }
    },
    "/sites/{siteId}/checks": {
      "get": {
        "operationId": "list-checks",
        "summary": "Lista kontroller",
        "description": "Kontrollerna för en check, nyast först.\n\nFör heartbeat-checkar är probeId null och varje rad är en ping eller ett uteblivet ping.",
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "description": "Checkens id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Antal poster, högst 1000. Standard 100.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Antal poster att hoppa över. Standard 0.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "checks": [
                    {
                      "id": "0b8e1d2c-3f4a-4b5c-9d6e-7f8091a2b3c4",
                      "siteId": "6f1c2b0e-8a4d-4c1e-9b7a-2d5f3e8c1a90",
                      "probeId": "9634ba23-eed0-4195-9feb-c75927b94871",
                      "isUp": true,
                      "statusCode": 200,
                      "responseTime": 212,
                      "errorMessage": null,
                      "resolvedIp": "46.62.255.136",
                      "sslValid": true,
                      "sslExpiresAt": "2027-01-03T09:37:55.000Z",
                      "checkedAt": "2026-11-12T13:40:02.000Z"
                    }
                  ],
                  "limit": 100,
                  "offset": 0
                }
              }
            }
          },
          "400": {
            "description": "Fel i det som skickats. details visar vad som är fel per fält.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Validation failed",
                  "details": {
                    "url": [
                      "URL is required for HTTP checks"
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Nyckel saknas, är fel, avstängd eller har gått ut.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Unauthorized",
                  "message": "Missing or invalid API key. Use: Authorization: Bearer upf_..."
                }
              }
            }
          },
          "402": {
            "description": "Organisationens provperiod har gått ut eller abonnemanget är inte aktivt.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Subscription required",
                  "code": "TRIAL_EXPIRED",
                  "message": "Your free trial has expired. Please subscribe to continue using Upfinity.",
                  "upgradeUrl": "/billing"
                }
              }
            }
          },
          "403": {
            "description": "Nyckeln saknar scope för endpointen, planen saknar funktionen eller planens antal checkar är uppnått.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Insufficient permissions",
                  "message": "This endpoint requires one of: write:sites",
                  "required": [
                    "write:sites"
                  ],
                  "available": [
                    "read:sites"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Checken finns inte eller tillhör en annan organisation.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Site not found"
                }
              }
            }
          },
          "429": {
            "description": "För många anrop. Vänta det antal sekunder som Retry-After anger.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Rate limit exceeded",
                  "message": "Too many requests. Try again in 42 seconds.",
                  "retryAfter": 42
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "read:checks"
      }
    },
    "/sites/{siteId}/incidents": {
      "get": {
        "operationId": "list-site-incidents",
        "summary": "Incidenter för en check",
        "description": "Incidenterna för en check, nyast först.\n\nduration är i sekunder och null så länge incidenten pågår. status är active eller resolved.",
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "description": "Checkens id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Antal poster, högst 500. Standard 50.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Antal poster att hoppa över. Standard 0.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "incidents": [
                    {
                      "id": "e1c2d3b4-a5f6-4e7d-8c9b-0a1b2c3d4e5f",
                      "siteId": "6f1c2b0e-8a4d-4c1e-9b7a-2d5f3e8c1a90",
                      "cause": "HTTP 502",
                      "startedAt": "2026-11-10T09:45:12.000Z",
                      "resolvedAt": "2026-11-10T10:15:40.000Z",
                      "duration": 1828,
                      "errorMessage": "Expected status 200, got 502",
                      "createdAt": "2026-11-10T09:45:12.000Z",
                      "status": "resolved"
                    }
                  ],
                  "limit": 50,
                  "offset": 0
                }
              }
            }
          },
          "400": {
            "description": "Fel i det som skickats. details visar vad som är fel per fält.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Validation failed",
                  "details": {
                    "url": [
                      "URL is required for HTTP checks"
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Nyckel saknas, är fel, avstängd eller har gått ut.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Unauthorized",
                  "message": "Missing or invalid API key. Use: Authorization: Bearer upf_..."
                }
              }
            }
          },
          "402": {
            "description": "Organisationens provperiod har gått ut eller abonnemanget är inte aktivt.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Subscription required",
                  "code": "TRIAL_EXPIRED",
                  "message": "Your free trial has expired. Please subscribe to continue using Upfinity.",
                  "upgradeUrl": "/billing"
                }
              }
            }
          },
          "403": {
            "description": "Nyckeln saknar scope för endpointen, planen saknar funktionen eller planens antal checkar är uppnått.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Insufficient permissions",
                  "message": "This endpoint requires one of: write:sites",
                  "required": [
                    "write:sites"
                  ],
                  "available": [
                    "read:sites"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Checken finns inte eller tillhör en annan organisation.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Site not found"
                }
              }
            }
          },
          "429": {
            "description": "För många anrop. Vänta det antal sekunder som Retry-After anger.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Rate limit exceeded",
                  "message": "Too many requests. Try again in 42 seconds.",
                  "retryAfter": 42
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "read:incidents"
      }
    },
    "/incidents": {
      "get": {
        "operationId": "list-incidents",
        "summary": "Alla incidenter",
        "description": "Incidenter för alla checkar i organisationen, nyast först.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Antal poster, högst 500. Standard 50.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Antal poster att hoppa över. Standard 0.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "active för pågående, resolved för avslutade. Utan värde: alla.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "incidents": [
                    {
                      "id": "e1c2d3b4-a5f6-4e7d-8c9b-0a1b2c3d4e5f",
                      "siteId": "6f1c2b0e-8a4d-4c1e-9b7a-2d5f3e8c1a90",
                      "cause": "HTTP 502",
                      "startedAt": "2026-11-12T13:37:00.000Z",
                      "resolvedAt": null,
                      "duration": null,
                      "errorMessage": "Expected status 200, got 502",
                      "createdAt": "2026-11-12T13:37:00.000Z",
                      "status": "active"
                    }
                  ],
                  "limit": 50,
                  "offset": 0
                }
              }
            }
          },
          "400": {
            "description": "Fel i det som skickats. details visar vad som är fel per fält.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Validation failed",
                  "details": {
                    "url": [
                      "URL is required for HTTP checks"
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Nyckel saknas, är fel, avstängd eller har gått ut.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Unauthorized",
                  "message": "Missing or invalid API key. Use: Authorization: Bearer upf_..."
                }
              }
            }
          },
          "402": {
            "description": "Organisationens provperiod har gått ut eller abonnemanget är inte aktivt.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Subscription required",
                  "code": "TRIAL_EXPIRED",
                  "message": "Your free trial has expired. Please subscribe to continue using Upfinity.",
                  "upgradeUrl": "/billing"
                }
              }
            }
          },
          "403": {
            "description": "Nyckeln saknar scope för endpointen, planen saknar funktionen eller planens antal checkar är uppnått.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Insufficient permissions",
                  "message": "This endpoint requires one of: write:sites",
                  "required": [
                    "write:sites"
                  ],
                  "available": [
                    "read:sites"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Checken finns inte eller tillhör en annan organisation.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Site not found"
                }
              }
            }
          },
          "429": {
            "description": "För många anrop. Vänta det antal sekunder som Retry-After anger.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Rate limit exceeded",
                  "message": "Too many requests. Try again in 42 seconds.",
                  "retryAfter": 42
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "read:incidents"
      }
    },
    "/sites/{siteId}/stats": {
      "get": {
        "operationId": "site-stats",
        "summary": "Statistik för en check",
        "description": "Sammanfattning och en rad per dag för perioden.\n\nuptime och uptimePercentage är i procent, totalDowntime i sekunder. Dagens datum finns med först efter nattens körning.",
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "description": "Checkens id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "Antal dagar bakåt, högst 90. Standard 30.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "siteId": "6f1c2b0e-8a4d-4c1e-9b7a-2d5f3e8c1a90",
                  "period": {
                    "days": 30,
                    "startDate": "2026-10-13T13:40:00.000Z"
                  },
                  "summary": {
                    "totalChecks": 43180,
                    "successfulChecks": 43171,
                    "uptime": 99.979,
                    "avgResponseTime": 209
                  },
                  "dailyStats": [
                    {
                      "siteId": "6f1c2b0e-8a4d-4c1e-9b7a-2d5f3e8c1a90",
                      "date": "2026-11-11",
                      "totalChecks": 1440,
                      "successfulChecks": 1440,
                      "failedChecks": 0,
                      "avgResponseTime": 207.4,
                      "minResponseTime": 151,
                      "maxResponseTime": 640,
                      "p95ResponseTime": 289,
                      "uptimePercentage": 100,
                      "totalDowntime": 0
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Fel i det som skickats. details visar vad som är fel per fält.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Validation failed",
                  "details": {
                    "url": [
                      "URL is required for HTTP checks"
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Nyckel saknas, är fel, avstängd eller har gått ut.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Unauthorized",
                  "message": "Missing or invalid API key. Use: Authorization: Bearer upf_..."
                }
              }
            }
          },
          "402": {
            "description": "Organisationens provperiod har gått ut eller abonnemanget är inte aktivt.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Subscription required",
                  "code": "TRIAL_EXPIRED",
                  "message": "Your free trial has expired. Please subscribe to continue using Upfinity.",
                  "upgradeUrl": "/billing"
                }
              }
            }
          },
          "403": {
            "description": "Nyckeln saknar scope för endpointen, planen saknar funktionen eller planens antal checkar är uppnått.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Insufficient permissions",
                  "message": "This endpoint requires one of: write:sites",
                  "required": [
                    "write:sites"
                  ],
                  "available": [
                    "read:sites"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Checken finns inte eller tillhör en annan organisation.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Site not found"
                }
              }
            }
          },
          "429": {
            "description": "För många anrop. Vänta det antal sekunder som Retry-After anger.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Rate limit exceeded",
                  "message": "Too many requests. Try again in 42 seconds.",
                  "retryAfter": 42
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "read:stats"
      }
    },
    "/stats/overview": {
      "get": {
        "operationId": "overview",
        "summary": "Översikt",
        "description": "Läget just nu för hela organisationen.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "overview": {
                    "totalSites": 126,
                    "sitesUp": 124,
                    "sitesDown": 1,
                    "sitesPaused": 1,
                    "activeIncidents": 1
                  }
                }
              }
            }
          },
          "400": {
            "description": "Fel i det som skickats. details visar vad som är fel per fält.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Validation failed",
                  "details": {
                    "url": [
                      "URL is required for HTTP checks"
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Nyckel saknas, är fel, avstängd eller har gått ut.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Unauthorized",
                  "message": "Missing or invalid API key. Use: Authorization: Bearer upf_..."
                }
              }
            }
          },
          "402": {
            "description": "Organisationens provperiod har gått ut eller abonnemanget är inte aktivt.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Subscription required",
                  "code": "TRIAL_EXPIRED",
                  "message": "Your free trial has expired. Please subscribe to continue using Upfinity.",
                  "upgradeUrl": "/billing"
                }
              }
            }
          },
          "403": {
            "description": "Nyckeln saknar scope för endpointen, planen saknar funktionen eller planens antal checkar är uppnått.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Insufficient permissions",
                  "message": "This endpoint requires one of: write:sites",
                  "required": [
                    "write:sites"
                  ],
                  "available": [
                    "read:sites"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Checken finns inte eller tillhör en annan organisation.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Site not found"
                }
              }
            }
          },
          "429": {
            "description": "För många anrop. Vänta det antal sekunder som Retry-After anger.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Rate limit exceeded",
                  "message": "Too many requests. Try again in 42 seconds.",
                  "retryAfter": 42
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "read:stats"
      }
    },
    "/probes": {
      "get": {
        "operationId": "list-probes",
        "summary": "Lista probar",
        "description": "Aktiva probar.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "probes": [
                    {
                      "id": "9634ba23-eed0-4195-9feb-c75927b94871",
                      "name": "Main Server",
                      "location": "Helsinki, Finland",
                      "continent": "eu-north",
                      "isActive": true,
                      "lastHeartbeat": "2026-11-12T13:40:11.000Z"
                    },
                    {
                      "id": "615d8871-5755-425f-874e-ae01828e0f20",
                      "name": "Stockholm Probe",
                      "location": "Stockholm, Sweden",
                      "continent": "eu-north",
                      "isActive": true,
                      "lastHeartbeat": "2026-11-12T13:40:05.000Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Fel i det som skickats. details visar vad som är fel per fält.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Validation failed",
                  "details": {
                    "url": [
                      "URL is required for HTTP checks"
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Nyckel saknas, är fel, avstängd eller har gått ut.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Unauthorized",
                  "message": "Missing or invalid API key. Use: Authorization: Bearer upf_..."
                }
              }
            }
          },
          "402": {
            "description": "Organisationens provperiod har gått ut eller abonnemanget är inte aktivt.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Subscription required",
                  "code": "TRIAL_EXPIRED",
                  "message": "Your free trial has expired. Please subscribe to continue using Upfinity.",
                  "upgradeUrl": "/billing"
                }
              }
            }
          },
          "403": {
            "description": "Nyckeln saknar scope för endpointen, planen saknar funktionen eller planens antal checkar är uppnått.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Insufficient permissions",
                  "message": "This endpoint requires one of: write:sites",
                  "required": [
                    "write:sites"
                  ],
                  "available": [
                    "read:sites"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Checken finns inte eller tillhör en annan organisation.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Site not found"
                }
              }
            }
          },
          "429": {
            "description": "För många anrop. Vänta det antal sekunder som Retry-After anger.",
            "content": {
              "application/json": {
                "example": {
                  "error": "Rate limit exceeded",
                  "message": "Too many requests. Try again in 42 seconds.",
                  "retryAfter": 42
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "read:probes"
      }
    }
  }
}