diff --git a/.github/workflows/spec-check.yml b/.github/workflows/spec-check.yml index 5fa9a14..435938d 100644 --- a/.github/workflows/spec-check.yml +++ b/.github/workflows/spec-check.yml @@ -23,7 +23,7 @@ jobs: - name: Download latest OpenAPI spec from monorepo run: | - gh api repos/devhelmhq/mono/contents/docs/openapi/monitoring-api.yaml \ + gh api repos/devhelmhq/mono/contents/docs/openapi/monitoring-api.json \ -H "Accept: application/vnd.github.raw+json" \ -o docs/openapi/monitoring-api.json env: diff --git a/docs/openapi/monitoring-api.json b/docs/openapi/monitoring-api.json index d3dfb3f..97856bf 100644 --- a/docs/openapi/monitoring-api.json +++ b/docs/openapi/monitoring-api.json @@ -183,6 +183,86 @@ } } } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } } } }, @@ -212,17 +292,97 @@ } } } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } } } } }, "/api/v1/alert-channels/{id}": { - "put": { + "get": { "tags": [ "Alert Channels" ], - "summary": "Update an alert channel's name and re-encrypt config", - "operationId": "update_14", + "summary": "Get a single alert channel by id", + "operationId": "get_8", "parameters": [ { "name": "id", @@ -234,16 +394,6 @@ } } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateAlertChannelRequest" - } - } - }, - "required": true - }, "responses": { "200": { "description": "OK", @@ -254,15 +404,95 @@ } } } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } } } }, - "delete": { + "put": { "tags": [ "Alert Channels" ], - "summary": "Soft-delete an alert channel and return affected policy summary", - "operationId": "delete_10", + "summary": "Update an alert channel's name and re-encrypt config", + "operationId": "update_14", "parameters": [ { "name": "id", @@ -274,27 +504,115 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateAlertChannelRequest" + } + } + }, + "required": true + }, "responses": { "200": { "description": "OK", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/DeleteChannelResult" + "$ref": "#/components/schemas/SingleValueResponseAlertChannelDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } } } - } - }, - "/api/v1/alert-channels/{id}/deliveries": { - "get": { + }, + "delete": { "tags": [ "Alert Channels" ], - "summary": "List delivery history for an alert channel", - "operationId": "listDeliveries_1", + "summary": "Soft-delete an alert channel and return affected policy summary", + "operationId": "delete_10", "parameters": [ { "name": "id", @@ -312,70 +630,87 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/TableValueResultAlertDeliveryDto" + "$ref": "#/components/schemas/DeleteChannelResult" } } } - } - } - } - }, - "/api/v1/alert-channels/{id}/test": { - "post": { - "tags": [ - "Alert Channels" - ], - "summary": "Test a saved alert channel's connectivity", - "operationId": "test_2", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseTestChannelResult" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/alert-channels/test": { - "post": { - "tags": [ - "Alert Channels" - ], - "summary": "Test alert channel connectivity using raw config (no saved channel required)", - "operationId": "testConfig", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TestAlertChannelRequest" + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "200": { - "description": "OK", + "404": { + "description": "Not found — the requested resource does not exist", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseTestChannelResult" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -383,14 +718,13 @@ } } }, - "/api/v1/alert-deliveries/{id}/attempts": { + "/api/v1/alert-channels/{id}/deliveries": { "get": { "tags": [ - "Alert Deliveries" + "Alert Channels" ], - "summary": "List delivery attempts for a specific alert delivery", - "description": "Returns the ordered list of delivery attempts (request/response audit data) for the given delivery ID.", - "operationId": "listAttempts", + "summary": "List delivery history for an alert channel", + "operationId": "listDeliveries_1", "parameters": [ { "name": "id", @@ -408,7 +742,87 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/TableValueResultDeliveryAttemptDto" + "$ref": "#/components/schemas/TableValueResultAlertDeliveryDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -416,14 +830,13 @@ } } }, - "/api/v1/alert-deliveries/{id}/retry": { + "/api/v1/alert-channels/{id}/test": { "post": { "tags": [ - "Alert Deliveries" + "Alert Channels" ], - "summary": "Retry a failed delivery", - "description": "Resets a FAILED delivery to RETRY_PENDING so the delivery worker re-attempts it.", - "operationId": "retry", + "summary": "Test a saved alert channel's connectivity", + "operationId": "test_2", "parameters": [ { "name": "id", @@ -441,122 +854,198 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseAlertDeliveryDto" + "$ref": "#/components/schemas/SingleValueResponseTestChannelResult" } } } - } - } - } - }, - "/api/v1/api-keys": { - "get": { - "tags": [ - "API Keys" - ], - "summary": "List API keys", - "operationId": "list_13", - "responses": { - "200": { - "description": "OK", + }, + "400": { + "description": "Bad request — the payload failed validation", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultApiKeyDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } } } - }, + } + }, + "/api/v1/alert-channels/test": { "post": { "tags": [ - "API Keys" + "Alert Channels" ], - "summary": "Create API key", - "operationId": "create_14", + "summary": "Test alert channel connectivity using raw config (no saved channel required)", + "operationId": "testConfig", "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateApiKeyRequest" + "$ref": "#/components/schemas/TestAlertChannelRequest" } } }, "required": true }, "responses": { - "201": { - "description": "Created", + "200": { + "description": "OK", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseApiKeyCreateResponse" + "$ref": "#/components/schemas/SingleValueResponseTestChannelResult" } } } - } - } - } - }, - "/api/v1/api-keys/{id}": { - "delete": { - "tags": [ - "API Keys" - ], - "summary": "Delete API key", - "operationId": "delete_11", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "204": { - "description": "No Content" - } - } - }, - "patch": { - "tags": [ - "API Keys" - ], - "summary": "Update API key", - "operationId": "update_15", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateApiKeyRequest" + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "200": { - "description": "OK", + "404": { + "description": "Not found — the requested resource does not exist", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseApiKeyDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -564,21 +1053,22 @@ } } }, - "/api/v1/api-keys/{id}/regenerate": { - "post": { + "/api/v1/alert-deliveries/{id}/attempts": { + "get": { "tags": [ - "API Keys" + "Alert Deliveries" ], - "summary": "Regenerate API key", - "operationId": "regenerate", + "summary": "List delivery attempts for a specific alert delivery", + "description": "Returns the ordered list of delivery attempts (request/response audit data) for the given delivery ID.", + "operationId": "listAttempts", "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { - "type": "integer", - "format": "int32" + "type": "string", + "format": "uuid" } } ], @@ -588,7 +1078,87 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseApiKeyCreateResponse" + "$ref": "#/components/schemas/TableValueResultDeliveryAttemptDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -596,21 +1166,22 @@ } } }, - "/api/v1/api-keys/{id}/revoke": { + "/api/v1/alert-deliveries/{id}/retry": { "post": { "tags": [ - "API Keys" + "Alert Deliveries" ], - "summary": "Revoke API key", - "operationId": "revoke_1", + "summary": "Retry a failed delivery", + "description": "Resets a FAILED delivery to RETRY_PENDING so the delivery worker re-attempts it.", + "operationId": "retry", "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { - "type": "integer", - "format": "int32" + "type": "string", + "format": "uuid" } } ], @@ -620,93 +1191,87 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseApiKeyDto" + "$ref": "#/components/schemas/SingleValueResponseAlertDeliveryDto" } } } - } - } - } - }, - "/api/v1/audit-log": { - "get": { - "tags": [ - "Audit Log" - ], - "summary": "List audit events for the current organization", - "operationId": "list_19", - "parameters": [ - { - "name": "action", - "in": "query", - "required": false, - "schema": { - "type": "string" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "actorId", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "format": "int32" + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "resourceType", - "in": "query", - "required": false, - "schema": { - "type": "string" + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "from", - "in": "query", - "required": false, - "schema": { - "type": "string", - "format": "date-time" + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "to", - "in": "query", - "required": false, - "schema": { - "type": "string", - "format": "date-time" + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "page", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "format": "int32", - "default": 0 + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "size", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "format": "int32", - "default": 50 + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "503": { + "description": "Service unavailable — try again shortly", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultAuditEventDto" + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -714,178 +1279,100 @@ } } }, - "/api/v1/auth/me": { + "/api/v1/api-keys": { "get": { "tags": [ - "API Auth" + "API Keys" ], - "summary": "Get current API key identity", - "description": "Returns the authenticated API key's metadata, organization, billing plan, entitlements with usage, and current rate-limit quota. Only available for API key authentication (Bearer dh_live_...).", - "operationId": "me_1", + "summary": "List API keys", + "operationId": "list_13", "responses": { "200": { "description": "OK", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseAuthMeResponse" + "$ref": "#/components/schemas/TableValueResultApiKeyDto" } } } - } - } - } - }, - "/api/v1/categories": { - "get": { - "tags": [ - "Status Data" - ], - "summary": "List categories with service counts", - "operationId": "listCategories", - "responses": { - "200": { - "description": "OK", + }, + "400": { + "description": "Bad request — the payload failed validation", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultCategoryDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/dashboard/overview": { - "get": { - "tags": [ - "Dashboard" - ], - "summary": "Dashboard overview", - "description": "Returns monitor status counts, average uptime windows, and incident aggregates for the authenticated org. Results are cached for 1 minute.", - "operationId": "overview", - "responses": { - "200": { - "description": "OK", + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseDashboardOverviewDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/deploy/lock": { - "get": { - "tags": [ - "Deploy Lock" - ], - "summary": "Get current deploy lock", - "description": "Returns the active deploy lock for the current workspace, if any.", - "operationId": "current", - "responses": { - "200": { - "description": "OK", + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseDeployLockDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - }, - "post": { - "tags": [ - "Deploy Lock" - ], - "summary": "Acquire deploy lock", - "description": "Acquires an exclusive deploy lock for the current workspace. Returns 409 Conflict if the workspace is already locked by another session.", - "operationId": "acquire", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AcquireDeployLockRequest" + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "201": { - "description": "Created", + "409": { + "description": "Conflict — the request collides with current resource state", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseDeployLockDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/deploy/lock/{lockId}": { - "delete": { - "tags": [ - "Deploy Lock" - ], - "summary": "Release deploy lock", - "description": "Releases a deploy lock by ID. Only the lock holder should call this.", - "operationId": "release", - "parameters": [ - { - "name": "lockId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/api/v1/deploy/lock/force": { - "delete": { - "tags": [ - "Deploy Lock" - ], - "summary": "Force-release deploy lock", - "description": "Forcibly removes any deploy lock on the current workspace. Use to break stale locks.", - "operationId": "forceRelease", - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/api/v1/environments": { - "get": { - "tags": [ - "Environments" - ], - "summary": "List environments", - "operationId": "list_12", - "responses": { - "200": { - "description": "OK", + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultEnvironmentDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -894,15 +1381,15 @@ }, "post": { "tags": [ - "Environments" + "API Keys" ], - "summary": "Create environment", - "operationId": "create_13", + "summary": "Create API key", + "operationId": "create_14", "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateEnvironmentRequest" + "$ref": "#/components/schemas/CreateApiKeyRequest" } } }, @@ -914,7 +1401,87 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseEnvironmentDto" + "$ref": "#/components/schemas/SingleValueResponseApiKeyCreateResponse" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -922,148 +1489,124 @@ } } }, - "/api/v1/environments/{slug}": { - "get": { + "/api/v1/api-keys/{id}": { + "delete": { "tags": [ - "Environments" + "API Keys" ], - "summary": "Get environment by slug", - "operationId": "get_7", + "summary": "Delete API key", + "operationId": "delete_11", "parameters": [ { - "name": "slug", + "name": "id", "in": "path", "required": true, "schema": { - "type": "string" + "type": "integer", + "format": "int32" } } ], "responses": { - "200": { - "description": "OK", + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseEnvironmentDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - }, - "put": { - "tags": [ - "Environments" - ], - "summary": "Update environment", - "operationId": "update_13", - "parameters": [ - { - "name": "slug", - "in": "path", - "required": true, - "schema": { - "type": "string" + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateEnvironmentRequest" + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "200": { - "description": "OK", + "404": { + "description": "Not found — the requested resource does not exist", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseEnvironmentDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - }, - "delete": { - "tags": [ - "Environments" - ], - "summary": "Delete environment", - "operationId": "delete_9", - "parameters": [ - { - "name": "slug", - "in": "path", - "required": true, - "schema": { - "type": "string" + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/api/v1/heartbeat/{token}": { - "get": { - "tags": [ - "Heartbeat" - ], - "summary": "Record a heartbeat ping (GET)", - "description": "Called by external systems (cron jobs, scheduled tasks) to signal liveness. Always returns 200 OK.", - "operationId": "pingGet", - "parameters": [ - { - "name": "token", - "in": "path", - "description": "Ping endpoint token for the heartbeat monitor", - "required": true, - "schema": { - "type": "string" + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", "content": { - "*/*": { + "application/json": { "schema": { - "type": "object", - "additionalProperties": { - "type": "boolean" - } + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } } } }, - "post": { + "patch": { "tags": [ - "Heartbeat" + "API Keys" ], - "summary": "Record a heartbeat ping (POST)", - "description": "Called by external systems to signal liveness with an optional JSON payload. The payload can be inspected by heartbeat_payload_contains assertions. Always returns 200 OK.", - "operationId": "pingPost", + "summary": "Update API key", + "operationId": "update_15", "parameters": [ { - "name": "token", + "name": "id", "in": "path", - "description": "Ping endpoint token for the heartbeat monitor", "required": true, "schema": { - "type": "string" + "type": "integer", + "format": "int32" } } ], @@ -1071,20 +1614,11 @@ "content": { "application/json": { "schema": { - "type": "string" - } - }, - "text/plain": { - "schema": { - "type": "string" - } - }, - "*/*": { - "schema": { - "type": "string" + "$ref": "#/components/schemas/UpdateApiKeyRequest" } } - } + }, + "required": true }, "responses": { "200": { @@ -1092,102 +1626,87 @@ "content": { "*/*": { "schema": { - "type": "object", - "additionalProperties": { - "type": "boolean" - } + "$ref": "#/components/schemas/SingleValueResponseApiKeyDto" } } } - } - } - } - }, - "/api/v1/incidents": { - "get": { - "tags": [ - "Incidents" - ], - "summary": "List incidents for the authenticated org", - "operationId": "list_11", - "parameters": [ - { - "name": "params", - "in": "query", - "required": true, - "schema": { - "$ref": "#/components/schemas/IncidentFilterParams" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultIncidentDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - }, - "post": { - "tags": [ - "Incidents" - ], - "summary": "Create a manual incident", - "operationId": "create_12", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateManualIncidentRequest" + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "201": { - "description": "Created", + "404": { + "description": "Not found — the requested resource does not exist", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseIncidentDetailDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/incidents/{id}": { - "get": { - "tags": [ - "Incidents" - ], - "summary": "Get incident details including update timeline", - "operationId": "get_10", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "500": { + "description": "Internal server error — see the message field for details", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseIncidentDetailDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -1195,154 +1714,111 @@ } } }, - "/api/v1/incidents/{id}/resolve": { + "/api/v1/api-keys/{id}/regenerate": { "post": { "tags": [ - "Incidents" + "API Keys" ], - "summary": "Resolve an incident", - "operationId": "resolve", + "summary": "Regenerate API key", + "operationId": "regenerate", "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { - "type": "string", - "format": "uuid" + "type": "integer", + "format": "int32" } } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ResolveIncidentRequest" - } - } - } - }, "responses": { "200": { "description": "OK", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseIncidentDetailDto" + "$ref": "#/components/schemas/SingleValueResponseApiKeyCreateResponse" } } } - } - } - } - }, - "/api/v1/incidents/{id}/updates": { - "post": { - "tags": [ - "Incidents" - ], - "summary": "Add an update to an incident (optionally change status)", - "operationId": "addUpdate", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AddIncidentUpdateRequest" + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "201": { - "description": "Created", + "403": { + "description": "Forbidden — the actor lacks permission for this resource", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseIncidentDetailDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/integrations": { - "get": { - "tags": [ - "Integrations" - ], - "summary": "List all supported integration types", - "description": "Returns the full static catalog of supported alert channel integration types with their metadata and config field schemas. Used by the frontend to dynamically render the 'Add Alert Channel' form.", - "operationId": "list_18", - "responses": { - "200": { - "description": "OK", + }, + "404": { + "description": "Not found — the requested resource does not exist", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultIntegrationDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/invites": { - "get": { - "tags": [ - "Invites" - ], - "summary": "List invites", - "operationId": "list_10", - "responses": { - "200": { - "description": "OK", + }, + "409": { + "description": "Conflict — the request collides with current resource state", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultInviteDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - }, - "post": { - "tags": [ - "Invites" - ], - "summary": "Create invite", - "operationId": "create_11", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateInviteRequest" + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "201": { - "description": "Created", + "502": { + "description": "Bad gateway — an upstream provider returned an error", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseInviteDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -1350,16 +1826,16 @@ } } }, - "/api/v1/invites/{inviteId}/resend": { + "/api/v1/api-keys/{id}/revoke": { "post": { "tags": [ - "Invites" + "API Keys" ], - "summary": "Resend invite", - "operationId": "resend", + "summary": "Revoke API key", + "operationId": "revoke_1", "parameters": [ { - "name": "inviteId", + "name": "id", "in": "path", "required": true, "schema": { @@ -1374,7 +1850,87 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseInviteDto" + "$ref": "#/components/schemas/SingleValueResponseApiKeyDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -1382,57 +1938,75 @@ } } }, - "/api/v1/invites/{inviteId}/revoke": { - "post": { + "/api/v1/audit-log": { + "get": { "tags": [ - "Invites" + "Audit Log" ], - "summary": "Revoke invite", - "operationId": "revoke", + "summary": "List audit events for the current organization", + "operationId": "list_19", "parameters": [ { - "name": "inviteId", - "in": "path", - "required": true, + "name": "action", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "actorId", + "in": "query", + "required": false, "schema": { "type": "integer", "format": "int32" } - } - ], - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/api/v1/maintenance-windows": { - "get": { - "tags": [ - "Maintenance Windows" - ], - "summary": "List maintenance windows for the authenticated org", - "description": "Returns maintenance windows for the caller's organisation. Optionally filter by monitor_id, and/or by status: 'active' (currently in window) or 'upcoming' (starts in the future).", - "operationId": "list_9", - "parameters": [ + }, { - "name": "monitorId", + "name": "resourceType", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "from", "in": "query", - "description": "Filter by monitor UUID", "required": false, "schema": { "type": "string", - "format": "uuid" + "format": "date-time" } }, { - "name": "filter", + "name": "to", "in": "query", - "description": "Filter by status: 'active' or 'upcoming'", "required": false, "schema": { - "type": "string" + "type": "string", + "format": "date-time" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "size", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 50 } } ], @@ -1442,37 +2016,11687 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/TableValueResultMaintenanceWindowDto" + "$ref": "#/components/schemas/TableValueResultAuditEventDto" } } } - } - } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/auth/me": { + "get": { + "tags": [ + "API Auth" + ], + "summary": "Get current API key identity", + "description": "Returns the authenticated API key's metadata, organization, billing plan, entitlements with usage, and current rate-limit quota. Only available for API key authentication (Bearer dh_live_...).", + "operationId": "me_1", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseAuthMeResponse" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/categories": { + "get": { + "tags": [ + "Status Data" + ], + "summary": "List categories with service counts", + "operationId": "listCategories", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultCategoryDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/dashboard/overview": { + "get": { + "tags": [ + "Dashboard" + ], + "summary": "Dashboard overview", + "description": "Returns monitor status counts, average uptime windows, and incident aggregates for the authenticated org. Results are cached for 1 minute.", + "operationId": "overview", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseDashboardOverviewDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/deploy/lock": { + "get": { + "tags": [ + "Deploy Lock" + ], + "summary": "Get current deploy lock", + "description": "Returns the active deploy lock for the current workspace, if any.", + "operationId": "current", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseDeployLockDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Deploy Lock" + ], + "summary": "Acquire deploy lock", + "description": "Acquires an exclusive deploy lock for the current workspace. Returns 409 Conflict if the workspace is already locked by another session.", + "operationId": "acquire", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AcquireDeployLockRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseDeployLockDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/deploy/lock/{lockId}": { + "delete": { + "tags": [ + "Deploy Lock" + ], + "summary": "Release deploy lock", + "description": "Releases a deploy lock by ID. Only the lock holder should call this.", + "operationId": "release", + "parameters": [ + { + "name": "lockId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/deploy/lock/force": { + "delete": { + "tags": [ + "Deploy Lock" + ], + "summary": "Force-release deploy lock", + "description": "Forcibly removes any deploy lock on the current workspace. Use to break stale locks.", + "operationId": "forceRelease", + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/environments": { + "get": { + "tags": [ + "Environments" + ], + "summary": "List environments", + "operationId": "list_12", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultEnvironmentDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Environments" + ], + "summary": "Create environment", + "operationId": "create_13", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateEnvironmentRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseEnvironmentDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/environments/{slug}": { + "get": { + "tags": [ + "Environments" + ], + "summary": "Get environment by slug", + "operationId": "get_7", + "parameters": [ + { + "name": "slug", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseEnvironmentDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "Environments" + ], + "summary": "Update environment", + "operationId": "update_13", + "parameters": [ + { + "name": "slug", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateEnvironmentRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseEnvironmentDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Environments" + ], + "summary": "Delete environment", + "operationId": "delete_9", + "parameters": [ + { + "name": "slug", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/heartbeat/{token}": { + "get": { + "tags": [ + "Heartbeat" + ], + "summary": "Record a heartbeat ping (GET)", + "description": "Called by external systems (cron jobs, scheduled tasks) to signal liveness. Always returns 200 OK.", + "operationId": "pingGet", + "parameters": [ + { + "name": "token", + "in": "path", + "description": "Ping endpoint token for the heartbeat monitor", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/HeartbeatPingResponse" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Heartbeat" + ], + "summary": "Record a heartbeat ping (POST)", + "description": "Called by external systems to signal liveness with an optional JSON payload. The payload can be inspected by heartbeat_payload_contains assertions. Always returns 200 OK.", + "operationId": "pingPost", + "parameters": [ + { + "name": "token", + "in": "path", + "description": "Ping endpoint token for the heartbeat monitor", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "string" + } + }, + "text/plain": { + "schema": { + "type": "string" + } + }, + "*/*": { + "schema": { + "type": "string" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/HeartbeatPingResponse" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/incidents": { + "get": { + "tags": [ + "Incidents" + ], + "summary": "List incidents for the authenticated org", + "operationId": "list_11", + "parameters": [ + { + "name": "params", + "in": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/IncidentFilterParams" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultIncidentDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Incidents" + ], + "summary": "Create a manual incident", + "operationId": "create_12", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateManualIncidentRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseIncidentDetailDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/incidents/{id}": { + "get": { + "tags": [ + "Incidents" + ], + "summary": "Get incident details including update timeline", + "operationId": "get_11", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseIncidentDetailDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/incidents/{id}/resolve": { + "post": { + "tags": [ + "Incidents" + ], + "summary": "Resolve an incident", + "operationId": "resolve", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResolveIncidentRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseIncidentDetailDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/incidents/{id}/updates": { + "post": { + "tags": [ + "Incidents" + ], + "summary": "Add an update to an incident (optionally change status)", + "operationId": "addUpdate", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddIncidentUpdateRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseIncidentDetailDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/integrations": { + "get": { + "tags": [ + "Integrations" + ], + "summary": "List all supported integration types", + "description": "Returns the full static catalog of supported alert channel integration types with their metadata and config field schemas. Used by the frontend to dynamically render the 'Add Alert Channel' form.", + "operationId": "list_18", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultIntegrationDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/invites": { + "get": { + "tags": [ + "Invites" + ], + "summary": "List invites", + "operationId": "list_10", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultInviteDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Invites" + ], + "summary": "Create invite", + "operationId": "create_11", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateInviteRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseInviteDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/invites/{inviteId}/resend": { + "post": { + "tags": [ + "Invites" + ], + "summary": "Resend invite", + "operationId": "resend", + "parameters": [ + { + "name": "inviteId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseInviteDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/invites/{inviteId}/revoke": { + "post": { + "tags": [ + "Invites" + ], + "summary": "Revoke invite", + "operationId": "revoke", + "parameters": [ + { + "name": "inviteId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/maintenance-windows": { + "get": { + "tags": [ + "Maintenance Windows" + ], + "summary": "List maintenance windows for the authenticated org", + "description": "Returns maintenance windows for the caller's organisation. Optionally filter by monitor_id, and/or by status: 'active' (currently in window) or 'upcoming' (starts in the future).", + "operationId": "list_9", + "parameters": [ + { + "name": "monitorId", + "in": "query", + "description": "Filter by monitor UUID", + "required": false, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "filter", + "in": "query", + "description": "Filter by status: 'active' or 'upcoming'", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultMaintenanceWindowDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Maintenance Windows" + ], + "summary": "Create a maintenance window", + "description": "Creates a new maintenance window. Set monitorId to null to create an org-wide window that suppresses alerts for all monitors.", + "operationId": "create_10", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateMaintenanceWindowRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseMaintenanceWindowDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/maintenance-windows/{id}": { + "get": { + "tags": [ + "Maintenance Windows" + ], + "summary": "Get a single maintenance window by ID", + "operationId": "getById_2", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseMaintenanceWindowDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "Maintenance Windows" + ], + "summary": "Update a maintenance window", + "operationId": "update_12", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateMaintenanceWindowRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseMaintenanceWindowDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Maintenance Windows" + ], + "summary": "Delete a maintenance window", + "operationId": "delete_8", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/members": { + "get": { + "tags": [ + "Members" + ], + "summary": "List organization members", + "operationId": "list_17", + "parameters": [ + { + "name": "pageable", + "in": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/Pageable" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultMemberDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/members/{userId}": { + "delete": { + "tags": [ + "Members" + ], + "summary": "Remove member from organization", + "operationId": "remove_2", + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/members/{userId}/role": { + "put": { + "tags": [ + "Members" + ], + "summary": "Change member role", + "operationId": "changeRole", + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChangeRoleRequest" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/members/{userId}/status": { + "put": { + "tags": [ + "Members" + ], + "summary": "Change member status", + "operationId": "changeStatus", + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChangeStatusRequest" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/monitors": { + "get": { + "tags": [ + "Monitors" + ], + "summary": "List monitors for the authenticated org", + "operationId": "list_8", + "parameters": [ + { + "name": "enabled", + "in": "query", + "description": "Filter by enabled state", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "type", + "in": "query", + "description": "Filter by monitor type", + "required": false, + "schema": { + "type": "string", + "enum": [ + "HTTP", + "DNS", + "MCP_SERVER", + "TCP", + "ICMP", + "HEARTBEAT" + ] + } + }, + { + "name": "managedBy", + "in": "query", + "description": "Filter by managed-by source", + "required": false, + "schema": { + "type": "string", + "enum": [ + "DASHBOARD", + "CLI", + "TERRAFORM" + ] + } + }, + { + "name": "tags", + "in": "query", + "description": "Filter by tag names, comma-separated (e.g. prod,critical)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "search", + "in": "query", + "description": "Case-insensitive name search", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "environmentId", + "in": "query", + "description": "Filter by environment ID", + "required": false, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "pageable", + "in": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/Pageable" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultMonitorDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Monitors" + ], + "summary": "Create a new monitor", + "operationId": "create_9", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateMonitorRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseMonitorDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/monitors/{id}": { + "get": { + "tags": [ + "Monitors" + ], + "summary": "Get a single monitor by id", + "operationId": "get_6", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseMonitorDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "Monitors" + ], + "summary": "Update a monitor", + "operationId": "update_11", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateMonitorRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseMonitorDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Monitors" + ], + "summary": "Soft-delete a monitor", + "operationId": "delete_7", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/monitors/{id}/pause": { + "post": { + "tags": [ + "Monitors" + ], + "summary": "Pause a monitor (set enabled=false)", + "operationId": "pause", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseMonitorDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/monitors/{id}/results": { + "get": { + "tags": [ + "Check Results" + ], + "summary": "List raw check results", + "description": "Returns check results for the given monitor with optional time-range, region, and pass/fail filtering. Uses cursor-based pagination — pass the returned `cursor` value on subsequent requests to retrieve the next page. The cursor encodes the original time bounds, so `from`/`to` are ignored when a cursor is present.", + "operationId": "getResults", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "from", + "in": "query", + "description": "Start of time range (ISO 8601, inclusive); defaults to 24 hours ago", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "to", + "in": "query", + "description": "End of time range (ISO 8601, inclusive); defaults to now", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "cursor", + "in": "query", + "description": "Opaque cursor from a previous response for pagination", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "limit", + "in": "query", + "description": "Maximum results per page (1–200)", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 50 + }, + "example": 50 + }, + { + "name": "region", + "in": "query", + "description": "Filter by region (e.g. us-east)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "passed", + "in": "query", + "description": "Filter by pass/fail status", + "required": false, + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "Paginated check results", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/CursorPageCheckResultDto" + } + } + } + }, + "400": { + "description": "Invalid query parameters", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/CursorPageCheckResultDto" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Monitor does not belong to the caller's org", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/CursorPageCheckResultDto" + } + } + } + }, + "404": { + "description": "Monitor not found", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/CursorPageCheckResultDto" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/monitors/{id}/results/summary": { + "get": { + "tags": [ + "Check Results" + ], + "summary": "Get results summary", + "description": "Returns a dashboard summary for the monitor: current status derived from the latest result per region, time-bucketed chart data, the 24-hour uptime percentage, and the selected window's uptime percentage.", + "operationId": "getSummary", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "chartWindow", + "in": "query", + "description": "Chart window: 24h returns hourly buckets, 7d/30d/90d return daily buckets", + "required": false, + "schema": { + "type": "string", + "enum": [ + "24h", + "7d", + "30d", + "90d" + ] + } + } + ], + "responses": { + "200": { + "description": "Results summary", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/ResultSummaryDto" + } + } + } + }, + "400": { + "description": "Invalid chartWindow parameter", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseResultSummaryDto" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Monitor does not belong to the caller's org", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseResultSummaryDto" + } + } + } + }, + "404": { + "description": "Monitor not found", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseResultSummaryDto" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/monitors/{id}/resume": { + "post": { + "tags": [ + "Monitors" + ], + "summary": "Resume a monitor (set enabled=true)", + "operationId": "resume", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseMonitorDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/monitors/{id}/rotate-token": { + "post": { + "tags": [ + "Monitors" + ], + "summary": "Rotate the ping token for a heartbeat monitor", + "description": "Generates a new ping token. The old token remains valid for 24 hours to allow cron jobs to be updated without downtime. Only supported for HEARTBEAT monitors.", + "operationId": "rotateToken", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseMonitorDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/monitors/{id}/tags": { + "get": { + "tags": [ + "Monitors" + ], + "summary": "Get all tags applied to a monitor", + "operationId": "getMonitorTags", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultTagDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Monitors" + ], + "summary": "Add tags to a monitor; supports existing tag IDs and inline creation of new tags", + "operationId": "addMonitorTags", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddMonitorTagsRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultTagDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Monitors" + ], + "summary": "Remove tags from a monitor by their IDs", + "operationId": "removeMonitorTags", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RemoveMonitorTagsRequest" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/monitors/{id}/test": { + "post": { + "tags": [ + "Monitors" + ], + "summary": "Test an existing monitor", + "description": "Runs the saved config and assertions of an existing monitor once, without persisting any result. Runs synchronously and returns the same shape as the ad-hoc test.", + "operationId": "testExisting", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseMonitorTestResultDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/monitors/{id}/uptime": { + "get": { + "tags": [ + "Check Results" + ], + "summary": "Get uptime statistics", + "description": "Returns uptime percentage and latency statistics for the requested time window, computed from continuous aggregates. Uses hourly aggregates for 24h/7d windows and daily aggregates for 30d/90d windows.", + "operationId": "getUptime", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "window", + "in": "query", + "description": "Time window for uptime calculation", + "required": false, + "schema": { + "type": "string", + "enum": [ + "24h", + "7d", + "30d", + "90d" + ] + } + } + ], + "responses": { + "200": { + "description": "Uptime statistics", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/UptimeDto" + } + } + } + }, + "400": { + "description": "Invalid window parameter", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseUptimeDto" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Monitor does not belong to the caller's org", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseUptimeDto" + } + } + } + }, + "404": { + "description": "Monitor not found", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseUptimeDto" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/monitors/{id}/versions": { + "get": { + "tags": [ + "Monitors" + ], + "summary": "List version history for a monitor", + "description": "Returns a paginated list of mutation snapshots for the monitor, newest first. Each version captures the full monitor config at the time of a PUT /monitors/{id} call.", + "operationId": "listVersions", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "pageable", + "in": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/Pageable" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultMonitorVersionDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/monitors/{id}/versions/{version}": { + "get": { + "tags": [ + "Monitors" + ], + "summary": "Get a specific version snapshot for a monitor", + "description": "Returns the full monitor config snapshot captured at the given version number.", + "operationId": "getVersion", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "version", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseMonitorVersionDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/monitors/{monitorId}/alert-channels": { + "put": { + "tags": [ + "Monitor Alert Channels" + ], + "summary": "Replace the linked alert channel set for a monitor", + "operationId": "setChannels", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetAlertChannelsRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseListUUID" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/monitors/{monitorId}/assertions": { + "post": { + "tags": [ + "Monitor Assertions" + ], + "summary": "Add an assertion to a monitor", + "operationId": "add", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateAssertionRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseMonitorAssertionDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/monitors/{monitorId}/assertions/{assertionId}": { + "put": { + "tags": [ + "Monitor Assertions" + ], + "summary": "Update an assertion on a monitor", + "operationId": "update_10", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "assertionId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateAssertionRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseMonitorAssertionDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Monitor Assertions" + ], + "summary": "Remove an assertion from a monitor", + "operationId": "remove_1", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "assertionId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/monitors/{monitorId}/auth": { + "put": { + "tags": [ + "Monitor Auth" + ], + "summary": "Update authentication config for a monitor", + "operationId": "update_9", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateMonitorAuthRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseMonitorAuthDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Monitor Auth" + ], + "summary": "Set authentication config for a monitor", + "operationId": "set", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetMonitorAuthRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseMonitorAuthDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Monitor Auth" + ], + "summary": "Remove authentication config from a monitor", + "operationId": "remove", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/monitors/{monitorId}/policy": { + "get": { + "tags": [ + "Incident Policies" + ], + "summary": "Get incident policy for a monitor", + "description": "Returns the trigger rules, confirmation settings, and recovery settings for the given monitor.", + "operationId": "get_5", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "description": "Monitor UUID", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Policy found", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/IncidentPolicyDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Monitor or policy not found", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseIncidentPolicyDto" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "Incident Policies" + ], + "summary": "Update incident policy for a monitor", + "description": "Replaces the trigger rules, confirmation settings, and recovery settings. All fields are validated before saving.", + "operationId": "update_8", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "description": "Monitor UUID", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateIncidentPolicyRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Policy updated", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/IncidentPolicyDto" + } + } + } + }, + "400": { + "description": "Validation error in JSONB shape", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseIncidentPolicyDto" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Monitor or policy not found", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseIncidentPolicyDto" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/monitors/bulk": { + "post": { + "tags": [ + "Monitors" + ], + "summary": "Bulk action on monitors", + "description": "Applies PAUSE, RESUME, DELETE, ADD_TAG, or REMOVE_TAG to a list of monitors. Returns a partial-success response indicating which monitors succeeded and which failed.", + "operationId": "bulkAction", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BulkMonitorActionRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseBulkMonitorActionResult" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/monitors/test": { + "post": { + "tags": [ + "Monitors" + ], + "summary": "Ad-hoc monitor test", + "description": "Executes a one-off check from an inline config without saving the monitor. Runs synchronously and returns status code, response time, assertion results, body preview, and headers.", + "operationId": "testAdHoc", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MonitorTestRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseMonitorTestResultDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/notification-dispatches": { + "get": { + "tags": [ + "Notification Dispatches" + ], + "summary": "List all dispatches for an incident", + "description": "Returns all notification dispatches for the given incident that belong to the authenticated org's policies. Each dispatch includes delivery records for all associated channels.", + "operationId": "listByIncident", + "parameters": [ + { + "name": "incident_id", + "in": "query", + "description": "UUID of the incident to inspect", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultNotificationDispatchDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/notification-dispatches/{id}": { + "get": { + "tags": [ + "Notification Dispatches" + ], + "summary": "Get a single dispatch with full escalation and delivery history", + "description": "Returns the dispatch state including current escalation step, acknowledgment info, and all delivery attempts made across every step.", + "operationId": "getById_3", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseNotificationDispatchDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/notification-dispatches/{id}/acknowledge": { + "post": { + "tags": [ + "Notification Dispatches" + ], + "summary": "Acknowledge a notification dispatch", + "description": "Marks the dispatch as acknowledged. The dispatch must be in DELIVERED or ESCALATING state. Sets acknowledgedAt, acknowledgedBy (actor email), and acknowledgedVia (DASHBOARD).", + "operationId": "acknowledge", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseNotificationDispatchDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/notification-policies": { + "get": { + "tags": [ + "Notification Policies" + ], + "summary": "List all notification policies for the authenticated org", + "operationId": "list_7", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultNotificationPolicyDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Notification Policies" + ], + "summary": "Create a notification policy with match rules and escalation chain", + "operationId": "create_8", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateNotificationPolicyRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseNotificationPolicyDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/notification-policies/{id}": { + "get": { + "tags": [ + "Notification Policies" + ], + "summary": "Get a notification policy by ID", + "operationId": "getById_1", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseNotificationPolicyDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "Notification Policies" + ], + "summary": "Update a notification policy", + "operationId": "update_7", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateNotificationPolicyRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseNotificationPolicyDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Notification Policies" + ], + "summary": "Delete a notification policy", + "operationId": "delete_6", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/notification-policies/{id}/dispatches": { + "get": { + "tags": [ + "Notification Policies" + ], + "summary": "List all dispatches (firing history) for a notification policy", + "operationId": "listDispatches", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultNotificationDispatchDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/notification-policies/{id}/test": { + "post": { + "tags": [ + "Notification Policies" + ], + "summary": "Dry-run: evaluate a policy's match rules against a supplied incident context", + "operationId": "test_1", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestNotificationPolicyRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseTestMatchResult" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/notifications": { + "get": { + "tags": [ + "Notifications" + ], + "summary": "List notifications for the current user", + "operationId": "list_16", + "parameters": [ + { + "name": "unreadOnly", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": false + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "size", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultNotificationDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/notifications/{id}/read": { + "put": { + "tags": [ + "Notifications" + ], + "summary": "Mark a notification as read", + "operationId": "markRead", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/notifications/read-all": { + "put": { + "tags": [ + "Notifications" + ], + "summary": "Mark all notifications as read", + "operationId": "markAllRead", + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/notifications/unread-count": { + "get": { + "tags": [ + "Notifications" + ], + "summary": "Get unread notification count", + "operationId": "unreadCount", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseLong" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/org": { + "get": { + "tags": [ + "Organizations" + ], + "summary": "Get the current organization", + "operationId": "get_4", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseOrganizationDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "Organizations" + ], + "summary": "Update the current organization", + "operationId": "update_6", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateOrgDetailsRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseOrganizationDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/resource-groups": { + "get": { + "tags": [ + "Resource Groups" + ], + "summary": "List all resource groups for the authenticated org with health summaries", + "operationId": "list_6", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultResourceGroupDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Resource Groups" + ], + "summary": "Create a new resource group", + "operationId": "create_7", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceGroupRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseResourceGroupDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/resource-groups/{id}": { + "get": { + "tags": [ + "Resource Groups" + ], + "summary": "Get a resource group by id with member statuses and inherited settings", + "description": "Pass includeMetrics=true to enrich each member with 24h uptime, chart data, and latency metrics.", + "operationId": "get_3", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "includeMetrics", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": false + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseResourceGroupDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "Resource Groups" + ], + "summary": "Update a resource group's name, description, alert policy, inherited settings, and health threshold", + "operationId": "update_5", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateResourceGroupRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseResourceGroupDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Resource Groups" + ], + "summary": "Delete a resource group (cascades to member rows)", + "operationId": "delete_5", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/resource-groups/{id}/health": { + "get": { + "tags": [ + "Resource Groups" + ], + "summary": "Get the detailed health breakdown for a resource group", + "description": "Returns member counts, worst-of status, and threshold-based health evaluation. The thresholdStatus field is populated only when a health threshold is configured.", + "operationId": "getHealth", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseResourceGroupHealthDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/resource-groups/{id}/members": { + "post": { + "tags": [ + "Resource Groups" + ], + "summary": "Add a monitor or service member to a resource group", + "operationId": "addMember_1", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddResourceGroupMemberRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseResourceGroupMemberDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/resource-groups/{id}/members/{memberId}": { + "delete": { + "tags": [ + "Resource Groups" + ], + "summary": "Remove a member from a resource group", + "operationId": "removeMember_1", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "memberId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/secrets": { + "get": { + "tags": [ + "Secrets" + ], + "summary": "List secrets", + "operationId": "list_5", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultSecretDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Secrets" + ], + "summary": "Create secret", + "operationId": "create_6", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateSecretRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseSecretDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/secrets/{key}": { + "put": { + "tags": [ + "Secrets" + ], + "summary": "Update secret", + "operationId": "update_4", + "parameters": [ + { + "name": "key", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateSecretRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseSecretDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Secrets" + ], + "summary": "Delete secret", + "operationId": "delete_4", + "parameters": [ + { + "name": "key", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/service-subscriptions": { + "get": { + "tags": [ + "Service Subscriptions" + ], + "summary": "List all service subscriptions for the organization", + "operationId": "list_15", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultServiceSubscriptionDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/service-subscriptions/{id}": { + "get": { + "tags": [ + "Service Subscriptions" + ], + "summary": "Get a subscription by its ID", + "operationId": "get_10", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseServiceSubscriptionDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } }, + "delete": { + "tags": [ + "Service Subscriptions" + ], + "summary": "Remove a subscription by its ID", + "description": "Removes a specific subscription (whole-service or component-level). No-op if not found.", + "operationId": "unsubscribe_1", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/service-subscriptions/{id}/alert-sensitivity": { + "patch": { + "tags": [ + "Service Subscriptions" + ], + "summary": "Update alert sensitivity for a subscription", + "description": "Controls which external incidents trigger alerts: ALL (any status change), INCIDENTS_ONLY (real vendor incidents, default), MAJOR_ONLY (only DOWN-level incidents).", + "operationId": "updateAlertSensitivity", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateAlertSensitivityRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseServiceSubscriptionDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/service-subscriptions/{slug}": { "post": { "tags": [ - "Maintenance Windows" + "Service Subscriptions" + ], + "summary": "Subscribe to a service or a component of a service", + "description": "Idempotent — returns the existing subscription if an identical one exists. Omit the request body or set componentId to null for a whole-service subscription. Free tier: max 10 subscriptions. Paid tier: unlimited.", + "operationId": "subscribe_1", + "parameters": [ + { + "name": "slug", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceSubscribeRequest" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseServiceSubscriptionDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/services": { + "get": { + "tags": [ + "Status Data" + ], + "summary": "List all enabled services (cursor-paginated)", + "operationId": "listServices", + "parameters": [ + { + "name": "category", + "in": "query", + "description": "Filter by category (exact match)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "status", + "in": "query", + "description": "Filter by current overall_status (exact match)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "published", + "in": "query", + "description": "Filter by published status for pSEO pages", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "cursor", + "in": "query", + "description": "Opaque cursor from a previous response", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "limit", + "in": "query", + "description": "Page size (1–100, default 20)", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/CursorPageServiceCatalogDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/services/{slugOrId}": { + "get": { + "tags": [ + "Status Data" + ], + "summary": "Get a single service by slug or UUID with current status, components, and recent incidents", + "operationId": "getService", + "parameters": [ + { + "name": "slugOrId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseServiceDetailDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/services/{slugOrId}/components": { + "get": { + "tags": [ + "Status Data" + ], + "summary": "List active components for a service with current status and inline uptime", + "operationId": "getComponents", + "parameters": [ + { + "name": "slugOrId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultServiceComponentDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/services/{slugOrId}/components/{componentId}/uptime": { + "get": { + "tags": [ + "Status Data" + ], + "summary": "Get daily uptime data for a component", + "description": "Pass either ``period`` (preset window) or an explicit ``from``/``to`` calendar window (ISO yyyy-MM-dd, max 730 days, ``to`` defaults to today). When both are supplied, the explicit window wins.", + "operationId": "getComponentUptime", + "parameters": [ + { + "name": "slugOrId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "componentId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "period", + "in": "query", + "description": "Preset time window (used when ``from`` is omitted)", + "required": false, + "schema": { + "type": "string", + "enum": [ + "7d", + "30d", + "90d", + "1y" + ] + } + }, + { + "name": "from", + "in": "query", + "description": "Explicit window start (ISO date); overrides ``period``", + "required": false, + "schema": { + "type": "string", + "format": "date" + } + }, + { + "name": "to", + "in": "query", + "description": "Explicit window end (ISO date); defaults to today", + "required": false, + "schema": { + "type": "string", + "format": "date" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultComponentUptimeDayDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/services/{slugOrId}/components/uptime": { + "get": { + "tags": [ + "Status Data" + ], + "summary": "Batch daily uptime data for all leaf components", + "description": "Returns daily uptime for every active non-group component with uptime data, keyed by component ID. Replaces N individual per-component uptime calls with a single request. Supports either a preset ``period`` or an explicit ``from``/``to`` window (ISO yyyy-MM-dd, max 730 days). The explicit window wins when both are supplied — this is what powers the sliding 90-day navigator on the public uptime history page.", + "operationId": "getBatchComponentUptime", + "parameters": [ + { + "name": "slugOrId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "period", + "in": "query", + "description": "Preset time window (used when ``from`` is omitted)", + "required": false, + "schema": { + "type": "string", + "enum": [ + "7d", + "30d", + "90d", + "1y" + ] + } + }, + { + "name": "from", + "in": "query", + "description": "Explicit window start (ISO date); overrides ``period``", + "required": false, + "schema": { + "type": "string", + "format": "date" + } + }, + { + "name": "to", + "in": "query", + "description": "Explicit window end (ISO date); defaults to today", + "required": false, + "schema": { + "type": "string", + "format": "date" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseBatchComponentUptimeDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/services/{slugOrId}/days/{date}": { + "get": { + "tags": [ + "Status Data" ], - "summary": "Create a maintenance window", - "description": "Creates a new maintenance window. Set monitorId to null to create an org-wide window that suppresses alerts for all monitors.", - "operationId": "create_10", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateMaintenanceWindowRequest" + "summary": "One-day rollup for a service: aggregated uptime, per-component impacts, and overlapping incidents", + "description": "Powers the click/hover-to-expand panel under each uptime bar on the public status page. Single round-trip — components, sums, and overlapping incidents (with affected component names) are returned in one response.", + "operationId": "getServiceDayDetail", + "parameters": [ + { + "name": "slugOrId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "date", + "in": "path", + "description": "UTC calendar day in ISO format (YYYY-MM-DD)", + "required": true, + "schema": { + "type": "string", + "format": "date" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseServiceDayDetailDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/api/v1/services/{slugOrId}/incidents": { + "get": { + "tags": [ + "Status Data" + ], + "summary": "List incident history for a service (paginated)", + "operationId": "listIncidents_1", + "parameters": [ + { + "name": "slugOrId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "from", + "in": "query", + "description": "Earliest start date (ISO 8601 date)", + "required": false, + "schema": { + "type": "string", + "format": "date" + } + }, + { + "name": "status", + "in": "query", + "description": "Filter: active (unresolved), resolved, or omit for all", + "required": false, + "schema": { + "type": "string", + "enum": [ + "active", + "resolved" + ] + } + }, + { + "name": "pageable", + "in": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/Pageable" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultServiceIncidentDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "201": { - "description": "Created", + "404": { + "description": "Not found — the requested resource does not exist", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseMaintenanceWindowDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -1480,16 +13704,24 @@ } } }, - "/api/v1/maintenance-windows/{id}": { + "/api/v1/services/{slugOrId}/incidents/{incidentId}": { "get": { "tags": [ - "Maintenance Windows" + "Status Data" ], - "summary": "Get a single maintenance window by ID", - "operationId": "getById_2", + "summary": "Get incident detail with full update timeline", + "operationId": "getIncident_1", "parameters": [ { - "name": "id", + "name": "slugOrId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "incidentId", "in": "path", "required": true, "schema": { @@ -1504,91 +13736,109 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseMaintenanceWindowDto" + "$ref": "#/components/schemas/SingleValueResponseServiceIncidentDetailDto" } } } - } - } - }, - "put": { - "tags": [ - "Maintenance Windows" - ], - "summary": "Update a maintenance window", - "operationId": "update_12", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateMaintenanceWindowRequest" + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "200": { - "description": "OK", + "403": { + "description": "Forbidden — the actor lacks permission for this resource", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseMaintenanceWindowDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - }, - "delete": { - "tags": [ - "Maintenance Windows" - ], - "summary": "Delete a maintenance window", - "operationId": "delete_8", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "204": { - "description": "No Content" } } } }, - "/api/v1/members": { + "/api/v1/services/{slugOrId}/live-status": { "get": { "tags": [ - "Members" + "Status Data" ], - "summary": "List organization members", - "operationId": "list_17", + "summary": "Lightweight live-status snapshot for polling", + "description": "Returns only the current overall status, component statuses, and active incident count. Designed for frequent polling with minimal payload.", + "operationId": "getServiceLiveStatus", "parameters": [ { - "name": "pageable", - "in": "query", + "name": "slugOrId", + "in": "path", "required": true, "schema": { - "$ref": "#/components/schemas/Pageable" + "type": "string" } } ], @@ -1598,191 +13848,251 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/TableValueResultMemberDto" + "$ref": "#/components/schemas/SingleValueResponseServiceLiveStatusDto" } } } - } - } - } - }, - "/api/v1/members/{userId}": { - "delete": { - "tags": [ - "Members" - ], - "summary": "Remove member from organization", - "operationId": "remove_2", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/api/v1/members/{userId}/role": { - "put": { - "tags": [ - "Members" - ], - "summary": "Change member role", - "operationId": "changeRole", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ChangeRoleRequest" + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "204": { - "description": "No Content" + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } } } } }, - "/api/v1/members/{userId}/status": { - "put": { + "/api/v1/services/{slugOrId}/maintenances": { + "get": { "tags": [ - "Members" + "Status Data" ], - "summary": "Change member status", - "operationId": "changeStatus", + "summary": "List scheduled maintenances for a service", + "operationId": "getScheduledMaintenances", "parameters": [ { - "name": "userId", + "name": "slugOrId", "in": "path", "required": true, "schema": { - "type": "integer", - "format": "int32" + "type": "string" + } + }, + { + "name": "status", + "in": "query", + "description": "Filter by status (e.g. scheduled, in_progress, verifying, completed)", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + } } } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ChangeStatusRequest" + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultScheduledMaintenanceDto" + } } } }, - "required": true - }, - "responses": { - "204": { - "description": "No Content" + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } } } } }, - "/api/v1/monitors": { + "/api/v1/services/{slugOrId}/poll-results": { "get": { "tags": [ - "Monitors" + "Status Data" ], - "summary": "List monitors for the authenticated org", - "operationId": "list_8", + "summary": "List poll results for a service (cursor-paginated)", + "operationId": "listPollResults", "parameters": [ { - "name": "enabled", - "in": "query", - "description": "Filter by enabled state", - "required": false, - "schema": { - "type": "boolean" - } - }, - { - "name": "type", - "in": "query", - "description": "Filter by monitor type", - "required": false, - "schema": { - "type": "string", - "enum": [ - "HTTP", - "DNS", - "MCP_SERVER", - "TCP", - "ICMP", - "HEARTBEAT" - ] - } - }, - { - "name": "managedBy", - "in": "query", - "description": "Filter by managed-by source", - "required": false, - "schema": { - "type": "string", - "enum": [ - "DASHBOARD", - "CLI", - "TERRAFORM" - ] - } - }, - { - "name": "tags", - "in": "query", - "description": "Filter by tag names, comma-separated (e.g. prod,critical)", - "required": false, + "name": "slugOrId", + "in": "path", + "required": true, "schema": { "type": "string" } }, { - "name": "search", + "name": "cursor", "in": "query", - "description": "Case-insensitive name search", + "description": "ISO 8601 timestamp cursor from a previous response", "required": false, "schema": { "type": "string" } }, { - "name": "environmentId", + "name": "limit", "in": "query", - "description": "Filter by environment ID", + "description": "Page size (1–100, default 50)", "required": false, "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "pageable", - "in": "query", - "required": true, - "schema": { - "$ref": "#/components/schemas/Pageable" + "type": "integer", + "format": "int32", + "default": 50 } } ], @@ -1792,153 +14102,122 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/TableValueResultMonitorDto" + "$ref": "#/components/schemas/CursorPageServicePollResultDto" } } } - } - } - }, - "post": { - "tags": [ - "Monitors" - ], - "summary": "Create a new monitor", - "operationId": "create_9", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateMonitorRequest" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "201": { - "description": "Created", + "401": { + "description": "Unauthorized — missing or invalid credentials", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseMonitorDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/monitors/{id}": { - "get": { - "tags": [ - "Monitors" - ], - "summary": "Get a single monitor by id", - "operationId": "get_6", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseMonitorDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - }, - "put": { - "tags": [ - "Monitors" - ], - "summary": "Update a monitor", - "operationId": "update_11", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateMonitorRequest" + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "200": { - "description": "OK", + "503": { + "description": "Service unavailable — try again shortly", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseMonitorDto" + "$ref": "#/components/schemas/ErrorResponse" } } } } } - }, - "delete": { + } + }, + "/api/v1/services/{slugOrId}/poll-summary": { + "get": { "tags": [ - "Monitors" + "Status Data" ], - "summary": "Soft-delete a monitor", - "operationId": "delete_7", + "summary": "Get aggregated poll metrics and chart data for a service", + "operationId": "getPollSummary", "parameters": [ { - "name": "id", + "name": "slugOrId", "in": "path", "required": true, "schema": { - "type": "string", - "format": "uuid" + "type": "string" } - } - ], - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/api/v1/monitors/{id}/pause": { - "post": { - "tags": [ - "Monitors" - ], - "summary": "Pause a monitor (set enabled=false)", - "operationId": "pause", - "parameters": [ + }, { - "name": "id", - "in": "path", - "required": true, + "name": "window", + "in": "query", + "description": "Time window", + "required": false, "schema": { "type": "string", - "format": "uuid" + "enum": [ + "24h", + "7d", + "30d" + ] } } ], @@ -1948,7 +14227,87 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseMonitorDto" + "$ref": "#/components/schemas/SingleValueResponseServicePollSummaryDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -1956,121 +14315,292 @@ } } }, - "/api/v1/monitors/{id}/results": { + "/api/v1/services/{slugOrId}/uptime": { "get": { "tags": [ - "Check Results" + "Status Data" ], - "summary": "List raw check results", - "description": "Returns check results for the given monitor with optional time-range, region, and pass/fail filtering. Uses cursor-based pagination — pass the returned `cursor` value on subsequent requests to retrieve the next page. The cursor encodes the original time bounds, so `from`/`to` are ignored when a cursor is present.", - "operationId": "getResults", + "summary": "Get uptime statistics for a service", + "description": "Uptime data aggregated across active non-group components.", + "operationId": "getServiceUptime", "parameters": [ { - "name": "id", + "name": "slugOrId", "in": "path", "required": true, "schema": { - "type": "string", - "format": "uuid" + "type": "string" } }, { - "name": "from", + "name": "period", "in": "query", - "description": "Start of time range (ISO 8601, inclusive); defaults to 24 hours ago", + "description": "Time window", "required": false, "schema": { "type": "string", - "format": "date-time" + "enum": [ + "24h", + "7d", + "30d", + "90d", + "1y", + "2y", + "all" + ] } }, { - "name": "to", + "name": "granularity", "in": "query", - "description": "End of time range (ISO 8601, inclusive); defaults to now", + "description": "Bucket granularity", "required": false, "schema": { "type": "string", - "format": "date-time" + "enum": [ + "hourly", + "daily", + "monthly" + ] + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseServiceUptimeResponse" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "BearerAuth": [] + } + ] + } + }, + "/api/v1/services/incidents": { + "get": { + "tags": [ + "Status Data" + ], + "summary": "List vendor incidents across all services (paginated)", + "description": "Cross-service vendor incident feed ordered by start date descending.", + "operationId": "listCrossServiceIncidents", + "parameters": [ { - "name": "cursor", + "name": "from", "in": "query", - "description": "Opaque cursor from a previous response for pagination", + "description": "Earliest start date (ISO 8601 date)", "required": false, "schema": { - "type": "string" + "type": "string", + "format": "date" } }, { - "name": "limit", + "name": "status", "in": "query", - "description": "Maximum results per page (1–200)", + "description": "Filter: active (unresolved), resolved, or omit for all", "required": false, "schema": { - "type": "integer", - "format": "int32", - "default": 50 - }, - "example": 50 + "type": "string", + "enum": [ + "active", + "resolved" + ] + } }, { - "name": "region", + "name": "category", "in": "query", - "description": "Filter by region (e.g. us-east)", + "description": "Filter by service category", "required": false, "schema": { "type": "string" } }, { - "name": "passed", + "name": "pageable", "in": "query", - "description": "Filter by pass/fail status", - "required": false, + "required": true, "schema": { - "type": "boolean" + "$ref": "#/components/schemas/Pageable" } } ], "responses": { "200": { - "description": "Paginated check results", + "description": "OK", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/CursorPage" + "$ref": "#/components/schemas/TableValueResultServiceIncidentDto" } } } }, "400": { - "description": "Invalid query parameters", + "description": "Bad request — the payload failed validation", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/CursorPageCheckResultDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { - "description": "Monitor does not belong to the caller's org", + "description": "Forbidden — the actor lacks permission for this resource", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/CursorPageCheckResultDto" + "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { - "description": "Monitor not found", + "description": "Not found — the requested resource does not exist", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/CursorPageCheckResultDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -2078,109 +14608,101 @@ } } }, - "/api/v1/monitors/{id}/results/summary": { + "/api/v1/services/summary": { "get": { "tags": [ - "Check Results" - ], - "summary": "Get results summary", - "description": "Returns a dashboard summary for the monitor: current status derived from the latest result per region, time-bucketed chart data, the 24-hour uptime percentage, and the selected window's uptime percentage.", - "operationId": "getSummary", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "chartWindow", - "in": "query", - "description": "Chart window: 24h returns hourly buckets, 7d/30d/90d return daily buckets", - "required": false, - "schema": { - "type": "string", - "enum": [ - "24h", - "7d", - "30d", - "90d" - ] - } - } + "Status Data" ], + "summary": "Global status summary across all services", + "description": "Returns aggregate counts of services by status and a list of services currently experiencing issues.", + "operationId": "getGlobalStatusSummary", "responses": { "200": { - "description": "Results summary", + "description": "OK", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/ResultSummaryDto" + "$ref": "#/components/schemas/SingleValueResponseGlobalStatusSummaryDto" } } } }, "400": { - "description": "Invalid chartWindow parameter", + "description": "Bad request — the payload failed validation", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseResultSummaryDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { - "description": "Monitor does not belong to the caller's org", + "description": "Forbidden — the actor lacks permission for this resource", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseResultSummaryDto" + "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { - "description": "Monitor not found", + "description": "Not found — the requested resource does not exist", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseResultSummaryDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/monitors/{id}/resume": { - "post": { - "tags": [ - "Monitors" - ], - "summary": "Resume a monitor (set enabled=true)", - "operationId": "resume", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "500": { + "description": "Internal server error — see the message field for details", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseMonitorDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -2188,64 +14710,100 @@ } } }, - "/api/v1/monitors/{id}/rotate-token": { - "post": { + "/api/v1/status-pages": { + "get": { "tags": [ - "Monitors" - ], - "summary": "Rotate the ping token for a heartbeat monitor", - "description": "Generates a new ping token. The old token remains valid for 24 hours to allow cron jobs to be updated without downtime. Only supported for HEARTBEAT monitors.", - "operationId": "rotateToken", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } + "Status Pages" ], + "summary": "List status pages for the workspace", + "operationId": "list_4", "responses": { "200": { "description": "OK", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseMonitorDto" + "$ref": "#/components/schemas/TableValueResultStatusPageDto" } } } - } - } - } - }, - "/api/v1/monitors/{id}/tags": { - "get": { - "tags": [ - "Monitors" - ], - "summary": "Get all tags applied to a monitor", - "operationId": "getMonitorTags", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultTagDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -2254,104 +14812,107 @@ }, "post": { "tags": [ - "Monitors" - ], - "summary": "Add tags to a monitor; supports existing tag IDs and inline creation of new tags", - "operationId": "addMonitorTags", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } + "Status Pages" ], + "summary": "Create a status page", + "operationId": "create_5", "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AddMonitorTagsRequest" + "$ref": "#/components/schemas/CreateStatusPageRequest" } } }, "required": true }, "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/TableValueResultTagDto" + "$ref": "#/components/schemas/SingleValueResponseStatusPageDto" } } } - } - } - }, - "delete": { - "tags": [ - "Monitors" - ], - "summary": "Remove tags from a monitor by their IDs", - "operationId": "removeMonitorTags", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RemoveMonitorTagsRequest" + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/api/v1/monitors/{id}/test": { - "post": { - "tags": [ - "Monitors" - ], - "summary": "Test an existing monitor", - "description": "Runs the saved config and assertions of an existing monitor once, without persisting any result. Runs synchronously and returns the same shape as the ad-hoc test.", - "operationId": "testExisting", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "404": { + "description": "Not found — the requested resource does not exist", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseMonitorTestResultDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -2359,14 +14920,13 @@ } } }, - "/api/v1/monitors/{id}/uptime": { + "/api/v1/status-pages/{id}": { "get": { "tags": [ - "Check Results" + "Status Pages" ], - "summary": "Get uptime statistics", - "description": "Returns uptime percentage and latency statistics for the requested time window, computed from continuous aggregates. Uses hourly aggregates for 24h/7d windows and daily aggregates for 30d/90d windows.", - "operationId": "getUptime", + "summary": "Get a status page", + "operationId": "get_2", "parameters": [ { "name": "id", @@ -2376,160 +14936,110 @@ "type": "string", "format": "uuid" } - }, - { - "name": "window", - "in": "query", - "description": "Time window for uptime calculation", - "required": false, - "schema": { - "type": "string", - "enum": [ - "24h", - "7d", - "30d", - "90d" - ] - } } ], "responses": { "200": { - "description": "Uptime statistics", + "description": "OK", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/UptimeDto" + "$ref": "#/components/schemas/SingleValueResponseStatusPageDto" } } } }, "400": { - "description": "Invalid window parameter", + "description": "Bad request — the payload failed validation", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseUptimeDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { - "description": "Monitor does not belong to the caller's org", + "description": "Forbidden — the actor lacks permission for this resource", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseUptimeDto" + "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { - "description": "Monitor not found", + "description": "Not found — the requested resource does not exist", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseUptimeDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/monitors/{id}/versions": { - "get": { - "tags": [ - "Monitors" - ], - "summary": "List version history for a monitor", - "description": "Returns a paginated list of mutation snapshots for the monitor, newest first. Each version captures the full monitor config at the time of a PUT /monitors/{id} call.", - "operationId": "listVersions", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } }, - { - "name": "pageable", - "in": "query", - "required": true, - "schema": { - "$ref": "#/components/schemas/Pageable" - } - } - ], - "responses": { - "200": { - "description": "OK", + "409": { + "description": "Conflict — the request collides with current resource state", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultMonitorVersionDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/monitors/{id}/versions/{version}": { - "get": { - "tags": [ - "Monitors" - ], - "summary": "Get a specific version snapshot for a monitor", - "description": "Returns the full monitor config snapshot captured at the given version number.", - "operationId": "getVersion", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "version", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "503": { + "description": "Service unavailable — try again shortly", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseMonitorVersionDto" + "$ref": "#/components/schemas/ErrorResponse" } } } } } - } - }, - "/api/v1/monitors/{monitorId}/alert-channels": { + }, "put": { "tags": [ - "Monitor Alert Channels" + "Status Pages" ], - "summary": "Replace the linked alert channel set for a monitor", - "operationId": "setChannels", + "summary": "Update a status page", + "operationId": "update_3", "parameters": [ { - "name": "monitorId", + "name": "id", "in": "path", "required": true, "schema": { @@ -2542,7 +15052,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SetAlertChannelsRequest" + "$ref": "#/components/schemas/UpdateStatusPageRequest" } } }, @@ -2554,100 +15064,87 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseListUUID" + "$ref": "#/components/schemas/SingleValueResponseStatusPageDto" } } } - } - } - } - }, - "/api/v1/monitors/{monitorId}/assertions": { - "post": { - "tags": [ - "Monitor Assertions" - ], - "summary": "Add an assertion to a monitor", - "operationId": "add", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateAssertionRequest" + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "201": { - "description": "Created", + "403": { + "description": "Forbidden — the actor lacks permission for this resource", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseMonitorAssertionDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/monitors/{monitorId}/assertions/{assertionId}": { - "put": { - "tags": [ - "Monitor Assertions" - ], - "summary": "Update an assertion on a monitor", - "operationId": "update_10", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "assertionId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateAssertionRequest" + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "200": { - "description": "OK", + "502": { + "description": "Bad gateway — an upstream provider returned an error", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseMonitorAssertionDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -2656,22 +15153,13 @@ }, "delete": { "tags": [ - "Monitor Assertions" + "Status Pages" ], - "summary": "Remove an assertion from a monitor", - "operationId": "remove_1", + "summary": "Delete a status page", + "operationId": "delete_3", "parameters": [ { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "assertionId", + "name": "id", "in": "path", "required": true, "schema": { @@ -2683,20 +15171,100 @@ "responses": { "204": { "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } } } } }, - "/api/v1/monitors/{monitorId}/auth": { - "put": { + "/api/v1/status-pages/{id}/components": { + "get": { "tags": [ - "Monitor Auth" + "Status Pages" ], - "summary": "Update authentication config for a monitor", - "operationId": "update_9", + "summary": "List all components", + "operationId": "listComponents", "parameters": [ { - "name": "monitorId", + "name": "id", "in": "path", "required": true, "schema": { @@ -2705,23 +15273,93 @@ } } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateMonitorAuthRequest" + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TableValueResultStatusPageComponentDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "200": { - "description": "OK", + "503": { + "description": "Service unavailable — try again shortly", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseMonitorAuthDto" + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -2730,13 +15368,13 @@ }, "post": { "tags": [ - "Monitor Auth" + "Status Pages" ], - "summary": "Set authentication config for a monitor", - "operationId": "set", + "summary": "Add a component to the status page", + "operationId": "createComponent", "parameters": [ { - "name": "monitorId", + "name": "id", "in": "path", "required": true, "schema": { @@ -2749,7 +15387,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SetMonitorAuthRequest" + "$ref": "#/components/schemas/CreateStatusPageComponentRequest" } } }, @@ -2761,92 +15399,114 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseMonitorAuthDto" + "$ref": "#/components/schemas/SingleValueResponseStatusPageComponentDto" } } } - } - } - }, - "delete": { - "tags": [ - "Monitor Auth" - ], - "summary": "Remove authentication config from a monitor", - "operationId": "remove", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/api/v1/monitors/{monitorId}/policy": { - "get": { - "tags": [ - "Incident Policies" - ], - "summary": "Get incident policy for a monitor", - "description": "Returns the trigger rules, confirmation settings, and recovery settings for the given monitor.", - "operationId": "get_5", - "parameters": [ - { - "name": "monitorId", - "in": "path", - "description": "Monitor UUID", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "Policy found", + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/IncidentPolicyDto" + "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { - "description": "Monitor or policy not found", + "description": "Not found — the requested resource does not exist", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseIncidentPolicyDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } } } - }, + } + }, + "/api/v1/status-pages/{id}/components/{componentId}": { "put": { "tags": [ - "Incident Policies" + "Status Pages" ], - "summary": "Update incident policy for a monitor", - "description": "Replaces the trigger rules, confirmation settings, and recovery settings. All fields are validated before saving.", - "operationId": "update_8", + "summary": "Update a component", + "operationId": "updateComponent", "parameters": [ { - "name": "monitorId", + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "componentId", "in": "path", - "description": "Monitor UUID", "required": true, "schema": { "type": "string", @@ -2858,7 +15518,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateIncidentPolicyRequest" + "$ref": "#/components/schemas/UpdateStatusPageComponentRequest" } } }, @@ -2866,177 +15526,103 @@ }, "responses": { "200": { - "description": "Policy updated", + "description": "OK", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/IncidentPolicyDto" + "$ref": "#/components/schemas/SingleValueResponseStatusPageComponentDto" } } } }, "400": { - "description": "Validation error in JSONB shape", + "description": "Bad request — the payload failed validation", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseIncidentPolicyDto" + "$ref": "#/components/schemas/ErrorResponse" } } } }, - "404": { - "description": "Monitor or policy not found", + "401": { + "description": "Unauthorized — missing or invalid credentials", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseIncidentPolicyDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/monitors/bulk": { - "post": { - "tags": [ - "Monitors" - ], - "summary": "Bulk action on monitors", - "description": "Applies PAUSE, RESUME, DELETE, ADD_TAG, or REMOVE_TAG to a list of monitors. Returns a partial-success response indicating which monitors succeeded and which failed.", - "operationId": "bulkAction", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BulkMonitorActionRequest" - } - } }, - "required": true - }, - "responses": { - "200": { - "description": "OK", + "403": { + "description": "Forbidden — the actor lacks permission for this resource", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseBulkMonitorActionResult" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/monitors/test": { - "post": { - "tags": [ - "Monitors" - ], - "summary": "Ad-hoc monitor test", - "description": "Executes a one-off check from an inline config without saving the monitor. Runs synchronously and returns status code, response time, assertion results, body preview, and headers.", - "operationId": "testAdHoc", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MonitorTestRequest" + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "200": { - "description": "OK", + "409": { + "description": "Conflict — the request collides with current resource state", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseMonitorTestResultDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/notification-dispatches": { - "get": { - "tags": [ - "Notification Dispatches" - ], - "summary": "List all dispatches for an incident", - "description": "Returns all notification dispatches for the given incident that belong to the authenticated org's policies. Each dispatch includes delivery records for all associated channels.", - "operationId": "listByIncident", - "parameters": [ - { - "name": "incident_id", - "in": "query", - "description": "UUID of the incident to inspect", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "500": { + "description": "Internal server error — see the message field for details", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultNotificationDispatchDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/notification-dispatches/{id}": { - "get": { - "tags": [ - "Notification Dispatches" - ], - "summary": "Get a single dispatch with full escalation and delivery history", - "description": "Returns the dispatch state including current escalation step, acknowledgment info, and all delivery attempts made across every step.", - "operationId": "getById_3", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "503": { + "description": "Service unavailable — try again shortly", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseNotificationDispatchDto" + "$ref": "#/components/schemas/ErrorResponse" } } } } } - } - }, - "/api/v1/notification-dispatches/{id}/acknowledge": { - "post": { + }, + "delete": { "tags": [ - "Notification Dispatches" + "Status Pages" ], - "summary": "Acknowledge a notification dispatch", - "description": "Marks the dispatch as acknowledged. The dispatch must be in DELIVERED or ESCALATING state. Sets acknowledgedAt, acknowledgedBy (actor email), and acknowledgedVia (DASHBOARD).", - "operationId": "acknowledge", + "summary": "Remove a component from the status page", + "operationId": "deleteComponent", "parameters": [ { "name": "id", @@ -3046,65 +15632,97 @@ "type": "string", "format": "uuid" } + }, + { + "name": "componentId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } } ], "responses": { - "200": { - "description": "OK", + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseNotificationDispatchDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/notification-policies": { - "get": { - "tags": [ - "Notification Policies" - ], - "summary": "List all notification policies for the authenticated org", - "operationId": "list_7", - "responses": { - "200": { - "description": "OK", + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultNotificationPolicyDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - }, - "post": { - "tags": [ - "Notification Policies" - ], - "summary": "Create a notification policy with match rules and escalation chain", - "operationId": "create_8", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateNotificationPolicyRequest" + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "201": { - "description": "Created", + "404": { + "description": "Not found — the requested resource does not exist", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseNotificationPolicyDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -3112,13 +15730,13 @@ } } }, - "/api/v1/notification-policies/{id}": { + "/api/v1/status-pages/{id}/components/{componentId}/uptime": { "get": { "tags": [ - "Notification Policies" + "Status Pages" ], - "summary": "Get a notification policy by ID", - "operationId": "getById_1", + "summary": "Get component uptime history (daily rollups)", + "operationId": "componentUptime_1", "parameters": [ { "name": "id", @@ -3128,6 +15746,25 @@ "type": "string", "format": "uuid" } + }, + { + "name": "componentId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "days", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 90 + } } ], "responses": { @@ -3136,19 +15773,101 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseNotificationPolicyDto" + "$ref": "#/components/schemas/TableValueResultStatusPageComponentUptimeDayDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } } } - }, + } + }, + "/api/v1/status-pages/{id}/components/reorder": { "put": { "tags": [ - "Notification Policies" + "Status Pages" ], - "summary": "Update a notification policy", - "operationId": "update_7", + "summary": "Batch reorder components (and optionally move between groups)", + "operationId": "reorderComponents", "parameters": [ { "name": "id", @@ -3164,56 +15883,106 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateNotificationPolicyRequest" + "$ref": "#/components/schemas/ReorderComponentsRequest" } } }, "required": true }, "responses": { - "200": { - "description": "OK", + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseNotificationPolicyDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - }, - "delete": { - "tags": [ - "Notification Policies" - ], - "summary": "Delete a notification policy", - "operationId": "delete_6", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "204": { - "description": "No Content" } } } }, - "/api/v1/notification-policies/{id}/dispatches": { + "/api/v1/status-pages/{id}/domains": { "get": { "tags": [ - "Notification Policies" + "Status Pages" ], - "summary": "List all dispatches (firing history) for a notification policy", - "operationId": "listDispatches", + "summary": "List custom domains", + "operationId": "listDomains", "parameters": [ { "name": "id", @@ -3231,21 +16000,99 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/TableValueResultNotificationDispatchDto" + "$ref": "#/components/schemas/TableValueResultStatusPageCustomDomainDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } } } - } - }, - "/api/v1/notification-policies/{id}/test": { + }, "post": { "tags": [ - "Notification Policies" + "Status Pages" ], - "summary": "Dry-run: evaluate a policy's match rules against a supplied incident context", - "operationId": "test_1", + "summary": "Add a custom domain", + "operationId": "addDomain", "parameters": [ { "name": "id", @@ -3261,71 +16108,99 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TestNotificationPolicyRequest" + "$ref": "#/components/schemas/AddCustomDomainRequest" } } }, "required": true }, "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseTestMatchResult" + "$ref": "#/components/schemas/SingleValueResponseStatusPageCustomDomainDto" } } } - } - } - } - }, - "/api/v1/notifications": { - "get": { - "tags": [ - "Notifications" - ], - "summary": "List notifications for the current user", - "operationId": "list_16", - "parameters": [ - { - "name": "unreadOnly", - "in": "query", - "required": false, - "schema": { - "type": "boolean", - "default": false + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "page", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "format": "int32", - "default": 0 + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "size", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "format": "int32", - "default": 20 + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "404": { + "description": "Not found — the requested resource does not exist", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultNotificationDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -3333,159 +16208,113 @@ } } }, - "/api/v1/notifications/{id}/read": { - "put": { + "/api/v1/status-pages/{id}/domains/{domainId}": { + "delete": { "tags": [ - "Notifications" + "Status Pages" ], - "summary": "Mark a notification as read", - "operationId": "markRead", + "summary": "Remove a custom domain", + "operationId": "removeDomain", "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { - "type": "integer", - "format": "int64" + "type": "string", + "format": "uuid" + } + }, + { + "name": "domainId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" } } ], "responses": { "204": { "description": "No Content" - } - } - } - }, - "/api/v1/notifications/read-all": { - "put": { - "tags": [ - "Notifications" - ], - "summary": "Mark all notifications as read", - "operationId": "markAllRead", - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/api/v1/notifications/unread-count": { - "get": { - "tags": [ - "Notifications" - ], - "summary": "Get unread notification count", - "operationId": "unreadCount", - "responses": { - "200": { - "description": "OK", + }, + "400": { + "description": "Bad request — the payload failed validation", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseLong" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/org": { - "get": { - "tags": [ - "Organizations" - ], - "summary": "Get the current organization", - "operationId": "get_4", - "responses": { - "200": { - "description": "OK", + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseOrganizationDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - }, - "put": { - "tags": [ - "Organizations" - ], - "summary": "Update the current organization", - "operationId": "update_6", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateOrgDetailsRequest" + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "200": { - "description": "OK", + "409": { + "description": "Conflict — the request collides with current resource state", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseOrganizationDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/resource-groups": { - "get": { - "tags": [ - "Resource Groups" - ], - "summary": "List all resource groups for the authenticated org with health summaries", - "operationId": "list_6", - "responses": { - "200": { - "description": "OK", + }, + "500": { + "description": "Internal server error — see the message field for details", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultResourceGroupDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - }, - "post": { - "tags": [ - "Resource Groups" - ], - "summary": "Create a new resource group", - "operationId": "create_7", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateResourceGroupRequest" + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "201": { - "description": "Created", + "503": { + "description": "Service unavailable — try again shortly", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseResourceGroupDto" + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -3493,14 +16322,13 @@ } } }, - "/api/v1/resource-groups/{id}": { - "get": { + "/api/v1/status-pages/{id}/domains/{domainId}/verify": { + "post": { "tags": [ - "Resource Groups" + "Status Pages" ], - "summary": "Get a resource group by id with member statuses and inherited settings", - "description": "Pass includeMetrics=true to enrich each member with 24h uptime, chart data, and latency metrics.", - "operationId": "get_3", + "summary": "Trigger domain verification check", + "operationId": "verifyDomain", "parameters": [ { "name": "id", @@ -3512,12 +16340,12 @@ } }, { - "name": "includeMetrics", - "in": "query", - "required": false, + "name": "domainId", + "in": "path", + "required": true, "schema": { - "type": "boolean", - "default": false + "type": "string", + "format": "uuid" } } ], @@ -3527,85 +16355,101 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseResourceGroupDto" + "$ref": "#/components/schemas/SingleValueResponseStatusPageCustomDomainDto" } } } - } - } - }, - "put": { - "tags": [ - "Resource Groups" - ], - "summary": "Update a resource group's name, description, alert policy, inherited settings, and health threshold", - "operationId": "update_5", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateResourceGroupRequest" + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "200": { - "description": "OK", + "403": { + "description": "Forbidden — the actor lacks permission for this resource", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseResourceGroupDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - }, - "delete": { - "tags": [ - "Resource Groups" - ], - "summary": "Delete a resource group (cascades to member rows)", - "operationId": "delete_5", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "204": { - "description": "No Content" } } } }, - "/api/v1/resource-groups/{id}/health": { + "/api/v1/status-pages/{id}/groups": { "get": { "tags": [ - "Resource Groups" + "Status Pages" ], - "summary": "Get the detailed health breakdown for a resource group", - "description": "Returns member counts, worst-of status, and threshold-based health evaluation. The thresholdStatus field is populated only when a health threshold is configured.", - "operationId": "getHealth", + "summary": "List component groups with nested components", + "operationId": "listGroups", "parameters": [ { "name": "id", @@ -3623,21 +16467,99 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseResourceGroupHealthDto" + "$ref": "#/components/schemas/TableValueResultStatusPageComponentGroupDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } } } - } - }, - "/api/v1/resource-groups/{id}/members": { + }, "post": { "tags": [ - "Resource Groups" + "Status Pages" ], - "summary": "Add a monitor or service member to a resource group", - "operationId": "addMember_1", + "summary": "Create a component group", + "operationId": "createGroup", "parameters": [ { "name": "id", @@ -3653,7 +16575,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AddResourceGroupMemberRequest" + "$ref": "#/components/schemas/CreateStatusPageComponentGroupRequest" } } }, @@ -3665,7 +16587,87 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseResourceGroupMemberDto" + "$ref": "#/components/schemas/SingleValueResponseStatusPageComponentGroupDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -3673,13 +16675,13 @@ } } }, - "/api/v1/resource-groups/{id}/members/{memberId}": { - "delete": { + "/api/v1/status-pages/{id}/groups/{groupId}": { + "put": { "tags": [ - "Resource Groups" + "Status Pages" ], - "summary": "Remove a member from a resource group", - "operationId": "removeMember_1", + "summary": "Update a component group", + "operationId": "updateGroup", "parameters": [ { "name": "id", @@ -3691,7 +16693,7 @@ } }, { - "name": "memberId", + "name": "groupId", "in": "path", "required": true, "schema": { @@ -3700,97 +16702,103 @@ } } ], - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/api/v1/secrets": { - "get": { - "tags": [ - "Secrets" - ], - "summary": "List secrets", - "operationId": "list_5", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStatusPageComponentGroupRequest" + } + } + }, + "required": true + }, "responses": { "200": { "description": "OK", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/TableValueResultSecretDto" + "$ref": "#/components/schemas/SingleValueResponseStatusPageComponentGroupDto" } } } - } - } - }, - "post": { - "tags": [ - "Secrets" - ], - "summary": "Create secret", - "operationId": "create_6", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateSecretRequest" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "201": { - "description": "Created", + "401": { + "description": "Unauthorized — missing or invalid credentials", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseSecretDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/secrets/{key}": { - "put": { - "tags": [ - "Secrets" - ], - "summary": "Update secret", - "operationId": "update_4", - "parameters": [ - { - "name": "key", - "in": "path", - "required": true, - "schema": { - "type": "string" + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateSecretRequest" + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "200": { - "description": "OK", + "409": { + "description": "Conflict — the request collides with current resource state", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseSecretDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -3799,41 +16807,110 @@ }, "delete": { "tags": [ - "Secrets" + "Status Pages" ], - "summary": "Delete secret", - "operationId": "delete_4", + "summary": "Delete a component group (components become ungrouped)", + "operationId": "deleteGroup", "parameters": [ { - "name": "key", + "name": "id", "in": "path", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" + } + }, + { + "name": "groupId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" } } ], "responses": { "204": { "description": "No Content" - } - } - } - }, - "/api/v1/service-subscriptions": { - "get": { - "tags": [ - "Service Subscriptions" - ], - "summary": "List all service subscriptions for the organization", - "operationId": "list_15", - "responses": { - "200": { - "description": "OK", + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultServiceSubscriptionDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -3841,13 +16918,13 @@ } } }, - "/api/v1/service-subscriptions/{id}": { + "/api/v1/status-pages/{id}/incidents": { "get": { "tags": [ - "Service Subscriptions" + "Status Pages" ], - "summary": "Get a subscription by its ID", - "operationId": "get_9", + "summary": "List incidents for this status page (filterable by status, paginated)", + "operationId": "listIncidents", "parameters": [ { "name": "id", @@ -3857,6 +16934,31 @@ "type": "string", "format": "uuid" } + }, + { + "name": "status", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "INVESTIGATING", + "IDENTIFIED", + "MONITORING", + "RESOLVED" + ] + } + } + }, + { + "name": "pageable", + "in": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/Pageable" + } } ], "responses": { @@ -3865,96 +16967,107 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseServiceSubscriptionDto" + "$ref": "#/components/schemas/TableValueResultStatusPageIncidentDto" } } } - } - } - }, - "delete": { - "tags": [ - "Service Subscriptions" - ], - "summary": "Remove a subscription by its ID", - "description": "Removes a specific subscription (whole-service or component-level). No-op if not found.", - "operationId": "unsubscribe_1", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/api/v1/service-subscriptions/{id}/alert-sensitivity": { - "patch": { - "tags": [ - "Service Subscriptions" - ], - "summary": "Update alert sensitivity for a subscription", - "description": "Controls which external incidents trigger alerts: ALL (any status change), INCIDENTS_ONLY (real vendor incidents, default), MAJOR_ONLY (only DOWN-level incidents).", - "operationId": "updateAlertSensitivity", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateAlertSensitivityRequest" + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "200": { - "description": "OK", + "404": { + "description": "Not found — the requested resource does not exist", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseServiceSubscriptionDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } } } - } - }, - "/api/v1/service-subscriptions/{slug}": { + }, "post": { "tags": [ - "Service Subscriptions" + "Status Pages" ], - "summary": "Subscribe to a service or a component of a service", - "description": "Idempotent — returns the existing subscription if an identical one exists. Omit the request body or set componentId to null for a whole-service subscription. Free tier: max 10 subscriptions. Paid tier: unlimited.", - "operationId": "subscribe_1", + "summary": "Create a status page incident (manual)", + "operationId": "createIncident", "parameters": [ { - "name": "slug", + "name": "id", "in": "path", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -3962,10 +17075,11 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ServiceSubscribeRequest" + "$ref": "#/components/schemas/CreateStatusPageIncidentRequest" } } - } + }, + "required": true }, "responses": { "201": { @@ -3973,139 +17087,87 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseServiceSubscriptionDto" + "$ref": "#/components/schemas/SingleValueResponseStatusPageIncidentDto" } } } - } - } - } - }, - "/api/v1/services": { - "get": { - "tags": [ - "Status Data" - ], - "summary": "List all enabled services (cursor-paginated)", - "operationId": "listServices", - "parameters": [ - { - "name": "category", - "in": "query", - "description": "Filter by category (exact match)", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "status", - "in": "query", - "description": "Filter by current overall_status (exact match)", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "published", - "in": "query", - "description": "Filter by published status for pSEO pages", - "required": false, - "schema": { - "type": "boolean" - } }, - { - "name": "cursor", - "in": "query", - "description": "Opaque cursor from a previous response", - "required": false, - "schema": { - "type": "string" + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "limit", - "in": "query", - "description": "Page size (1–100, default 20)", - "required": false, - "schema": { - "type": "integer", - "format": "int32", - "default": 20 - } - } - ], - "responses": { - "200": { - "description": "OK", + "401": { + "description": "Unauthorized — missing or invalid credentials", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/CursorPageServiceCatalogDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/services/{slugOrId}": { - "get": { - "tags": [ - "Status Data" - ], - "summary": "Get a single service by slug or UUID with current status, components, and recent incidents", - "operationId": "getService", - "parameters": [ - { - "name": "slugOrId", - "in": "path", - "required": true, - "schema": { - "type": "string" + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "404": { + "description": "Not found — the requested resource does not exist", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseServiceDetailDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/services/{slugOrId}/components": { - "get": { - "tags": [ - "Status Data" - ], - "summary": "List active components for a service with current status and inline uptime", - "operationId": "getComponents", - "parameters": [ - { - "name": "slugOrId", - "in": "path", - "required": true, - "schema": { - "type": "string" + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "500": { + "description": "Internal server error — see the message field for details", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultServiceComponentDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -4113,45 +17175,31 @@ } } }, - "/api/v1/services/{slugOrId}/components/{componentId}/uptime": { + "/api/v1/status-pages/{id}/incidents/{incidentId}": { "get": { "tags": [ - "Status Data" + "Status Pages" ], - "summary": "Get daily uptime data for a component", - "operationId": "getComponentUptime", + "summary": "Get incident details with timeline", + "operationId": "getIncident", "parameters": [ { - "name": "slugOrId", + "name": "id", "in": "path", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { - "name": "componentId", + "name": "incidentId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } - }, - { - "name": "period", - "in": "query", - "description": "Time window", - "required": false, - "schema": { - "type": "string", - "enum": [ - "7d", - "30d", - "90d", - "1y" - ] - } } ], "responses": { @@ -4160,179 +17208,236 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/TableValueResultComponentUptimeDayDto" + "$ref": "#/components/schemas/SingleValueResponseStatusPageIncidentDto" } } } - } - } - } - }, - "/api/v1/services/{slugOrId}/components/uptime": { - "get": { - "tags": [ - "Status Data" - ], - "summary": "Batch daily uptime data for all leaf components", - "description": "Returns daily uptime for every active non-group component with uptime data, keyed by component ID. Replaces N individual per-component uptime calls with a single request.", - "operationId": "getBatchComponentUptime", - "parameters": [ - { - "name": "slugOrId", - "in": "path", - "required": true, - "schema": { - "type": "string" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "period", - "in": "query", - "description": "Time window", - "required": false, - "schema": { - "type": "string", - "enum": [ - "7d", - "30d", - "90d", - "1y" - ] + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", "content": { - "*/*": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseMapStringListComponentUptimeDayDto" + "$ref": "#/components/schemas/ErrorResponse" } } } } } - } - }, - "/api/v1/services/{slugOrId}/days/{date}": { - "get": { + }, + "put": { "tags": [ - "Status Data" + "Status Pages" ], - "summary": "One-day rollup for a service: aggregated uptime, per-component impacts, and overlapping incidents", - "description": "Powers the click/hover-to-expand panel under each uptime bar on the public status page. Single round-trip — components, sums, and overlapping incidents (with affected component names) are returned in one response.", - "operationId": "getServiceDayDetail", + "summary": "Update an incident", + "operationId": "updateIncident", "parameters": [ { - "name": "slugOrId", + "name": "id", "in": "path", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { - "name": "date", + "name": "incidentId", "in": "path", - "description": "UTC calendar day in ISO format (YYYY-MM-DD)", "required": true, "schema": { "type": "string", - "format": "date" + "format": "uuid" } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStatusPageIncidentRequest" + } + } + }, + "required": true + }, "responses": { "200": { "description": "OK", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseServiceDayDetailDto" + "$ref": "#/components/schemas/SingleValueResponseStatusPageIncidentDto" } } } - } - } - } - }, - "/api/v1/services/{slugOrId}/incidents": { - "get": { - "tags": [ - "Status Data" - ], - "summary": "List incident history for a service (paginated)", - "operationId": "listIncidents_1", - "parameters": [ - { - "name": "slugOrId", - "in": "path", - "required": true, - "schema": { - "type": "string" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "from", - "in": "query", - "description": "Earliest start date (ISO 8601 date)", - "required": false, - "schema": { - "type": "string", - "format": "date" + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "status", - "in": "query", - "description": "Filter: active (unresolved), resolved, or omit for all", - "required": false, - "schema": { - "type": "string", - "enum": [ - "active", - "resolved" - ] + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "pageable", - "in": "query", - "required": true, - "schema": { - "$ref": "#/components/schemas/Pageable" + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "503": { + "description": "Service unavailable — try again shortly", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultServiceIncidentDto" + "$ref": "#/components/schemas/ErrorResponse" } } } } } - } - }, - "/api/v1/services/{slugOrId}/incidents/{incidentId}": { - "get": { + }, + "delete": { "tags": [ - "Status Data" + "Status Pages" ], - "summary": "Get incident detail with full update timeline", - "operationId": "getIncident_1", + "summary": "Delete an incident", + "operationId": "deleteIncident", "parameters": [ { - "name": "slugOrId", + "name": "id", "in": "path", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -4346,138 +17451,85 @@ } ], "responses": { - "200": { - "description": "OK", + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseServiceIncidentDetailDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/services/{slugOrId}/live-status": { - "get": { - "tags": [ - "Status Data" - ], - "summary": "Lightweight live-status snapshot for polling", - "description": "Returns only the current overall status, component statuses, and active incident count. Designed for frequent polling with minimal payload.", - "operationId": "getServiceLiveStatus", - "parameters": [ - { - "name": "slugOrId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseServiceLiveStatusDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/services/{slugOrId}/maintenances": { - "get": { - "tags": [ - "Status Data" - ], - "summary": "List scheduled maintenances for a service", - "operationId": "getScheduledMaintenances", - "parameters": [ - { - "name": "slugOrId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } }, - { - "name": "status", - "in": "query", - "description": "Filter by status (e.g. scheduled, in_progress, verifying, completed)", - "required": false, - "schema": { - "type": "array", - "items": { - "type": "string" + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "404": { + "description": "Not found — the requested resource does not exist", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultScheduledMaintenanceDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/services/{slugOrId}/poll-results": { - "get": { - "tags": [ - "Status Data" - ], - "summary": "List poll results for a service (cursor-paginated)", - "operationId": "listPollResults", - "parameters": [ - { - "name": "slugOrId", - "in": "path", - "required": true, - "schema": { - "type": "string" + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "cursor", - "in": "query", - "description": "ISO 8601 timestamp cursor from a previous response", - "required": false, - "schema": { - "type": "string" + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "limit", - "in": "query", - "description": "Page size (1–100, default 50)", - "required": false, - "schema": { - "type": "integer", - "format": "int32", - "default": 50 + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "503": { + "description": "Service unavailable — try again shortly", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/CursorPageServicePollResultDto" + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -4485,44 +17537,113 @@ } } }, - "/api/v1/services/{slugOrId}/poll-summary": { - "get": { + "/api/v1/status-pages/{id}/incidents/{incidentId}/dismiss": { + "post": { "tags": [ - "Status Data" + "Status Pages" ], - "summary": "Get aggregated poll metrics and chart data for a service", - "operationId": "getPollSummary", + "summary": "Dismiss a draft incident (deletes it permanently)", + "operationId": "dismissIncident", "parameters": [ { - "name": "slugOrId", + "name": "id", "in": "path", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { - "name": "window", - "in": "query", - "description": "Time window", - "required": false, + "name": "incidentId", + "in": "path", + "required": true, "schema": { "type": "string", - "enum": [ - "24h", - "7d", - "30d" - ] + "format": "uuid" } } ], "responses": { - "200": { - "description": "OK", + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseServicePollSummaryDto" + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -4530,192 +17651,168 @@ } } }, - "/api/v1/services/{slugOrId}/uptime": { - "get": { + "/api/v1/status-pages/{id}/incidents/{incidentId}/publish": { + "post": { "tags": [ - "Status Data" + "Status Pages" ], - "summary": "Get uptime statistics for a service", - "description": "Uptime data aggregated across active non-group components.", - "operationId": "getServiceUptime", + "summary": "Publish a draft incident (sets publishedAt, applies component statuses, notifies subscribers)", + "operationId": "publishIncident", "parameters": [ { - "name": "slugOrId", + "name": "id", "in": "path", "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "period", - "in": "query", - "description": "Time window", - "required": false, "schema": { "type": "string", - "enum": [ - "24h", - "7d", - "30d", - "90d", - "1y", - "2y", - "all" - ] + "format": "uuid" } }, { - "name": "granularity", - "in": "query", - "description": "Bucket granularity", - "required": false, + "name": "incidentId", + "in": "path", + "required": true, "schema": { "type": "string", - "enum": [ - "hourly", - "daily", - "monthly" - ] + "format": "uuid" } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublishStatusPageIncidentRequest" + } + } + } + }, "responses": { "200": { "description": "OK", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseServiceUptimeResponse" + "$ref": "#/components/schemas/SingleValueResponseStatusPageIncidentDto" } } } - } - }, - "security": [ - { - "BearerAuth": [] - } - ] - } - }, - "/api/v1/services/incidents": { - "get": { - "tags": [ - "Status Data" - ], - "summary": "List vendor incidents across all services (paginated)", - "description": "Cross-service vendor incident feed ordered by start date descending.", - "operationId": "listCrossServiceIncidents", - "parameters": [ - { - "name": "from", - "in": "query", - "description": "Earliest start date (ISO 8601 date)", - "required": false, - "schema": { - "type": "string", - "format": "date" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "status", - "in": "query", - "description": "Filter: active (unresolved), resolved, or omit for all", - "required": false, - "schema": { - "type": "string", - "enum": [ - "active", - "resolved" - ] + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "category", - "in": "query", - "description": "Filter by service category", - "required": false, - "schema": { - "type": "string" + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "pageable", - "in": "query", - "required": true, - "schema": { - "$ref": "#/components/schemas/Pageable" + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "409": { + "description": "Conflict — the request collides with current resource state", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultServiceIncidentDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/services/summary": { - "get": { - "tags": [ - "Status Data" - ], - "summary": "Global status summary across all services", - "description": "Returns aggregate counts of services by status and a list of services currently experiencing issues.", - "operationId": "getGlobalStatusSummary", - "responses": { - "200": { - "description": "OK", + }, + "500": { + "description": "Internal server error — see the message field for details", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseGlobalStatusSummaryDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/status-pages": { - "get": { - "tags": [ - "Status Pages" - ], - "summary": "List status pages for the workspace", - "operationId": "list_4", - "responses": { - "200": { - "description": "OK", + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultStatusPageDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } } } - }, + } + }, + "/api/v1/status-pages/{id}/incidents/{incidentId}/updates": { "post": { "tags": [ "Status Pages" ], - "summary": "Create a status page", - "operationId": "create_5", + "summary": "Post an incident timeline update", + "operationId": "postIncidentUpdate", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "incidentId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateStatusPageRequest" + "$ref": "#/components/schemas/CreateStatusPageIncidentUpdateRequest" } } }, @@ -4725,53 +17822,103 @@ "201": { "description": "Created", "content": { - "*/*": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseStatusPageIncidentDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseStatusPageDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/status-pages/{id}": { - "get": { - "tags": [ - "Status Pages" - ], - "summary": "Get a status page", - "operationId": "get_2", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "503": { + "description": "Service unavailable — try again shortly", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseStatusPageDto" + "$ref": "#/components/schemas/ErrorResponse" } } } } } - }, + } + }, + "/api/v1/status-pages/{id}/layout/reorder": { "put": { "tags": [ "Status Pages" ], - "summary": "Update a status page", - "operationId": "update_3", + "summary": "Reorder page-level layout: groups and ungrouped components share one ordering", + "operationId": "reorderLayout", "parameters": [ { "name": "id", @@ -4787,56 +17934,106 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateStatusPageRequest" + "$ref": "#/components/schemas/ReorderPageLayoutRequest" } } }, "required": true }, "responses": { - "200": { - "description": "OK", + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseStatusPageDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - }, - "delete": { - "tags": [ - "Status Pages" - ], - "summary": "Delete a status page", - "operationId": "delete_3", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "204": { - "description": "No Content" } } } }, - "/api/v1/status-pages/{id}/components": { + "/api/v1/status-pages/{id}/subscribers": { "get": { "tags": [ "Status Pages" ], - "summary": "List all components", - "operationId": "listComponents", + "summary": "List confirmed subscribers (paginated)", + "operationId": "listSubscribers", "parameters": [ { "name": "id", @@ -4846,6 +18043,14 @@ "type": "string", "format": "uuid" } + }, + { + "name": "pageable", + "in": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/Pageable" + } } ], "responses": { @@ -4854,7 +18059,87 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/TableValueResultStatusPageComponentDto" + "$ref": "#/components/schemas/TableValueResultStatusPageSubscriberDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -4865,8 +18150,8 @@ "tags": [ "Status Pages" ], - "summary": "Add a component to the status page", - "operationId": "createComponent", + "summary": "Add a subscriber (immediately confirmed, skips double opt-in)", + "operationId": "addSubscriber", "parameters": [ { "name": "id", @@ -4882,7 +18167,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateStatusPageComponentRequest" + "$ref": "#/components/schemas/AdminAddSubscriberRequest" } } }, @@ -4894,70 +18179,101 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseStatusPageComponentDto" + "$ref": "#/components/schemas/SingleValueResponseStatusPageSubscriberDto" } } } - } - } - } - }, - "/api/v1/status-pages/{id}/components/{componentId}": { - "put": { - "tags": [ - "Status Pages" - ], - "summary": "Update a component", - "operationId": "updateComponent", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "componentId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateStatusPageComponentRequest" + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "200": { - "description": "OK", + "502": { + "description": "Bad gateway — an upstream provider returned an error", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseStatusPageComponentDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } } } - }, + } + }, + "/api/v1/status-pages/{id}/subscribers/{subscriberId}": { "delete": { "tags": [ "Status Pages" ], - "summary": "Remove a component from the status page", - "operationId": "deleteComponent", + "summary": "Remove a subscriber", + "operationId": "removeSubscriber", "parameters": [ { "name": "id", @@ -4969,7 +18285,7 @@ } }, { - "name": "componentId", + "name": "subscriberId", "in": "path", "required": true, "schema": { @@ -4981,44 +18297,104 @@ "responses": { "204": { "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } } } } }, - "/api/v1/status-pages/{id}/components/{componentId}/uptime": { + "/api/v1/tags": { "get": { "tags": [ - "Status Pages" + "Tags" ], - "summary": "Get component uptime history (daily rollups)", - "operationId": "componentUptime_1", + "summary": "List tags for the authenticated organization", + "operationId": "list_3", "parameters": [ { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "componentId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "days", + "name": "pageable", "in": "query", - "required": false, + "required": true, "schema": { - "type": "integer", - "format": "int32", - "default": 90 + "$ref": "#/components/schemas/Pageable" } } ], @@ -5028,56 +18404,210 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/TableValueResultComponentUptimeDayDto" + "$ref": "#/components/schemas/TableValueResultTagDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } } } - } - }, - "/api/v1/status-pages/{id}/components/reorder": { - "put": { + }, + "post": { "tags": [ - "Status Pages" - ], - "summary": "Batch reorder components (and optionally move between groups)", - "operationId": "reorderComponents", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } + "Tags" ], + "summary": "Create a new tag", + "operationId": "create_4", "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReorderComponentsRequest" + "$ref": "#/components/schemas/CreateTagRequest" } } }, "required": true }, "responses": { - "204": { - "description": "No Content" + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseTagDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } } } } }, - "/api/v1/status-pages/{id}/domains": { + "/api/v1/tags/{id}": { "get": { "tags": [ - "Status Pages" - ], - "summary": "List custom domains", - "operationId": "listDomains", + "Tags" + ], + "summary": "Get a tag by ID", + "operationId": "getById", "parameters": [ { "name": "id", @@ -5095,19 +18625,99 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/TableValueResultStatusPageCustomDomainDto" + "$ref": "#/components/schemas/SingleValueResponseTagDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } } } }, - "post": { + "put": { "tags": [ - "Status Pages" + "Tags" ], - "summary": "Add a custom domain", - "operationId": "addDomain", + "summary": "Update a tag's name and/or color", + "operationId": "update_2", "parameters": [ { "name": "id", @@ -5123,138 +18733,111 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AddCustomDomainRequest" + "$ref": "#/components/schemas/UpdateTagRequest" } } }, "required": true }, "responses": { - "201": { - "description": "Created", + "200": { + "description": "OK", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseStatusPageCustomDomainDto" + "$ref": "#/components/schemas/SingleValueResponseTagDto" } } } - } - } - } - }, - "/api/v1/status-pages/{id}/domains/{domainId}": { - "delete": { - "tags": [ - "Status Pages" - ], - "summary": "Remove a custom domain", - "operationId": "removeDomain", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "domainId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/api/v1/status-pages/{id}/domains/{domainId}/verify": { - "post": { - "tags": [ - "Status Pages" - ], - "summary": "Trigger domain verification check", - "operationId": "verifyDomain", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "domainId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "409": { + "description": "Conflict — the request collides with current resource state", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseStatusPageCustomDomainDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/status-pages/{id}/groups": { - "get": { - "tags": [ - "Status Pages" - ], - "summary": "List component groups with nested components", - "operationId": "listGroups", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultStatusPageComponentGroupDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } } } }, - "post": { + "delete": { "tags": [ - "Status Pages" + "Tags" ], - "summary": "Create a component group", - "operationId": "createGroup", + "summary": "Delete a tag (cascades to all monitor associations)", + "operationId": "delete_2", "parameters": [ { "name": "id", @@ -5266,23 +18849,86 @@ } } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateStatusPageComponentGroupRequest" + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "201": { - "description": "Created", + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseStatusPageComponentGroupDto" + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -5290,123 +18936,116 @@ } } }, - "/api/v1/status-pages/{id}/groups/{groupId}": { - "put": { + "/api/v1/vaults/rotate": { + "post": { "tags": [ - "Status Pages" + "Vault" ], - "summary": "Update a component group", - "operationId": "updateGroup", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + "summary": "Rotate DEK", + "description": "Generates a new Data Encryption Key, re-encrypts all secrets and alert-channel configs, and bumps the vault version. Admin-only. Pipeline DEK caches expire within ~10 minutes.", + "operationId": "rotateDek", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SingleValueResponseDekRotationResultDto" + } + } } }, - { - "name": "groupId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateStatusPageComponentGroupRequest" + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "200": { - "description": "OK", + "403": { + "description": "Forbidden — the actor lacks permission for this resource", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseStatusPageComponentGroupDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - }, - "delete": { - "tags": [ - "Status Pages" - ], - "summary": "Delete a component group (components become ungrouped)", - "operationId": "deleteGroup", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "groupId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "204": { - "description": "No Content" } } } }, - "/api/v1/status-pages/{id}/incidents": { + "/api/v1/webhooks": { "get": { "tags": [ - "Status Pages" + "Webhooks" ], - "summary": "List incidents for this status page (filterable by status, paginated)", - "operationId": "listIncidents", + "summary": "List webhook endpoints for the authenticated org", + "operationId": "list_2", "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "status", - "in": "query", - "required": false, - "schema": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "INVESTIGATING", - "IDENTIFIED", - "MONITORING", - "RESOLVED" - ] - } - } - }, { "name": "pageable", "in": "query", @@ -5422,217 +19061,210 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/TableValueResultStatusPageIncidentDto" + "$ref": "#/components/schemas/TableValueResultWebhookEndpointDto" } } } - } - } - }, - "post": { - "tags": [ - "Status Pages" - ], - "summary": "Create a status page incident (manual)", - "operationId": "createIncident", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateStatusPageIncidentRequest" + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "201": { - "description": "Created", + "403": { + "description": "Forbidden — the actor lacks permission for this resource", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseStatusPageIncidentDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/status-pages/{id}/incidents/{incidentId}": { - "get": { - "tags": [ - "Status Pages" - ], - "summary": "Get incident details with timeline", - "operationId": "getIncident", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "incidentId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "500": { + "description": "Internal server error — see the message field for details", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseStatusPageIncidentDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } } } }, - "put": { + "post": { "tags": [ - "Status Pages" - ], - "summary": "Update an incident", - "operationId": "updateIncident", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "incidentId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } + "Webhooks" ], + "summary": "Register a new webhook endpoint", + "operationId": "create_3", "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateStatusPageIncidentRequest" + "$ref": "#/components/schemas/CreateWebhookEndpointRequest" } } }, "required": true }, "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseStatusPageIncidentDto" + "$ref": "#/components/schemas/SingleValueResponseWebhookEndpointDto" } } } - } - } - }, - "delete": { - "tags": [ - "Status Pages" - ], - "summary": "Delete an incident", - "operationId": "deleteIncident", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "incidentId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/api/v1/status-pages/{id}/incidents/{incidentId}/dismiss": { - "post": { - "tags": [ - "Status Pages" - ], - "summary": "Dismiss a draft incident (deletes it permanently)", - "operationId": "dismissIncident", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "incidentId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "204": { - "description": "No Content" } } } }, - "/api/v1/status-pages/{id}/incidents/{incidentId}/publish": { - "post": { + "/api/v1/webhooks/{id}": { + "get": { "tags": [ - "Status Pages" + "Webhooks" ], - "summary": "Publish a draft incident (sets publishedAt, applies component statuses, notifies subscribers)", - "operationId": "publishIncident", + "summary": "Get a single webhook endpoint", + "operationId": "get_1", "parameters": [ { "name": "id", @@ -5642,98 +19274,107 @@ "type": "string", "format": "uuid" } - }, - { - "name": "incidentId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublishStatusPageIncidentRequest" - } - } - } - }, "responses": { "200": { "description": "OK", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseStatusPageIncidentDto" + "$ref": "#/components/schemas/SingleValueResponseWebhookEndpointDto" } } } - } - } - } - }, - "/api/v1/status-pages/{id}/incidents/{incidentId}/updates": { - "post": { - "tags": [ - "Status Pages" - ], - "summary": "Post an incident timeline update", - "operationId": "postIncidentUpdate", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "incidentId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateStatusPageIncidentUpdateRequest" + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "201": { - "description": "Created", + "404": { + "description": "Not found — the requested resource does not exist", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseStatusPageIncidentDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/status-pages/{id}/layout/reorder": { + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, "put": { "tags": [ - "Status Pages" + "Webhooks" ], - "summary": "Reorder page-level layout: groups and ungrouped components share one ordering", - "operationId": "reorderLayout", + "summary": "Update a webhook endpoint", + "operationId": "update_1", "parameters": [ { "name": "id", @@ -5749,64 +19390,111 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReorderPageLayoutRequest" + "$ref": "#/components/schemas/UpdateWebhookEndpointRequest" } } }, "required": true }, - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/api/v1/status-pages/{id}/subscribers": { - "get": { - "tags": [ - "Status Pages" - ], - "summary": "List confirmed subscribers (paginated)", - "operationId": "listSubscribers", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "pageable", - "in": "query", - "required": true, - "schema": { - "$ref": "#/components/schemas/Pageable" - } - } - ], "responses": { "200": { "description": "OK", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/TableValueResultStatusPageSubscriberDto" + "$ref": "#/components/schemas/SingleValueResponseWebhookEndpointDto" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } } } }, - "post": { + "delete": { "tags": [ - "Status Pages" + "Webhooks" ], - "summary": "Add a subscriber (immediately confirmed, skips double opt-in)", - "operationId": "addSubscriber", + "summary": "Delete a webhook endpoint", + "operationId": "delete_1", "parameters": [ { "name": "id", @@ -5818,23 +19506,86 @@ } } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AdminAddSubscriberRequest" + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "201": { - "description": "Created", + "401": { + "description": "Unauthorized — missing or invalid credentials", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseStatusPageSubscriberDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -5842,13 +19593,13 @@ } } }, - "/api/v1/status-pages/{id}/subscribers/{subscriberId}": { - "delete": { + "/api/v1/webhooks/{id}/deliveries": { + "get": { "tags": [ - "Status Pages" + "Webhooks" ], - "summary": "Remove a subscriber", - "operationId": "removeSubscriber", + "summary": "List recent deliveries for a webhook endpoint", + "operationId": "listDeliveries", "parameters": [ { "name": "id", @@ -5860,36 +19611,13 @@ } }, { - "name": "subscriberId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/api/v1/tags": { - "get": { - "tags": [ - "Tags" - ], - "summary": "List tags for the authenticated organization", - "operationId": "list_3", - "parameters": [ - { - "name": "pageable", + "name": "limit", "in": "query", - "required": true, + "required": false, "schema": { - "$ref": "#/components/schemas/Pageable" + "type": "integer", + "format": "int32", + "default": 20 } } ], @@ -5899,80 +19627,101 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/TableValueResultTagDto" + "$ref": "#/components/schemas/TableValueResultWebhookDeliveryDto" } } } - } - } - }, - "post": { - "tags": [ - "Tags" - ], - "summary": "Create a new tag", - "operationId": "create_4", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateTagRequest" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "201": { - "description": "Created", + "502": { + "description": "Bad gateway — an upstream provider returned an error", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseTagDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/tags/{id}": { - "get": { - "tags": [ - "Tags" - ], - "summary": "Get a tag by ID", - "operationId": "getById", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "503": { + "description": "Service unavailable — try again shortly", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseTagDto" + "$ref": "#/components/schemas/ErrorResponse" } } } } } - }, - "put": { + } + }, + "/api/v1/webhooks/{id}/test": { + "post": { "tags": [ - "Tags" + "Webhooks" ], - "summary": "Update a tag's name and/or color", - "operationId": "update_2", + "summary": "Send a test delivery to a webhook endpoint", + "operationId": "test", "parameters": [ { "name": "id", @@ -5988,11 +19737,10 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateTagRequest" + "$ref": "#/components/schemas/TestWebhookEndpointRequest" } } - }, - "required": true + } }, "responses": { "200": { @@ -6000,52 +19748,189 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseTagDto" + "$ref": "#/components/schemas/SingleValueResponseWebhookTestResult" } } } - } - } - }, - "delete": { - "tags": [ - "Tags" - ], - "summary": "Delete a tag (cascades to all monitor associations)", - "operationId": "delete_2", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "204": { - "description": "No Content" } } } }, - "/api/v1/vaults/rotate": { - "post": { + "/api/v1/webhooks/events": { + "get": { "tags": [ - "Vault" + "Webhooks" ], - "summary": "Rotate DEK", - "description": "Generates a new Data Encryption Key, re-encrypts all secrets and alert-channel configs, and bumps the vault version. Admin-only. Pipeline DEK caches expire within ~10 minutes.", - "operationId": "rotateDek", + "summary": "List all available webhook event types", + "description": "Returns the full catalog of supported outbound webhook event types with their surface grouping and human-readable descriptions. Use this to populate subscription checkboxes when creating or updating a webhook endpoint.", + "operationId": "listEvents", "responses": { "200": { "description": "OK", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseDekRotationResultDto" + "$ref": "#/components/schemas/WebhookEventCatalogResponse" + } + } + } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -6053,59 +19938,100 @@ } } }, - "/api/v1/webhooks": { + "/api/v1/webhooks/signing-secret": { "get": { "tags": [ "Webhooks" ], - "summary": "List webhook endpoints for the authenticated org", - "operationId": "list_2", - "parameters": [ - { - "name": "pageable", - "in": "query", - "required": true, - "schema": { - "$ref": "#/components/schemas/Pageable" - } - } - ], + "summary": "Get signing secret metadata for the authenticated org", + "operationId": "getSigningSecretInfo", "responses": { "200": { "description": "OK", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/TableValueResultWebhookEndpointDto" + "$ref": "#/components/schemas/SingleValueResponseWebhookSigningSecretDto" } } } - } - } - }, - "post": { - "tags": [ - "Webhooks" - ], - "summary": "Register a new webhook endpoint", - "operationId": "create_3", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateWebhookEndpointRequest" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "201": { - "description": "Created", + "500": { + "description": "Internal server error — see the message field for details", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseWebhookEndpointDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -6113,136 +20039,100 @@ } } }, - "/api/v1/webhooks/{id}": { - "get": { + "/api/v1/webhooks/signing-secret/rotate": { + "post": { "tags": [ "Webhooks" ], - "summary": "Get a single webhook endpoint", - "operationId": "get_1", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], + "summary": "Generate or rotate the organization webhook signing secret", + "operationId": "rotateSigningSecret", "responses": { "200": { "description": "OK", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseWebhookEndpointDto" + "$ref": "#/components/schemas/SingleValueResponseString" } } } - } - } - }, - "put": { - "tags": [ - "Webhooks" - ], - "summary": "Update a webhook endpoint", - "operationId": "update_1", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateWebhookEndpointRequest" + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, - "required": true - }, - "responses": { - "200": { - "description": "OK", + "403": { + "description": "Forbidden — the actor lacks permission for this resource", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseWebhookEndpointDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - }, - "delete": { - "tags": [ - "Webhooks" - ], - "summary": "Delete a webhook endpoint", - "operationId": "delete_1", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/api/v1/webhooks/{id}/deliveries": { - "get": { - "tags": [ - "Webhooks" - ], - "summary": "List recent deliveries for a webhook endpoint", - "operationId": "listDeliveries", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, - { - "name": "limit", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "format": "int32", - "default": 20 + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultWebhookDeliveryDto" + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -6250,135 +20140,110 @@ } } }, - "/api/v1/webhooks/{id}/test": { - "post": { + "/api/v1/workspaces": { + "get": { "tags": [ - "Webhooks" + "Workspaces" ], - "summary": "Send a test delivery to a webhook endpoint", - "operationId": "test", + "summary": "List workspaces", + "operationId": "list_1", "parameters": [ { - "name": "id", - "in": "path", + "name": "pageable", + "in": "query", "required": true, "schema": { - "type": "string", - "format": "uuid" + "$ref": "#/components/schemas/Pageable" } } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TestWebhookEndpointRequest" - } - } - } - }, "responses": { "200": { "description": "OK", "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseWebhookTestResult" + "$ref": "#/components/schemas/TableValueResultWorkspaceDto" } } } - } - } - } - }, - "/api/v1/webhooks/events": { - "get": { - "tags": [ - "Webhooks" - ], - "summary": "List all available webhook event types", - "description": "Returns the full catalog of supported outbound webhook event types with their surface grouping and human-readable descriptions. Use this to populate subscription checkboxes when creating or updating a webhook endpoint.", - "operationId": "listEvents", - "responses": { - "200": { - "description": "OK", + }, + "400": { + "description": "Bad request — the payload failed validation", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/WebhookEventCatalogResponse" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/webhooks/signing-secret": { - "get": { - "tags": [ - "Webhooks" - ], - "summary": "Get signing secret metadata for the authenticated org", - "operationId": "getSigningSecretInfo", - "responses": { - "200": { - "description": "OK", + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseWebhookSigningSecretDto" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/webhooks/signing-secret/rotate": { - "post": { - "tags": [ - "Webhooks" - ], - "summary": "Generate or rotate the organization webhook signing secret", - "operationId": "rotateSigningSecret", - "responses": { - "200": { - "description": "OK", + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/SingleValueResponseString" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - } - } - }, - "/api/v1/workspaces": { - "get": { - "tags": [ - "Workspaces" - ], - "summary": "List workspaces", - "operationId": "list_1", - "parameters": [ - { - "name": "pageable", - "in": "query", - "required": true, - "schema": { - "$ref": "#/components/schemas/Pageable" + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "503": { + "description": "Service unavailable — try again shortly", "content": { - "*/*": { + "application/json": { "schema": { - "$ref": "#/components/schemas/TableValueResultWorkspaceDto" + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -6411,6 +20276,86 @@ } } } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } } } } @@ -6443,6 +20388,86 @@ } } } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } } } }, @@ -6483,6 +20508,86 @@ } } } + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } } } }, @@ -6506,6 +20611,86 @@ "responses": { "204": { "description": "No Content" + }, + "400": { + "description": "Bad request — the payload failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — missing or invalid credentials", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden — the actor lacks permission for this resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not found — the requested resource does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "Conflict — the request collides with current resource state", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error — see the message field for details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "502": { + "description": "Bad gateway — an upstream provider returned an error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service unavailable — try again shortly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } } } } @@ -6780,7 +20965,10 @@ "eventType", "id", "incidentId", - "status" + "status", + "stepNumber", + "fireCount", + "attemptCount" ], "type": "object", "properties": { @@ -6879,40 +21067,37 @@ }, "ApiKeyAuthConfig": { "required": [ + "type", "headerName" - ], - "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/MonitorAuthConfig" - }, - { - "type": "object", - "properties": { - "headerName": { - "minLength": 1, - "pattern": "^[A-Za-z0-9\\-_]+$", - "type": "string", - "description": "HTTP header name that carries the API key" - }, - "vaultSecretId": { - "type": "string", - "description": "Vault secret ID for the API key value", - "format": "uuid", - "nullable": true - } - }, - "required": [ - "headerName" + ], + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "api_key" ] + }, + "headerName": { + "minLength": 1, + "pattern": "^[A-Za-z0-9\\-_]+$", + "type": "string", + "description": "HTTP header name that carries the API key" + }, + "vaultSecretId": { + "type": "string", + "description": "Vault secret ID for the API key value", + "format": "uuid", + "nullable": true } - ] + } }, "ApiKeyCreateResponse": { "required": [ "createdAt", "key", - "name" + "name", + "id" ], "type": "object", "properties": { @@ -6950,7 +21135,8 @@ "createdAt", "key", "name", - "updatedAt" + "updatedAt", + "id" ], "type": "object", "properties": { @@ -7000,69 +21186,11 @@ }, "description": "API key for programmatic access to the DevHelm API" }, - "AssertionConfig": { - "required": [ - "type" - ], - "type": "object", - "properties": { - "type": { - "type": "string" - } - }, - "description": "New assertion configuration (full replacement)", - "discriminator": { - "propertyName": "type", - "mapping": { - "status_code": "#/components/schemas/StatusCodeAssertion", - "response_time": "#/components/schemas/ResponseTimeAssertion", - "body_contains": "#/components/schemas/BodyContainsAssertion", - "json_path": "#/components/schemas/JsonPathAssertion", - "header_value": "#/components/schemas/HeaderValueAssertion", - "regex_body": "#/components/schemas/RegexBodyAssertion", - "dns_resolves": "#/components/schemas/DnsResolvesAssertion", - "dns_response_time": "#/components/schemas/DnsResponseTimeAssertion", - "dns_expected_ips": "#/components/schemas/DnsExpectedIpsAssertion", - "dns_expected_cname": "#/components/schemas/DnsExpectedCnameAssertion", - "dns_record_contains": "#/components/schemas/DnsRecordContainsAssertion", - "dns_record_equals": "#/components/schemas/DnsRecordEqualsAssertion", - "dns_txt_contains": "#/components/schemas/DnsTxtContainsAssertion", - "dns_min_answers": "#/components/schemas/DnsMinAnswersAssertion", - "dns_max_answers": "#/components/schemas/DnsMaxAnswersAssertion", - "dns_response_time_warn": "#/components/schemas/DnsResponseTimeWarnAssertion", - "dns_ttl_low": "#/components/schemas/DnsTtlLowAssertion", - "dns_ttl_high": "#/components/schemas/DnsTtlHighAssertion", - "mcp_connects": "#/components/schemas/McpConnectsAssertion", - "mcp_response_time": "#/components/schemas/McpResponseTimeAssertion", - "mcp_has_capability": "#/components/schemas/McpHasCapabilityAssertion", - "mcp_tool_available": "#/components/schemas/McpToolAvailableAssertion", - "mcp_min_tools": "#/components/schemas/McpMinToolsAssertion", - "mcp_protocol_version": "#/components/schemas/McpProtocolVersionAssertion", - "mcp_response_time_warn": "#/components/schemas/McpResponseTimeWarnAssertion", - "mcp_tool_count_changed": "#/components/schemas/McpToolCountChangedAssertion", - "ssl_expiry": "#/components/schemas/SslExpiryAssertion", - "response_size": "#/components/schemas/ResponseSizeAssertion", - "redirect_count": "#/components/schemas/RedirectCountAssertion", - "redirect_target": "#/components/schemas/RedirectTargetAssertion", - "response_time_warn": "#/components/schemas/ResponseTimeWarnAssertion", - "tcp_connects": "#/components/schemas/TcpConnectsAssertion", - "tcp_response_time": "#/components/schemas/TcpResponseTimeAssertion", - "tcp_response_time_warn": "#/components/schemas/TcpResponseTimeWarnAssertion", - "icmp_reachable": "#/components/schemas/IcmpReachableAssertion", - "icmp_response_time": "#/components/schemas/IcmpResponseTimeAssertion", - "icmp_response_time_warn": "#/components/schemas/IcmpResponseTimeWarnAssertion", - "icmp_packet_loss": "#/components/schemas/IcmpPacketLossAssertion", - "heartbeat_received": "#/components/schemas/HeartbeatReceivedAssertion", - "heartbeat_max_interval": "#/components/schemas/HeartbeatMaxIntervalAssertion", - "heartbeat_interval_drift": "#/components/schemas/HeartbeatIntervalDriftAssertion", - "heartbeat_payload_contains": "#/components/schemas/HeartbeatPayloadContainsAssertion" - } - } - }, "AssertionResultDto": { "required": [ "severity", - "type" + "type", + "passed" ], "type": "object", "properties": { @@ -7107,7 +21235,8 @@ "required": [ "assertionType", "message", - "severity" + "severity", + "passed" ], "type": "object", "properties": { @@ -7190,7 +21319,8 @@ "AuditEventDto": { "required": [ "action", - "createdAt" + "createdAt", + "id" ], "type": "object", "properties": { @@ -7272,65 +21402,83 @@ }, "BasicAuthConfig": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/MonitorAuthConfig" + "properties": { + "type": { + "type": "string", + "enum": [ + "basic" + ] }, - { + "vaultSecretId": { + "type": "string", + "description": "Vault secret ID holding Basic auth username and password", + "format": "uuid", + "nullable": true + } + }, + "required": [ + "type" + ] + }, + "BatchComponentUptimeDto": { + "required": [ + "components" + ], + "type": "object", + "properties": { + "components": { "type": "object", - "properties": { - "vaultSecretId": { - "type": "string", - "description": "Vault secret ID holding Basic auth username and password", - "format": "uuid", - "nullable": true + "additionalProperties": { + "type": "array", + "description": "Map of component ID → list of per-day uptime entries (oldest → newest)", + "items": { + "$ref": "#/components/schemas/ComponentUptimeDayDto" } - } + }, + "description": "Map of component ID → list of per-day uptime entries (oldest → newest)" } - ] + }, + "description": "Batch daily uptime per component, keyed by component ID" }, "BearerAuthConfig": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/MonitorAuthConfig" + "properties": { + "type": { + "type": "string", + "enum": [ + "bearer" + ] }, - { - "type": "object", - "properties": { - "vaultSecretId": { - "type": "string", - "description": "Vault secret ID holding the bearer token value", - "format": "uuid", - "nullable": true - } - } + "vaultSecretId": { + "type": "string", + "description": "Vault secret ID holding the bearer token value", + "format": "uuid", + "nullable": true } + }, + "required": [ + "type" ] }, "BodyContainsAssertion": { "required": [ + "type", "substring" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "substring": { - "minLength": 1, - "type": "string", - "description": "Substring that must appear in the response body" - } - }, - "required": [ - "substring" + "properties": { + "type": { + "type": "string", + "enum": [ + "body_contains" ] + }, + "substring": { + "minLength": 1, + "type": "string", + "description": "Substring that must appear in the response body" } - ] + } }, "BulkMonitorActionRequest": { "required": [ @@ -7352,7 +21500,7 @@ }, "action": { "type": "string", - "description": "Action to perform: PAUSE, RESUME, DELETE, ADD_TAG, REMOVE_TAG", + "description": "Action to apply to every monitor in the bulk request", "enum": [ "PAUSE", "RESUME", @@ -7410,7 +21558,8 @@ }, "CategoryDto": { "required": [ - "category" + "category", + "serviceCount" ], "type": "object", "properties": { @@ -7465,30 +21614,6 @@ }, "description": "Update an organization member's status" }, - "ChannelConfig": { - "required": [ - "channelType" - ], - "type": "object", - "properties": { - "channelType": { - "type": "string" - } - }, - "description": "New channel configuration (full replacement, not partial update)", - "discriminator": { - "propertyName": "channelType", - "mapping": { - "email": "#/components/schemas/EmailChannelConfig", - "slack": "#/components/schemas/SlackChannelConfig", - "webhook": "#/components/schemas/WebhookChannelConfig", - "pagerduty": "#/components/schemas/PagerDutyChannelConfig", - "opsgenie": "#/components/schemas/OpsGenieChannelConfig", - "teams": "#/components/schemas/TeamsChannelConfig", - "discord": "#/components/schemas/DiscordChannelConfig" - } - } - }, "ChartBucketDto": { "required": [ "bucket" @@ -7612,7 +21737,8 @@ "required": [ "id", "region", - "timestamp" + "timestamp", + "passed" ], "type": "object", "properties": { @@ -7671,24 +21797,42 @@ "description": "A single check result from a monitor run" }, "CheckTypeDetailsDto": { - "required": [ - "check_type" - ], - "type": "object", - "properties": { - "check_type": { - "type": "string" - } - }, "description": "Check-type-specific details — polymorphic by check_type discriminator", "discriminator": { - "propertyName": "check_type" - } + "propertyName": "check_type", + "mapping": { + "http": "#/components/schemas/Http", + "tcp": "#/components/schemas/Tcp", + "icmp": "#/components/schemas/Icmp", + "dns": "#/components/schemas/Dns", + "mcp_server": "#/components/schemas/McpServer" + } + }, + "oneOf": [ + { + "$ref": "#/components/schemas/Http" + }, + { + "$ref": "#/components/schemas/Tcp" + }, + { + "$ref": "#/components/schemas/Icmp" + }, + { + "$ref": "#/components/schemas/Dns" + }, + { + "$ref": "#/components/schemas/McpServer" + } + ] }, "ComponentImpact": { "required": [ "componentId", - "componentName" + "componentName", + "uptimePercentage", + "partialOutageSeconds", + "majorOutageSeconds" ], "type": "object", "properties": { @@ -7726,7 +21870,8 @@ }, "ComponentPosition": { "required": [ - "componentId" + "componentId", + "displayOrder" ], "type": "object", "properties": { @@ -7774,40 +21919,51 @@ }, "ComponentUptimeDayDto": { "required": [ - "date" + "date", + "source", + "partialOutageSeconds", + "majorOutageSeconds", + "degradedSeconds", + "uptimePercentage" ], "type": "object", "properties": { "date": { "type": "string", - "description": "Start-of-day timestamp for this bucket (UTC midnight)", + "description": "Date of the daily bucket (ISO 8601)", "format": "date-time" }, "partialOutageSeconds": { "type": "integer", - "description": "Seconds of partial outage on this day", + "description": "Seconds of partial outage observed on this day", "format": "int32" }, "majorOutageSeconds": { "type": "integer", - "description": "Seconds of major outage on this day", + "description": "Seconds of major outage observed on this day", + "format": "int32" + }, + "degradedSeconds": { + "type": "integer", + "description": "Seconds the component spent in degraded performance on this day", "format": "int32" }, "uptimePercentage": { "type": "number", - "description": "Computed uptime percentage using weighted formula", + "description": "Computed uptime percentage for the day", "format": "double" }, - "incidents": { - "type": "array", - "description": "Incidents that overlapped this day", - "nullable": true, - "items": { - "$ref": "#/components/schemas/IncidentRef" - } + "eventsJson": { + "type": "string", + "description": "Incident event references for this day as raw JSON", + "nullable": true + }, + "source": { + "type": "string", + "description": "Data source: vendor_reported or incident_derived" } }, - "description": "Daily uptime data for a status page component" + "description": "Daily uptime data for a component" }, "ComponentUptimeSummaryDto": { "required": [ @@ -7846,7 +22002,9 @@ }, "ConfirmationPolicy": { "required": [ - "type" + "type", + "minRegionsFailing", + "maxWaitSeconds" ], "type": "object", "properties": { @@ -7932,8 +22090,7 @@ }, "CreateAssertionRequest": { "required": [ - "config", - "severity" + "config" ], "type": "object", "properties": { @@ -8069,7 +22226,8 @@ }, "severity": { "type": "string", - "description": "Outcome severity: FAIL (fails the check) or WARN (warns without failing)", + "description": "Outcome severity: FAIL (fails the check) or WARN (warns without failing, default: FAIL)", + "nullable": true, "enum": [ "fail", "warn" @@ -8081,7 +22239,8 @@ "CreateEnvironmentRequest": { "required": [ "name", - "slug" + "slug", + "isDefault" ], "type": "object", "properties": { @@ -8345,11 +22504,8 @@ }, "CreateNotificationPolicyRequest": { "required": [ - "enabled", "escalation", - "matchRules", - "name", - "priority" + "name" ], "type": "object", "properties": { @@ -8362,6 +22518,7 @@ "matchRules": { "type": "array", "description": "Match rules to evaluate (all must pass; omit or empty for catch-all)", + "nullable": true, "items": { "$ref": "#/components/schemas/MatchRule" } @@ -8372,12 +22529,14 @@ "enabled": { "type": "boolean", "description": "Whether this policy is enabled (default true)", + "nullable": true, "default": true }, "priority": { "type": "integer", "description": "Evaluation priority; higher value = evaluated first (default 0)", "format": "int32", + "nullable": true, "default": 0 } }, @@ -8856,35 +23015,10 @@ }, "description": "Create a new workspace within the organization" }, - "CursorPage": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "type": "array", - "description": "Items on this page", - "items": { - "type": "object", - "description": "Items on this page" - } - }, - "nextCursor": { - "type": "string", - "description": "Opaque cursor for the next page; null when there are no more results", - "nullable": true - }, - "hasMore": { - "type": "boolean", - "description": "Whether more results exist beyond this page" - } - }, - "description": "Cursor-paginated response for time-series and append-only data" - }, "CursorPageCheckResultDto": { "required": [ - "data" + "data", + "hasMore" ], "type": "object", "properties": { @@ -8909,7 +23043,8 @@ }, "CursorPageServiceCatalogDto": { "required": [ - "data" + "data", + "hasMore" ], "type": "object", "properties": { @@ -8934,7 +23069,8 @@ }, "CursorPageServicePollResultDto": { "required": [ - "data" + "data", + "hasMore" ], "type": "object", "properties": { @@ -8979,7 +23115,8 @@ "id", "impact", "status", - "title" + "title", + "scheduled" ], "type": "object", "properties": { @@ -9041,7 +23178,11 @@ }, "DekRotationResultDto": { "required": [ - "rotatedAt" + "rotatedAt", + "previousDekVersion", + "newDekVersion", + "secretsReEncrypted", + "channelsReEncrypted" ], "type": "object", "properties": { @@ -9098,7 +23239,8 @@ "attemptedAt", "deliveryId", "id", - "status" + "status", + "attemptNumber" ], "type": "object", "properties": { @@ -9202,423 +23344,454 @@ }, "DiscordChannelConfig": { "required": [ + "channelType", "webhookUrl" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/ChannelConfig" + "properties": { + "channelType": { + "type": "string", + "enum": [ + "discord" + ] + }, + "webhookUrl": { + "minLength": 1, + "type": "string", + "description": "Discord webhook URL" + }, + "mentionRoleId": { + "type": "string", + "description": "Optional Discord role ID to mention in notifications", + "nullable": true + } + } + }, + "Dns": { + "type": "object", + "description": "DNS check-type-specific details", + "properties": { + "check_type": { + "type": "string", + "enum": [ + "dns" + ] + }, + "hostname": { + "type": "string", + "description": "Target hostname", + "nullable": true + }, + "requestedTypes": { + "type": "array", + "description": "Requested DNS record types", + "nullable": true, + "items": { + "type": "string", + "description": "Requested DNS record types", + "nullable": true + } + }, + "usedResolver": { + "type": "string", + "description": "Resolver used for lookup", + "nullable": true }, - { + "records": { "type": "object", - "properties": { - "webhookUrl": { - "minLength": 1, - "type": "string", - "description": "Discord webhook URL" - }, - "mentionRoleId": { - "type": "string", - "description": "Optional Discord role ID to mention in notifications", + "additionalProperties": { + "type": "array", + "description": "Resolved DNS records keyed by record type", + "nullable": true, + "items": { + "type": "object", + "additionalProperties": { + "type": "object", + "description": "Resolved DNS records keyed by record type", + "nullable": true + }, + "description": "Resolved DNS records keyed by record type", "nullable": true } }, - "required": [ - "webhookUrl" - ] + "description": "Resolved DNS records keyed by record type", + "nullable": true + }, + "attempts": { + "type": "array", + "description": "DNS resolution attempts", + "nullable": true, + "items": { + "type": "object", + "additionalProperties": { + "type": "object", + "description": "DNS resolution attempts", + "nullable": true + }, + "description": "DNS resolution attempts", + "nullable": true + } + }, + "failureKind": { + "type": "string", + "description": "Kind of DNS failure, if any", + "nullable": true } + }, + "required": [ + "check_type" ] }, "DnsExpectedCnameAssertion": { "required": [ + "type", "value" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "value": { - "minLength": 1, - "type": "string", - "description": "Expected CNAME target the resolution must include" - } - }, - "required": [ - "value" + "properties": { + "type": { + "type": "string", + "enum": [ + "dns_expected_cname" ] + }, + "value": { + "minLength": 1, + "type": "string", + "description": "Expected CNAME target the resolution must include" } - ] + } }, "DnsExpectedIpsAssertion": { "required": [ + "type", "ips" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "ips": { - "minItems": 1, - "type": "array", - "description": "Allowed IP addresses; at least one resolved address must match", - "items": { - "type": "string", - "description": "Allowed IP addresses; at least one resolved address must match" - } - } - }, - "required": [ - "ips" + "properties": { + "type": { + "type": "string", + "enum": [ + "dns_expected_ips" ] + }, + "ips": { + "minItems": 1, + "type": "array", + "description": "Allowed IP addresses; at least one resolved address must match", + "items": { + "type": "string", + "description": "Allowed IP addresses; at least one resolved address must match" + } } - ] + } }, "DnsMaxAnswersAssertion": { "required": [ - "recordType" + "type", + "recordType", + "max" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "recordType": { - "minLength": 1, - "type": "string", - "description": "DNS record type whose answer count is checked" - }, - "max": { - "type": "integer", - "description": "Maximum number of answers allowed for that record type", - "format": "int32" - } - }, - "required": [ - "recordType", - "max" + "properties": { + "type": { + "type": "string", + "enum": [ + "dns_max_answers" ] + }, + "recordType": { + "minLength": 1, + "type": "string", + "description": "DNS record type whose answer count is checked" + }, + "max": { + "type": "integer", + "description": "Maximum number of answers allowed for that record type", + "format": "int32" } - ] + } }, "DnsMinAnswersAssertion": { "required": [ - "recordType" + "type", + "recordType", + "min" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "recordType": { - "minLength": 1, - "type": "string", - "description": "DNS record type whose answer count is checked" - }, - "min": { - "type": "integer", - "description": "Minimum number of answers required for that record type", - "format": "int32" - } - }, - "required": [ - "recordType", - "min" + "properties": { + "type": { + "type": "string", + "enum": [ + "dns_min_answers" ] + }, + "recordType": { + "minLength": 1, + "type": "string", + "description": "DNS record type whose answer count is checked" + }, + "min": { + "type": "integer", + "description": "Minimum number of answers required for that record type", + "format": "int32" } - ] + } }, "DnsMonitorConfig": { "required": [ "hostname" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/MonitorConfig" + "properties": { + "hostname": { + "minLength": 1, + "type": "string", + "description": "Domain name to resolve" }, - { - "type": "object", - "properties": { - "hostname": { - "minLength": 1, - "type": "string", - "description": "Domain name to resolve" - }, - "recordTypes": { - "type": "array", - "description": "DNS record types to query: A, AAAA, CNAME, MX, NS, TXT, SRV, SOA, CAA, PTR", - "nullable": true, - "items": { - "type": "string", - "description": "DNS record types to query: A, AAAA, CNAME, MX, NS, TXT, SRV, SOA, CAA, PTR", - "nullable": true, - "enum": [ - "A", - "AAAA", - "CNAME", - "MX", - "NS", - "TXT", - "SRV", - "SOA", - "CAA", - "PTR" - ] - } - }, - "nameservers": { - "type": "array", - "description": "Custom nameservers to query (uses system defaults if omitted)", - "nullable": true, - "items": { - "type": "string", - "description": "Custom nameservers to query (uses system defaults if omitted)", - "nullable": true - } - }, - "timeoutMs": { - "type": "integer", - "description": "Per-query timeout in milliseconds", - "format": "int32", - "nullable": true - }, - "totalTimeoutMs": { - "type": "integer", - "description": "Total timeout for all queries in milliseconds", - "format": "int32", - "nullable": true - } - }, - "required": [ - "hostname" - ] + "recordTypes": { + "type": "array", + "description": "DNS record types to query: A, AAAA, CNAME, MX, NS, TXT, SRV, SOA, CAA, PTR", + "nullable": true, + "items": { + "type": "string", + "description": "DNS record types to query: A, AAAA, CNAME, MX, NS, TXT, SRV, SOA, CAA, PTR", + "nullable": true, + "enum": [ + "A", + "AAAA", + "CNAME", + "MX", + "NS", + "TXT", + "SRV", + "SOA", + "CAA", + "PTR" + ] + } + }, + "nameservers": { + "type": "array", + "description": "Custom nameservers to query (uses system defaults if omitted)", + "nullable": true, + "items": { + "type": "string", + "description": "Custom nameservers to query (uses system defaults if omitted)", + "nullable": true + } + }, + "timeoutMs": { + "type": "integer", + "description": "Per-query timeout in milliseconds", + "format": "int32", + "nullable": true + }, + "totalTimeoutMs": { + "type": "integer", + "description": "Total timeout for all queries in milliseconds", + "format": "int32", + "nullable": true } - ] + } }, "DnsRecordContainsAssertion": { "required": [ + "type", "recordType", "substring" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "recordType": { - "minLength": 1, - "type": "string", - "description": "DNS record type to assert on (A, AAAA, CNAME, MX, TXT)" - }, - "substring": { - "minLength": 1, - "type": "string", - "description": "Substring that must appear in a matching record value" - } - }, - "required": [ - "recordType", - "substring" + "properties": { + "type": { + "type": "string", + "enum": [ + "dns_record_contains" ] + }, + "recordType": { + "minLength": 1, + "type": "string", + "description": "DNS record type to assert on (A, AAAA, CNAME, MX, TXT)" + }, + "substring": { + "minLength": 1, + "type": "string", + "description": "Substring that must appear in a matching record value" } - ] + } }, "DnsRecordEqualsAssertion": { "required": [ + "type", "recordType", "value" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "recordType": { - "minLength": 1, - "type": "string", - "description": "DNS record type to assert on (A, AAAA, CNAME, MX, TXT)" - }, - "value": { - "minLength": 1, - "type": "string", - "description": "Expected DNS record value for an exact match" - } - }, - "required": [ - "recordType", - "value" + "properties": { + "type": { + "type": "string", + "enum": [ + "dns_record_equals" ] + }, + "recordType": { + "minLength": 1, + "type": "string", + "description": "DNS record type to assert on (A, AAAA, CNAME, MX, TXT)" + }, + "value": { + "minLength": 1, + "type": "string", + "description": "Expected DNS record value for an exact match" } - ] + } }, "DnsResolvesAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" + "properties": { + "type": { + "type": "string", + "enum": [ + "dns_resolves" + ] } + }, + "required": [ + "type" ] }, "DnsResponseTimeAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "maxMs": { - "type": "integer", - "description": "Maximum allowed DNS resolution time in milliseconds", - "format": "int32" - } - }, - "required": [ - "maxMs" + "properties": { + "type": { + "type": "string", + "enum": [ + "dns_response_time" ] + }, + "maxMs": { + "type": "integer", + "description": "Maximum allowed DNS resolution time in milliseconds", + "format": "int32" } + }, + "required": [ + "type", + "maxMs" ] }, "DnsResponseTimeWarnAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "warnMs": { - "type": "integer", - "description": "DNS resolution time in milliseconds that triggers a warning only", - "format": "int32" - } - }, - "required": [ - "warnMs" + "properties": { + "type": { + "type": "string", + "enum": [ + "dns_response_time_warn" ] - } - ] - }, - "DnsTtlHighAssertion": { - "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" }, - { - "type": "object", - "properties": { - "maxTtl": { - "type": "integer", - "description": "Maximum TTL in seconds before a high-TTL warning is raised", - "format": "int32" - } - }, - "required": [ - "maxTtl" + "warnMs": { + "type": "integer", + "description": "DNS resolution time in milliseconds that triggers a warning only", + "format": "int32" + } + }, + "required": [ + "type", + "warnMs" + ] + }, + "DnsTtlHighAssertion": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "dns_ttl_high" ] + }, + "maxTtl": { + "type": "integer", + "description": "Maximum TTL in seconds before a high-TTL warning is raised", + "format": "int32" } + }, + "required": [ + "type", + "maxTtl" ] }, "DnsTtlLowAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "minTtl": { - "type": "integer", - "description": "Minimum acceptable TTL in seconds before a warning is raised", - "format": "int32" - } - }, - "required": [ - "minTtl" + "properties": { + "type": { + "type": "string", + "enum": [ + "dns_ttl_low" ] + }, + "minTtl": { + "type": "integer", + "description": "Minimum acceptable TTL in seconds before a warning is raised", + "format": "int32" } + }, + "required": [ + "type", + "minTtl" ] }, "DnsTxtContainsAssertion": { "required": [ + "type", "substring" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "substring": { - "minLength": 1, - "type": "string", - "description": "Substring that must appear in at least one TXT record" - } - }, - "required": [ - "substring" + "properties": { + "type": { + "type": "string", + "enum": [ + "dns_txt_contains" ] + }, + "substring": { + "minLength": 1, + "type": "string", + "description": "Substring that must appear in at least one TXT record" } - ] + } }, "EmailChannelConfig": { "required": [ + "channelType", "recipients" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/ChannelConfig" - }, - { - "type": "object", - "properties": { - "recipients": { - "minItems": 1, - "type": "array", - "description": "Email addresses to send notifications to", - "items": { - "type": "string", - "description": "Email addresses to send notifications to", - "format": "email" - } - } - }, - "required": [ - "recipients" + "properties": { + "channelType": { + "type": "string", + "enum": [ + "email" ] + }, + "recipients": { + "minItems": 1, + "type": "array", + "description": "Email addresses to send notifications to", + "items": { + "type": "string", + "description": "Email addresses to send notifications to", + "format": "email" + } } - ] + } }, "EntitlementDto": { "required": [ - "key" + "key", + "value", + "defaultValue", + "overridden" ], "type": "object", "properties": { @@ -9650,7 +23823,10 @@ "name", "slug", "updatedAt", - "variables" + "variables", + "orgId", + "monitorCount", + "isDefault" ], "type": "object", "properties": { @@ -9704,6 +23880,39 @@ }, "description": "Environment with variable substitutions for monitor configs" }, + "ErrorResponse": { + "required": [ + "message", + "status", + "timestamp" + ], + "type": "object", + "properties": { + "status": { + "type": "integer", + "description": "HTTP status code (mirrors the response status line)", + "format": "int32", + "example": 404 + }, + "message": { + "type": "string", + "description": "Human-readable error message; safe to surface to end users", + "example": "Monitor not found" + }, + "timestamp": { + "type": "integer", + "description": "Server time when the error was produced (epoch milliseconds)", + "format": "int64", + "example": 1737302400000 + } + }, + "description": "Uniform error envelope returned for every non-2xx response", + "example": { + "status": 404, + "message": "Monitor not found", + "timestamp": 1737302400000 + } + }, "EscalationChain": { "required": [ "steps" @@ -9733,7 +23942,8 @@ }, "EscalationStep": { "required": [ - "channelIds" + "channelIds", + "delayMinutes" ], "type": "object", "properties": { @@ -9789,7 +23999,15 @@ }, "GlobalStatusSummaryDto": { "required": [ - "servicesWithIssues" + "servicesWithIssues", + "totalServices", + "operationalCount", + "degradedCount", + "partialOutageCount", + "majorOutageCount", + "maintenanceCount", + "unknownCount", + "activeIncidentCount" ], "type": "object", "properties": { @@ -9868,130 +24086,112 @@ }, "HeaderAuthConfig": { "required": [ + "type", "headerName" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/MonitorAuthConfig" - }, - { - "type": "object", - "properties": { - "headerName": { - "minLength": 1, - "pattern": "^[A-Za-z0-9\\-_]+$", - "type": "string", - "description": "Custom HTTP header name for the secret value" - }, - "vaultSecretId": { - "type": "string", - "description": "Vault secret ID for the header value", - "format": "uuid", - "nullable": true - } - }, - "required": [ - "headerName" + "properties": { + "type": { + "type": "string", + "enum": [ + "header" ] + }, + "headerName": { + "minLength": 1, + "pattern": "^[A-Za-z0-9\\-_]+$", + "type": "string", + "description": "Custom HTTP header name for the secret value" + }, + "vaultSecretId": { + "type": "string", + "description": "Vault secret ID for the header value", + "format": "uuid", + "nullable": true } - ] + } }, "HeaderValueAssertion": { "required": [ - "expected", + "type", "headerName", + "expected", "operator" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" + "properties": { + "type": { + "type": "string", + "enum": [ + "header_value" + ] }, - { - "type": "object", - "properties": { - "headerName": { - "minLength": 1, - "type": "string", - "description": "HTTP header name to assert on" - }, - "expected": { - "minLength": 1, - "type": "string", - "description": "Expected value to compare against" - }, - "operator": { - "type": "string", - "description": "Comparison operator (equals, contains, less_than, greater_than, etc.)", - "enum": [ - "equals", - "contains", - "less_than", - "greater_than", - "matches", - "range" - ] - } - }, - "required": [ - "headerName", - "expected", - "operator" + "headerName": { + "minLength": 1, + "type": "string", + "description": "HTTP header name to assert on" + }, + "expected": { + "minLength": 1, + "type": "string", + "description": "Expected value to compare against" + }, + "operator": { + "type": "string", + "description": "Comparison operator (equals, contains, less_than, greater_than, etc.)", + "enum": [ + "equals", + "contains", + "less_than", + "greater_than", + "matches", + "range" ] } - ] + } }, "HeartbeatIntervalDriftAssertion": { "required": [ + "type", "maxDeviationPercent" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "maxDeviationPercent": { - "maximum": 100, - "minimum": 1, - "type": "integer", - "description": "Max percent drift from expected ping interval before warning (non-fatal)", - "format": "int32" - } - }, - "required": [ - "maxDeviationPercent" + "properties": { + "type": { + "type": "string", + "enum": [ + "heartbeat_interval_drift" ] + }, + "maxDeviationPercent": { + "maximum": 100, + "minimum": 1, + "type": "integer", + "description": "Max percent drift from expected ping interval before warning (non-fatal)", + "format": "int32" } - ] + } }, "HeartbeatMaxIntervalAssertion": { "required": [ + "type", "maxSeconds" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "maxSeconds": { - "minimum": 1, - "type": "integer", - "description": "Maximum allowed gap in seconds between consecutive heartbeat pings", - "format": "int32" - } - }, - "required": [ - "maxSeconds" + "properties": { + "type": { + "type": "string", + "enum": [ + "heartbeat_max_interval" ] + }, + "maxSeconds": { + "minimum": 1, + "type": "integer", + "description": "Maximum allowed gap in seconds between consecutive heartbeat pings", + "format": "int32" } - ] + } }, "HeartbeatMonitorConfig": { "required": [ @@ -9999,70 +24199,103 @@ "gracePeriod" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/MonitorConfig" + "properties": { + "expectedInterval": { + "maximum": 86400, + "minimum": 1, + "type": "integer", + "description": "Expected heartbeat interval in seconds", + "format": "int32" }, - { - "type": "object", - "properties": { - "expectedInterval": { - "maximum": 86400, - "minimum": 1, - "type": "integer", - "description": "Expected heartbeat interval in seconds", - "format": "int32" - }, - "gracePeriod": { - "minimum": 1, - "type": "integer", - "description": "Grace period in seconds before marking as down", - "format": "int32" - } - }, - "required": [ - "expectedInterval", - "gracePeriod" - ] + "gracePeriod": { + "minimum": 1, + "type": "integer", + "description": "Grace period in seconds before marking as down", + "format": "int32" } - ] + } }, "HeartbeatPayloadContainsAssertion": { "required": [ + "type", "path", "value" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" + "properties": { + "type": { + "type": "string", + "enum": [ + "heartbeat_payload_contains" + ] }, - { - "type": "object", - "properties": { - "path": { - "minLength": 1, - "type": "string", - "description": "JSONPath expression into the heartbeat ping JSON payload" - }, - "value": { - "type": "string", - "description": "Expected value to compare against at that path" - } - }, - "required": [ - "path", - "value" + "path": { + "minLength": 1, + "type": "string", + "description": "JSONPath expression into the heartbeat ping JSON payload" + }, + "value": { + "type": "string", + "description": "Expected value to compare against at that path" + } + } + }, + "HeartbeatPingResponse": { + "required": [ + "ok" + ], + "type": "object", + "properties": { + "ok": { + "type": "boolean", + "description": "Always true on a 2xx response", + "example": true + } + }, + "description": "Acknowledgement that a heartbeat ping was accepted" + }, + "HeartbeatReceivedAssertion": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "heartbeat_received" ] } + }, + "required": [ + "type" ] }, - "HeartbeatReceivedAssertion": { + "Http": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" + "description": "HTTP check-type-specific details", + "properties": { + "check_type": { + "type": "string", + "enum": [ + "http" + ] + }, + "timing": { + "type": "object", + "additionalProperties": { + "type": "object", + "description": "Request phase timing breakdown", + "nullable": true + }, + "description": "Request phase timing breakdown", + "nullable": true + }, + "bodyTruncated": { + "type": "boolean", + "description": "Whether the response body was truncated before storage", + "nullable": true } + }, + "required": [ + "check_type" ] }, "HttpMonitorConfig": { @@ -10071,174 +24304,218 @@ "url" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/MonitorConfig" + "properties": { + "url": { + "minLength": 1, + "type": "string", + "description": "Target URL to send requests to" }, - { + "method": { + "type": "string", + "description": "HTTP method: GET, POST, PUT, PATCH, DELETE, or HEAD", + "enum": [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE", + "HEAD" + ] + }, + "customHeaders": { "type": "object", - "properties": { - "url": { - "minLength": 1, - "type": "string", - "description": "Target URL to send requests to" - }, - "method": { - "type": "string", - "description": "HTTP method: GET, POST, PUT, PATCH, DELETE, or HEAD", - "enum": [ - "GET", - "POST", - "PUT", - "PATCH", - "DELETE", - "HEAD" - ] - }, - "customHeaders": { - "type": "object", - "additionalProperties": { - "type": "string", - "description": "Additional HTTP headers to include in requests", - "nullable": true - }, - "description": "Additional HTTP headers to include in requests", - "nullable": true - }, - "requestBody": { - "type": "string", - "description": "Request body content for POST/PUT/PATCH methods", - "nullable": true - }, - "contentType": { - "type": "string", - "description": "Content-Type header value for the request body", - "nullable": true - }, - "verifyTls": { - "type": "boolean", - "description": "Whether to verify TLS certificates (default: true)", - "nullable": true - } + "additionalProperties": { + "type": "string", + "description": "Additional HTTP headers to include in requests", + "nullable": true }, - "required": [ - "url", - "method" + "description": "Additional HTTP headers to include in requests", + "nullable": true + }, + "requestBody": { + "type": "string", + "description": "Request body content for POST/PUT/PATCH methods", + "nullable": true + }, + "contentType": { + "type": "string", + "description": "Content-Type header value for the request body", + "nullable": true + }, + "verifyTls": { + "type": "boolean", + "description": "Whether to verify TLS certificates (default: true)", + "nullable": true + } + } + }, + "Icmp": { + "required": [ + "check_type", + "host" + ], + "type": "object", + "description": "ICMP (ping) check-type-specific details", + "properties": { + "check_type": { + "type": "string", + "enum": [ + "icmp" ] + }, + "host": { + "type": "string", + "description": "Target host", + "example": "1.1.1.1" + }, + "packetsSent": { + "type": "integer", + "description": "Number of ICMP packets sent", + "format": "int32", + "nullable": true + }, + "packetsReceived": { + "type": "integer", + "description": "Number of ICMP packets received", + "format": "int32", + "nullable": true + }, + "packetLoss": { + "type": "number", + "description": "Packet loss percentage", + "format": "double", + "nullable": true, + "example": 0 + }, + "avgRttMs": { + "type": "number", + "description": "Average round-trip time in ms", + "format": "double", + "nullable": true + }, + "minRttMs": { + "type": "number", + "description": "Minimum round-trip time in ms", + "format": "double", + "nullable": true + }, + "maxRttMs": { + "type": "number", + "description": "Maximum round-trip time in ms", + "format": "double", + "nullable": true + }, + "jitterMs": { + "type": "number", + "description": "Jitter in ms", + "format": "double", + "nullable": true } - ] + } }, "IcmpMonitorConfig": { "required": [ "host" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/MonitorConfig" + "properties": { + "host": { + "minLength": 1, + "type": "string", + "description": "Target hostname or IP address to ping" }, - { - "type": "object", - "properties": { - "host": { - "minLength": 1, - "type": "string", - "description": "Target hostname or IP address to ping" - }, - "packetCount": { - "maximum": 20, - "minimum": 1, - "type": "integer", - "description": "Number of ICMP packets to send", - "format": "int32", - "nullable": true - }, - "timeoutMs": { - "type": "integer", - "description": "Ping timeout in milliseconds", - "format": "int32", - "nullable": true - } - }, - "required": [ - "host" - ] + "packetCount": { + "maximum": 20, + "minimum": 1, + "type": "integer", + "description": "Number of ICMP packets to send", + "format": "int32", + "nullable": true + }, + "timeoutMs": { + "type": "integer", + "description": "Ping timeout in milliseconds", + "format": "int32", + "nullable": true } - ] + } }, "IcmpPacketLossAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "maxPercent": { - "maximum": 100, - "exclusiveMaximum": false, - "minimum": 0, - "exclusiveMinimum": false, - "type": "number", - "description": "Maximum allowed packet loss percentage before the check fails (0–100)", - "format": "double" - } - }, - "required": [ - "maxPercent" + "properties": { + "type": { + "type": "string", + "enum": [ + "icmp_packet_loss" ] + }, + "maxPercent": { + "maximum": 100, + "exclusiveMaximum": false, + "minimum": 0, + "exclusiveMinimum": false, + "type": "number", + "description": "Maximum allowed packet loss percentage before the check fails (0–100)", + "format": "double" } + }, + "required": [ + "type", + "maxPercent" ] }, "IcmpReachableAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" + "properties": { + "type": { + "type": "string", + "enum": [ + "icmp_reachable" + ] } + }, + "required": [ + "type" ] }, "IcmpResponseTimeAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "maxMs": { - "type": "integer", - "description": "Maximum average ICMP round-trip time in milliseconds", - "format": "int32" - } - }, - "required": [ - "maxMs" + "properties": { + "type": { + "type": "string", + "enum": [ + "icmp_response_time" ] + }, + "maxMs": { + "type": "integer", + "description": "Maximum average ICMP round-trip time in milliseconds", + "format": "int32" } + }, + "required": [ + "type", + "maxMs" ] }, "IcmpResponseTimeWarnAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "warnMs": { - "type": "integer", - "description": "ICMP round-trip time in milliseconds that triggers a warning only", - "format": "int32" - } - }, - "required": [ - "warnMs" + "properties": { + "type": { + "type": "string", + "enum": [ + "icmp_response_time_warn" ] + }, + "warnMs": { + "type": "integer", + "description": "ICMP round-trip time in milliseconds that triggers a warning only", + "format": "int32" } + }, + "required": [ + "type", + "warnMs" ] }, "IncidentDetailDto": { @@ -10274,7 +24551,10 @@ "severity", "source", "status", - "updatedAt" + "updatedAt", + "organizationId", + "reopenCount", + "statusPageVisible" ], "type": "object", "properties": { @@ -10434,22 +24714,22 @@ }, "monitorName": { "type": "string", - "description": "Name of the associated monitor; populated on list responses", + "description": "Name of the associated monitor; populated on list responses. Omitted from JSON (undefined to SDKs) on detail responses, treat missing as null.", "nullable": true }, "serviceName": { "type": "string", - "description": "Name of the associated service; populated on list responses", + "description": "Name of the associated service; populated on list responses. Omitted from JSON (undefined to SDKs) on detail responses, treat missing as null.", "nullable": true }, "serviceSlug": { "type": "string", - "description": "Slug of the associated service; populated on list responses", + "description": "Slug of the associated service; populated on list responses. Omitted from JSON (undefined to SDKs) on detail responses, treat missing as null.", "nullable": true }, "monitorType": { "type": "string", - "description": "Type of the associated monitor; populated on list responses", + "description": "Type of the associated monitor; populated on list responses. Omitted from JSON (undefined to SDKs) on detail responses, treat missing as null.", "nullable": true }, "resourceGroupId": { @@ -10460,7 +24740,7 @@ }, "resourceGroupName": { "type": "string", - "description": "Name of the resource group; populated on list responses", + "description": "Name of the resource group; populated on list responses. Omitted from JSON (undefined to SDKs) on detail responses, treat missing as null.", "nullable": true } }, @@ -10471,6 +24751,7 @@ "properties": { "status": { "type": "string", + "description": "Filter by incident lifecycle status; null returns every status", "nullable": true, "enum": [ "WATCHING", @@ -10481,6 +24762,7 @@ }, "severity": { "type": "string", + "description": "Filter by severity; null returns every severity", "nullable": true, "enum": [ "DOWN", @@ -10490,6 +24772,7 @@ }, "source": { "type": "string", + "description": "Filter by where the incident originated (auto, manual, third-party)", "nullable": true, "enum": [ "AUTOMATIC", @@ -10501,49 +24784,60 @@ }, "monitorId": { "type": "string", + "description": "Only return incidents tied to this monitor ID", "format": "uuid", "nullable": true }, "serviceId": { "type": "string", + "description": "Only return incidents tied to this service ID (third-party services)", "format": "uuid", "nullable": true }, "resourceGroupId": { "type": "string", + "description": "Only return incidents whose monitor belongs to this resource group", "format": "uuid", "nullable": true }, "tagId": { "type": "string", + "description": "Only return incidents whose monitor carries this tag", "format": "uuid", "nullable": true }, "environmentId": { "type": "string", + "description": "Only return incidents whose monitor lives in this environment", "format": "uuid", "nullable": true }, "startedFrom": { "type": "string", + "description": "Earliest startedAt to include (inclusive, ISO 8601)", "format": "date-time", "nullable": true }, "startedTo": { "type": "string", + "description": "Latest startedAt to include (inclusive, ISO 8601)", "format": "date-time", "nullable": true }, "page": { "minimum": 0, "type": "integer", - "format": "int32" + "description": "Zero-based page index (default: 0)", + "format": "int32", + "example": 0 }, "size": { "maximum": 200, "minimum": 1, "type": "integer", - "format": "int32" + "description": "Number of incidents per page (1–200, default: 10)", + "format": "int32", + "example": 10 } }, "required": [ @@ -10662,7 +24956,8 @@ "required": [ "createdAt", "id", - "incidentId" + "incidentId", + "notifySubscribers" ], "type": "object", "properties": { @@ -10837,7 +25132,8 @@ "required": [ "email", "expiresAt", - "roleOffered" + "roleOffered", + "inviteId" ], "type": "object", "properties": { @@ -10881,53 +25177,48 @@ }, "JsonPathAssertion": { "required": [ + "type", + "path", "expected", - "operator", - "path" + "operator" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" + "properties": { + "type": { + "type": "string", + "enum": [ + "json_path" + ] }, - { - "type": "object", - "properties": { - "path": { - "minLength": 1, - "type": "string", - "description": "JSONPath expression to extract a value from the response body" - }, - "expected": { - "minLength": 1, - "type": "string", - "description": "Expected value to compare against" - }, - "operator": { - "type": "string", - "description": "Comparison operator (equals, contains, less_than, greater_than, etc.)", - "enum": [ - "equals", - "contains", - "less_than", - "greater_than", - "matches", - "range" - ] - } - }, - "required": [ - "path", - "expected", - "operator" + "path": { + "minLength": 1, + "type": "string", + "description": "JSONPath expression to extract a value from the response body" + }, + "expected": { + "minLength": 1, + "type": "string", + "description": "Expected value to compare against" + }, + "operator": { + "type": "string", + "description": "Comparison operator (equals, contains, less_than, greater_than, etc.)", + "enum": [ + "equals", + "contains", + "less_than", + "greater_than", + "matches", + "range" ] } - ] + } }, "KeyInfo": { "required": [ "createdAt", - "name" + "name", + "id" ], "type": "object", "properties": { @@ -10968,7 +25259,8 @@ "statusPageId", "statusPageName", "statusPageSlug", - "title" + "title", + "scheduled" ], "type": "object", "properties": { @@ -11076,7 +25368,9 @@ "createdAt", "endsAt", "id", - "startsAt" + "startsAt", + "organizationId", + "suppressAlerts" ], "type": "object", "properties": { @@ -11176,121 +25470,169 @@ }, "McpConnectsAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" + "properties": { + "type": { + "type": "string", + "enum": [ + "mcp_connects" + ] } + }, + "required": [ + "type" ] }, "McpHasCapabilityAssertion": { "required": [ + "type", "capability" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "capability": { - "minLength": 1, - "type": "string", - "description": "Capability name the server must advertise, e.g. tools or resources" - } - }, - "required": [ - "capability" + "properties": { + "type": { + "type": "string", + "enum": [ + "mcp_has_capability" ] + }, + "capability": { + "minLength": 1, + "type": "string", + "description": "Capability name the server must advertise, e.g. tools or resources" } - ] + } }, "McpMinToolsAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "min": { - "type": "integer", - "description": "Minimum number of tools the server must expose", - "format": "int32" - } - }, - "required": [ - "min" + "properties": { + "type": { + "type": "string", + "enum": [ + "mcp_min_tools" ] + }, + "min": { + "type": "integer", + "description": "Minimum number of tools the server must expose", + "format": "int32" } + }, + "required": [ + "type", + "min" ] }, "McpProtocolVersionAssertion": { "required": [ + "type", "version" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "version": { - "minLength": 1, - "type": "string", - "description": "Expected MCP protocol version string from the server handshake" - } - }, - "required": [ - "version" + "properties": { + "type": { + "type": "string", + "enum": [ + "mcp_protocol_version" ] + }, + "version": { + "minLength": 1, + "type": "string", + "description": "Expected MCP protocol version string from the server handshake" } - ] + } }, "McpResponseTimeAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "maxMs": { - "type": "integer", - "description": "Maximum allowed MCP check duration in milliseconds", - "format": "int32" - } - }, - "required": [ - "maxMs" + "properties": { + "type": { + "type": "string", + "enum": [ + "mcp_response_time" ] + }, + "maxMs": { + "type": "integer", + "description": "Maximum allowed MCP check duration in milliseconds", + "format": "int32" } + }, + "required": [ + "type", + "maxMs" ] }, "McpResponseTimeWarnAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" + "properties": { + "type": { + "type": "string", + "enum": [ + "mcp_response_time_warn" + ] }, - { + "warnMs": { + "type": "integer", + "description": "MCP check duration in milliseconds that triggers a warning only", + "format": "int32" + } + }, + "required": [ + "type", + "warnMs" + ] + }, + "McpServer": { + "type": "object", + "description": "MCP server check-type-specific details", + "properties": { + "check_type": { + "type": "string", + "enum": [ + "mcp_server" + ] + }, + "url": { + "type": "string", + "description": "MCP server URL", + "nullable": true + }, + "protocolVersion": { + "type": "string", + "description": "MCP protocol version", + "nullable": true + }, + "serverInfo": { "type": "object", - "properties": { - "warnMs": { - "type": "integer", - "description": "MCP check duration in milliseconds that triggers a warning only", - "format": "int32" - } + "additionalProperties": { + "type": "object", + "description": "MCP server info (name, version, etc.)", + "nullable": true }, - "required": [ - "warnMs" - ] + "description": "MCP server info (name, version, etc.)", + "nullable": true + }, + "toolCount": { + "type": "integer", + "description": "Number of tools exposed", + "format": "int32", + "nullable": true + }, + "resourceCount": { + "type": "integer", + "description": "Number of resources exposed", + "format": "int32", + "nullable": true + }, + "promptCount": { + "type": "integer", + "description": "Number of prompts exposed", + "format": "int32", + "nullable": true } + }, + "required": [ + "check_type" ] }, "McpServerMonitorConfig": { @@ -11298,88 +25640,72 @@ "command" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/MonitorConfig" + "properties": { + "command": { + "minLength": 1, + "type": "string", + "description": "Command to execute to start the MCP server" }, - { + "args": { + "type": "array", + "description": "Command-line arguments for the MCP server process", + "nullable": true, + "items": { + "type": "string", + "description": "Command-line arguments for the MCP server process", + "nullable": true + } + }, + "env": { "type": "object", - "properties": { - "command": { - "minLength": 1, - "type": "string", - "description": "Command to execute to start the MCP server" - }, - "args": { - "type": "array", - "description": "Command-line arguments for the MCP server process", - "nullable": true, - "items": { - "type": "string", - "description": "Command-line arguments for the MCP server process", - "nullable": true - } - }, - "env": { - "type": "object", - "additionalProperties": { - "type": "string", - "description": "Environment variables to pass to the MCP server process", - "nullable": true - }, - "description": "Environment variables to pass to the MCP server process", - "nullable": true - } + "additionalProperties": { + "type": "string", + "description": "Environment variables to pass to the MCP server process", + "nullable": true }, - "required": [ - "command" - ] + "description": "Environment variables to pass to the MCP server process", + "nullable": true } - ] + } }, "McpToolAvailableAssertion": { "required": [ + "type", "toolName" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "toolName": { - "minLength": 1, - "type": "string", - "description": "MCP tool name that must appear in the server's tool list" - } - }, - "required": [ - "toolName" + "properties": { + "type": { + "type": "string", + "enum": [ + "mcp_tool_available" ] + }, + "toolName": { + "minLength": 1, + "type": "string", + "description": "MCP tool name that must appear in the server's tool list" } - ] + } }, "McpToolCountChangedAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "expectedCount": { - "type": "integer", - "description": "Expected tool count; warns when the live count differs", - "format": "int32" - } - }, - "required": [ - "expectedCount" + "properties": { + "type": { + "type": "string", + "enum": [ + "mcp_tool_count_changed" ] + }, + "expectedCount": { + "type": "integer", + "description": "Expected tool count; warns when the live count differs", + "format": "int32" } + }, + "required": [ + "type", + "expectedCount" ] }, "MemberDto": { @@ -11387,7 +25713,8 @@ "createdAt", "email", "orgRole", - "status" + "status", + "userId" ], "type": "object", "properties": { @@ -11639,15 +25966,6 @@ } }, "MonitorAuthConfig": { - "required": [ - "type" - ], - "type": "object", - "properties": { - "type": { - "type": "string" - } - }, "description": "New authentication configuration (full replacement)", "discriminator": { "propertyName": "type", @@ -11657,7 +25975,21 @@ "header": "#/components/schemas/HeaderAuthConfig", "api_key": "#/components/schemas/ApiKeyAuthConfig" } - } + }, + "oneOf": [ + { + "$ref": "#/components/schemas/BearerAuthConfig" + }, + { + "$ref": "#/components/schemas/BasicAuthConfig" + }, + { + "$ref": "#/components/schemas/HeaderAuthConfig" + }, + { + "$ref": "#/components/schemas/ApiKeyAuthConfig" + } + ] }, "MonitorAuthDto": { "required": [ @@ -11703,10 +26035,6 @@ } } }, - "MonitorConfig": { - "type": "object", - "description": "Protocol-specific monitor configuration" - }, "MonitorDto": { "required": [ "config", @@ -11716,7 +26044,10 @@ "name", "regions", "type", - "updatedAt" + "updatedAt", + "organizationId", + "frequencySeconds", + "enabled" ], "type": "object", "properties": { @@ -11984,7 +26315,8 @@ }, "MonitorTestResultDto": { "required": [ - "assertionResults" + "assertionResults", + "passed" ], "type": "object", "properties": { @@ -12056,7 +26388,8 @@ "createdAt", "id", "monitorId", - "snapshot" + "snapshot", + "version" ], "type": "object", "properties": { @@ -12136,7 +26469,8 @@ "incidentId", "policyId", "status", - "updatedAt" + "updatedAt", + "currentStep" ], "type": "object", "properties": { @@ -12235,7 +26569,9 @@ "required": [ "createdAt", "title", - "type" + "type", + "id", + "read" ], "type": "object", "properties": { @@ -12286,7 +26622,10 @@ "id", "matchRules", "name", - "updatedAt" + "updatedAt", + "organizationId", + "enabled", + "priority" ], "type": "object", "properties": { @@ -12339,37 +26678,34 @@ }, "OpsGenieChannelConfig": { "required": [ + "channelType", "apiKey" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/ChannelConfig" - }, - { - "type": "object", - "properties": { - "apiKey": { - "minLength": 1, - "type": "string", - "description": "OpsGenie API key for alert creation" - }, - "region": { - "type": "string", - "description": "OpsGenie API region: us or eu", - "nullable": true - } - }, - "required": [ - "apiKey" + "properties": { + "channelType": { + "type": "string", + "enum": [ + "opsgenie" ] + }, + "apiKey": { + "minLength": 1, + "type": "string", + "description": "OpsGenie API key for alert creation" + }, + "region": { + "type": "string", + "description": "OpsGenie API region: us or eu", + "nullable": true } - ] + } }, "OrganizationDto": { "required": [ "email", - "name" + "name", + "id" ], "type": "object", "properties": { @@ -12407,7 +26743,8 @@ }, "OrgInfo": { "required": [ - "name" + "name", + "id" ], "type": "object", "properties": { @@ -12451,32 +26788,28 @@ }, "PagerDutyChannelConfig": { "required": [ + "channelType", "routingKey" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/ChannelConfig" - }, - { - "type": "object", - "properties": { - "routingKey": { - "minLength": 1, - "type": "string", - "description": "PagerDuty Events API v2 routing (integration) key" - }, - "severityOverride": { - "type": "string", - "description": "Override PagerDuty severity mapping", - "nullable": true - } - }, - "required": [ - "routingKey" + "properties": { + "channelType": { + "type": "string", + "enum": [ + "pagerduty" ] + }, + "routingKey": { + "minLength": 1, + "type": "string", + "description": "PagerDuty Events API v2 routing (integration) key" + }, + "severityOverride": { + "type": "string", + "description": "Override PagerDuty severity mapping", + "nullable": true } - ] + } }, "PageSection": { "type": "object", @@ -12508,7 +26841,8 @@ "required": [ "entitlements", "tier", - "usage" + "usage", + "trialActive" ], "type": "object", "properties": { @@ -12560,7 +26894,8 @@ }, "PollChartBucketDto": { "required": [ - "bucket" + "bucket", + "totalPolls" ], "type": "object", "properties": { @@ -12696,93 +27031,84 @@ "cooldownMinutes" ] }, - "RedirectCountAssertion": { - "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "maxCount": { - "type": "integer", - "description": "Maximum number of HTTP redirects allowed before the check fails", - "format": "int32" - } - }, - "required": [ - "maxCount" + "RedirectCountAssertion": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "redirect_count" ] + }, + "maxCount": { + "type": "integer", + "description": "Maximum number of HTTP redirects allowed before the check fails", + "format": "int32" } + }, + "required": [ + "type", + "maxCount" ] }, "RedirectTargetAssertion": { "required": [ + "type", "expected", "operator" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" + "properties": { + "type": { + "type": "string", + "enum": [ + "redirect_target" + ] }, - { - "type": "object", - "properties": { - "expected": { - "minLength": 1, - "type": "string", - "description": "Expected final URL after following redirects" - }, - "operator": { - "type": "string", - "description": "Comparison operator (equals, contains, less_than, greater_than, etc.)", - "enum": [ - "equals", - "contains", - "less_than", - "greater_than", - "matches", - "range" - ] - } - }, - "required": [ - "expected", - "operator" + "expected": { + "minLength": 1, + "type": "string", + "description": "Expected final URL after following redirects" + }, + "operator": { + "type": "string", + "description": "Comparison operator (equals, contains, less_than, greater_than, etc.)", + "enum": [ + "equals", + "contains", + "less_than", + "greater_than", + "matches", + "range" ] } - ] + } }, "RegexBodyAssertion": { "required": [ + "type", "pattern" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "pattern": { - "minLength": 1, - "type": "string", - "description": "Regular expression the response body must match" - } - }, - "required": [ - "pattern" + "properties": { + "type": { + "type": "string", + "enum": [ + "regex_body" ] + }, + "pattern": { + "minLength": 1, + "type": "string", + "description": "Regular expression the response body must match" } - ] + } }, "RegionStatusDto": { "required": [ "region", - "timestamp" + "timestamp", + "passed" ], "type": "object", "properties": { @@ -12878,14 +27204,12 @@ "description": "Reorder page-level layout: groups and ungrouped components share one ordering" }, "ResolveIncidentRequest": { - "required": [ - "body" - ], "type": "object", "properties": { "body": { "type": "string", - "description": "Optional resolution message or post-mortem notes" + "description": "Optional resolution message or post-mortem notes", + "nullable": true } } }, @@ -12896,7 +27220,9 @@ "id", "name", "slug", - "updatedAt" + "updatedAt", + "organizationId", + "suppressMemberAlerts" ], "type": "object", "properties": { @@ -13026,7 +27352,10 @@ }, "ResourceGroupHealthDto": { "required": [ - "status" + "status", + "totalMembers", + "operationalCount", + "activeIncidents" ], "type": "object", "properties": { @@ -13195,65 +27524,62 @@ }, "ResponseSizeAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "maxBytes": { - "type": "integer", - "description": "Maximum response body size in bytes before the check fails", - "format": "int32" - } - }, - "required": [ - "maxBytes" + "properties": { + "type": { + "type": "string", + "enum": [ + "response_size" ] + }, + "maxBytes": { + "type": "integer", + "description": "Maximum response body size in bytes before the check fails", + "format": "int32" } + }, + "required": [ + "type", + "maxBytes" ] }, "ResponseTimeAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "thresholdMs": { - "type": "integer", - "description": "Maximum allowed response time in milliseconds before the check fails", - "format": "int32" - } - }, - "required": [ - "thresholdMs" + "properties": { + "type": { + "type": "string", + "enum": [ + "response_time" ] + }, + "thresholdMs": { + "type": "integer", + "description": "Maximum allowed response time in milliseconds before the check fails", + "format": "int32" } + }, + "required": [ + "type", + "thresholdMs" ] }, "ResponseTimeWarnAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "warnMs": { - "type": "integer", - "description": "HTTP response time in milliseconds that triggers a warning only", - "format": "int32" - } - }, - "required": [ - "warnMs" + "properties": { + "type": { + "type": "string", + "enum": [ + "response_time_warn" ] + }, + "warnMs": { + "type": "integer", + "description": "HTTP response time in milliseconds that triggers a warning only", + "format": "int32" } + }, + "required": [ + "type", + "warnMs" ] }, "ResultSummaryDto": { @@ -13307,7 +27633,9 @@ }, "RetryStrategy": { "required": [ - "type" + "type", + "maxRetries", + "interval" ], "type": "object", "properties": { @@ -13413,7 +27741,8 @@ "id", "key", "updatedAt", - "valueHash" + "valueHash", + "dekVersion" ], "type": "object", "properties": { @@ -13485,7 +27814,12 @@ "id", "name", "slug", - "updatedAt" + "updatedAt", + "pollingIntervalSeconds", + "enabled", + "published", + "componentCount", + "activeIncidentCount" ], "type": "object", "properties": { @@ -13569,7 +27903,12 @@ "lastSeenAt", "lifecycleStatus", "name", - "status" + "status", + "showcase", + "onlyShowIfDegraded", + "hasUptime", + "displayAggregatedUptime", + "isGroup" ], "type": "object", "properties": { @@ -13679,7 +28018,10 @@ "required": [ "components", "date", - "incidents" + "incidents", + "totalPartialOutageSeconds", + "totalMajorOutageSeconds", + "totalDegradedSeconds" ], "type": "object", "properties": { @@ -13704,6 +28046,11 @@ "description": "Sum of major outage seconds across all leaf components", "format": "int64" }, + "totalDegradedSeconds": { + "type": "integer", + "description": "Sum of degraded performance seconds across all leaf components", + "format": "int64" + }, "components": { "type": "array", "description": "Per-component impact rows for the day (only components with uptime data)", @@ -13732,7 +28079,9 @@ "name", "recentIncidents", "slug", - "updatedAt" + "updatedAt", + "pollingIntervalSeconds", + "enabled" ], "type": "object", "properties": { @@ -13983,7 +28332,8 @@ }, "ServiceLiveStatusDto": { "required": [ - "componentStatuses" + "componentStatuses", + "activeIncidentCount" ], "type": "object", "properties": { @@ -14014,7 +28364,10 @@ "ServicePollResultDto": { "required": [ "serviceId", - "timestamp" + "timestamp", + "passed", + "componentCount", + "degradedCount" ], "type": "object", "properties": { @@ -14076,7 +28429,9 @@ "ServicePollSummaryDto": { "required": [ "chartData", - "window" + "window", + "totalPolls", + "passedPolls" ], "type": "object", "properties": { @@ -14169,7 +28524,9 @@ "serviceId", "slug", "subscribedAt", - "subscriptionId" + "subscriptionId", + "pollingIntervalSeconds", + "enabled" ], "type": "object", "properties": { @@ -14390,6 +28747,17 @@ } } }, + "SingleValueResponseBatchComponentUptimeDto": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/BatchComponentUptimeDto" + } + } + }, "SingleValueResponseBulkMonitorActionResult": { "required": [ "data" @@ -14527,23 +28895,6 @@ } } }, - "SingleValueResponseMapStringListComponentUptimeDayDto": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ComponentUptimeDayDto" - } - } - } - } - }, "SingleValueResponseMonitorAssertionDto": { "required": [ "data" @@ -14931,91 +29282,81 @@ }, "SlackChannelConfig": { "required": [ + "channelType", "webhookUrl" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/ChannelConfig" - }, - { - "type": "object", - "properties": { - "webhookUrl": { - "minLength": 1, - "type": "string", - "description": "Slack incoming webhook URL" - }, - "mentionText": { - "type": "string", - "description": "Optional mention text included in notifications, e.g. @channel", - "nullable": true - } - }, - "required": [ - "webhookUrl" + "properties": { + "channelType": { + "type": "string", + "enum": [ + "slack" ] + }, + "webhookUrl": { + "minLength": 1, + "type": "string", + "description": "Slack incoming webhook URL" + }, + "mentionText": { + "type": "string", + "description": "Optional mention text included in notifications, e.g. @channel", + "nullable": true } - ] + } }, "SslExpiryAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "minDaysRemaining": { - "type": "integer", - "description": "Minimum days before TLS certificate expiry; fails or warns below this threshold", - "format": "int32" - } - }, - "required": [ - "minDaysRemaining" + "properties": { + "type": { + "type": "string", + "enum": [ + "ssl_expiry" ] + }, + "minDaysRemaining": { + "type": "integer", + "description": "Minimum days before TLS certificate expiry; fails or warns below this threshold", + "format": "int32" } + }, + "required": [ + "type", + "minDaysRemaining" ] }, "StatusCodeAssertion": { "required": [ + "type", "expected", "operator" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" + "properties": { + "type": { + "type": "string", + "enum": [ + "status_code" + ] }, - { - "type": "object", - "properties": { - "expected": { - "minLength": 1, - "type": "string", - "description": "Expected status code, range pattern, or wildcard such as 2xx" - }, - "operator": { - "type": "string", - "description": "Comparison operator (equals, contains, less_than, greater_than, etc.)", - "enum": [ - "equals", - "contains", - "less_than", - "greater_than", - "matches", - "range" - ] - } - }, - "required": [ - "expected", - "operator" + "expected": { + "minLength": 1, + "type": "string", + "description": "Expected status code, range pattern, or wildcard such as 2xx" + }, + "operator": { + "type": "string", + "description": "Comparison operator (equals, contains, less_than, greater_than, etc.)", + "enum": [ + "equals", + "contains", + "less_than", + "greater_than", + "matches", + "range" ] } - ] + } }, "StatusPageBranding": { "type": "object", @@ -15128,7 +29469,11 @@ "name", "statusPageId", "type", - "updatedAt" + "updatedAt", + "showUptime", + "displayOrder", + "pageOrder", + "excludeFromOverall" ], "type": "object", "properties": { @@ -15216,7 +29561,10 @@ "id", "name", "statusPageId", - "updatedAt" + "updatedAt", + "displayOrder", + "pageOrder", + "collapsed" ], "type": "object", "properties": { @@ -15239,29 +29587,69 @@ "type": "integer", "format": "int32" }, - "pageOrder": { + "pageOrder": { + "type": "integer", + "format": "int32" + }, + "collapsed": { + "type": "boolean" + }, + "components": { + "type": "array", + "nullable": true, + "items": { + "$ref": "#/components/schemas/StatusPageComponentDto" + } + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + } + } + }, + "StatusPageComponentUptimeDayDto": { + "required": [ + "date", + "partialOutageSeconds", + "majorOutageSeconds", + "uptimePercentage" + ], + "type": "object", + "properties": { + "date": { + "type": "string", + "description": "Start-of-day timestamp for this bucket (UTC midnight)", + "format": "date-time" + }, + "partialOutageSeconds": { + "type": "integer", + "description": "Seconds of partial outage on this day", + "format": "int32" + }, + "majorOutageSeconds": { "type": "integer", + "description": "Seconds of major outage on this day", "format": "int32" }, - "collapsed": { - "type": "boolean" + "uptimePercentage": { + "type": "number", + "description": "Computed uptime percentage using weighted formula", + "format": "double" }, - "components": { + "incidents": { "type": "array", + "description": "Incidents that overlapped this day", "nullable": true, "items": { - "$ref": "#/components/schemas/StatusPageComponentDto" + "$ref": "#/components/schemas/IncidentRef" } - }, - "createdAt": { - "type": "string", - "format": "date-time" - }, - "updatedAt": { - "type": "string", - "format": "date-time" } - } + }, + "description": "Daily uptime data for a status page component" }, "StatusPageCustomDomainDto": { "required": [ @@ -15272,7 +29660,8 @@ "updatedAt", "verificationCnameTarget", "verificationMethod", - "verificationToken" + "verificationToken", + "primary" ], "type": "object", "properties": { @@ -15339,7 +29728,10 @@ "name", "slug", "updatedAt", - "visibility" + "visibility", + "organizationId", + "workspaceId", + "enabled" ], "type": "object", "properties": { @@ -15456,7 +29848,9 @@ "status", "statusPageId", "title", - "updatedAt" + "updatedAt", + "scheduled", + "autoResolve" ], "type": "object", "properties": { @@ -15567,7 +29961,8 @@ "body", "createdAt", "id", - "status" + "status", + "notifySubscribers" ], "type": "object", "properties": { @@ -15613,7 +30008,8 @@ "required": [ "createdAt", "email", - "id" + "id", + "confirmed" ], "type": "object", "properties": { @@ -15658,7 +30054,9 @@ }, "TableValueResultAlertChannelDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -15688,7 +30086,9 @@ }, "TableValueResultAlertDeliveryDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -15718,7 +30118,9 @@ }, "TableValueResultApiKeyDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -15748,7 +30150,9 @@ }, "TableValueResultAuditEventDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -15778,7 +30182,9 @@ }, "TableValueResultCategoryDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -15808,7 +30214,9 @@ }, "TableValueResultComponentUptimeDayDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -15838,7 +30246,9 @@ }, "TableValueResultDeliveryAttemptDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -15868,7 +30278,9 @@ }, "TableValueResultEnvironmentDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -15898,7 +30310,9 @@ }, "TableValueResultIncidentDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -15928,7 +30342,9 @@ }, "TableValueResultIntegrationDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -15958,7 +30374,9 @@ }, "TableValueResultInviteDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -15988,7 +30406,9 @@ }, "TableValueResultMaintenanceWindowDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16018,7 +30438,9 @@ }, "TableValueResultMemberDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16048,7 +30470,9 @@ }, "TableValueResultMonitorDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16078,7 +30502,9 @@ }, "TableValueResultMonitorVersionDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16108,7 +30534,9 @@ }, "TableValueResultNotificationDispatchDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16138,7 +30566,9 @@ }, "TableValueResultNotificationDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16168,7 +30598,9 @@ }, "TableValueResultNotificationPolicyDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16198,7 +30630,9 @@ }, "TableValueResultResourceGroupDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16228,7 +30662,9 @@ }, "TableValueResultScheduledMaintenanceDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16258,7 +30694,9 @@ }, "TableValueResultSecretDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16288,7 +30726,9 @@ }, "TableValueResultServiceComponentDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16318,7 +30758,9 @@ }, "TableValueResultServiceIncidentDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16348,7 +30790,9 @@ }, "TableValueResultServiceSubscriptionDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16378,7 +30822,9 @@ }, "TableValueResultStatusPageComponentDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16408,7 +30854,9 @@ }, "TableValueResultStatusPageComponentGroupDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16436,9 +30884,43 @@ } } }, + "TableValueResultStatusPageComponentUptimeDayDto": { + "required": [ + "data", + "hasNext", + "hasPrev" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StatusPageComponentUptimeDayDto" + } + }, + "hasNext": { + "type": "boolean" + }, + "hasPrev": { + "type": "boolean" + }, + "totalElements": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "totalPages": { + "type": "integer", + "format": "int32", + "nullable": true + } + } + }, "TableValueResultStatusPageCustomDomainDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16468,7 +30950,9 @@ }, "TableValueResultStatusPageDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16498,7 +30982,9 @@ }, "TableValueResultStatusPageIncidentDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16528,7 +31014,9 @@ }, "TableValueResultStatusPageSubscriberDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16558,7 +31046,9 @@ }, "TableValueResultTagDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16588,7 +31078,9 @@ }, "TableValueResultWebhookDeliveryDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16618,7 +31110,9 @@ }, "TableValueResultWebhookEndpointDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16648,7 +31142,9 @@ }, "TableValueResultWorkspaceDto": { "required": [ - "data" + "data", + "hasNext", + "hasPrev" ], "type": "object", "properties": { @@ -16682,7 +31178,8 @@ "createdAt", "id", "name", - "updatedAt" + "updatedAt", + "organizationId" ], "type": "object", "properties": { @@ -16719,117 +31216,139 @@ }, "description": "Tag for organizing and filtering monitors" }, + "Tcp": { + "required": [ + "check_type", + "host", + "port", + "connected" + ], + "type": "object", + "description": "TCP check-type-specific details", + "properties": { + "check_type": { + "type": "string", + "enum": [ + "tcp" + ] + }, + "host": { + "type": "string", + "description": "Target host", + "example": "db.example.com" + }, + "port": { + "type": "integer", + "description": "Target port", + "format": "int32", + "example": 5432 + }, + "connected": { + "type": "boolean", + "description": "Whether a TCP connection was established" + } + } + }, "TcpConnectsAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" + "properties": { + "type": { + "type": "string", + "enum": [ + "tcp_connects" + ] } + }, + "required": [ + "type" ] }, "TcpMonitorConfig": { "required": [ - "host" + "host", + "port" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/MonitorConfig" + "properties": { + "host": { + "minLength": 1, + "type": "string", + "description": "Target hostname or IP address" }, - { - "type": "object", - "properties": { - "host": { - "minLength": 1, - "type": "string", - "description": "Target hostname or IP address" - }, - "port": { - "maximum": 65535, - "minimum": 1, - "type": "integer", - "description": "TCP port to connect to", - "format": "int32" - }, - "timeoutMs": { - "type": "integer", - "description": "Connection timeout in milliseconds", - "format": "int32", - "nullable": true - } - }, - "required": [ - "host", - "port" - ] + "port": { + "maximum": 65535, + "minimum": 1, + "type": "integer", + "description": "TCP port to connect to", + "format": "int32" + }, + "timeoutMs": { + "type": "integer", + "description": "Connection timeout in milliseconds", + "format": "int32", + "nullable": true } - ] + } }, "TcpResponseTimeAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "maxMs": { - "type": "integer", - "description": "Maximum TCP connect time in milliseconds before the check fails", - "format": "int32" - } - }, - "required": [ - "maxMs" + "properties": { + "type": { + "type": "string", + "enum": [ + "tcp_response_time" ] + }, + "maxMs": { + "type": "integer", + "description": "Maximum TCP connect time in milliseconds before the check fails", + "format": "int32" } + }, + "required": [ + "type", + "maxMs" ] }, "TcpResponseTimeWarnAssertion": { "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/AssertionConfig" - }, - { - "type": "object", - "properties": { - "warnMs": { - "type": "integer", - "description": "TCP connect time in milliseconds that triggers a warning only", - "format": "int32" - } - }, - "required": [ - "warnMs" + "properties": { + "type": { + "type": "string", + "enum": [ + "tcp_response_time_warn" ] + }, + "warnMs": { + "type": "integer", + "description": "TCP connect time in milliseconds that triggers a warning only", + "format": "int32" } + }, + "required": [ + "type", + "warnMs" ] }, "TeamsChannelConfig": { "required": [ + "channelType", "webhookUrl" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/ChannelConfig" - }, - { - "type": "object", - "properties": { - "webhookUrl": { - "minLength": 1, - "type": "string", - "description": "Microsoft Teams incoming webhook URL" - } - }, - "required": [ - "webhookUrl" + "properties": { + "channelType": { + "type": "string", + "enum": [ + "teams" ] + }, + "webhookUrl": { + "minLength": 1, + "type": "string", + "description": "Microsoft Teams incoming webhook URL" } - ] + } }, "TestAlertChannelRequest": { "required": [ @@ -16867,7 +31386,8 @@ }, "TestChannelResult": { "required": [ - "message" + "message", + "success" ], "type": "object", "properties": { @@ -16882,7 +31402,8 @@ "TestMatchResult": { "required": [ "matchedRules", - "unmatchedRules" + "unmatchedRules", + "matched" ], "type": "object", "properties": { @@ -17453,9 +31974,24 @@ }, "config": { "nullable": true, - "allOf": [ + "oneOf": [ + { + "$ref": "#/components/schemas/DnsMonitorConfig" + }, + { + "$ref": "#/components/schemas/HeartbeatMonitorConfig" + }, + { + "$ref": "#/components/schemas/HttpMonitorConfig" + }, + { + "$ref": "#/components/schemas/IcmpMonitorConfig" + }, + { + "$ref": "#/components/schemas/McpServerMonitorConfig" + }, { - "$ref": "#/components/schemas/MonitorConfig" + "$ref": "#/components/schemas/TcpMonitorConfig" } ] }, @@ -18009,7 +32545,8 @@ }, "UptimeBucketDto": { "required": [ - "timestamp" + "timestamp", + "totalPolls" ], "type": "object", "properties": { @@ -18080,42 +32617,38 @@ }, "WebhookChannelConfig": { "required": [ + "channelType", "url" ], "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/ChannelConfig" + "properties": { + "channelType": { + "type": "string", + "enum": [ + "webhook" + ] }, - { + "url": { + "minLength": 1, + "type": "string", + "description": "Webhook endpoint URL that receives alert payloads" + }, + "signingSecret": { + "type": "string", + "description": "Optional HMAC signing secret for payload verification", + "nullable": true + }, + "customHeaders": { "type": "object", - "properties": { - "url": { - "minLength": 1, - "type": "string", - "description": "Webhook endpoint URL that receives alert payloads" - }, - "signingSecret": { - "type": "string", - "description": "Optional HMAC signing secret for payload verification", - "nullable": true - }, - "customHeaders": { - "type": "object", - "additionalProperties": { - "type": "string", - "description": "Additional HTTP headers to include in webhook requests", - "nullable": true - }, - "description": "Additional HTTP headers to include in webhook requests", - "nullable": true - } + "additionalProperties": { + "type": "string", + "description": "Additional HTTP headers to include in webhook requests", + "nullable": true }, - "required": [ - "url" - ] + "description": "Additional HTTP headers to include in webhook requests", + "nullable": true } - ] + } }, "WebhookDeliveryDto": { "required": [ @@ -18124,7 +32657,9 @@ "eventId", "eventType", "id", - "status" + "status", + "attemptCount", + "maxAttempts" ], "type": "object", "properties": { @@ -18194,7 +32729,9 @@ "id", "subscribedEvents", "updatedAt", - "url" + "url", + "enabled", + "consecutiveFailures" ], "type": "object", "properties": { @@ -18308,7 +32845,8 @@ }, "WebhookTestResult": { "required": [ - "message" + "message", + "success" ], "type": "object", "properties": { @@ -18334,7 +32872,9 @@ "required": [ "createdAt", "name", - "updatedAt" + "updatedAt", + "id", + "orgId" ], "type": "object", "properties": { diff --git a/scripts/preprocess_spec.py b/scripts/preprocess_spec.py deleted file mode 100644 index 746db51..0000000 --- a/scripts/preprocess_spec.py +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env python3 -"""Preprocess the vendored OpenAPI spec before running datamodel-codegen. - -Applies structural fixes that code generators need: -1. setRequiredFields — mark non-nullable fields as required -2. pushRequiredIntoAllOf — propagate required into allOf members -""" - -from __future__ import annotations - -import json -import sys -from pathlib import Path - - -_REQUEST_PREFIXES = ( - "Create", "Update", "Add", "Acquire", "Resolve", "Reorder", - "Test", "Change", "Admin", "Bulk", "Monitor", -) - - -def set_required_fields(spec: dict) -> None: - schemas = spec.get("components", {}).get("schemas", {}) - for schema_name, schema in schemas.items(): - if schema.get("type") != "object" or "properties" not in schema: - continue - - if isinstance(schema.get("required"), list): - is_req = schema_name.endswith("Request") and schema_name.startswith( - _REQUEST_PREFIXES - ) - if not is_req: - for prop, prop_schema in schema.get("properties", {}).items(): - if prop_schema.get("nullable"): - continue - if prop in schema["required"]: - continue - if prop_schema.get("allOf"): - continue - if "default" in prop_schema: - continue - schema["required"].append(prop) - continue - - is_request = schema_name.endswith("Request") and schema_name.startswith( - _REQUEST_PREFIXES - ) - if is_request: - continue - - required = [] - for prop, prop_schema in schema.get("properties", {}).items(): - if prop_schema.get("nullable"): - continue - if prop_schema.get("allOf"): - continue - if "default" in prop_schema: - continue - required.append(prop) - if required: - schema["required"] = required - - -def push_required_into_all_of(spec: dict) -> None: - schemas = spec.get("components", {}).get("schemas", {}) - for schema in schemas.values(): - if not isinstance(schema.get("required"), list): - continue - if not isinstance(schema.get("allOf"), list): - continue - for member in schema["allOf"]: - if "properties" not in member: - continue - member_required = [f for f in schema["required"] if f in member["properties"]] - if member_required: - existing = member.get("required", []) - member["required"] = list(set(existing + member_required)) - - - -# fix_missing_nullable — REMOVED. -# The root cause (Lombok not copying @Nullable to getters) was fixed in the API -# by adding `jakarta.annotation.Nullable` to lombok.copyableAnnotations. The -# generated OpenAPI spec now correctly marks nullable fields via the existing -# PropertyCustomizer in OpenApiConfig.java. All DTO fields also have explicit -# @Nullable or @NotNull/@NotBlank annotations, enforced by DtoAnnotationTest. - - -def main() -> None: - if len(sys.argv) != 3: - print(f"Usage: {sys.argv[0]} ", file=sys.stderr) - sys.exit(1) - - input_path = Path(sys.argv[1]) - output_path = Path(sys.argv[2]) - - spec = json.loads(input_path.read_text()) - set_required_fields(spec) - push_required_into_all_of(spec) - - output_path.write_text(json.dumps(spec, indent=2)) - print(f"Preprocessed: {input_path} -> {output_path}") - - -if __name__ == "__main__": - main() diff --git a/scripts/typegen.sh b/scripts/typegen.sh index 8464f45..dd398f4 100755 --- a/scripts/typegen.sh +++ b/scripts/typegen.sh @@ -2,6 +2,14 @@ # # Regenerate Pydantic models from the vendored OpenAPI spec. # +# Uses @devhelm/openapi-tools for preprocessing (shared with all surfaces), +# then runs datamodel-codegen for Pydantic model generation. +# +# Preprocessing resolution order: +# 1. $OPENAPI_TOOLS env var (explicit override) +# 2. Local monorepo sibling (../mini/packages/openapi-tools) +# 3. npx from npm (CI / standalone) +# set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" @@ -16,8 +24,23 @@ if [[ ! -f "$INPUT" ]]; then exit 1 fi -echo "=> Preprocessing OpenAPI spec..." -python3 "$SCRIPT_DIR/preprocess_spec.py" "$INPUT" "$PREPROCESSED" +resolve_openapi_tools() { + if [[ -n "${OPENAPI_TOOLS:-}" ]]; then + echo "$OPENAPI_TOOLS" + return + fi + local local_cli="$ROOT_DIR/../mini/packages/openapi-tools/dist/cli.js" + if [[ -f "$local_cli" ]]; then + echo "node $local_cli" + return + fi + echo "npx --yes --package=@devhelm/openapi-tools devhelm-openapi" +} + +TOOLS_CMD=$(resolve_openapi_tools) + +echo "=> Preprocessing OpenAPI spec (via @devhelm/openapi-tools)..." +$TOOLS_CMD preprocess "$INPUT" "$PREPROCESSED" echo "=> Generating Pydantic models from preprocessed spec..." diff --git a/src/devhelm/__init__.py b/src/devhelm/__init__.py index 52d51cb..a41cec7 100644 --- a/src/devhelm/__init__.py +++ b/src/devhelm/__init__.py @@ -2,6 +2,7 @@ from devhelm._errors import AuthError, DevhelmError from devhelm._pagination import CursorPage, Page +from devhelm._validation import RequestBody from devhelm.client import Devhelm from devhelm.resources.alert_channels import AlertChannels from devhelm.resources.api_keys import ApiKeys @@ -20,13 +21,18 @@ from devhelm.types import ( AcquireDeployLockRequest, AddCustomDomainRequest, + AddIncidentUpdateRequest, AddResourceGroupMemberRequest, AdminAddSubscriberRequest, + AffectedComponentStatus, AlertChannelDto, + AlertDeliveryStatus, ApiKeyCreateResponse, ApiKeyDto, + AssertionSeverity, AssertionTestResultDto, CheckResultDto, + ConfirmationPolicyType, CreateAlertChannelRequest, CreateApiKeyRequest, CreateEnvironmentRequest, @@ -42,32 +48,59 @@ CreateStatusPageRequest, CreateTagRequest, CreateWebhookEndpointRequest, + CustomDomainStatus, DashboardOverviewDto, DeployLockDto, EnvironmentDto, IncidentDetailDto, IncidentDto, + IncidentNewStatus, + IncidentOldStatus, + IncidentSeverity, + IncidentStatus, + IncidentUpdateCreatedBy, + LinkedIncidentStatus, + MembershipStatus, + MemberStatus, + MonitorAssertionSeverity, + MonitorCurrentStatus, MonitorDto, + MonitorDtoType, + MonitorType, MonitorVersionDto, + NotificationDispatchStatus, NotificationPolicyDto, + PublishIncidentStatus, ReorderComponentsRequest, ResolveIncidentRequest, ResourceGroupDto, + ResourceGroupHealthStatus, ResourceGroupMemberDto, SecretDto, ServiceSubscriptionDto, StatusPageBranding, + StatusPageComponentCurrentStatus, StatusPageComponentDto, + StatusPageComponentDtoType, StatusPageComponentGroupDto, + StatusPageComponentType, StatusPageCustomDomainDto, StatusPageDto, StatusPageIncidentComponentDto, + StatusPageIncidentComponentStatus, StatusPageIncidentDto, + StatusPageIncidentStatus, StatusPageIncidentUpdateDto, + StatusPageOverallStatus, StatusPageSubscriberDto, + StatusPageUpdateCreatedBy, + StatusPageUpdateStatus, TagDto, TestChannelResult, + TriggerRuleSeverity, + TriggerRuleType, UpdateAlertChannelRequest, + UpdateAssertionSeverity, UpdateEnvironmentRequest, UpdateMonitorRequest, UpdateNotificationPolicyRequest, @@ -92,6 +125,8 @@ # Pagination "Page", "CursorPage", + # Typing helpers + "RequestBody", # Resource classes "Monitors", "Incidents", @@ -139,6 +174,7 @@ "TestChannelResult", "WebhookTestResult", # Request types + "AddIncidentUpdateRequest", "CreateStatusPageRequest", "UpdateStatusPageRequest", "CreateStatusPageComponentRequest", @@ -172,4 +208,36 @@ "UpdateWebhookEndpointRequest", "CreateApiKeyRequest", "AcquireDeployLockRequest", + # Enum aliases (descriptive names for codegen-numbered enums) + "AffectedComponentStatus", + "AlertDeliveryStatus", + "AssertionSeverity", + "ConfirmationPolicyType", + "CustomDomainStatus", + "IncidentNewStatus", + "IncidentOldStatus", + "IncidentSeverity", + "IncidentStatus", + "IncidentUpdateCreatedBy", + "LinkedIncidentStatus", + "MemberStatus", + "MembershipStatus", + "MonitorAssertionSeverity", + "MonitorCurrentStatus", + "MonitorDtoType", + "MonitorType", + "NotificationDispatchStatus", + "PublishIncidentStatus", + "ResourceGroupHealthStatus", + "StatusPageComponentCurrentStatus", + "StatusPageComponentDtoType", + "StatusPageComponentType", + "StatusPageIncidentComponentStatus", + "StatusPageIncidentStatus", + "StatusPageOverallStatus", + "StatusPageUpdateCreatedBy", + "StatusPageUpdateStatus", + "TriggerRuleSeverity", + "TriggerRuleType", + "UpdateAssertionSeverity", ] diff --git a/src/devhelm/_generated.py b/src/devhelm/_generated.py index 7f09059..6db3fcb 100644 --- a/src/devhelm/_generated.py +++ b/src/devhelm/_generated.py @@ -1,9 +1,9 @@ # generated by datamodel-codegen: # filename: .openapi-preprocessed.json -# timestamp: 2026-04-18T23:23:30+00:00 +# timestamp: 2026-04-20T17:00:30+00:00 from __future__ import annotations -from typing import Annotated, Any +from typing import Annotated, Any, Literal from pydantic import AwareDatetime, BaseModel, EmailStr, Field, RootModel from enum import StrEnum from uuid import UUID @@ -286,6 +286,29 @@ class AlertDeliveryDto(BaseModel): created_at: Annotated[AwareDatetime, Field(alias="createdAt")] +class Type(StrEnum): + api_key = "api_key" + + +class ApiKeyAuthConfig(BaseModel): + type: Literal["api_key"] + header_name: Annotated[ + str, + Field( + alias="headerName", + description="HTTP header name that carries the API key", + min_length=1, + pattern="^[A-Za-z0-9\\-_]+$", + ), + ] + vault_secret_id: Annotated[ + UUID | None, + Field( + alias="vaultSecretId", description="Vault secret ID for the API key value" + ), + ] = None + + class ApiKeyCreateResponse(BaseModel): id: Annotated[int, Field(description="Unique API key identifier")] name: Annotated[ @@ -350,10 +373,6 @@ class ApiKeyDto(BaseModel): ] = None -class AssertionConfig(BaseModel): - type: str - - class Severity(StrEnum): fail = "fail" warn = "warn" @@ -480,7 +499,42 @@ class AuditEventDto(BaseModel): ] -class BodyContainsAssertion(AssertionConfig): +class Type1(StrEnum): + basic = "basic" + + +class BasicAuthConfig(BaseModel): + type: Literal["basic"] + vault_secret_id: Annotated[ + UUID | None, + Field( + alias="vaultSecretId", + description="Vault secret ID holding Basic auth username and password", + ), + ] = None + + +class Type2(StrEnum): + bearer = "bearer" + + +class BearerAuthConfig(BaseModel): + type: Literal["bearer"] + vault_secret_id: Annotated[ + UUID | None, + Field( + alias="vaultSecretId", + description="Vault secret ID holding the bearer token value", + ), + ] = None + + +class Type3(StrEnum): + body_contains = "body_contains" + + +class BodyContainsAssertion(BaseModel): + type: Type3 substring: Annotated[ str, Field( @@ -534,10 +588,6 @@ class ChangeStatusRequest(BaseModel): ] -class ChannelConfig(BaseModel): - channel_type: Annotated[str, Field(alias="channelType")] - - class ChartBucketDto(BaseModel): bucket: Annotated[ AwareDatetime, @@ -580,10 +630,6 @@ class ChartBucketDto(BaseModel): ] = None -class CheckTypeDetailsDto(BaseModel): - check_type: str - - class ComponentImpact(BaseModel): component_id: Annotated[ UUID, Field(alias="componentId", description="Status page component UUID") @@ -645,6 +691,50 @@ class ComponentStatusDto(BaseModel): ] +class ComponentUptimeDayDto(BaseModel): + date: Annotated[ + AwareDatetime, Field(description="Date of the daily bucket (ISO 8601)") + ] + partial_outage_seconds: Annotated[ + int, + Field( + alias="partialOutageSeconds", + description="Seconds of partial outage observed on this day", + ), + ] + major_outage_seconds: Annotated[ + int, + Field( + alias="majorOutageSeconds", + description="Seconds of major outage observed on this day", + ), + ] + degraded_seconds: Annotated[ + int, + Field( + alias="degradedSeconds", + description="Seconds the component spent in degraded performance on this day", + ), + ] + uptime_percentage: Annotated[ + float, + Field( + alias="uptimePercentage", + description="Computed uptime percentage for the day", + ), + ] + events_json: Annotated[ + str | None, + Field( + alias="eventsJson", + description="Incident event references for this day as raw JSON", + ), + ] = None + source: Annotated[ + str, Field(description="Data source: vendor_reported or incident_derived") + ] + + class ComponentUptimeSummaryDto(BaseModel): day: Annotated[ float | None, @@ -667,13 +757,13 @@ class ComponentUptimeSummaryDto(BaseModel): ] -class Type(StrEnum): +class Type4(StrEnum): multi_region = "multi_region" class ConfirmationPolicy(BaseModel): type: Annotated[ - Type, + Type4, Field(description="How incident confirmation is coordinated across regions"), ] min_regions_failing: Annotated[ @@ -731,12 +821,12 @@ class CreateEnvironmentRequest(BaseModel): Field(description="Initial key-value variable pairs for this environment"), ] = None is_default: Annotated[ - bool | None, + bool, Field( alias="isDefault", description="Whether this is the default environment for new monitors", ), - ] = None + ] class RoleOffered(StrEnum): @@ -822,7 +912,7 @@ class CreateManualIncidentRequest(BaseModel): ] = None -class Type1(StrEnum): +class Type5(StrEnum): http = "HTTP" dns = "DNS" mcp_server = "MCP_SERVER" @@ -879,7 +969,7 @@ class CreateStatusPageComponentGroupRequest(BaseModel): ] = None -class Type2(StrEnum): +class Type6(StrEnum): monitor = "MONITOR" group = "GROUP" static = "STATIC" @@ -898,7 +988,7 @@ class CreateStatusPageComponentRequest(BaseModel): ), ] = None type: Annotated[ - Type2, Field(description="Component type: MONITOR, GROUP, or STATIC") + Type6, Field(description="Component type: MONITOR, GROUP, or STATIC") ] monitor_id: Annotated[ UUID | None, @@ -1100,23 +1190,6 @@ class CreateWorkspaceRequest(BaseModel): name: Annotated[str, Field(description="Workspace name", min_length=1)] -class CursorPage(BaseModel): - data: Annotated[list[dict[str, Any]], Field(description="Items on this page")] - next_cursor: Annotated[ - str | None, - Field( - alias="nextCursor", - description="Opaque cursor for the next page; null when there are no more results", - ), - ] = None - has_more: Annotated[ - bool, - Field( - alias="hasMore", description="Whether more results exist beyond this page" - ), - ] - - class DayIncident(BaseModel): id: Annotated[UUID, Field(description="Status page incident UUID")] title: Annotated[str, Field(description="Incident title")] @@ -1280,7 +1353,12 @@ class DeployLockDto(BaseModel): ] -class DiscordChannelConfig(ChannelConfig): +class ChannelType1(StrEnum): + discord = "discord" + + +class DiscordChannelConfig(BaseModel): + channel_type: Annotated[ChannelType1, Field(alias="channelType")] webhook_url: Annotated[ str, Field(alias="webhookUrl", description="Discord webhook URL", min_length=1) ] @@ -1293,7 +1371,40 @@ class DiscordChannelConfig(ChannelConfig): ] = None -class DnsExpectedCnameAssertion(AssertionConfig): +class CheckType(StrEnum): + dns = "dns" + + +class Dns(BaseModel): + check_type: Literal["dns"] + hostname: Annotated[str | None, Field(description="Target hostname")] = None + requested_types: Annotated[ + list[str] | None, + Field(alias="requestedTypes", description="Requested DNS record types"), + ] = None + used_resolver: Annotated[ + str | None, Field(alias="usedResolver", description="Resolver used for lookup") + ] = None + records: Annotated[ + dict[str, list[dict[str, dict[str, Any]]]] | None, + Field(description="Resolved DNS records keyed by record type"), + ] = None + attempts: Annotated[ + list[dict[str, dict[str, Any]]] | None, + Field(description="DNS resolution attempts"), + ] = None + failure_kind: Annotated[ + str | None, + Field(alias="failureKind", description="Kind of DNS failure, if any"), + ] = None + + +class Type7(StrEnum): + dns_expected_cname = "dns_expected_cname" + + +class DnsExpectedCnameAssertion(BaseModel): + type: Type7 value: Annotated[ str, Field( @@ -1303,7 +1414,12 @@ class DnsExpectedCnameAssertion(AssertionConfig): ] -class DnsExpectedIpsAssertion(AssertionConfig): +class Type8(StrEnum): + dns_expected_ips = "dns_expected_ips" + + +class DnsExpectedIpsAssertion(BaseModel): + type: Type8 ips: Annotated[ list[str], Field( @@ -1313,7 +1429,12 @@ class DnsExpectedIpsAssertion(AssertionConfig): ] -class DnsMaxAnswersAssertion(AssertionConfig): +class Type9(StrEnum): + dns_max_answers = "dns_max_answers" + + +class DnsMaxAnswersAssertion(BaseModel): + type: Type9 record_type: Annotated[ str, Field( @@ -1327,7 +1448,12 @@ class DnsMaxAnswersAssertion(AssertionConfig): ] -class DnsMinAnswersAssertion(AssertionConfig): +class Type10(StrEnum): + dns_min_answers = "dns_min_answers" + + +class DnsMinAnswersAssertion(BaseModel): + type: Type10 record_type: Annotated[ str, Field( @@ -1355,7 +1481,40 @@ class RecordType(StrEnum): ptr = "PTR" -class DnsRecordContainsAssertion(AssertionConfig): +class DnsMonitorConfig(BaseModel): + hostname: Annotated[str, Field(description="Domain name to resolve", min_length=1)] + record_types: Annotated[ + list[RecordType] | None, + Field( + alias="recordTypes", + description="DNS record types to query: A, AAAA, CNAME, MX, NS, TXT, SRV, SOA, CAA, PTR", + ), + ] = None + nameservers: Annotated[ + list[str] | None, + Field( + description="Custom nameservers to query (uses system defaults if omitted)" + ), + ] = None + timeout_ms: Annotated[ + int | None, + Field(alias="timeoutMs", description="Per-query timeout in milliseconds"), + ] = None + total_timeout_ms: Annotated[ + int | None, + Field( + alias="totalTimeoutMs", + description="Total timeout for all queries in milliseconds", + ), + ] = None + + +class Type11(StrEnum): + dns_record_contains = "dns_record_contains" + + +class DnsRecordContainsAssertion(BaseModel): + type: Type11 record_type: Annotated[ str, Field( @@ -1373,7 +1532,12 @@ class DnsRecordContainsAssertion(AssertionConfig): ] -class DnsRecordEqualsAssertion(AssertionConfig): +class Type12(StrEnum): + dns_record_equals = "dns_record_equals" + + +class DnsRecordEqualsAssertion(BaseModel): + type: Type12 record_type: Annotated[ str, Field( @@ -1388,11 +1552,20 @@ class DnsRecordEqualsAssertion(AssertionConfig): ] -class DnsResolvesAssertion(AssertionConfig): - pass +class Type13(StrEnum): + dns_resolves = "dns_resolves" + + +class DnsResolvesAssertion(BaseModel): + type: Type13 -class DnsResponseTimeAssertion(AssertionConfig): +class Type14(StrEnum): + dns_response_time = "dns_response_time" + + +class DnsResponseTimeAssertion(BaseModel): + type: Type14 max_ms: Annotated[ int, Field( @@ -1402,7 +1575,12 @@ class DnsResponseTimeAssertion(AssertionConfig): ] -class DnsResponseTimeWarnAssertion(AssertionConfig): +class Type15(StrEnum): + dns_response_time_warn = "dns_response_time_warn" + + +class DnsResponseTimeWarnAssertion(BaseModel): + type: Type15 warn_ms: Annotated[ int, Field( @@ -1412,7 +1590,12 @@ class DnsResponseTimeWarnAssertion(AssertionConfig): ] -class DnsTtlHighAssertion(AssertionConfig): +class Type16(StrEnum): + dns_ttl_high = "dns_ttl_high" + + +class DnsTtlHighAssertion(BaseModel): + type: Type16 max_ttl: Annotated[ int, Field( @@ -1422,7 +1605,12 @@ class DnsTtlHighAssertion(AssertionConfig): ] -class DnsTtlLowAssertion(AssertionConfig): +class Type17(StrEnum): + dns_ttl_low = "dns_ttl_low" + + +class DnsTtlLowAssertion(BaseModel): + type: Type17 min_ttl: Annotated[ int, Field( @@ -1432,7 +1620,12 @@ class DnsTtlLowAssertion(AssertionConfig): ] -class DnsTxtContainsAssertion(AssertionConfig): +class Type18(StrEnum): + dns_txt_contains = "dns_txt_contains" + + +class DnsTxtContainsAssertion(BaseModel): + type: Type18 substring: Annotated[ str, Field( @@ -1442,7 +1635,12 @@ class DnsTxtContainsAssertion(AssertionConfig): ] -class EmailChannelConfig(ChannelConfig): +class ChannelType2(StrEnum): + email = "email" + + +class EmailChannelConfig(BaseModel): + channel_type: Annotated[ChannelType2, Field(alias="channelType")] recipients: Annotated[ list[EmailStr], Field(description="Email addresses to send notifications to", min_length=1), @@ -1508,6 +1706,30 @@ class EnvironmentDto(BaseModel): ] +class ErrorResponse(BaseModel): + status: Annotated[ + int, + Field( + description="HTTP status code (mirrors the response status line)", + examples=[404], + ), + ] + message: Annotated[ + str, + Field( + description="Human-readable error message; safe to surface to end users", + examples=["Monitor not found"], + ), + ] + timestamp: Annotated[ + int, + Field( + description="Server time when the error was produced (epoch milliseconds)", + examples=[1737302400000], + ), + ] + + class EscalationStep(BaseModel): delay_minutes: Annotated[ int, @@ -1562,6 +1784,33 @@ class GroupComponentOrder(BaseModel): ] +class Type19(StrEnum): + header = "header" + + +class HeaderAuthConfig(BaseModel): + type: Literal["header"] + header_name: Annotated[ + str, + Field( + alias="headerName", + description="Custom HTTP header name for the secret value", + min_length=1, + pattern="^[A-Za-z0-9\\-_]+$", + ), + ] + vault_secret_id: Annotated[ + UUID | None, + Field( + alias="vaultSecretId", description="Vault secret ID for the header value" + ), + ] = None + + +class Type20(StrEnum): + header_value = "header_value" + + class Operator(StrEnum): equals = "equals" contains = "contains" @@ -1571,7 +1820,8 @@ class Operator(StrEnum): range = "range" -class HeaderValueAssertion(AssertionConfig): +class HeaderValueAssertion(BaseModel): + type: Type20 header_name: Annotated[ str, Field( @@ -1591,7 +1841,12 @@ class HeaderValueAssertion(AssertionConfig): ] -class HeartbeatIntervalDriftAssertion(AssertionConfig): +class Type21(StrEnum): + heartbeat_interval_drift = "heartbeat_interval_drift" + + +class HeartbeatIntervalDriftAssertion(BaseModel): + type: Type21 max_deviation_percent: Annotated[ int, Field( @@ -1603,7 +1858,12 @@ class HeartbeatIntervalDriftAssertion(AssertionConfig): ] -class HeartbeatMaxIntervalAssertion(AssertionConfig): +class Type22(StrEnum): + heartbeat_max_interval = "heartbeat_max_interval" + + +class HeartbeatMaxIntervalAssertion(BaseModel): + type: Type22 max_seconds: Annotated[ int, Field( @@ -1614,21 +1874,75 @@ class HeartbeatMaxIntervalAssertion(AssertionConfig): ] -class HeartbeatPayloadContainsAssertion(AssertionConfig): - path: Annotated[ - str, +class HeartbeatMonitorConfig(BaseModel): + expected_interval: Annotated[ + int, Field( - description="JSONPath expression into the heartbeat ping JSON payload", - min_length=1, + alias="expectedInterval", + description="Expected heartbeat interval in seconds", + ge=1, + le=86400, ), ] - value: Annotated[ - str, Field(description="Expected value to compare against at that path") + grace_period: Annotated[ + int, + Field( + alias="gracePeriod", + description="Grace period in seconds before marking as down", + ge=1, + ), + ] + + +class Type23(StrEnum): + heartbeat_payload_contains = "heartbeat_payload_contains" + + +class HeartbeatPayloadContainsAssertion(BaseModel): + type: Type23 + path: Annotated[ + str, + Field( + description="JSONPath expression into the heartbeat ping JSON payload", + min_length=1, + ), + ] + value: Annotated[ + str, Field(description="Expected value to compare against at that path") + ] + + +class HeartbeatPingResponse(BaseModel): + ok: Annotated[ + bool, Field(description="Always true on a 2xx response", examples=[True]) ] -class HeartbeatReceivedAssertion(AssertionConfig): - pass +class Type24(StrEnum): + heartbeat_received = "heartbeat_received" + + +class HeartbeatReceivedAssertion(BaseModel): + type: Type24 + + +class CheckType1(StrEnum): + http = "http" + + +class Http(BaseModel): + check_type: Literal["http"] + timing: Annotated[ + dict[str, dict[str, Any]] | None, + Field(description="Request phase timing breakdown"), + ] = None + body_truncated: Annotated[ + bool | None, + Field( + alias="bodyTruncated", + description="Whether the response body was truncated before storage", + ), + ] = None class Method(StrEnum): @@ -1640,7 +1954,103 @@ class Method(StrEnum): head = "HEAD" -class IcmpPacketLossAssertion(AssertionConfig): +class HttpMonitorConfig(BaseModel): + url: Annotated[ + str, Field(description="Target URL to send requests to", min_length=1) + ] + method: Annotated[ + Method, Field(description="HTTP method: GET, POST, PUT, PATCH, DELETE, or HEAD") + ] + custom_headers: Annotated[ + dict[str, str] | None, + Field( + alias="customHeaders", + description="Additional HTTP headers to include in requests", + ), + ] = None + request_body: Annotated[ + str | None, + Field( + alias="requestBody", + description="Request body content for POST/PUT/PATCH methods", + ), + ] = None + content_type: Annotated[ + str | None, + Field( + alias="contentType", + description="Content-Type header value for the request body", + ), + ] = None + verify_tls: Annotated[ + bool | None, + Field( + alias="verifyTls", + description="Whether to verify TLS certificates (default: true)", + ), + ] = None + + +class CheckType2(StrEnum): + icmp = "icmp" + + +class Icmp(BaseModel): + check_type: Literal["icmp"] + host: Annotated[str, Field(description="Target host", examples=["1.1.1.1"])] + packets_sent: Annotated[ + int | None, + Field(alias="packetsSent", description="Number of ICMP packets sent"), + ] = None + packets_received: Annotated[ + int | None, + Field(alias="packetsReceived", description="Number of ICMP packets received"), + ] = None + packet_loss: Annotated[ + float | None, + Field(alias="packetLoss", description="Packet loss percentage", examples=[0]), + ] = None + avg_rtt_ms: Annotated[ + float | None, + Field(alias="avgRttMs", description="Average round-trip time in ms"), + ] = None + min_rtt_ms: Annotated[ + float | None, + Field(alias="minRttMs", description="Minimum round-trip time in ms"), + ] = None + max_rtt_ms: Annotated[ + float | None, + Field(alias="maxRttMs", description="Maximum round-trip time in ms"), + ] = None + jitter_ms: Annotated[ + float | None, Field(alias="jitterMs", description="Jitter in ms") + ] = None + + +class IcmpMonitorConfig(BaseModel): + host: Annotated[ + str, Field(description="Target hostname or IP address to ping", min_length=1) + ] + packet_count: Annotated[ + int | None, + Field( + alias="packetCount", + description="Number of ICMP packets to send", + ge=1, + le=20, + ), + ] = None + timeout_ms: Annotated[ + int | None, Field(alias="timeoutMs", description="Ping timeout in milliseconds") + ] = None + + +class Type25(StrEnum): + icmp_packet_loss = "icmp_packet_loss" + + +class IcmpPacketLossAssertion(BaseModel): + type: Type25 max_percent: Annotated[ float, Field( @@ -1652,11 +2062,20 @@ class IcmpPacketLossAssertion(AssertionConfig): ] -class IcmpReachableAssertion(AssertionConfig): - pass +class Type26(StrEnum): + icmp_reachable = "icmp_reachable" + + +class IcmpReachableAssertion(BaseModel): + type: Type26 + + +class Type27(StrEnum): + icmp_response_time = "icmp_response_time" -class IcmpResponseTimeAssertion(AssertionConfig): +class IcmpResponseTimeAssertion(BaseModel): + type: Type27 max_ms: Annotated[ int, Field( @@ -1666,7 +2085,12 @@ class IcmpResponseTimeAssertion(AssertionConfig): ] -class IcmpResponseTimeWarnAssertion(AssertionConfig): +class Type28(StrEnum): + icmp_response_time_warn = "icmp_response_time_warn" + + +class IcmpResponseTimeWarnAssertion(BaseModel): + type: Type28 warn_ms: Annotated[ int, Field( @@ -1845,28 +2269,28 @@ class IncidentDto(BaseModel): str | None, Field( alias="monitorName", - description="Name of the associated monitor; populated on list responses", + description="Name of the associated monitor; populated on list responses. Omitted from JSON (undefined to SDKs) on detail responses, treat missing as null.", ), ] = None service_name: Annotated[ str | None, Field( alias="serviceName", - description="Name of the associated service; populated on list responses", + description="Name of the associated service; populated on list responses. Omitted from JSON (undefined to SDKs) on detail responses, treat missing as null.", ), ] = None service_slug: Annotated[ str | None, Field( alias="serviceSlug", - description="Slug of the associated service; populated on list responses", + description="Slug of the associated service; populated on list responses. Omitted from JSON (undefined to SDKs) on detail responses, treat missing as null.", ), ] = None monitor_type: Annotated[ str | None, Field( alias="monitorType", - description="Type of the associated monitor; populated on list responses", + description="Type of the associated monitor; populated on list responses. Omitted from JSON (undefined to SDKs) on detail responses, treat missing as null.", ), ] = None resource_group_id: Annotated[ @@ -1880,24 +2304,89 @@ class IncidentDto(BaseModel): str | None, Field( alias="resourceGroupName", - description="Name of the resource group; populated on list responses", + description="Name of the resource group; populated on list responses. Omitted from JSON (undefined to SDKs) on detail responses, treat missing as null.", ), ] = None class IncidentFilterParams(BaseModel): - status: Status6 | None = None - severity: Severity3 | None = None - source: Source | None = None - monitor_id: Annotated[UUID | None, Field(alias="monitorId")] = None - service_id: Annotated[UUID | None, Field(alias="serviceId")] = None - resource_group_id: Annotated[UUID | None, Field(alias="resourceGroupId")] = None - tag_id: Annotated[UUID | None, Field(alias="tagId")] = None - environment_id: Annotated[UUID | None, Field(alias="environmentId")] = None - started_from: Annotated[AwareDatetime | None, Field(alias="startedFrom")] = None - started_to: Annotated[AwareDatetime | None, Field(alias="startedTo")] = None - page: Annotated[int, Field(ge=0)] - size: Annotated[int, Field(ge=1, le=200)] + status: Annotated[ + Status6 | None, + Field( + description="Filter by incident lifecycle status; null returns every status" + ), + ] = None + severity: Annotated[ + Severity3 | None, + Field(description="Filter by severity; null returns every severity"), + ] = None + source: Annotated[ + Source | None, + Field( + description="Filter by where the incident originated (auto, manual, third-party)" + ), + ] = None + monitor_id: Annotated[ + UUID | None, + Field( + alias="monitorId", + description="Only return incidents tied to this monitor ID", + ), + ] = None + service_id: Annotated[ + UUID | None, + Field( + alias="serviceId", + description="Only return incidents tied to this service ID (third-party services)", + ), + ] = None + resource_group_id: Annotated[ + UUID | None, + Field( + alias="resourceGroupId", + description="Only return incidents whose monitor belongs to this resource group", + ), + ] = None + tag_id: Annotated[ + UUID | None, + Field( + alias="tagId", + description="Only return incidents whose monitor carries this tag", + ), + ] = None + environment_id: Annotated[ + UUID | None, + Field( + alias="environmentId", + description="Only return incidents whose monitor lives in this environment", + ), + ] = None + started_from: Annotated[ + AwareDatetime | None, + Field( + alias="startedFrom", + description="Earliest startedAt to include (inclusive, ISO 8601)", + ), + ] = None + started_to: Annotated[ + AwareDatetime | None, + Field( + alias="startedTo", + description="Latest startedAt to include (inclusive, ISO 8601)", + ), + ] = None + page: Annotated[ + int, Field(description="Zero-based page index (default: 0)", examples=[0], ge=0) + ] + size: Annotated[ + int, + Field( + description="Number of incidents per page (1–200, default: 10)", + examples=[10], + ge=1, + le=200, + ), + ] class IncidentRef(BaseModel): @@ -1988,7 +2477,12 @@ class InviteDto(BaseModel): ] = None -class JsonPathAssertion(AssertionConfig): +class Type29(StrEnum): + json_path = "json_path" + + +class JsonPathAssertion(BaseModel): + type: Type29 path: Annotated[ str, Field( @@ -2135,11 +2629,20 @@ class MatchRule(BaseModel): ] = None -class McpConnectsAssertion(AssertionConfig): - pass +class Type30(StrEnum): + mcp_connects = "mcp_connects" + + +class McpConnectsAssertion(BaseModel): + type: Type30 + + +class Type31(StrEnum): + mcp_has_capability = "mcp_has_capability" -class McpHasCapabilityAssertion(AssertionConfig): +class McpHasCapabilityAssertion(BaseModel): + type: Type31 capability: Annotated[ str, Field( @@ -2149,13 +2652,23 @@ class McpHasCapabilityAssertion(AssertionConfig): ] -class McpMinToolsAssertion(AssertionConfig): +class Type32(StrEnum): + mcp_min_tools = "mcp_min_tools" + + +class McpMinToolsAssertion(BaseModel): + type: Type32 min: Annotated[ int, Field(description="Minimum number of tools the server must expose") ] -class McpProtocolVersionAssertion(AssertionConfig): +class Type33(StrEnum): + mcp_protocol_version = "mcp_protocol_version" + + +class McpProtocolVersionAssertion(BaseModel): + type: Type33 version: Annotated[ str, Field( @@ -2165,7 +2678,12 @@ class McpProtocolVersionAssertion(AssertionConfig): ] -class McpResponseTimeAssertion(AssertionConfig): +class Type34(StrEnum): + mcp_response_time = "mcp_response_time" + + +class McpResponseTimeAssertion(BaseModel): + type: Type34 max_ms: Annotated[ int, Field( @@ -2175,7 +2693,12 @@ class McpResponseTimeAssertion(AssertionConfig): ] -class McpResponseTimeWarnAssertion(AssertionConfig): +class Type35(StrEnum): + mcp_response_time_warn = "mcp_response_time_warn" + + +class McpResponseTimeWarnAssertion(BaseModel): + type: Type35 warn_ms: Annotated[ int, Field( @@ -2185,7 +2708,53 @@ class McpResponseTimeWarnAssertion(AssertionConfig): ] -class McpToolAvailableAssertion(AssertionConfig): +class CheckType3(StrEnum): + mcp_server = "mcp_server" + + +class McpServer(BaseModel): + check_type: Literal["mcp_server"] + url: Annotated[str | None, Field(description="MCP server URL")] = None + protocol_version: Annotated[ + str | None, Field(alias="protocolVersion", description="MCP protocol version") + ] = None + server_info: Annotated[ + dict[str, dict[str, Any]] | None, + Field(alias="serverInfo", description="MCP server info (name, version, etc.)"), + ] = None + tool_count: Annotated[ + int | None, Field(alias="toolCount", description="Number of tools exposed") + ] = None + resource_count: Annotated[ + int | None, + Field(alias="resourceCount", description="Number of resources exposed"), + ] = None + prompt_count: Annotated[ + int | None, Field(alias="promptCount", description="Number of prompts exposed") + ] = None + + +class McpServerMonitorConfig(BaseModel): + command: Annotated[ + str, + Field(description="Command to execute to start the MCP server", min_length=1), + ] + args: Annotated[ + list[str] | None, + Field(description="Command-line arguments for the MCP server process"), + ] = None + env: Annotated[ + dict[str, str] | None, + Field(description="Environment variables to pass to the MCP server process"), + ] = None + + +class Type36(StrEnum): + mcp_tool_available = "mcp_tool_available" + + +class McpToolAvailableAssertion(BaseModel): + type: Type36 tool_name: Annotated[ str, Field( @@ -2196,7 +2765,12 @@ class McpToolAvailableAssertion(AssertionConfig): ] -class McpToolCountChangedAssertion(AssertionConfig): +class Type37(StrEnum): + mcp_tool_count_changed = "mcp_tool_count_changed" + + +class McpToolCountChangedAssertion(BaseModel): + type: Type37 expected_count: Annotated[ int, Field( @@ -2247,8 +2821,16 @@ class Severity6(StrEnum): warn = "warn" -class MonitorAuthConfig(BaseModel): - type: str +class MonitorAuthConfig( + RootModel[BearerAuthConfig | BasicAuthConfig | HeaderAuthConfig | ApiKeyAuthConfig] +): + root: Annotated[ + BearerAuthConfig | BasicAuthConfig | HeaderAuthConfig | ApiKeyAuthConfig, + Field( + description="New authentication configuration (full replacement)", + discriminator="type", + ), + ] class AuthType(StrEnum): @@ -2258,11 +2840,14 @@ class AuthType(StrEnum): api_key = "api_key" -class MonitorConfig(BaseModel): - pass +class MonitorAuthDto(BaseModel): + id: UUID + monitor_id: Annotated[UUID, Field(alias="monitorId")] + auth_type: Annotated[AuthType, Field(alias="authType")] + config: ApiKeyAuthConfig | BasicAuthConfig | BearerAuthConfig | HeaderAuthConfig -class Type3(StrEnum): +class Type38(StrEnum): http = "HTTP" dns = "DNS" mcp_server = "MCP_SERVER" @@ -2469,7 +3054,12 @@ class NotificationDto(BaseModel): ] -class OpsGenieChannelConfig(ChannelConfig): +class ChannelType3(StrEnum): + opsgenie = "opsgenie" + + +class OpsGenieChannelConfig(BaseModel): + channel_type: Annotated[ChannelType3, Field(alias="channelType")] api_key: Annotated[ str, Field( @@ -2509,7 +3099,12 @@ class Pageable(BaseModel): sort: list[str] -class PagerDutyChannelConfig(ChannelConfig): +class ChannelType4(StrEnum): + pagerduty = "pagerduty" + + +class PagerDutyChannelConfig(BaseModel): + channel_type: Annotated[ChannelType4, Field(alias="channelType")] routing_key: Annotated[ str, Field( @@ -2691,7 +3286,12 @@ class RecoveryPolicy(BaseModel): ] -class RedirectCountAssertion(AssertionConfig): +class Type40(StrEnum): + redirect_count = "redirect_count" + + +class RedirectCountAssertion(BaseModel): + type: Type40 max_count: Annotated[ int, Field( @@ -2701,7 +3301,12 @@ class RedirectCountAssertion(AssertionConfig): ] -class RedirectTargetAssertion(AssertionConfig): +class Type41(StrEnum): + redirect_target = "redirect_target" + + +class RedirectTargetAssertion(BaseModel): + type: Type41 expected: Annotated[ str, Field(description="Expected final URL after following redirects", min_length=1), @@ -2714,7 +3319,12 @@ class RedirectTargetAssertion(AssertionConfig): ] -class RegexBodyAssertion(AssertionConfig): +class Type42(StrEnum): + regex_body = "regex_body" + + +class RegexBodyAssertion(BaseModel): + type: Type42 pattern: Annotated[ str, Field( @@ -2789,9 +3399,10 @@ class ReorderPageLayoutRequest(BaseModel): class ResolveIncidentRequest(BaseModel): body: Annotated[ - str, Field(description="Optional resolution message or post-mortem notes") - ] - + str | None, + Field(description="Optional resolution message or post-mortem notes"), + ] = None + class Status12(StrEnum): operational = "operational" @@ -2947,7 +3558,12 @@ class ResourceGroupMemberDto(BaseModel): ] = None -class ResponseSizeAssertion(AssertionConfig): +class Type43(StrEnum): + response_size = "response_size" + + +class ResponseSizeAssertion(BaseModel): + type: Type43 max_bytes: Annotated[ int, Field( @@ -2957,7 +3573,12 @@ class ResponseSizeAssertion(AssertionConfig): ] -class ResponseTimeAssertion(AssertionConfig): +class Type44(StrEnum): + response_time = "response_time" + + +class ResponseTimeAssertion(BaseModel): + type: Type44 threshold_ms: Annotated[ int, Field( @@ -2967,7 +3588,12 @@ class ResponseTimeAssertion(AssertionConfig): ] -class ResponseTimeWarnAssertion(AssertionConfig): +class Type45(StrEnum): + response_time_warn = "response_time_warn" + + +class ResponseTimeWarnAssertion(BaseModel): + type: Type45 warn_ms: Annotated[ int, Field( @@ -3264,6 +3890,13 @@ class ServiceDayDetailDto(BaseModel): description="Sum of major outage seconds across all leaf components", ), ] + total_degraded_seconds: Annotated[ + int, + Field( + alias="totalDegradedSeconds", + description="Sum of degraded performance seconds across all leaf components", + ), + ] components: Annotated[ list[ComponentImpact], Field( @@ -3532,6 +4165,10 @@ class SetAlertChannelsRequest(BaseModel): ] +class SetMonitorAuthRequest(BaseModel): + config: ApiKeyAuthConfig | BasicAuthConfig | BearerAuthConfig | HeaderAuthConfig + + class SingleValueResponseAlertChannelDto(BaseModel): data: AlertChannelDto @@ -3576,6 +4213,10 @@ class SingleValueResponseMaintenanceWindowDto(BaseModel): data: MaintenanceWindowDto +class SingleValueResponseMonitorAuthDto(BaseModel): + data: MonitorAuthDto + + class SingleValueResponseMonitorTestResultDto(BaseModel): data: MonitorTestResultDto @@ -3624,7 +4265,12 @@ class SingleValueResponseString(BaseModel): data: str -class SlackChannelConfig(ChannelConfig): +class ChannelType5(StrEnum): + slack = "slack" + + +class SlackChannelConfig(BaseModel): + channel_type: Annotated[ChannelType5, Field(alias="channelType")] webhook_url: Annotated[ str, Field( @@ -3640,7 +4286,12 @@ class SlackChannelConfig(ChannelConfig): ] = None -class SslExpiryAssertion(AssertionConfig): +class Type46(StrEnum): + ssl_expiry = "ssl_expiry" + + +class SslExpiryAssertion(BaseModel): + type: Type46 min_days_remaining: Annotated[ int, Field( @@ -3650,7 +4301,12 @@ class SslExpiryAssertion(AssertionConfig): ] -class StatusCodeAssertion(AssertionConfig): +class Type47(StrEnum): + status_code = "status_code" + + +class StatusCodeAssertion(BaseModel): + type: Type47 expected: Annotated[ str, Field( @@ -3791,7 +4447,7 @@ class StatusPageBranding(BaseModel): ] = None -class Type5(StrEnum): +class Type48(StrEnum): monitor = "MONITOR" group = "GROUP" static = "STATIC" @@ -3811,7 +4467,7 @@ class StatusPageComponentDto(BaseModel): group_id: Annotated[UUID | None, Field(alias="groupId")] = None name: Annotated[str, Field(min_length=1)] description: str | None = None - type: Type5 + type: Type48 monitor_id: Annotated[UUID | None, Field(alias="monitorId")] = None resource_group_id: Annotated[UUID | None, Field(alias="resourceGroupId")] = None current_status: Annotated[CurrentStatus1, Field(alias="currentStatus")] @@ -3837,6 +4493,38 @@ class StatusPageComponentGroupDto(BaseModel): updated_at: Annotated[AwareDatetime, Field(alias="updatedAt")] +class StatusPageComponentUptimeDayDto(BaseModel): + date: Annotated[ + AwareDatetime, + Field(description="Start-of-day timestamp for this bucket (UTC midnight)"), + ] + partial_outage_seconds: Annotated[ + int, + Field( + alias="partialOutageSeconds", + description="Seconds of partial outage on this day", + ), + ] + major_outage_seconds: Annotated[ + int, + Field( + alias="majorOutageSeconds", + description="Seconds of major outage on this day", + ), + ] + uptime_percentage: Annotated[ + float, + Field( + alias="uptimePercentage", + description="Computed uptime percentage using weighted formula", + ), + ] + incidents: Annotated[ + list[IncidentRef] | None, + Field(description="Incidents that overlapped this day"), + ] = None + + class Status14(StrEnum): pending_verification = "PENDING_VERIFICATION" verification_failed = "VERIFICATION_FAILED" @@ -3983,6 +4671,14 @@ class TableValueResultCategoryDto(BaseModel): total_pages: Annotated[int | None, Field(alias="totalPages")] = None +class TableValueResultComponentUptimeDayDto(BaseModel): + data: list[ComponentUptimeDayDto] + has_next: Annotated[bool, Field(alias="hasNext")] + has_prev: Annotated[bool, Field(alias="hasPrev")] + total_elements: Annotated[int | None, Field(alias="totalElements")] = None + total_pages: Annotated[int | None, Field(alias="totalPages")] = None + + class TableValueResultDeliveryAttemptDto(BaseModel): data: list[DeliveryAttemptDto] has_next: Annotated[bool, Field(alias="hasNext")] @@ -4103,6 +4799,14 @@ class TableValueResultStatusPageComponentGroupDto(BaseModel): total_pages: Annotated[int | None, Field(alias="totalPages")] = None +class TableValueResultStatusPageComponentUptimeDayDto(BaseModel): + data: list[StatusPageComponentUptimeDayDto] + has_next: Annotated[bool, Field(alias="hasNext")] + has_prev: Annotated[bool, Field(alias="hasPrev")] + total_elements: Annotated[int | None, Field(alias="totalElements")] = None + total_pages: Annotated[int | None, Field(alias="totalPages")] = None + + class TableValueResultStatusPageCustomDomainDto(BaseModel): data: list[StatusPageCustomDomainDto] has_next: Annotated[bool, Field(alias="hasNext")] @@ -4150,11 +4854,28 @@ class TagDto(BaseModel): ] -class TcpConnectsAssertion(AssertionConfig): - pass +class CheckType4(StrEnum): + tcp = "tcp" + + +class Tcp(BaseModel): + check_type: Literal["tcp"] + host: Annotated[str, Field(description="Target host", examples=["db.example.com"])] + port: Annotated[int, Field(description="Target port", examples=[5432])] + connected: Annotated[ + bool, Field(description="Whether a TCP connection was established") + ] + + +class Type49(StrEnum): + tcp_connects = "tcp_connects" + +class TcpConnectsAssertion(BaseModel): + type: Type49 -class TcpMonitorConfig(MonitorConfig): + +class TcpMonitorConfig(BaseModel): host: Annotated[ str, Field(description="Target hostname or IP address", min_length=1) ] @@ -4165,7 +4886,12 @@ class TcpMonitorConfig(MonitorConfig): ] = None -class TcpResponseTimeAssertion(AssertionConfig): +class Type50(StrEnum): + tcp_response_time = "tcp_response_time" + + +class TcpResponseTimeAssertion(BaseModel): + type: Type50 max_ms: Annotated[ int, Field( @@ -4175,7 +4901,12 @@ class TcpResponseTimeAssertion(AssertionConfig): ] -class TcpResponseTimeWarnAssertion(AssertionConfig): +class Type51(StrEnum): + tcp_response_time_warn = "tcp_response_time_warn" + + +class TcpResponseTimeWarnAssertion(BaseModel): + type: Type51 warn_ms: Annotated[ int, Field( @@ -4185,7 +4916,12 @@ class TcpResponseTimeWarnAssertion(AssertionConfig): ] -class TeamsChannelConfig(ChannelConfig): +class ChannelType6(StrEnum): + teams = "teams" + + +class TeamsChannelConfig(BaseModel): + channel_type: Annotated[ChannelType6, Field(alias="channelType")] webhook_url: Annotated[ str, Field( @@ -4341,7 +5077,7 @@ class TlsInfoDto(BaseModel): ] = None -class Type6(StrEnum): +class Type52(StrEnum): consecutive_failures = "consecutive_failures" failures_in_window = "failures_in_window" response_time = "response_time" @@ -4366,7 +5102,7 @@ class AggregationType(StrEnum): class TriggerRule(BaseModel): type: Annotated[ - Type6, + Type52, Field( description="Condition that opens or escalates an incident from check results" ), @@ -4550,6 +5286,10 @@ class UpdateMaintenanceWindowRequest(BaseModel): ] = None +class UpdateMonitorAuthRequest(BaseModel): + config: ApiKeyAuthConfig | BasicAuthConfig | BearerAuthConfig | HeaderAuthConfig + + class UpdateOrgDetailsRequest(BaseModel): name: Annotated[ str, @@ -4981,7 +5721,12 @@ class UptimeDto(BaseModel): ] = None -class WebhookChannelConfig(ChannelConfig): +class ChannelType7(StrEnum): + webhook = "webhook" + + +class WebhookChannelConfig(BaseModel): + channel_type: Annotated[ChannelType7, Field(alias="channelType")] url: Annotated[ str, Field( @@ -5143,24 +5888,6 @@ class AddMonitorTagsRequest(BaseModel): ] = None -class ApiKeyAuthConfig(MonitorAuthConfig): - header_name: Annotated[ - str, - Field( - alias="headerName", - description="HTTP header name that carries the API key", - min_length=1, - pattern="^[A-Za-z0-9\\-_]+$", - ), - ] - vault_secret_id: Annotated[ - UUID | None, - Field( - alias="vaultSecretId", description="Vault secret ID for the API key value" - ), - ] = None - - class AuthMeResponse(BaseModel): key: KeyInfo organization: OrgInfo @@ -5168,24 +5895,13 @@ class AuthMeResponse(BaseModel): rate_limits: Annotated[RateLimitInfo, Field(alias="rateLimits")] -class BasicAuthConfig(MonitorAuthConfig): - vault_secret_id: Annotated[ - UUID | None, - Field( - alias="vaultSecretId", - description="Vault secret ID holding Basic auth username and password", - ), - ] = None - - -class BearerAuthConfig(MonitorAuthConfig): - vault_secret_id: Annotated[ - UUID | None, +class BatchComponentUptimeDto(BaseModel): + components: Annotated[ + dict[str, list[ComponentUptimeDayDto]], Field( - alias="vaultSecretId", - description="Vault secret ID holding the bearer token value", + description="Map of component ID → list of per-day uptime entries (oldest → newest)" ), - ] = None + ] class BulkMonitorActionRequest(BaseModel): @@ -5200,9 +5916,7 @@ class BulkMonitorActionRequest(BaseModel): ] action: Annotated[ Action, - Field( - description="Action to perform: PAUSE, RESUME, DELETE, ADD_TAG, REMOVE_TAG" - ), + Field(description="Action to apply to every monitor in the bulk request"), ] tag_ids: Annotated[ list[UUID] | None, @@ -5232,206 +5946,150 @@ class BulkMonitorActionResult(BaseModel): ] -class CheckResultDetailsDto(BaseModel): - status_code: Annotated[ - int | None, +class CheckTypeDetailsDto(RootModel[Http | Tcp | Icmp | Dns | McpServer]): + root: Annotated[ + Http | Tcp | Icmp | Dns | McpServer, Field( - alias="statusCode", - description="HTTP status code of the response", - examples=[200], + description="Check-type-specific details — polymorphic by check_type discriminator", + discriminator="check_type", ), - ] = None - response_headers: Annotated[ - dict[str, list[str]] | None, - Field(alias="responseHeaders", description="HTTP response headers"), - ] = None - response_body_snapshot: Annotated[ - str | None, + ] + + +class CreateAlertChannelRequest(BaseModel): + name: Annotated[ + str, Field( - alias="responseBodySnapshot", - description="Raw response body snapshot (may be HTML, XML, JSON, or plain text)", + description="Human-readable name for this alert channel", + max_length=255, + min_length=0, ), - ] = None - assertion_results: Annotated[ - list[AssertionResultDto] | None, + ] + config: ( + DiscordChannelConfig + | EmailChannelConfig + | OpsGenieChannelConfig + | PagerDutyChannelConfig + | SlackChannelConfig + | TeamsChannelConfig + | WebhookChannelConfig + ) + + +class CreateAssertionRequest(BaseModel): + config: ( + BodyContainsAssertion + | DnsExpectedCnameAssertion + | DnsExpectedIpsAssertion + | DnsMaxAnswersAssertion + | DnsMinAnswersAssertion + | DnsRecordContainsAssertion + | DnsRecordEqualsAssertion + | DnsResolvesAssertion + | DnsResponseTimeAssertion + | DnsResponseTimeWarnAssertion + | DnsTtlHighAssertion + | DnsTtlLowAssertion + | DnsTxtContainsAssertion + | HeaderValueAssertion + | HeartbeatIntervalDriftAssertion + | HeartbeatMaxIntervalAssertion + | HeartbeatPayloadContainsAssertion + | HeartbeatReceivedAssertion + | IcmpPacketLossAssertion + | IcmpReachableAssertion + | IcmpResponseTimeAssertion + | IcmpResponseTimeWarnAssertion + | JsonPathAssertion + | McpConnectsAssertion + | McpHasCapabilityAssertion + | McpMinToolsAssertion + | McpProtocolVersionAssertion + | McpResponseTimeAssertion + | McpResponseTimeWarnAssertion + | McpToolAvailableAssertion + | McpToolCountChangedAssertion + | RedirectCountAssertion + | RedirectTargetAssertion + | RegexBodyAssertion + | ResponseSizeAssertion + | ResponseTimeAssertion + | ResponseTimeWarnAssertion + | SslExpiryAssertion + | StatusCodeAssertion + | TcpConnectsAssertion + | TcpResponseTimeAssertion + | TcpResponseTimeWarnAssertion + ) + severity: Annotated[ + Severity | None, Field( - alias="assertionResults", - description="Individual assertion evaluation results", + description="Outcome severity: FAIL (fails the check) or WARN (warns without failing, default: FAIL)" ), ] = None - tls_info: Annotated[TlsInfoDto | None, Field(alias="tlsInfo")] = None - redirect_count: Annotated[ + + +class CreateMonitorRequest(BaseModel): + name: Annotated[ + str, + Field( + description="Human-readable name for this monitor", + max_length=255, + min_length=0, + ), + ] + type: Annotated[Type5, Field(description="Monitor protocol type")] + config: ( + DnsMonitorConfig + | HeartbeatMonitorConfig + | HttpMonitorConfig + | IcmpMonitorConfig + | McpServerMonitorConfig + | TcpMonitorConfig + ) + frequency_seconds: Annotated[ int | None, Field( - alias="redirectCount", - description="Number of HTTP redirects followed", - examples=[2], + alias="frequencySeconds", + description="Check frequency in seconds (30–86400); null defaults to plan minimum (60s on most paid plans)", ), ] = None - redirect_target: Annotated[ - str | None, - Field(alias="redirectTarget", description="Final URL after redirects"), - ] = None - response_size_bytes: Annotated[ - int | None, - Field( - alias="responseSizeBytes", - description="Response body size in bytes", - examples=[4096], - ), + enabled: Annotated[ + bool | None, Field(description="Whether the monitor is active (default: true)") ] = None - check_details: Annotated[ - CheckTypeDetailsDto | None, Field(alias="checkDetails") + regions: Annotated[ + list[str] | None, + Field(description="Probe regions to run checks from, e.g. us-east, eu-west"), ] = None - - -class CheckResultDto(BaseModel): - id: Annotated[UUID, Field(description="Unique identifier of the check result")] - timestamp: Annotated[ - AwareDatetime, - Field(description="Timestamp when the check was executed (ISO 8601)"), - ] - region: Annotated[ - str, - Field(description="Region where the check was executed", examples=["us-east"]), - ] - response_time_ms: Annotated[ - int | None, + managed_by: Annotated[ + ManagedBy, Field( - alias="responseTimeMs", - description="Response time in milliseconds", - examples=[123], + alias="managedBy", description="Who manages this monitor: DASHBOARD or CLI" ), - ] = None - passed: Annotated[ - bool, Field(description="Whether the check passed", examples=[True]) ] - failure_reason: Annotated[ - str | None, + environment_id: Annotated[ + UUID | None, Field( - alias="failureReason", description="Reason for failure when passed=false" + alias="environmentId", + description="Environment to associate with this monitor", ), ] = None - severity_hint: Annotated[ - str | None, - Field( - alias="severityHint", - description="Severity hint: 'down' for hard failures, 'degraded' for warn-only failures, null when passing", - ), + assertions: Annotated[ + list[CreateAssertionRequest] | None, + Field(description="Assertions to evaluate against each check result"), ] = None - details: CheckResultDetailsDto | None = None - check_id: Annotated[ - UUID | None, - Field( - alias="checkId", - description="Unique execution trace ID for cross-service correlation", - ), + auth: MonitorAuthConfig | None = None + incident_policy: Annotated[ + UpdateIncidentPolicyRequest | None, Field(alias="incidentPolicy") ] = None - - -class ComponentUptimeDayDto(BaseModel): - date: Annotated[ - AwareDatetime, - Field(description="Start-of-day timestamp for this bucket (UTC midnight)"), - ] - partial_outage_seconds: Annotated[ - int, - Field( - alias="partialOutageSeconds", - description="Seconds of partial outage on this day", - ), - ] - major_outage_seconds: Annotated[ - int, - Field( - alias="majorOutageSeconds", - description="Seconds of major outage on this day", - ), - ] - uptime_percentage: Annotated[ - float, + alert_channel_ids: Annotated[ + list[UUID] | None, Field( - alias="uptimePercentage", - description="Computed uptime percentage using weighted formula", + alias="alertChannelIds", + description="Alert channels to notify when this monitor triggers", ), - ] - incidents: Annotated[ - list[IncidentRef] | None, - Field(description="Incidents that overlapped this day"), ] = None - - -class CreateAlertChannelRequest(BaseModel): - name: Annotated[ - str, - Field( - description="Human-readable name for this alert channel", - max_length=255, - min_length=0, - ), - ] - config: ( - DiscordChannelConfig - | EmailChannelConfig - | OpsGenieChannelConfig - | PagerDutyChannelConfig - | SlackChannelConfig - | TeamsChannelConfig - | WebhookChannelConfig - ) - - -class CreateAssertionRequest(BaseModel): - config: ( - BodyContainsAssertion - | DnsExpectedCnameAssertion - | DnsExpectedIpsAssertion - | DnsMaxAnswersAssertion - | DnsMinAnswersAssertion - | DnsRecordContainsAssertion - | DnsRecordEqualsAssertion - | DnsResolvesAssertion - | DnsResponseTimeAssertion - | DnsResponseTimeWarnAssertion - | DnsTtlHighAssertion - | DnsTtlLowAssertion - | DnsTxtContainsAssertion - | HeaderValueAssertion - | HeartbeatIntervalDriftAssertion - | HeartbeatMaxIntervalAssertion - | HeartbeatPayloadContainsAssertion - | HeartbeatReceivedAssertion - | IcmpPacketLossAssertion - | IcmpReachableAssertion - | IcmpResponseTimeAssertion - | IcmpResponseTimeWarnAssertion - | JsonPathAssertion - | McpConnectsAssertion - | McpHasCapabilityAssertion - | McpMinToolsAssertion - | McpProtocolVersionAssertion - | McpResponseTimeAssertion - | McpResponseTimeWarnAssertion - | McpToolAvailableAssertion - | McpToolCountChangedAssertion - | RedirectCountAssertion - | RedirectTargetAssertion - | RegexBodyAssertion - | ResponseSizeAssertion - | ResponseTimeAssertion - | ResponseTimeWarnAssertion - | SslExpiryAssertion - | StatusCodeAssertion - | TcpConnectsAssertion - | TcpResponseTimeAssertion - | TcpResponseTimeWarnAssertion - ) - severity: Annotated[ - Severity, - Field( - description="Outcome severity: FAIL (fails the check) or WARN (warns without failing)" - ), - ] + tags: AddMonitorTagsRequest | None = None class CreateResourceGroupRequest(BaseModel): @@ -5572,23 +6230,6 @@ class CreateStatusPageRequest(BaseModel): ] = None -class CursorPageCheckResultDto(BaseModel): - data: Annotated[list[CheckResultDto], Field(description="Items on this page")] - next_cursor: Annotated[ - str | None, - Field( - alias="nextCursor", - description="Opaque cursor for the next page; null when there are no more results", - ), - ] = None - has_more: Annotated[ - bool, - Field( - alias="hasMore", description="Whether more results exist beyond this page" - ), - ] - - class CursorPageServiceCatalogDto(BaseModel): data: Annotated[list[ServiceCatalogDto], Field(description="Items on this page")] next_cursor: Annotated[ @@ -5628,34 +6269,6 @@ class DashboardOverviewDto(BaseModel): incidents: IncidentsSummaryDto -class DnsMonitorConfig(MonitorConfig): - hostname: Annotated[str, Field(description="Domain name to resolve", min_length=1)] - record_types: Annotated[ - list[RecordType] | None, - Field( - alias="recordTypes", - description="DNS record types to query: A, AAAA, CNAME, MX, NS, TXT, SRV, SOA, CAA, PTR", - ), - ] = None - nameservers: Annotated[ - list[str] | None, - Field( - description="Custom nameservers to query (uses system defaults if omitted)" - ), - ] = None - timeout_ms: Annotated[ - int | None, - Field(alias="timeoutMs", description="Per-query timeout in milliseconds"), - ] = None - total_timeout_ms: Annotated[ - int | None, - Field( - alias="totalTimeoutMs", - description="Total timeout for all queries in milliseconds", - ), - ] = None - - class EscalationChain(BaseModel): steps: Annotated[ list[EscalationStep], @@ -5736,99 +6349,6 @@ class GlobalStatusSummaryDto(BaseModel): ] -class HeaderAuthConfig(MonitorAuthConfig): - header_name: Annotated[ - str, - Field( - alias="headerName", - description="Custom HTTP header name for the secret value", - min_length=1, - pattern="^[A-Za-z0-9\\-_]+$", - ), - ] - vault_secret_id: Annotated[ - UUID | None, - Field( - alias="vaultSecretId", description="Vault secret ID for the header value" - ), - ] = None - - -class HeartbeatMonitorConfig(MonitorConfig): - expected_interval: Annotated[ - int, - Field( - alias="expectedInterval", - description="Expected heartbeat interval in seconds", - ge=1, - le=86400, - ), - ] - grace_period: Annotated[ - int, - Field( - alias="gracePeriod", - description="Grace period in seconds before marking as down", - ge=1, - ), - ] - - -class HttpMonitorConfig(MonitorConfig): - url: Annotated[ - str, Field(description="Target URL to send requests to", min_length=1) - ] - method: Annotated[ - Method, Field(description="HTTP method: GET, POST, PUT, PATCH, DELETE, or HEAD") - ] - custom_headers: Annotated[ - dict[str, str] | None, - Field( - alias="customHeaders", - description="Additional HTTP headers to include in requests", - ), - ] = None - request_body: Annotated[ - str | None, - Field( - alias="requestBody", - description="Request body content for POST/PUT/PATCH methods", - ), - ] = None - content_type: Annotated[ - str | None, - Field( - alias="contentType", - description="Content-Type header value for the request body", - ), - ] = None - verify_tls: Annotated[ - bool | None, - Field( - alias="verifyTls", - description="Whether to verify TLS certificates (default: true)", - ), - ] = None - - -class IcmpMonitorConfig(MonitorConfig): - host: Annotated[ - str, Field(description="Target hostname or IP address to ping", min_length=1) - ] - packet_count: Annotated[ - int | None, - Field( - alias="packetCount", - description="Number of ICMP packets to send", - ge=1, - le=20, - ), - ] = None - timeout_ms: Annotated[ - int | None, Field(alias="timeoutMs", description="Ping timeout in milliseconds") - ] = None - - class IncidentDetailDto(BaseModel): incident: IncidentDto updates: list[IncidentUpdateDto] @@ -5896,21 +6416,6 @@ class IntegrationDto(BaseModel): config_schema: Annotated[IntegrationConfigSchemaDto, Field(alias="configSchema")] -class McpServerMonitorConfig(MonitorConfig): - command: Annotated[ - str, - Field(description="Command to execute to start the MCP server", min_length=1), - ] - args: Annotated[ - list[str] | None, - Field(description="Command-line arguments for the MCP server process"), - ] = None - env: Annotated[ - dict[str, str] | None, - Field(description="Environment variables to pass to the MCP server process"), - ] = None - - class MonitorAssertionDto(BaseModel): id: UUID monitor_id: Annotated[UUID, Field(alias="monitorId")] @@ -5962,13 +6467,6 @@ class MonitorAssertionDto(BaseModel): severity: Severity6 -class MonitorAuthDto(BaseModel): - id: UUID - monitor_id: Annotated[UUID, Field(alias="monitorId")] - auth_type: Annotated[AuthType, Field(alias="authType")] - config: ApiKeyAuthConfig | BasicAuthConfig | BearerAuthConfig | HeaderAuthConfig - - class MonitorDto(BaseModel): id: Annotated[UUID, Field(description="Unique monitor identifier")] organization_id: Annotated[ @@ -5980,7 +6478,7 @@ class MonitorDto(BaseModel): name: Annotated[ str, Field(description="Human-readable name for this monitor", min_length=1) ] - type: Type3 + type: Type38 config: ( DnsMonitorConfig | HeartbeatMonitorConfig @@ -6031,9 +6529,7 @@ class MonitorDto(BaseModel): ), ] = None environment: Summary | None = None - auth: ( - ApiKeyAuthConfig | BasicAuthConfig | BearerAuthConfig | HeaderAuthConfig | None - ) = None + auth: MonitorAuthConfig | None = None incident_policy: Annotated[ IncidentPolicyDto | None, Field(alias="incidentPolicy") ] = None @@ -6047,7 +6543,7 @@ class MonitorDto(BaseModel): class MonitorTestRequest(BaseModel): - type: Annotated[Type3, Field(description="Monitor protocol type to test")] + type: Annotated[Type38, Field(description="Monitor protocol type to test")] config: ( DnsMonitorConfig | HeartbeatMonitorConfig @@ -6303,14 +6799,14 @@ class ServiceUptimeResponse(BaseModel): ] = None -class SetMonitorAuthRequest(BaseModel): - config: ApiKeyAuthConfig | BasicAuthConfig | BearerAuthConfig | HeaderAuthConfig - - class SingleValueResponseAuthMeResponse(BaseModel): data: AuthMeResponse +class SingleValueResponseBatchComponentUptimeDto(BaseModel): + data: BatchComponentUptimeDto + + class SingleValueResponseBulkMonitorActionResult(BaseModel): data: BulkMonitorActionResult @@ -6331,18 +6827,10 @@ class SingleValueResponseIncidentPolicyDto(BaseModel): data: IncidentPolicyDto -class SingleValueResponseMapStringListComponentUptimeDayDto(BaseModel): - data: dict[str, list[ComponentUptimeDayDto]] - - class SingleValueResponseMonitorAssertionDto(BaseModel): data: MonitorAssertionDto -class SingleValueResponseMonitorAuthDto(BaseModel): - data: MonitorAuthDto - - class SingleValueResponseMonitorDto(BaseModel): data: MonitorDto @@ -6450,14 +6938,6 @@ class StatusPageIncidentDto(BaseModel): updated_at: Annotated[AwareDatetime, Field(alias="updatedAt")] -class TableValueResultComponentUptimeDayDto(BaseModel): - data: list[ComponentUptimeDayDto] - has_next: Annotated[bool, Field(alias="hasNext")] - has_prev: Annotated[bool, Field(alias="hasPrev")] - total_elements: Annotated[int | None, Field(alias="totalElements")] = None - total_pages: Annotated[int | None, Field(alias="totalPages")] = None - - class TableValueResultIntegrationDto(BaseModel): data: list[IntegrationDto] has_next: Annotated[bool, Field(alias="hasNext")] @@ -6570,10 +7050,6 @@ class UpdateAlertChannelRequest(BaseModel): ) -class UpdateMonitorAuthRequest(BaseModel): - config: ApiKeyAuthConfig | BasicAuthConfig | BearerAuthConfig | HeaderAuthConfig - - class UpdateMonitorRequest(BaseModel): name: Annotated[ str | None, @@ -6583,7 +7059,15 @@ class UpdateMonitorRequest(BaseModel): min_length=0, ), ] = None - config: MonitorConfig | None = None + config: ( + DnsMonitorConfig + | HeartbeatMonitorConfig + | HttpMonitorConfig + | IcmpMonitorConfig + | McpServerMonitorConfig + | TcpMonitorConfig + | None + ) = None frequency_seconds: Annotated[ int | None, Field( @@ -6623,9 +7107,7 @@ class UpdateMonitorRequest(BaseModel): list[CreateAssertionRequest] | None, Field(description="Replace all assertions; null preserves current"), ] = None - auth: ( - ApiKeyAuthConfig | BasicAuthConfig | BearerAuthConfig | HeaderAuthConfig | None - ) = None + auth: MonitorAuthConfig | None = None clear_auth: Annotated[ bool | None, Field(alias="clearAuth", description="Set to true to remove authentication"), @@ -6672,69 +7154,101 @@ class UpdateNotificationPolicyRequest(BaseModel): ] = None -class CreateMonitorRequest(BaseModel): - name: Annotated[ - str, +class CheckResultDetailsDto(BaseModel): + status_code: Annotated[ + int | None, Field( - description="Human-readable name for this monitor", - max_length=255, - min_length=0, + alias="statusCode", + description="HTTP status code of the response", + examples=[200], ), - ] - type: Annotated[Type1, Field(description="Monitor protocol type")] - config: ( - DnsMonitorConfig - | HeartbeatMonitorConfig - | HttpMonitorConfig - | IcmpMonitorConfig - | McpServerMonitorConfig - | TcpMonitorConfig - ) - frequency_seconds: Annotated[ - int | None, + ] = None + response_headers: Annotated[ + dict[str, list[str]] | None, + Field(alias="responseHeaders", description="HTTP response headers"), + ] = None + response_body_snapshot: Annotated[ + str | None, Field( - alias="frequencySeconds", - description="Check frequency in seconds (30–86400); null defaults to plan minimum (60s on most paid plans)", + alias="responseBodySnapshot", + description="Raw response body snapshot (may be HTML, XML, JSON, or plain text)", ), ] = None - enabled: Annotated[ - bool | None, Field(description="Whether the monitor is active (default: true)") + assertion_results: Annotated[ + list[AssertionResultDto] | None, + Field( + alias="assertionResults", + description="Individual assertion evaluation results", + ), ] = None - regions: Annotated[ - list[str] | None, - Field(description="Probe regions to run checks from, e.g. us-east, eu-west"), + tls_info: Annotated[TlsInfoDto | None, Field(alias="tlsInfo")] = None + redirect_count: Annotated[ + int | None, + Field( + alias="redirectCount", + description="Number of HTTP redirects followed", + examples=[2], + ), ] = None - managed_by: Annotated[ - ManagedBy, + redirect_target: Annotated[ + str | None, + Field(alias="redirectTarget", description="Final URL after redirects"), + ] = None + response_size_bytes: Annotated[ + int | None, Field( - alias="managedBy", description="Who manages this monitor: DASHBOARD or CLI" + alias="responseSizeBytes", + description="Response body size in bytes", + examples=[4096], ), + ] = None + check_details: Annotated[ + CheckTypeDetailsDto | None, Field(alias="checkDetails") + ] = None + + +class CheckResultDto(BaseModel): + id: Annotated[UUID, Field(description="Unique identifier of the check result")] + timestamp: Annotated[ + AwareDatetime, + Field(description="Timestamp when the check was executed (ISO 8601)"), ] - environment_id: Annotated[ - UUID | None, + region: Annotated[ + str, + Field(description="Region where the check was executed", examples=["us-east"]), + ] + response_time_ms: Annotated[ + int | None, Field( - alias="environmentId", - description="Environment to associate with this monitor", + alias="responseTimeMs", + description="Response time in milliseconds", + examples=[123], ), ] = None - assertions: Annotated[ - list[CreateAssertionRequest] | None, - Field(description="Assertions to evaluate against each check result"), + passed: Annotated[ + bool, Field(description="Whether the check passed", examples=[True]) + ] + failure_reason: Annotated[ + str | None, + Field( + alias="failureReason", description="Reason for failure when passed=false" + ), ] = None - auth: ( - ApiKeyAuthConfig | BasicAuthConfig | BearerAuthConfig | HeaderAuthConfig | None - ) = None - incident_policy: Annotated[ - UpdateIncidentPolicyRequest | None, Field(alias="incidentPolicy") + severity_hint: Annotated[ + str | None, + Field( + alias="severityHint", + description="Severity hint: 'down' for hard failures, 'degraded' for warn-only failures, null when passing", + ), ] = None - alert_channel_ids: Annotated[ - list[UUID] | None, + details: CheckResultDetailsDto | None = None + check_id: Annotated[ + UUID | None, Field( - alias="alertChannelIds", - description="Alert channels to notify when this monitor triggers", + alias="checkId", + description="Unique execution trace ID for cross-service correlation", ), ] = None - tags: AddMonitorTagsRequest | None = None class CreateNotificationPolicyRequest(BaseModel): @@ -6747,21 +7261,38 @@ class CreateNotificationPolicyRequest(BaseModel): ), ] match_rules: Annotated[ - list[MatchRule], + list[MatchRule] | None, Field( alias="matchRules", description="Match rules to evaluate (all must pass; omit or empty for catch-all)", ), - ] + ] = None escalation: EscalationChain enabled: Annotated[ - bool, Field(description="Whether this policy is enabled (default true)") - ] + bool | None, Field(description="Whether this policy is enabled (default true)") + ] = True priority: Annotated[ - int, + int | None, Field( description="Evaluation priority; higher value = evaluated first (default 0)" ), + ] = 0 + + +class CursorPageCheckResultDto(BaseModel): + data: Annotated[list[CheckResultDto], Field(description="Items on this page")] + next_cursor: Annotated[ + str | None, + Field( + alias="nextCursor", + description="Opaque cursor for the next page; null when there are no more results", + ), + ] = None + has_more: Annotated[ + bool, + Field( + alias="hasMore", description="Whether more results exist beyond this page" + ), ] diff --git a/src/devhelm/_http.py b/src/devhelm/_http.py index def1416..12e2abe 100644 --- a/src/devhelm/_http.py +++ b/src/devhelm/_http.py @@ -64,46 +64,61 @@ def path_param(value: str | int) -> str: return quote(str(value), safe="") -def _serialize_body(body: Any) -> Any: - """Convert a Pydantic model or dict to a JSON-serializable dict.""" +_JsonResponse = dict[str, object] | list[object] | None + + +def _serialize_body( + body: BaseModel | dict[str, object] | None, +) -> dict[str, object] | None: + """Convert a Pydantic model to a JSON-serializable dict. + + Rejects raw dicts to prevent callers from bypassing Pydantic + validation. All request bodies must be validated model instances. + """ if isinstance(body, BaseModel): return body.model_dump(mode="json", by_alias=True, exclude_none=True) + if isinstance(body, dict): + raise DevhelmError( + "VALIDATION", + "Raw dicts are not accepted as request bodies. " + "Use the generated Pydantic model instead.", + 0, + ) return body -def checked_fetch(response: httpx.Response) -> Any: +def checked_fetch(response: httpx.Response) -> _JsonResponse: """Check an httpx response and raise DevhelmError on failure.""" if response.is_success: if response.status_code == 204: return None - return response.json() + return response.json() # type: ignore[no-any-return] raise error_from_response(response.status_code, response.text) -def unwrap_single(resp: Any) -> Any: - """Unwrap a SingleValueResponse envelope: {data: T} -> T.""" - if isinstance(resp, dict) and "data" in resp: - return resp["data"] - return resp - - def api_get( client: httpx.Client, path: str, params: dict[str, Any] | None = None -) -> Any: +) -> _JsonResponse: return checked_fetch(client.get(path, params=params)) -def api_post(client: httpx.Client, path: str, body: Any = None) -> Any: +def api_post( + client: httpx.Client, path: str, body: BaseModel | dict[str, object] | None = None +) -> _JsonResponse: if body is None: return checked_fetch(client.post(path)) return checked_fetch(client.post(path, json=_serialize_body(body))) -def api_put(client: httpx.Client, path: str, body: Any) -> Any: +def api_put( + client: httpx.Client, path: str, body: BaseModel | dict[str, object] | None +) -> _JsonResponse: return checked_fetch(client.put(path, json=_serialize_body(body))) -def api_patch(client: httpx.Client, path: str, body: Any) -> Any: +def api_patch( + client: httpx.Client, path: str, body: BaseModel | dict[str, object] | None +) -> _JsonResponse: return checked_fetch(client.patch(path, json=_serialize_body(body))) diff --git a/src/devhelm/_pagination.py b/src/devhelm/_pagination.py index bfe7c25..a5db2dc 100644 --- a/src/devhelm/_pagination.py +++ b/src/devhelm/_pagination.py @@ -1,7 +1,7 @@ from __future__ import annotations from dataclasses import dataclass, field -from typing import Any, Generic, TypeVar +from typing import Any, Generic, TypeVar, cast import httpx from pydantic import BaseModel @@ -66,8 +66,12 @@ def fetch_page( data=items, has_next=bool(resp.get("hasNext")) if isinstance(resp, dict) else False, has_prev=bool(resp.get("hasPrev")) if isinstance(resp, dict) else False, - total_elements=resp.get("totalElements") if isinstance(resp, dict) else None, - total_pages=resp.get("totalPages") if isinstance(resp, dict) else None, + total_elements=cast(int | None, resp.get("totalElements")) + if isinstance(resp, dict) + else None, + total_pages=cast(int | None, resp.get("totalPages")) + if isinstance(resp, dict) + else None, ) @@ -90,6 +94,8 @@ def fetch_cursor_page( items = parse_list(model_class, raw_items, f"GET {path}") return CursorPage( data=items, - next_cursor=(resp.get("nextCursor") if isinstance(resp, dict) else None), + next_cursor=cast(str | None, resp.get("nextCursor")) + if isinstance(resp, dict) + else None, has_more=bool(resp.get("hasMore")) if isinstance(resp, dict) else False, ) diff --git a/src/devhelm/_validation.py b/src/devhelm/_validation.py index 171c062..408238a 100644 --- a/src/devhelm/_validation.py +++ b/src/devhelm/_validation.py @@ -1,4 +1,4 @@ -"""Pydantic-based response validation for API responses. +"""Pydantic-based validation for API requests and responses. Parses raw JSON dicts through generated Pydantic models, catching shape mismatches before they propagate as silent bugs. @@ -6,7 +6,8 @@ from __future__ import annotations -from typing import Any, TypeVar +from collections.abc import Mapping +from typing import Any, TypeAlias, TypeVar, Union from pydantic import BaseModel, TypeAdapter, ValidationError @@ -14,6 +15,37 @@ M = TypeVar("M", bound=BaseModel) +# Public type alias used in resource method signatures. Every "create"/ +# "update" body parameter is annotated as ``RequestBody[]`` so that +# both fully-typed model instances *and* plain dicts (the most ergonomic +# form, and the one shown throughout the README) type-check under +# ``mypy --strict``. ``validate_request`` normalises both forms before +# any HTTP call is made. +RequestBody: TypeAlias = Union[M, Mapping[str, Any]] + + +def validate_request( + model_class: type[M], body: RequestBody[M], context: str = "" +) -> M: + """Validate a request body against its Pydantic model before sending. + + If *body* is already an instance of *model_class* it passes through. + Dicts and other mappings are coerced through ``model_validate``, + raising ``DevhelmError`` on constraint violations. + """ + if isinstance(body, model_class): + return body + try: + return model_class.model_validate(body) + except ValidationError as e: + ctx = f" ({context})" if context else "" + raise DevhelmError( + "VALIDATION", + f"Request validation failed{ctx}: {e.error_count()} error(s)", + 0, + str(e), + ) from e + def parse_model(model_class: type[M], data: Any, context: str = "") -> M: """Parse a raw dict/JSON through a Pydantic model, raising DevhelmError on failure.""" diff --git a/src/devhelm/resources/alert_channels.py b/src/devhelm/resources/alert_channels.py index e2c37b6..07da2ef 100644 --- a/src/devhelm/resources/alert_channels.py +++ b/src/devhelm/resources/alert_channels.py @@ -10,7 +10,7 @@ ) from devhelm._http import api_delete, api_get, api_post, api_put, path_param from devhelm._pagination import Page, fetch_all_pages, fetch_page -from devhelm._validation import parse_single +from devhelm._validation import RequestBody, parse_single, validate_request class AlertChannels: @@ -37,16 +37,20 @@ def get(self, id: int | str) -> AlertChannelDto: f"GET /api/v1/alert-channels/{id}", ) - def create(self, body: CreateAlertChannelRequest) -> AlertChannelDto: + def create(self, body: RequestBody[CreateAlertChannelRequest]) -> AlertChannelDto: """Create a new alert channel.""" + body = validate_request(CreateAlertChannelRequest, body, "alertChannels.create") return parse_single( AlertChannelDto, api_post(self._client, "/api/v1/alert-channels", body), "POST /api/v1/alert-channels", ) - def update(self, id: int | str, body: UpdateAlertChannelRequest) -> AlertChannelDto: + def update( + self, id: int | str, body: RequestBody[UpdateAlertChannelRequest] + ) -> AlertChannelDto: """Update an alert channel.""" + body = validate_request(UpdateAlertChannelRequest, body, "alertChannels.update") return parse_single( AlertChannelDto, api_put(self._client, f"/api/v1/alert-channels/{path_param(id)}", body), diff --git a/src/devhelm/resources/api_keys.py b/src/devhelm/resources/api_keys.py index 8ef1fb2..ec40991 100644 --- a/src/devhelm/resources/api_keys.py +++ b/src/devhelm/resources/api_keys.py @@ -5,7 +5,7 @@ from devhelm._generated import ApiKeyCreateResponse, ApiKeyDto, CreateApiKeyRequest from devhelm._http import api_delete, api_post, path_param from devhelm._pagination import Page, fetch_all_pages, fetch_page -from devhelm._validation import parse_single +from devhelm._validation import RequestBody, parse_single, validate_request class ApiKeys: @@ -22,8 +22,9 @@ def list_page(self, page: int, size: int) -> Page[ApiKeyDto]: """List API keys with manual page control.""" return fetch_page(self._client, "/api/v1/api-keys", ApiKeyDto, page, size) - def create(self, body: CreateApiKeyRequest) -> ApiKeyCreateResponse: + def create(self, body: RequestBody[CreateApiKeyRequest]) -> ApiKeyCreateResponse: """Create an API key. Returns the key value (shown only once).""" + body = validate_request(CreateApiKeyRequest, body, "apiKeys.create") return parse_single( ApiKeyCreateResponse, api_post(self._client, "/api/v1/api-keys", body), diff --git a/src/devhelm/resources/deploy_lock.py b/src/devhelm/resources/deploy_lock.py index 880f889..d95dd71 100644 --- a/src/devhelm/resources/deploy_lock.py +++ b/src/devhelm/resources/deploy_lock.py @@ -4,7 +4,7 @@ from devhelm._generated import AcquireDeployLockRequest, DeployLockDto from devhelm._http import api_delete, api_get, api_post, path_param -from devhelm._validation import parse_model, parse_single +from devhelm._validation import RequestBody, parse_model, parse_single, validate_request class DeployLock: @@ -13,8 +13,9 @@ class DeployLock: def __init__(self, client: httpx.Client) -> None: self._client = client - def acquire(self, body: AcquireDeployLockRequest) -> DeployLockDto: + def acquire(self, body: RequestBody[AcquireDeployLockRequest]) -> DeployLockDto: """Acquire a deploy lock.""" + body = validate_request(AcquireDeployLockRequest, body, "deployLock.acquire") return parse_single( DeployLockDto, api_post(self._client, "/api/v1/deploy/lock", body), diff --git a/src/devhelm/resources/environments.py b/src/devhelm/resources/environments.py index 086ddf0..6ae1fab 100644 --- a/src/devhelm/resources/environments.py +++ b/src/devhelm/resources/environments.py @@ -9,7 +9,7 @@ ) from devhelm._http import api_delete, api_get, api_post, api_put, path_param from devhelm._pagination import Page, fetch_all_pages, fetch_page -from devhelm._validation import parse_single +from devhelm._validation import RequestBody, parse_single, validate_request class Environments: @@ -36,16 +36,20 @@ def get(self, slug: str) -> EnvironmentDto: f"GET /api/v1/environments/{slug}", ) - def create(self, body: CreateEnvironmentRequest) -> EnvironmentDto: + def create(self, body: RequestBody[CreateEnvironmentRequest]) -> EnvironmentDto: """Create an environment.""" + body = validate_request(CreateEnvironmentRequest, body, "environments.create") return parse_single( EnvironmentDto, api_post(self._client, "/api/v1/environments", body), "POST /api/v1/environments", ) - def update(self, slug: str, body: UpdateEnvironmentRequest) -> EnvironmentDto: + def update( + self, slug: str, body: RequestBody[UpdateEnvironmentRequest] + ) -> EnvironmentDto: """Update an environment.""" + body = validate_request(UpdateEnvironmentRequest, body, "environments.update") return parse_single( EnvironmentDto, api_put(self._client, f"/api/v1/environments/{path_param(slug)}", body), diff --git a/src/devhelm/resources/incidents.py b/src/devhelm/resources/incidents.py index 1088128..fb05577 100644 --- a/src/devhelm/resources/incidents.py +++ b/src/devhelm/resources/incidents.py @@ -10,7 +10,7 @@ ) from devhelm._http import api_delete, api_get, api_post, path_param from devhelm._pagination import Page, fetch_all_pages, fetch_page -from devhelm._validation import parse_single +from devhelm._validation import RequestBody, parse_single, validate_request class Incidents: @@ -35,8 +35,11 @@ def get(self, id: int | str) -> IncidentDetailDto: f"GET /api/v1/incidents/{id}", ) - def create(self, body: CreateManualIncidentRequest) -> IncidentDetailDto: + def create( + self, body: RequestBody[CreateManualIncidentRequest] + ) -> IncidentDetailDto: """Create a manual incident.""" + body = validate_request(CreateManualIncidentRequest, body, "incidents.create") return parse_single( IncidentDetailDto, api_post(self._client, "/api/v1/incidents", body), @@ -44,9 +47,11 @@ def create(self, body: CreateManualIncidentRequest) -> IncidentDetailDto: ) def resolve( - self, id: int | str, body: ResolveIncidentRequest | None = None + self, id: int | str, body: RequestBody[ResolveIncidentRequest] | None = None ) -> IncidentDetailDto: """Resolve an incident.""" + if body is not None: + body = validate_request(ResolveIncidentRequest, body, "incidents.resolve") return parse_single( IncidentDetailDto, api_post(self._client, f"/api/v1/incidents/{path_param(id)}/resolve", body), diff --git a/src/devhelm/resources/monitors.py b/src/devhelm/resources/monitors.py index 348e3e8..afbc917 100644 --- a/src/devhelm/resources/monitors.py +++ b/src/devhelm/resources/monitors.py @@ -18,7 +18,7 @@ fetch_cursor_page, fetch_page, ) -from devhelm._validation import parse_single +from devhelm._validation import RequestBody, parse_single, validate_request class Monitors: @@ -43,16 +43,20 @@ def get(self, id: int | str) -> MonitorDto: f"GET /api/v1/monitors/{id}", ) - def create(self, body: CreateMonitorRequest) -> MonitorDto: + def create(self, body: RequestBody[CreateMonitorRequest]) -> MonitorDto: """Create a new monitor.""" + body = validate_request(CreateMonitorRequest, body, "monitors.create") return parse_single( MonitorDto, api_post(self._client, "/api/v1/monitors", body), "POST /api/v1/monitors", ) - def update(self, id: int | str, body: UpdateMonitorRequest) -> MonitorDto: + def update( + self, id: int | str, body: RequestBody[UpdateMonitorRequest] + ) -> MonitorDto: """Update an existing monitor.""" + body = validate_request(UpdateMonitorRequest, body, "monitors.update") return parse_single( MonitorDto, api_put(self._client, f"/api/v1/monitors/{path_param(id)}", body), diff --git a/src/devhelm/resources/notification_policies.py b/src/devhelm/resources/notification_policies.py index 1d8c452..0ec6bf1 100644 --- a/src/devhelm/resources/notification_policies.py +++ b/src/devhelm/resources/notification_policies.py @@ -9,7 +9,7 @@ ) from devhelm._http import api_delete, api_get, api_post, api_put, path_param from devhelm._pagination import Page, fetch_all_pages, fetch_page -from devhelm._validation import parse_single +from devhelm._validation import RequestBody, parse_single, validate_request class NotificationPolicies: @@ -42,8 +42,13 @@ def get(self, id: int | str) -> NotificationPolicyDto: f"GET /api/v1/notification-policies/{id}", ) - def create(self, body: CreateNotificationPolicyRequest) -> NotificationPolicyDto: + def create( + self, body: RequestBody[CreateNotificationPolicyRequest] + ) -> NotificationPolicyDto: """Create a notification policy.""" + body = validate_request( + CreateNotificationPolicyRequest, body, "notificationPolicies.create" + ) return parse_single( NotificationPolicyDto, api_post(self._client, "/api/v1/notification-policies", body), @@ -51,9 +56,12 @@ def create(self, body: CreateNotificationPolicyRequest) -> NotificationPolicyDto ) def update( - self, id: int | str, body: UpdateNotificationPolicyRequest + self, id: int | str, body: RequestBody[UpdateNotificationPolicyRequest] ) -> NotificationPolicyDto: """Update a notification policy.""" + body = validate_request( + UpdateNotificationPolicyRequest, body, "notificationPolicies.update" + ) return parse_single( NotificationPolicyDto, api_put( @@ -68,6 +76,4 @@ def delete(self, id: int | str) -> None: def test(self, id: int | str) -> None: """Send a test dispatch to verify policy routing.""" - api_post( - self._client, f"/api/v1/notification-policies/{path_param(id)}/test", {} - ) + api_post(self._client, f"/api/v1/notification-policies/{path_param(id)}/test") diff --git a/src/devhelm/resources/resource_groups.py b/src/devhelm/resources/resource_groups.py index f957bd4..ee13cc2 100644 --- a/src/devhelm/resources/resource_groups.py +++ b/src/devhelm/resources/resource_groups.py @@ -11,7 +11,7 @@ ) from devhelm._http import api_delete, api_get, api_post, api_put, path_param from devhelm._pagination import Page, fetch_all_pages, fetch_page -from devhelm._validation import parse_single +from devhelm._validation import RequestBody, parse_single, validate_request class ResourceGroups: @@ -40,8 +40,11 @@ def get(self, id: int | str) -> ResourceGroupDto: f"GET /api/v1/resource-groups/{id}", ) - def create(self, body: CreateResourceGroupRequest) -> ResourceGroupDto: + def create(self, body: RequestBody[CreateResourceGroupRequest]) -> ResourceGroupDto: """Create a resource group.""" + body = validate_request( + CreateResourceGroupRequest, body, "resourceGroups.create" + ) return parse_single( ResourceGroupDto, api_post(self._client, "/api/v1/resource-groups", body), @@ -49,9 +52,12 @@ def create(self, body: CreateResourceGroupRequest) -> ResourceGroupDto: ) def update( - self, id: int | str, body: UpdateResourceGroupRequest + self, id: int | str, body: RequestBody[UpdateResourceGroupRequest] ) -> ResourceGroupDto: """Update a resource group.""" + body = validate_request( + UpdateResourceGroupRequest, body, "resourceGroups.update" + ) return parse_single( ResourceGroupDto, api_put(self._client, f"/api/v1/resource-groups/{path_param(id)}", body), @@ -63,9 +69,12 @@ def delete(self, id: int | str) -> None: api_delete(self._client, f"/api/v1/resource-groups/{path_param(id)}") def add_member( - self, group_id: int | str, body: AddResourceGroupMemberRequest + self, group_id: int | str, body: RequestBody[AddResourceGroupMemberRequest] ) -> ResourceGroupMemberDto: """Add a member to a resource group.""" + body = validate_request( + AddResourceGroupMemberRequest, body, "resourceGroups.addMember" + ) return parse_single( ResourceGroupMemberDto, api_post( diff --git a/src/devhelm/resources/secrets.py b/src/devhelm/resources/secrets.py index 1b9f459..d9ad97d 100644 --- a/src/devhelm/resources/secrets.py +++ b/src/devhelm/resources/secrets.py @@ -5,7 +5,7 @@ from devhelm._generated import CreateSecretRequest, SecretDto, UpdateSecretRequest from devhelm._http import api_delete, api_post, api_put, path_param from devhelm._pagination import Page, fetch_all_pages, fetch_page -from devhelm._validation import parse_single +from devhelm._validation import RequestBody, parse_single, validate_request class Secrets: @@ -22,16 +22,18 @@ def list_page(self, page: int, size: int) -> Page[SecretDto]: """List secrets with manual page control.""" return fetch_page(self._client, "/api/v1/secrets", SecretDto, page, size) - def create(self, body: CreateSecretRequest) -> SecretDto: + def create(self, body: RequestBody[CreateSecretRequest]) -> SecretDto: """Create a secret.""" + body = validate_request(CreateSecretRequest, body, "secrets.create") return parse_single( SecretDto, api_post(self._client, "/api/v1/secrets", body), "POST /api/v1/secrets", ) - def update(self, key: str, body: UpdateSecretRequest) -> SecretDto: + def update(self, key: str, body: RequestBody[UpdateSecretRequest]) -> SecretDto: """Update a secret by key.""" + body = validate_request(UpdateSecretRequest, body, "secrets.update") return parse_single( SecretDto, api_put(self._client, f"/api/v1/secrets/{path_param(key)}", body), diff --git a/src/devhelm/resources/status_pages.py b/src/devhelm/resources/status_pages.py index 0ddd131..e066d93 100644 --- a/src/devhelm/resources/status_pages.py +++ b/src/devhelm/resources/status_pages.py @@ -24,7 +24,7 @@ ) from devhelm._http import api_delete, api_get, api_post, api_put, path_param from devhelm._pagination import Page, fetch_all_pages, fetch_page -from devhelm._validation import parse_single +from devhelm._validation import RequestBody, parse_single, validate_request _BASE = "/api/v1/status-pages" @@ -46,9 +46,12 @@ def list(self, page_id: int | str) -> list[StatusPageComponentDto]: ) def create( - self, page_id: int | str, body: CreateStatusPageComponentRequest + self, page_id: int | str, body: RequestBody[CreateStatusPageComponentRequest] ) -> StatusPageComponentDto: """Add a component to a status page.""" + body = validate_request( + CreateStatusPageComponentRequest, body, "statusPages.components.create" + ) return parse_single( StatusPageComponentDto, api_post(self._client, f"{_page_path(page_id)}/components", body), @@ -59,9 +62,12 @@ def update( self, page_id: int | str, component_id: int | str, - body: UpdateStatusPageComponentRequest, + body: RequestBody[UpdateStatusPageComponentRequest], ) -> StatusPageComponentDto: """Update a component.""" + body = validate_request( + UpdateStatusPageComponentRequest, body, "statusPages.components.update" + ) return parse_single( StatusPageComponentDto, api_put( @@ -78,8 +84,13 @@ def delete(self, page_id: int | str, component_id: int | str) -> None: self._client, f"{_page_path(page_id)}/components/{path_param(component_id)}" ) - def reorder(self, page_id: int | str, body: ReorderComponentsRequest) -> None: + def reorder( + self, page_id: int | str, body: RequestBody[ReorderComponentsRequest] + ) -> None: """Batch reorder components.""" + body = validate_request( + ReorderComponentsRequest, body, "statusPages.components.reorder" + ) api_put(self._client, f"{_page_path(page_id)}/components/reorder", body) @@ -96,9 +107,14 @@ def list(self, page_id: int | str) -> list[StatusPageComponentGroupDto]: ) def create( - self, page_id: int | str, body: CreateStatusPageComponentGroupRequest + self, + page_id: int | str, + body: RequestBody[CreateStatusPageComponentGroupRequest], ) -> StatusPageComponentGroupDto: """Create a component group.""" + body = validate_request( + CreateStatusPageComponentGroupRequest, body, "statusPages.groups.create" + ) return parse_single( StatusPageComponentGroupDto, api_post(self._client, f"{_page_path(page_id)}/groups", body), @@ -109,9 +125,12 @@ def update( self, page_id: int | str, group_id: int | str, - body: UpdateStatusPageComponentGroupRequest, + body: RequestBody[UpdateStatusPageComponentGroupRequest], ) -> StatusPageComponentGroupDto: """Update a component group.""" + body = validate_request( + UpdateStatusPageComponentGroupRequest, body, "statusPages.groups.update" + ) return parse_single( StatusPageComponentGroupDto, api_put( @@ -157,9 +176,12 @@ def get(self, page_id: int | str, incident_id: int | str) -> StatusPageIncidentD ) def create( - self, page_id: int | str, body: CreateStatusPageIncidentRequest + self, page_id: int | str, body: RequestBody[CreateStatusPageIncidentRequest] ) -> StatusPageIncidentDto: """Create a status page incident.""" + body = validate_request( + CreateStatusPageIncidentRequest, body, "statusPages.incidents.create" + ) return parse_single( StatusPageIncidentDto, api_post(self._client, f"{_page_path(page_id)}/incidents", body), @@ -170,9 +192,12 @@ def update( self, page_id: int | str, incident_id: int | str, - body: UpdateStatusPageIncidentRequest, + body: RequestBody[UpdateStatusPageIncidentRequest], ) -> StatusPageIncidentDto: """Update an incident.""" + body = validate_request( + UpdateStatusPageIncidentRequest, body, "statusPages.incidents.update" + ) return parse_single( StatusPageIncidentDto, api_put( @@ -187,9 +212,14 @@ def post_update( self, page_id: int | str, incident_id: int | str, - body: CreateStatusPageIncidentUpdateRequest, + body: RequestBody[CreateStatusPageIncidentUpdateRequest], ) -> StatusPageIncidentDto: """Post a timeline update on an incident.""" + body = validate_request( + CreateStatusPageIncidentUpdateRequest, + body, + "statusPages.incidents.postUpdate", + ) return parse_single( StatusPageIncidentDto, api_post( @@ -246,9 +276,12 @@ def list( ) def add( - self, page_id: int | str, body: AdminAddSubscriberRequest + self, page_id: int | str, body: RequestBody[AdminAddSubscriberRequest] ) -> StatusPageSubscriberDto: """Add a subscriber (admin).""" + body = validate_request( + AdminAddSubscriberRequest, body, "statusPages.subscribers.add" + ) return parse_single( StatusPageSubscriberDto, api_post(self._client, f"{_page_path(page_id)}/subscribers", body), @@ -276,9 +309,10 @@ def list(self, page_id: int | str) -> list[StatusPageCustomDomainDto]: ) def add( - self, page_id: int | str, body: AddCustomDomainRequest + self, page_id: int | str, body: RequestBody[AddCustomDomainRequest] ) -> StatusPageCustomDomainDto: """Add a custom domain.""" + body = validate_request(AddCustomDomainRequest, body, "statusPages.domains.add") return parse_single( StatusPageCustomDomainDto, api_post(self._client, f"{_page_path(page_id)}/domains", body), @@ -335,16 +369,20 @@ def get(self, id: int | str) -> StatusPageDto: f"GET {_page_path(id)}", ) - def create(self, body: CreateStatusPageRequest) -> StatusPageDto: + def create(self, body: RequestBody[CreateStatusPageRequest]) -> StatusPageDto: """Create a status page.""" + body = validate_request(CreateStatusPageRequest, body, "statusPages.create") return parse_single( StatusPageDto, api_post(self._client, _BASE, body), "POST /api/v1/status-pages", ) - def update(self, id: int | str, body: UpdateStatusPageRequest) -> StatusPageDto: + def update( + self, id: int | str, body: RequestBody[UpdateStatusPageRequest] + ) -> StatusPageDto: """Update a status page.""" + body = validate_request(UpdateStatusPageRequest, body, "statusPages.update") return parse_single( StatusPageDto, api_put(self._client, _page_path(id), body), diff --git a/src/devhelm/resources/tags.py b/src/devhelm/resources/tags.py index efce14a..02481dd 100644 --- a/src/devhelm/resources/tags.py +++ b/src/devhelm/resources/tags.py @@ -5,7 +5,7 @@ from devhelm._generated import CreateTagRequest, TagDto, UpdateTagRequest from devhelm._http import api_delete, api_get, api_post, api_put, path_param from devhelm._pagination import Page, fetch_all_pages, fetch_page -from devhelm._validation import parse_single +from devhelm._validation import RequestBody, parse_single, validate_request class Tags: @@ -30,14 +30,16 @@ def get(self, id: int | str) -> TagDto: f"GET /api/v1/tags/{id}", ) - def create(self, body: CreateTagRequest) -> TagDto: + def create(self, body: RequestBody[CreateTagRequest]) -> TagDto: """Create a tag.""" + body = validate_request(CreateTagRequest, body, "tags.create") return parse_single( TagDto, api_post(self._client, "/api/v1/tags", body), "POST /api/v1/tags" ) - def update(self, id: int | str, body: UpdateTagRequest) -> TagDto: + def update(self, id: int | str, body: RequestBody[UpdateTagRequest]) -> TagDto: """Update a tag.""" + body = validate_request(UpdateTagRequest, body, "tags.update") return parse_single( TagDto, api_put(self._client, f"/api/v1/tags/{path_param(id)}", body), diff --git a/src/devhelm/resources/webhooks.py b/src/devhelm/resources/webhooks.py index e20f797..2dc9a90 100644 --- a/src/devhelm/resources/webhooks.py +++ b/src/devhelm/resources/webhooks.py @@ -10,7 +10,7 @@ ) from devhelm._http import api_delete, api_get, api_post, api_put, path_param from devhelm._pagination import Page, fetch_all_pages, fetch_page -from devhelm._validation import parse_single +from devhelm._validation import RequestBody, parse_single, validate_request class Webhooks: @@ -37,8 +37,11 @@ def get(self, id: int | str) -> WebhookEndpointDto: f"GET /api/v1/webhooks/{id}", ) - def create(self, body: CreateWebhookEndpointRequest) -> WebhookEndpointDto: + def create( + self, body: RequestBody[CreateWebhookEndpointRequest] + ) -> WebhookEndpointDto: """Create a webhook endpoint.""" + body = validate_request(CreateWebhookEndpointRequest, body, "webhooks.create") return parse_single( WebhookEndpointDto, api_post(self._client, "/api/v1/webhooks", body), @@ -46,9 +49,10 @@ def create(self, body: CreateWebhookEndpointRequest) -> WebhookEndpointDto: ) def update( - self, id: int | str, body: UpdateWebhookEndpointRequest + self, id: int | str, body: RequestBody[UpdateWebhookEndpointRequest] ) -> WebhookEndpointDto: """Update a webhook endpoint.""" + body = validate_request(UpdateWebhookEndpointRequest, body, "webhooks.update") return parse_single( WebhookEndpointDto, api_put(self._client, f"/api/v1/webhooks/{path_param(id)}", body), diff --git a/src/devhelm/types.py b/src/devhelm/types.py index 937b7ab..7852f44 100644 --- a/src/devhelm/types.py +++ b/src/devhelm/types.py @@ -3,20 +3,50 @@ These are the stable public names consumers should import from `devhelm`. The underlying `_generated` module is auto-generated by `datamodel-codegen` from the vendored OpenAPI spec; do not import from it directly. + +Enum Alias Mapping +================== +``datamodel-codegen`` produces numbered suffixes (Status1 … Status15, Type1 … Type6, +etc.) when multiple OpenAPI schemas share the same enum name. The aliases below +give each generated enum a descriptive, context-aware name based on the DTO that +references it. + +When ``_generated.py`` is regenerated the numbering may shift. Verify aliases by +checking which DTO class references each generated enum name, then update the +imports here to match. + +Duplicate-value groups (same members, different Python classes): + StatusPageIncidentStatus ≡ LinkedIncidentStatus ≡ PublishIncidentStatus ≡ StatusPageUpdateStatus + MembershipStatus ≡ MemberStatus + AssertionSeverity ≡ MonitorAssertionSeverity ≡ UpdateAssertionSeverity + MonitorType ≡ MonitorDtoType + StatusPageComponentType ≡ StatusPageComponentDtoType + IncidentUpdateCreatedBy ≡ StatusPageUpdateCreatedBy """ from __future__ import annotations from devhelm._generated import ( + # ── DTOs ────────────────────────────────────────────────────────────── AcquireDeployLockRequest, + # ── Enums: unique names (no suffix collisions) ──────────────────────── + Action, AddCustomDomainRequest, + AddIncidentUpdateRequest, AddResourceGroupMemberRequest, AdminAddSubscriberRequest, + AggregationType, AlertChannelDto, + AlertSensitivity, ApiKeyCreateResponse, ApiKeyDto, AssertionTestResultDto, + AssertionType, + AuthType, + ChangedVia, + ChannelType, CheckResultDto, # noqa: F401 + CompletionReason, CreateAlertChannelRequest, CreateApiKeyRequest, CreateEnvironmentRequest, @@ -35,17 +65,31 @@ DashboardOverviewDto, DeployLockDto, EnvironmentDto, + EventType, + HealthThresholdType, + Impact, IncidentDetailDto, IncidentDto, + IncidentMode, + ManagedBy, + Method, MonitorDto, MonitorVersionDto, NotificationPolicyDto, + Operator, + OrgRole, + PublishStatusPageIncidentRequest, + RecordType, ReorderComponentsRequest, + ResolutionReason, ResolveIncidentRequest, ResourceGroupDto, ResourceGroupMemberDto, # noqa: F401 + RoleOffered, + Scope, SecretDto, ServiceSubscriptionDto, + Source, StatusPageBranding, StatusPageComponentDto, StatusPageComponentGroupDto, @@ -57,6 +101,9 @@ StatusPageSubscriberDto, TagDto, TestChannelResult, + ThresholdStatus, + Tier, + TierAvailability, UpdateAlertChannelRequest, UpdateEnvironmentRequest, UpdateMonitorRequest, @@ -69,13 +116,108 @@ UpdateStatusPageRequest, UpdateTagRequest, UpdateWebhookEndpointRequest, + VerificationMethod, + Visibility, WebhookEndpointDto, WebhookTestResult, ) +from devhelm._generated import ( + ComponentStatus as StatusPageIncidentComponentStatus, # StatusPageIncidentComponentDto.component_status +) +from devhelm._generated import ( + # + # CreatedBy enums + CreatedBy as IncidentUpdateCreatedBy, # IncidentUpdateDto.created_by +) +from devhelm._generated import ( + CreatedBy1 as StatusPageUpdateCreatedBy, # StatusPageIncidentUpdateDto.created_by +) +from devhelm._generated import ( + # + # CurrentStatus enums + CurrentStatus as MonitorCurrentStatus, # ResultSummaryDto.current_status +) +from devhelm._generated import ( + CurrentStatus1 as StatusPageComponentCurrentStatus, # StatusPageComponentDto.current_status +) +from devhelm._generated import ( + # + # NewStatus / OldStatus / OverallStatus / ComponentStatus — already + # semi-descriptive but aliased for consistency with the rest of the SDK. + NewStatus as IncidentNewStatus, # AddIncidentUpdateRequest.new_status +) +from devhelm._generated import ( + OldStatus as IncidentOldStatus, # IncidentUpdateDto.old_status +) +from devhelm._generated import ( + OverallStatus as StatusPageOverallStatus, # StatusPageDto.overall_status +) +from devhelm._generated import ( + # + # Severity enums + Severity as AssertionSeverity, # AssertionResultDto.severity +) +from devhelm._generated import ( + Severity3 as IncidentSeverity, # CreateManualIncidentRequest.severity +) +from devhelm._generated import ( + Severity6 as MonitorAssertionSeverity, # MonitorAssertionDto.severity +) +from devhelm._generated import Severity7 as TriggerRuleSeverity # TriggerRule.severity +from devhelm._generated import ( + Severity8 as UpdateAssertionSeverity, # UpdateAssertionRequest.severity +) +from devhelm._generated import ( + # ── Enums: ambiguous generated names → descriptive aliases ──────────── + # + # Status enums + Status as AffectedComponentStatus, # AffectedComponent.status +) +from devhelm._generated import Status1 as AlertDeliveryStatus # AlertDeliveryDto.status +from devhelm._generated import Status2 as MembershipStatus # ChangeStatusRequest.status +from devhelm._generated import ( + Status3 as StatusPageIncidentStatus, # CreateStatusPageIncidentRequest.status +) +from devhelm._generated import Status6 as IncidentStatus # IncidentDto.status +from devhelm._generated import ( + Status8 as LinkedIncidentStatus, # LinkedStatusPageIncidentDto.status +) +from devhelm._generated import Status9 as MemberStatus # MemberDto.status +from devhelm._generated import ( + Status10 as NotificationDispatchStatus, # NotificationDispatchDto.status +) +from devhelm._generated import ( + Status11 as PublishIncidentStatus, # PublishStatusPageIncidentRequest.status +) +from devhelm._generated import ( + Status12 as ResourceGroupHealthStatus, # ResourceGroupHealthDto.status +) +from devhelm._generated import ( + Status14 as CustomDomainStatus, # StatusPageCustomDomainDto.status +) +from devhelm._generated import ( + Status15 as StatusPageUpdateStatus, # StatusPageIncidentUpdateDto.status +) +from devhelm._generated import ( + # + # Type enums + Type as ConfirmationPolicyType, # ConfirmationPolicy.type +) +from devhelm._generated import Type1 as MonitorType # CreateMonitorRequest.type +from devhelm._generated import ( + Type2 as StatusPageComponentType, # CreateStatusPageComponentRequest.type +) +from devhelm._generated import Type3 as MonitorDtoType # MonitorDto.type +from devhelm._generated import ( + Type5 as StatusPageComponentDtoType, # StatusPageComponentDto.type +) +from devhelm._generated import Type6 as TriggerRuleType # TriggerRule.type __all__ = [ + # ── DTOs ────────────────────────────────────────────────────────────── "AcquireDeployLockRequest", "AddCustomDomainRequest", + "AddIncidentUpdateRequest", "AddResourceGroupMemberRequest", "AdminAddSubscriberRequest", "AlertChannelDto", @@ -106,6 +248,7 @@ "MonitorDto", "MonitorVersionDto", "NotificationPolicyDto", + "PublishStatusPageIncidentRequest", "ReorderComponentsRequest", "ResolveIncidentRequest", "ResourceGroupDto", @@ -137,4 +280,69 @@ "UpdateWebhookEndpointRequest", "WebhookEndpointDto", "WebhookTestResult", + # ── Enums: unique names ─────────────────────────────────────────────── + "Action", + "AggregationType", + "AlertSensitivity", + "AssertionType", + "AuthType", + "ChangedVia", + "ChannelType", + "CompletionReason", + "EventType", + "HealthThresholdType", + "Impact", + "IncidentMode", + "ManagedBy", + "Method", + "Operator", + "OrgRole", + "RecordType", + "ResolutionReason", + "RoleOffered", + "Scope", + "Source", + "ThresholdStatus", + "Tier", + "TierAvailability", + "VerificationMethod", + "Visibility", + # ── Enums: descriptive aliases for ambiguous generated names ────────── + # Status + "AffectedComponentStatus", + "AlertDeliveryStatus", + "CustomDomainStatus", + "IncidentStatus", + "LinkedIncidentStatus", + "MemberStatus", + "MembershipStatus", + "NotificationDispatchStatus", + "PublishIncidentStatus", + "ResourceGroupHealthStatus", + "StatusPageIncidentStatus", + "StatusPageUpdateStatus", + # Severity + "AssertionSeverity", + "IncidentSeverity", + "MonitorAssertionSeverity", + "TriggerRuleSeverity", + "UpdateAssertionSeverity", + # Type + "ConfirmationPolicyType", + "MonitorDtoType", + "MonitorType", + "StatusPageComponentDtoType", + "StatusPageComponentType", + "TriggerRuleType", + # CurrentStatus + "MonitorCurrentStatus", + "StatusPageComponentCurrentStatus", + # CreatedBy + "IncidentUpdateCreatedBy", + "StatusPageUpdateCreatedBy", + # Already semi-descriptive, aliased for consistency + "IncidentNewStatus", + "IncidentOldStatus", + "StatusPageIncidentComponentStatus", + "StatusPageOverallStatus", ] diff --git a/tests/test_http.py b/tests/test_http.py index b2d607a..0652d6a 100644 --- a/tests/test_http.py +++ b/tests/test_http.py @@ -6,30 +6,9 @@ from pydantic import BaseModel, Field from devhelm._errors import DevhelmError -from devhelm._http import DevhelmConfig, build_client, path_param, unwrap_single +from devhelm._http import DevhelmConfig, build_client, path_param from devhelm._validation import parse_list, parse_model, parse_single -# ---------- unwrap_single (raw JSON envelope) ---------- - - -class TestUnwrapSingle: - def test_unwraps_data_key(self) -> None: - assert unwrap_single({"data": {"id": 1}}) == {"id": 1} - - def test_returns_bare_value(self) -> None: - assert unwrap_single({"id": 1}) == {"id": 1} - - def test_unwraps_null_data(self) -> None: - assert unwrap_single({"data": None}) is None - - def test_unwraps_nested_data(self) -> None: - result = unwrap_single({"data": {"data": "inner"}}) - assert result == {"data": "inner"} - - def test_non_dict_passthrough(self) -> None: - assert unwrap_single([1, 2, 3]) == [1, 2, 3] - - # ---------- path_param ---------- @@ -178,11 +157,14 @@ def test_pydantic_model_serialized(self) -> None: result = _serialize_body(item) assert result == {"myField": "test"} - def test_dict_passthrough(self) -> None: + def test_dict_rejected(self) -> None: + import pytest + + from devhelm._errors import DevhelmError from devhelm._http import _serialize_body - d = {"key": "value"} - assert _serialize_body(d) is d + with pytest.raises(DevhelmError, match="Raw dicts are not accepted"): + _serialize_body({"key": "value"}) def test_none_passthrough(self) -> None: from devhelm._http import _serialize_body diff --git a/tests/test_negative_validation.py b/tests/test_negative_validation.py index ce1e35c..5c4b71b 100644 --- a/tests/test_negative_validation.py +++ b/tests/test_negative_validation.py @@ -8,6 +8,7 @@ from __future__ import annotations +from typing import Any from uuid import uuid4 import pytest @@ -92,8 +93,8 @@ # --------------------------------------------------------------------------- -def _monitor(**kw: object) -> dict: - base: dict = { +def _monitor(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { "id": UID, "organizationId": 1, "name": "M", @@ -110,8 +111,8 @@ def _monitor(**kw: object) -> dict: return base -def _incident(**kw: object) -> dict: - base: dict = { +def _incident(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { "id": UID, "organizationId": 1, "source": "MANUAL", @@ -128,8 +129,8 @@ def _incident(**kw: object) -> dict: return base -def _alert_channel(**kw: object) -> dict: - base: dict = { +def _alert_channel(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { "id": UID, "name": "ch", "channelType": "slack", @@ -140,8 +141,8 @@ def _alert_channel(**kw: object) -> dict: return base -def _api_key(**kw: object) -> dict: - base: dict = { +def _api_key(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { "id": 1, "name": "k", "key": "dh_live_x", @@ -152,8 +153,8 @@ def _api_key(**kw: object) -> dict: return base -def _environment(**kw: object) -> dict: - base: dict = { +def _environment(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { "id": UID, "orgId": 1, "name": "prod", @@ -168,8 +169,8 @@ def _environment(**kw: object) -> dict: return base -def _secret(**kw: object) -> dict: - base: dict = { +def _secret(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { "id": UID, "key": "MY_KEY", "dekVersion": 1, @@ -181,8 +182,8 @@ def _secret(**kw: object) -> dict: return base -def _tag(**kw: object) -> dict: - base: dict = { +def _tag(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { "id": UID, "organizationId": 1, "name": "prod", @@ -194,8 +195,8 @@ def _tag(**kw: object) -> dict: return base -def _webhook(**kw: object) -> dict: - base: dict = { +def _webhook(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { "id": UID, "url": "https://hook.example.com", "subscribedEvents": ["monitor.created"], @@ -208,14 +209,19 @@ def _webhook(**kw: object) -> dict: return base -def _deploy_lock(**kw: object) -> dict: - base: dict = {"id": UID, "lockedBy": "ci-job-42", "lockedAt": NOW, "expiresAt": NOW} +def _deploy_lock(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { + "id": UID, + "lockedBy": "ci-job-42", + "lockedAt": NOW, + "expiresAt": NOW, + } base.update(kw) return base -def _resource_group(**kw: object) -> dict: - base: dict = { +def _resource_group(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { "id": UID, "organizationId": 1, "name": "rg", @@ -234,8 +240,8 @@ def _resource_group(**kw: object) -> dict: return base -def _notification_policy(**kw: object) -> dict: - base: dict = { +def _notification_policy(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { "id": UID, "organizationId": 1, "name": "np", @@ -250,8 +256,8 @@ def _notification_policy(**kw: object) -> dict: return base -def _status_page(**kw: object) -> dict: - base: dict = { +def _status_page(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { "id": UID, "organizationId": 1, "workspaceId": 1, @@ -268,8 +274,8 @@ def _status_page(**kw: object) -> dict: return base -def _sp_component(**kw: object) -> dict: - base: dict = { +def _sp_component(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { "id": UID, "statusPageId": UID, "name": "API", @@ -286,8 +292,8 @@ def _sp_component(**kw: object) -> dict: return base -def _sp_group(**kw: object) -> dict: - base: dict = { +def _sp_group(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { "id": UID, "statusPageId": UID, "name": "Infra", @@ -301,8 +307,8 @@ def _sp_group(**kw: object) -> dict: return base -def _sp_incident(**kw: object) -> dict: - base: dict = { +def _sp_incident(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { "id": UID, "statusPageId": UID, "title": "Down", @@ -318,8 +324,8 @@ def _sp_incident(**kw: object) -> dict: return base -def _sp_incident_update(**kw: object) -> dict: - base: dict = { +def _sp_incident_update(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { "id": UID, "status": "INVESTIGATING", "body": "Looking into it", @@ -330,14 +336,19 @@ def _sp_incident_update(**kw: object) -> dict: return base -def _sp_subscriber(**kw: object) -> dict: - base: dict = {"id": UID, "email": "a@b.com", "confirmed": True, "createdAt": NOW} +def _sp_subscriber(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { + "id": UID, + "email": "a@b.com", + "confirmed": True, + "createdAt": NOW, + } base.update(kw) return base -def _sp_custom_domain(**kw: object) -> dict: - base: dict = { +def _sp_custom_domain(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { "id": UID, "hostname": "status.example.com", "status": "ACTIVE", @@ -352,8 +363,8 @@ def _sp_custom_domain(**kw: object) -> dict: return base -def _sp_incident_component(**kw: object) -> dict: - base: dict = { +def _sp_incident_component(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { "statusPageComponentId": UID, "componentStatus": "OPERATIONAL", "componentName": "API", @@ -362,14 +373,19 @@ def _sp_incident_component(**kw: object) -> dict: return base -def _check_result(**kw: object) -> dict: - base: dict = {"id": UID, "timestamp": NOW, "region": "us-east", "passed": True} +def _check_result(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { + "id": UID, + "timestamp": NOW, + "region": "us-east", + "passed": True, + } base.update(kw) return base -def _incident_policy(**kw: object) -> dict: - base: dict = { +def _incident_policy(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { "id": UID, "monitorId": UID, "triggerRules": [ @@ -392,8 +408,8 @@ def _incident_policy(**kw: object) -> dict: return base -def _monitor_version(**kw: object) -> dict: - base: dict = { +def _monitor_version(**kw: object) -> dict[str, Any]: + base: dict[str, Any] = { "id": UID, "monitorId": UID, "version": 1, @@ -405,7 +421,7 @@ def _monitor_version(**kw: object) -> dict: return base -def _del(d: dict, key: str) -> dict: +def _del(d: dict[str, Any], key: str) -> dict[str, Any]: c = dict(d) del c[key] return c @@ -904,44 +920,24 @@ def test_missing_name(self) -> None: } ) - def test_missing_match_rules(self) -> None: - with pytest.raises(ValidationError, match="matchRules"): - CreateNotificationPolicyRequest.model_validate( - { - "name": "np", - "escalation": {"steps": [{"delayMinutes": 0, "channelIds": [UID]}]}, - "enabled": True, - "priority": 0, - } - ) - def test_missing_escalation(self) -> None: with pytest.raises(ValidationError, match="escalation"): CreateNotificationPolicyRequest.model_validate( {"name": "np", "matchRules": [], "enabled": True, "priority": 0} ) - def test_missing_enabled(self) -> None: - with pytest.raises(ValidationError, match="enabled"): - CreateNotificationPolicyRequest.model_validate( - { - "name": "np", - "matchRules": [], - "escalation": {"steps": [{"delayMinutes": 0, "channelIds": [UID]}]}, - "priority": 0, - } - ) - - def test_missing_priority(self) -> None: - with pytest.raises(ValidationError, match="priority"): - CreateNotificationPolicyRequest.model_validate( - { - "name": "np", - "matchRules": [], - "escalation": {"steps": [{"delayMinutes": 0, "channelIds": [UID]}]}, - "enabled": True, - } - ) + def test_missing_optional_fields_accepted(self) -> None: + """matchRules / enabled / priority are optional in the spec; only name + + escalation are required. The model must accept payloads without them.""" + model = CreateNotificationPolicyRequest.model_validate( + { + "name": "np", + "escalation": {"steps": [{"delayMinutes": 0, "channelIds": [UID]}]}, + } + ) + assert model.match_rules is None + assert model.enabled is None or model.enabled is True + assert model.priority is None or model.priority == 0 def test_null_name(self) -> None: with pytest.raises(ValidationError): @@ -2781,13 +2777,15 @@ def test_invalid_component_id(self) -> None: class TestResolveIncidentRequestNegative: - def test_missing_body(self) -> None: - with pytest.raises(ValidationError, match="body"): - ResolveIncidentRequest.model_validate({}) - - def test_null_body(self) -> None: - with pytest.raises(ValidationError): - ResolveIncidentRequest.model_validate({"body": None}) + def test_missing_body_accepted(self) -> None: + """body is optional (nullable) — empty payload should be accepted.""" + model = ResolveIncidentRequest.model_validate({}) + assert model.body is None + + def test_null_body_accepted(self) -> None: + """Explicit null body matches the spec's nullable=true and should be ok.""" + model = ResolveIncidentRequest.model_validate({"body": None}) + assert model.body is None def test_wrong_body_type(self) -> None: with pytest.raises(ValidationError): diff --git a/tests/test_schemas.py b/tests/test_schemas.py index bfad2c4..5888a8f 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -201,9 +201,9 @@ def test_valid(self) -> None: r = ResolveIncidentRequest.model_validate({"body": "All clear"}) assert r.body == "All clear" - def test_missing_body_raises(self) -> None: - with pytest.raises(ValidationError, match="body"): - ResolveIncidentRequest.model_validate({}) + def test_missing_body_accepted(self) -> None: + r = ResolveIncidentRequest.model_validate({}) + assert r.body is None class TestCreateManualIncidentRequest: diff --git a/tests/test_spec_parity.py b/tests/test_spec_parity.py new file mode 100644 index 0000000..e3e289e --- /dev/null +++ b/tests/test_spec_parity.py @@ -0,0 +1,387 @@ +"""Spec parity tests. + +These tests are the canary for *logical* drift between the vendored +``docs/openapi/monitoring-api.json`` and the SDK surface. ``spec-check.yml`` +already verifies that the spec re-generates without crashing and that the +package compiles, but it does **not** notice when: + +* a public DTO that shipped in a previous SDK release silently disappears from + the spec (or gets renamed), or +* a required field on a shipped request shrinks or grows, or +* a path used by a hand-written resource method is no longer present in the + spec. + +The hand-built dictionaries in ``test_schemas.py`` exercise the *runtime* +behaviour of the Pydantic models, but they are decoupled from the spec — they +will keep passing even if the spec drops the schema entirely (because they +import the generated class, which is also re-generated from the same spec). +The assertions below close that loop by reading the OpenAPI document at test +time and asserting structural agreement against the SDK's public surface. +""" + +from __future__ import annotations + +import json +from collections.abc import Mapping +from pathlib import Path +from typing import Any, get_type_hints + +import pytest +from pydantic import BaseModel + +import devhelm +from devhelm import _generated +from devhelm.resources.alert_channels import AlertChannels +from devhelm.resources.api_keys import ApiKeys +from devhelm.resources.deploy_lock import DeployLock +from devhelm.resources.environments import Environments +from devhelm.resources.incidents import Incidents +from devhelm.resources.monitors import Monitors +from devhelm.resources.notification_policies import NotificationPolicies +from devhelm.resources.resource_groups import ResourceGroups +from devhelm.resources.secrets import Secrets +from devhelm.resources.status_pages import StatusPages +from devhelm.resources.status_pages import _Components as StatusPageComponents +from devhelm.resources.status_pages import _Domains as StatusPageCustomDomains +from devhelm.resources.status_pages import _Groups as StatusPageComponentGroups +from devhelm.resources.status_pages import _Incidents as StatusPageIncidents +from devhelm.resources.status_pages import _Subscribers as StatusPageSubscribers +from devhelm.resources.tags import Tags +from devhelm.resources.webhooks import Webhooks + +SPEC_PATH = Path(__file__).parent.parent / "docs" / "openapi" / "monitoring-api.json" + + +@pytest.fixture(scope="module") +def spec() -> dict[str, Any]: + """Load the vendored OpenAPI spec once per module.""" + loaded: dict[str, Any] = json.loads(SPEC_PATH.read_text()) + return loaded + + +@pytest.fixture(scope="module") +def schemas(spec: dict[str, Any]) -> dict[str, Any]: + result: dict[str, Any] = spec["components"]["schemas"] + return result + + +@pytest.fixture(scope="module") +def paths(spec: dict[str, Any]) -> dict[str, Any]: + result: dict[str, Any] = spec["paths"] + return result + + +# ---------- 1. Schema parity: every public DTO must exist in the spec ---------- + + +# DTOs that are intentionally hand-defined in the SDK (not from the spec) or +# that the spec omits because they are response wrappers materialised +# server-side. Keep this list small and document each entry. +SCHEMAS_NOT_IN_SPEC: frozenset[str] = frozenset( + { + # Pagination wrappers — generated from `Page` / `CursorPage` generics + # and not present as standalone schemas in the OpenAPI document. + "Page", + "CursorPage", + } +) + + +def _public_dto_names() -> list[str]: + """Public DTOs the SDK re-exports that map 1:1 to spec schemas.""" + candidates: list[str] = [] + for name in devhelm.__all__: + obj = getattr(devhelm, name, None) + if obj is None: + continue + if not isinstance(obj, type): + continue + if not issubclass(obj, BaseModel): + continue + if name in SCHEMAS_NOT_IN_SPEC: + continue + candidates.append(name) + return sorted(candidates) + + +@pytest.mark.parametrize("dto_name", _public_dto_names()) +def test_public_dto_exists_in_spec(dto_name: str, schemas: dict[str, Any]) -> None: + """Every public DTO/Request type the SDK re-exports must exist in the spec.""" + assert dto_name in schemas, ( + f"{dto_name} is exported from `devhelm` but missing from the OpenAPI " + "spec. Either it was renamed/removed upstream (regenerate _generated.py) " + "or it should be added to SCHEMAS_NOT_IN_SPEC with a comment." + ) + + +# ---------- 2. Required-field parity for request DTOs ---------- + + +# ``RequestBody`` body parameters resolved per resource — every entry here is +# (model_class, ``method`` name) — kept narrow on purpose: these are the +# request shapes documented in the README and used in our typed examples. +REQUEST_DTO_NAMES: list[str] = sorted( + { + "AcquireDeployLockRequest", + "AddCustomDomainRequest", + "AddIncidentUpdateRequest", + "AddResourceGroupMemberRequest", + "AdminAddSubscriberRequest", + "CreateAlertChannelRequest", + "CreateApiKeyRequest", + "CreateEnvironmentRequest", + "CreateManualIncidentRequest", + "CreateMonitorRequest", + "CreateNotificationPolicyRequest", + "CreateResourceGroupRequest", + "CreateSecretRequest", + "CreateStatusPageComponentGroupRequest", + "CreateStatusPageComponentRequest", + "CreateStatusPageIncidentRequest", + "CreateStatusPageIncidentUpdateRequest", + "CreateStatusPageRequest", + "CreateTagRequest", + "CreateWebhookEndpointRequest", + "ReorderComponentsRequest", + "ResolveIncidentRequest", + "UpdateAlertChannelRequest", + "UpdateEnvironmentRequest", + "UpdateMonitorRequest", + "UpdateNotificationPolicyRequest", + "UpdateResourceGroupRequest", + "UpdateSecretRequest", + "UpdateStatusPageComponentGroupRequest", + "UpdateStatusPageComponentRequest", + "UpdateStatusPageIncidentRequest", + "UpdateStatusPageRequest", + "UpdateTagRequest", + "UpdateWebhookEndpointRequest", + } +) + + +@pytest.mark.parametrize("dto_name", REQUEST_DTO_NAMES) +def test_request_required_fields_match_spec( + dto_name: str, schemas: dict[str, Any] +) -> None: + """Required fields on the spec must be required on the Pydantic model. + + A request DTO going from required → optional in the spec without the + Pydantic model following suit means the SDK silently rejects payloads + the API would accept. The reverse — required field added to the spec + but missing on the model — means the SDK lets you send an obviously + malformed payload. Either way, this test catches the drift. + """ + spec_schema = schemas.get(dto_name) + assert spec_schema is not None, ( + f"{dto_name} listed in REQUEST_DTO_NAMES but missing from spec " + "(see test_public_dto_exists_in_spec for details)." + ) + + spec_required = set(spec_schema.get("required", [])) + + model_cls = getattr(_generated, dto_name) + model_required = { + # Pydantic stores the wire name as ``alias`` when one is set, and + # falls back to the Python attribute name otherwise. + (info.alias or field_name) + for field_name, info in model_cls.model_fields.items() + if info.is_required() + } + + # The spec is the source of truth; model_required must be a *superset* + # of spec_required. Models may legitimately mark additional fields as + # required for extra runtime safety, but they must never relax a field + # the spec considers required. + missing = spec_required - model_required + assert not missing, ( + f"{dto_name}: fields {sorted(missing)} are required in the OpenAPI " + "spec but optional on the generated Pydantic model. Re-run " + "`uv run datamodel-codegen` after pulling the latest spec, or align " + "the manual override." + ) + + +# ---------- 3. Method body parameters accept ``Mapping[str, Any]`` ---------- + + +# Resource classes whose mutating methods take ``RequestBody[T]`` body params. +# Listed here so a contributor adding a new resource gets the parity check +# for free. Each tuple is (resource_class, [(method_name, expected_model)]). +RESOURCE_BODY_METHODS: list[tuple[type, list[tuple[str, str]]]] = [ + ( + Monitors, + [("create", "CreateMonitorRequest"), ("update", "UpdateMonitorRequest")], + ), + ( + Incidents, + [ + ("create", "CreateManualIncidentRequest"), + ("resolve", "ResolveIncidentRequest"), + ], + ), + ( + AlertChannels, + [ + ("create", "CreateAlertChannelRequest"), + ("update", "UpdateAlertChannelRequest"), + ], + ), + ( + NotificationPolicies, + [ + ("create", "CreateNotificationPolicyRequest"), + ("update", "UpdateNotificationPolicyRequest"), + ], + ), + ( + Environments, + [ + ("create", "CreateEnvironmentRequest"), + ("update", "UpdateEnvironmentRequest"), + ], + ), + (Secrets, [("create", "CreateSecretRequest"), ("update", "UpdateSecretRequest")]), + (Tags, [("create", "CreateTagRequest"), ("update", "UpdateTagRequest")]), + ( + ResourceGroups, + [ + ("create", "CreateResourceGroupRequest"), + ("update", "UpdateResourceGroupRequest"), + ("add_member", "AddResourceGroupMemberRequest"), + ], + ), + ( + Webhooks, + [ + ("create", "CreateWebhookEndpointRequest"), + ("update", "UpdateWebhookEndpointRequest"), + ], + ), + (ApiKeys, [("create", "CreateApiKeyRequest")]), + (DeployLock, [("acquire", "AcquireDeployLockRequest")]), + ( + StatusPages, + [("create", "CreateStatusPageRequest"), ("update", "UpdateStatusPageRequest")], + ), + ( + StatusPageComponents, + [ + ("create", "CreateStatusPageComponentRequest"), + ("update", "UpdateStatusPageComponentRequest"), + ("reorder", "ReorderComponentsRequest"), + ], + ), + ( + StatusPageComponentGroups, + [ + ("create", "CreateStatusPageComponentGroupRequest"), + ("update", "UpdateStatusPageComponentGroupRequest"), + ], + ), + ( + StatusPageIncidents, + [ + ("create", "CreateStatusPageIncidentRequest"), + ("update", "UpdateStatusPageIncidentRequest"), + ("post_update", "CreateStatusPageIncidentUpdateRequest"), + ], + ), + (StatusPageSubscribers, [("add", "AdminAddSubscriberRequest")]), + (StatusPageCustomDomains, [("add", "AddCustomDomainRequest")]), +] + + +def _resource_method_params() -> list[tuple[type, str, str]]: + return [ + (cls, method, expected) + for cls, methods in RESOURCE_BODY_METHODS + for method, expected in methods + ] + + +@pytest.mark.parametrize( + "resource_cls,method_name,expected_model", + _resource_method_params(), + ids=lambda v: v if isinstance(v, str) else v.__name__, +) +def test_resource_method_body_accepts_dict( + resource_cls: type, method_name: str, expected_model: str +) -> None: + """Every mutating resource method's ``body`` param accepts a dict. + + The ticket calls out the dict-vs-model ergonomics conflict: README + examples show dicts (``client.monitors.create({"name": "foo"})``) but + earlier signatures only accepted the Pydantic model. We resolved this + by widening the type to ``RequestBody[T] = T | Mapping[str, Any]``. + The assertion below ensures every body parameter actually has the + union — without it, ``mypy --strict`` would reject the documented + usage. + """ + method = getattr(resource_cls, method_name) + hints = get_type_hints(method) + assert "body" in hints, f"{resource_cls.__name__}.{method_name} has no body param" + annotation = hints["body"] + + # ``RequestBody[T]`` resolves to a ``Union[T, Mapping[str, Any]]``. + # ``get_type_hints`` returns it as ``typing.Union`` so we can introspect + # the args directly. Optional bodies (``RequestBody[T] | None``) carry + # an extra ``NoneType`` arm. + args = getattr(annotation, "__args__", ()) + assert args, ( + f"{resource_cls.__name__}.{method_name} body annotation is not a Union; " + f"got {annotation!r}" + ) + + arg_names = {getattr(a, "__name__", str(a)) for a in args} + # Mapping[str, Any] becomes typing.Mapping after ``get_type_hints``; its + # origin is collections.abc.Mapping. We accept either spelling. + has_mapping = any(getattr(a, "__origin__", None) is Mapping for a in args) + assert has_mapping, ( + f"{resource_cls.__name__}.{method_name} body should accept " + f"Mapping[str, Any] for dict ergonomics; got args {arg_names}" + ) + assert expected_model in arg_names, ( + f"{resource_cls.__name__}.{method_name} body should also accept " + f"the typed {expected_model}; got args {arg_names}" + ) + + +# ---------- 4. Hand-written paths exist in the spec ---------- + + +# Paths the SDK calls that aren't bound to a single resource method we want +# to enumerate above. These are the substring matchers we use to assert the +# path appears in the spec at all (the SDK templates ``{id}`` etc. by hand, +# so we check the leading segments). +SDK_PATH_PREFIXES: list[str] = [ + "/api/v1/monitors", + "/api/v1/incidents", + "/api/v1/alert-channels", + "/api/v1/notification-policies", + "/api/v1/environments", + "/api/v1/secrets", + "/api/v1/tags", + "/api/v1/resource-groups", + "/api/v1/webhooks", + "/api/v1/api-keys", + "/api/v1/deploy/lock", + "/api/v1/status-pages", + "/api/v1/service-subscriptions", +] + + +@pytest.mark.parametrize("prefix", SDK_PATH_PREFIXES) +def test_sdk_path_prefix_in_spec(prefix: str, paths: dict[str, Any]) -> None: + """Every top-level path the SDK hits must exist in the spec. + + Catches the case where the API renames an endpoint family (e.g. + ``/v1/monitors`` → ``/v2/monitors``) and the SDK still calls the old + one. ``spec-check.yml`` would not catch this because the spec still + parses cleanly. + """ + assert any(p.startswith(prefix) for p in paths), ( + f"No spec path starts with {prefix}. Either the API was renamed " + "(update the SDK resources) or this prefix should be removed from " + "SDK_PATH_PREFIXES." + ) diff --git a/tests/test_typing.py b/tests/test_typing.py new file mode 100644 index 0000000..e8a91d3 --- /dev/null +++ b/tests/test_typing.py @@ -0,0 +1,66 @@ +"""Lock in strict typing posture for the SDK. + +These tests ensure that: + * mypy strict + ``disallow_any_explicit`` runs cleanly across the entire + package, including the previously-excluded ``_generated.py``. + * The single justified ``# type: ignore`` comment is the only one in + hand-written code (generated code may add ``[assignment]`` for the + ``HealthThresholdType.count`` collision documented in ``scripts/typegen.sh``). + +Skipped automatically if mypy is unavailable in the runtime environment (e.g. +when shipping the wheel without the ``dev`` group installed). +""" + +from __future__ import annotations + +import re +import shutil +import subprocess +from pathlib import Path + +import pytest + +ROOT = Path(__file__).resolve().parent.parent +SRC = ROOT / "src" / "devhelm" + + +def _have_mypy() -> bool: + return shutil.which("mypy") is not None + + +@pytest.mark.skipif(not _have_mypy(), reason="mypy not installed") +def test_mypy_strict_passes_including_generated() -> None: + result = subprocess.run( # noqa: S603 — fixed argv, no shell. + ["mypy", "src"], cwd=ROOT, check=False, capture_output=True, text=True + ) + assert result.returncode == 0, ( + "mypy strict (with disallow_any_explicit) must pass for the entire " + "package including the generated models.\n" + f"stdout:\n{result.stdout}\n\nstderr:\n{result.stderr}" + ) + + +def test_handwritten_modules_have_only_documented_type_ignores() -> None: + """Catch sneaky `# type: ignore` additions outside the generated file.""" + pattern = re.compile(r"#\s*type:\s*ignore") + offenders: list[str] = [] + for path in SRC.rglob("*.py"): + if path.name == "_generated.py": + continue + text = path.read_text() + for lineno, line in enumerate(text.splitlines(), start=1): + if pattern.search(line): + offenders.append(f"{path.relative_to(ROOT)}:{lineno}: {line.strip()}") + expected = { + "src/devhelm/_validation.py:79: adapter: TypeAdapter[list[M]] = " + "TypeAdapter(list[model_class]) # type: ignore[valid-type]", + "src/devhelm/_http.py:95: return response.json() # type: " + "ignore[no-any-return]", + } + actual = set(offenders) + extra = actual - expected + assert not extra, ( + "Unexpected `# type: ignore` comments outside the generated file. " + "Each suppression must be documented and added to the test allow-list:\n" + + "\n".join(sorted(extra)) + )