{
  "components": {
    "responses": {
      "BadRequest": {
        "content": {
          "application/json": {
            "example": {
              "error": "validation_error",
              "error_description": "Invalid input parameters",
              "status": 400
            },
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "description": "Bad Request"
      },
      "Conflict": {
        "content": {
          "application/json": {
            "example": {
              "error": "conflict",
              "error_description": "Resource already exists",
              "status": 409
            },
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "description": "Conflict"
      },
      "Forbidden": {
        "content": {
          "application/json": {
            "example": {
              "error": "forbidden",
              "error_description": "Access denied",
              "status": 403
            },
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "description": "Forbidden"
      },
      "FreeOrgLimit": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/FreeOrgLimitError"
            }
          }
        },
        "description": "Conflict - one free organization per owner"
      },
      "InternalServerError": {
        "content": {
          "application/json": {
            "example": {
              "error": "internal_server_error",
              "error_description": "An unexpected error occurred",
              "status": 500
            },
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "description": "Internal Server Error"
      },
      "NotFound": {
        "content": {
          "application/json": {
            "example": {
              "error": "not_found",
              "error_description": "Resource not found",
              "status": 404
            },
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "description": "Not Found"
      },
      "PlanFeatureRequired": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PlanFeatureError"
            }
          }
        },
        "description": "Payment Required - the feature is not included in the organization's plan"
      },
      "PlanLimitReached": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PlanLimitError"
            }
          }
        },
        "description": "Payment Required - a count cap of the organization's plan was reached"
      },
      "RateLimited": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RateLimitError"
            }
          }
        },
        "description": "Too Many Requests for the organization's plan; retry after `Retry-After` seconds",
        "headers": {
          "Retry-After": {
            "description": "Seconds until the bucket resets (the same value as `retry_after` in the body).",
            "schema": {
              "type": "integer"
            }
          }
        }
      },
      "TooManyRequests": {
        "content": {
          "application/json": {
            "example": {
              "error": "RATE_LIMIT_COOLDOWN",
              "error_description": "Please wait before retrying",
              "status": 429
            },
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "description": "Too Many Requests"
      },
      "Unauthorized": {
        "content": {
          "application/json": {
            "example": {
              "error": "unauthorized",
              "error_description": "Authentication required",
              "status": 401
            },
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "description": "Unauthorized"
      }
    },
    "schemas": {
      "AIInsight": {
        "description": "An AI-generated insight based on alert data analysis",
        "properties": {
          "action": {
            "$ref": "#/components/schemas/InsightAction"
          },
          "confidence": {
            "description": "Confidence score indicating how reliable the insight is (0-100); null for a `review`",
            "example": 87,
            "maximum": 100,
            "minimum": 0,
            "nullable": true,
            "type": "integer"
          },
          "description": {
            "description": "Detailed explanation of the insight (max 500 characters)",
            "example": "Analysis of last 30 days shows critical alerts peak in the afternoon. Consider adding dedicated on-call coverage during this window.",
            "maxLength": 500,
            "type": "string"
          },
          "dismissed_at": {
            "description": "When the insight was dismissed by a user (if dismissed)",
            "example": null,
            "format": "date-time",
            "type": "string"
          },
          "dismissed_by": {
            "description": "User ID who dismissed the insight (if dismissed)",
            "example": null,
            "format": "uuid",
            "type": "string"
          },
          "expires_at": {
            "description": "When the insight expires and will be automatically removed, unless a later run refreshes it",
            "example": "2026-01-07T12:00:00Z",
            "format": "date-time",
            "type": "string"
          },
          "generated_at": {
            "description": "When the insight was generated",
            "example": "2026-01-05T12:00:00Z",
            "format": "date-time",
            "type": "string"
          },
          "insight_id": {
            "description": "Unique identifier for the insight",
            "example": "550e8400-e29b-41d4-a716-446655440000",
            "format": "uuid",
            "type": "string"
          },
          "last_seen_at": {
            "description": "When the analysis last found the insight still true. Equals\n`generated_at` until a later run refreshes the insight (which also\nmoves `expires_at` and updates `metadata.metrics`).\n",
            "example": "2026-01-06T03:00:00Z",
            "format": "date-time",
            "type": "string"
          },
          "metadata": {
            "$ref": "#/components/schemas/InsightMetadata"
          },
          "occurrences": {
            "description": "How many runs produced this insight (1 at creation, one more per refresh)",
            "example": 2,
            "minimum": 1,
            "type": "integer"
          },
          "organization_id": {
            "description": "ID of the organization this insight belongs to",
            "example": "org-123",
            "format": "uuid",
            "type": "string"
          },
          "organization_name": {
            "description": "URL-compliant name of the organization",
            "example": "acme-corp",
            "type": "string"
          },
          "severity": {
            "$ref": "#/components/schemas/InsightSeverity"
          },
          "title": {
            "description": "Short headline for the insight (max 120 characters)",
            "example": "70% of P1 alerts occur between 2-4 PM",
            "maxLength": 120,
            "type": "string"
          },
          "type": {
            "$ref": "#/components/schemas/InsightType"
          }
        },
        "required": [
          "insight_id",
          "organization_id",
          "organization_name",
          "type",
          "severity",
          "title",
          "description",
          "confidence",
          "metadata",
          "generated_at",
          "expires_at"
        ],
        "type": "object"
      },
      "AIInsightsPagination": {
        "description": "Pagination information for insights response",
        "properties": {
          "has_more": {
            "description": "Whether there are more insights available",
            "example": false,
            "type": "boolean"
          },
          "limit": {
            "description": "Maximum number of insights requested",
            "example": 10,
            "type": "integer"
          },
          "total": {
            "description": "Total number of insights returned",
            "example": 5,
            "type": "integer"
          }
        },
        "required": [
          "total",
          "limit",
          "has_more"
        ],
        "type": "object"
      },
      "AIInsightsResponse": {
        "description": "Response containing AI-generated insights. AI insights are a Pro plan\nfeature: for a Free plan organization the answer is still a 200, with no\ninsights, `locked: true`, `reason: plan_feature_required` and the\norganization's Plan page in `upgrade_path`.\n",
        "example": {
          "insights": [
            {
              "action": {
                "label": "View P1 Alerts",
                "link": "/acme-corp/alerts?severity=P1",
                "type": "internal"
              },
              "confidence": 87,
              "description": "Analysis of last 30 days shows critical alerts peak in the afternoon. Consider adding dedicated on-call coverage during this window.",
              "expires_at": "2026-01-07T12:00:00Z",
              "generated_at": "2026-01-05T12:00:00Z",
              "insight_id": "550e8400-e29b-41d4-a716-446655440000",
              "metadata": {
                "affected_entities": [
                  "Platform Team",
                  "Infrastructure Team"
                ],
                "category": "time_pattern",
                "metrics": {
                  "peak_hour_end": 16,
                  "peak_hour_start": 14,
                  "percentage": 70,
                  "sample_size": 120
                },
                "time_range": {
                  "end": "2026-01-05T00:00:00Z",
                  "start": "2025-12-06T00:00:00Z"
                }
              },
              "organization_id": "org-123",
              "organization_name": "acme-corp",
              "severity": "info",
              "title": "70% of P1 alerts occur between 2-4 PM",
              "type": "pattern"
            }
          ],
          "meta": {
            "generated_at": "2026-01-05T12:00:00Z",
            "next_generation": "2026-01-05T13:00:00Z"
          },
          "pagination": {
            "has_more": false,
            "limit": 10,
            "total": 1
          }
        },
        "properties": {
          "feature": {
            "description": "The locked feature (only with `locked: true`)",
            "enum": [
              "ai_insights"
            ],
            "type": "string"
          },
          "insights": {
            "description": "List of AI insights (empty when locked)",
            "items": {
              "$ref": "#/components/schemas/AIInsight"
            },
            "type": "array"
          },
          "locked": {
            "description": "`true` when the organization's plan does not include AI insights",
            "type": "boolean"
          },
          "meta": {
            "$ref": "#/components/schemas/AIInsightsResponseMeta"
          },
          "pagination": {
            "$ref": "#/components/schemas/AIInsightsPagination"
          },
          "plan": {
            "description": "The organization's plan (only with `locked: true`)",
            "enum": [
              "free",
              "paid"
            ],
            "type": "string"
          },
          "reason": {
            "description": "Why the insights are locked (only with `locked: true`)",
            "enum": [
              "plan_feature_required"
            ],
            "type": "string"
          },
          "upgrade_path": {
            "description": "The app route of the organization's Plan page (only with `locked: true`)",
            "example": "/org/acme-corp/settings/plan",
            "type": "string"
          }
        },
        "required": [
          "insights",
          "pagination",
          "meta"
        ],
        "type": "object"
      },
      "AIInsightsResponseMeta": {
        "description": "Metadata about the insights response (both fields null on a locked response)",
        "properties": {
          "generated_at": {
            "description": "When the analysis last produced or confirmed one of the returned insights (`null` when there are none)",
            "example": "2026-01-05T12:00:00Z",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "next_generation": {
            "description": "When the insights are next refreshed (every hour)",
            "example": "2026-01-05T13:00:00Z",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "generated_at",
          "next_generation"
        ],
        "type": "object"
      },
      "AccountState": {
        "description": "The deletion state of an account. `active` is the normal state; `pending_deletion` means the user asked for the deletion and the account is purged at `deletion_purge_at` unless it is restored before. `grace_days` is the length of the grace period (30).\n",
        "example": {
          "deletion_purge_at": "2026-11-05T10:00:00.000Z",
          "deletion_requested_at": "2026-10-06T10:00:00.000Z",
          "grace_days": 30,
          "status": "pending_deletion"
        },
        "properties": {
          "deletion_purge_at": {
            "description": "When the account and the organizations it owns are purged; null while active.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "deletion_requested_at": {
            "description": "When the deletion was requested; null while active.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "grace_days": {
            "example": 30,
            "type": "integer"
          },
          "status": {
            "enum": [
              "active",
              "pending_deletion"
            ],
            "type": "string"
          }
        },
        "required": [
          "status",
          "grace_days"
        ],
        "type": "object"
      },
      "AckTokenError": {
        "description": "A JSON error of the ACK token route (web app call). `not_found`,\n`already_acknowledged` and `already_closed` are final: the token was\nconsumed. `ack_failed` (an outage) and `bad_request` keep the token so\nthe page can retry, as the 503 `alert_busy` (AlertBusyError) does.\n",
        "example": {
          "acknowledged_by_name": "Ana Torres",
          "error": "already_acknowledged",
          "error_description": "This alert was already acknowledged by Ana Torres",
          "status": 409
        },
        "properties": {
          "acknowledged_by_name": {
            "description": "Who acknowledged the alert first (`already_acknowledged`, when the name is known).",
            "type": "string"
          },
          "error": {
            "enum": [
              "not_found",
              "already_acknowledged",
              "already_closed",
              "ack_failed",
              "bad_request"
            ],
            "type": "string"
          },
          "error_description": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          }
        },
        "required": [
          "error",
          "error_description",
          "status"
        ],
        "type": "object"
      },
      "AckTokenResponse": {
        "description": "The JSON answer of the ACK token route to the web app's confirmation\npage (the e-mail's \"Acknowledge\" button).\n",
        "example": {
          "alert": {
            "acknowledged_at": "2023-10-15T10:35:00Z",
            "acknowledged_by": "user-123",
            "acknowledged_by_name": "Ana Torres",
            "alert_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
            "href": "/v1/organizations/Acme Corp/incident-management/Platform/alerts/a1b2c3d4-5678-90ab-cdef-1234567890ab",
            "organization_name": "Acme Corp",
            "severity": "P2",
            "status": "acknowledged",
            "team_name": "Platform",
            "title": "High CPU Usage on web-server-01"
          },
          "message": "Alert acknowledged successfully"
        },
        "properties": {
          "alert": {
            "description": "What the confirmation page shows.",
            "properties": {
              "acknowledged_at": {
                "format": "date-time",
                "type": "string"
              },
              "acknowledged_by": {
                "description": "User id of the token's user, now the assignee.",
                "type": "string"
              },
              "acknowledged_by_name": {
                "description": "Display name of the token's user (the user id when it cannot be resolved).",
                "type": "string"
              },
              "alert_id": {
                "format": "uuid",
                "type": "string"
              },
              "href": {
                "description": "API path of the alert.",
                "type": "string"
              },
              "organization_name": {
                "type": "string"
              },
              "severity": {
                "type": "string"
              },
              "status": {
                "enum": [
                  "acknowledged"
                ],
                "type": "string"
              },
              "team_name": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "type": "object"
          },
          "message": {
            "example": "Alert acknowledged successfully",
            "type": "string"
          }
        },
        "required": [
          "message",
          "alert"
        ],
        "type": "object"
      },
      "Acquisition": {
        "description": "The campaign identifiers the web app captured on the landing page, stored on\nthe organization for attribution. Only these fields are kept (strings of at\nmost 256 characters); anything else is dropped. The 201 of POST /organizations\nanswers what it kept; no read answers them (they are the creator's ad click ids).\n",
        "nullable": true,
        "properties": {
          "captured_at": {
            "format": "date-time",
            "type": "string"
          },
          "fbclid": {
            "type": "string"
          },
          "gbraid": {
            "type": "string"
          },
          "gclid": {
            "type": "string"
          },
          "landing_path": {
            "type": "string"
          },
          "lp_variant": {
            "type": "string"
          },
          "msclkid": {
            "type": "string"
          },
          "utm_campaign": {
            "type": "string"
          },
          "utm_content": {
            "type": "string"
          },
          "utm_medium": {
            "type": "string"
          },
          "utm_source": {
            "type": "string"
          },
          "utm_term": {
            "type": "string"
          },
          "wbraid": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "ActivityList": {
        "example": {
          "filters": {
            "activity_type": "alertAction",
            "limit": 50
          },
          "info_level": "basic",
          "items": [
            {
              "activity_id": "act-123",
              "activity_type": "alertAction",
              "actor": "user-456",
              "actor_name": "Jane Smith",
              "alert_id": "alert-789",
              "description": "Alert acknowledged via mobile app",
              "is_recent": false,
              "status": "completed",
              "time_ago_minutes": 125,
              "timestamp": "2023-10-15T10:35:00Z"
            }
          ],
          "statistics": {
            "by_type": {
              "alertAction": 8,
              "alertRecipient": 2,
              "system": 5
            },
            "recent_count": 3,
            "total": 15
          },
          "total_count": 15
        },
        "properties": {
          "activities": {
            "description": "List of activities for the alert, newest first. At most `limit` (default 50, maximum 100) are returned and there is no cursor; use `since` to poll for newer ones.",
            "items": {
              "$ref": "#/components/schemas/AlertActivity"
            },
            "type": "array"
          },
          "filters": {
            "description": "Applied filters for this query.",
            "properties": {
              "activity_type": {
                "enum": [
                  "system",
                  "alertAction",
                  "alertRecipient",
                  "notification",
                  "integration"
                ],
                "type": "string"
              },
              "actor": {
                "type": "string"
              },
              "limit": {
                "type": "integer"
              },
              "since": {
                "format": "date-time",
                "type": "string"
              }
            },
            "type": "object"
          },
          "info_level": {
            "description": "Information level used (basic or extended)",
            "enum": [
              "basic",
              "extended"
            ],
            "type": "string"
          },
          "statistics": {
            "description": "Activity statistics summary",
            "type": "object"
          },
          "total_count": {
            "description": "Total number of activities for this alert.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "ActivityListExtended": {
        "description": "Enhanced activity list response when info=extended parameter is used. This schema documents the additional fields added to activity details when the user ids are enriched with the names of the organization's members (ids outside the organization are answered as they are).",
        "example": {
          "info_level": "extended",
          "items": [
            {
              "activity_id": "act-456",
              "activity_type": "alertAction",
              "actor": "user-123",
              "actor_name": "John Doe",
              "alert_id": "alert-789",
              "description": "Assignee changed",
              "details": {
                "action": "assigned",
                "new_assignee": "user-789",
                "new_assignee_name": "Bob Johnson",
                "old_assignee": "user-456",
                "old_assignee_name": "Jane Smith",
                "source": "web_ui"
              },
              "is_recent": true,
              "time_ago_minutes": 30,
              "timestamp": "2023-10-15T11:00:00Z"
            }
          ],
          "statistics": {
            "by_type": {
              "alertAction": 8,
              "system": 5
            },
            "recent_count": 3,
            "total": 15
          },
          "total_count": 15
        },
        "properties": {
          "activities": {
            "description": "List of activities enriched with user names, newest first (see ActivityList)",
            "items": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/AlertActivity"
                },
                {
                  "properties": {
                    "details": {
                      "description": "Activity details enriched with user names (in addition to base AlertActivity.details fields)",
                      "properties": {
                        "assigned_by": {
                          "description": "User ID who assigned",
                          "type": "string"
                        },
                        "assigned_by_name": {
                          "description": "Display name of assigner (only in extended mode)",
                          "type": "string"
                        },
                        "assigned_to": {
                          "description": "User ID assigned to",
                          "type": "string"
                        },
                        "assigned_to_name": {
                          "description": "Display name of assigned user (only in extended mode)",
                          "type": "string"
                        },
                        "new_assignee": {
                          "description": "New assignee user ID",
                          "type": "string"
                        },
                        "new_assignee_name": {
                          "description": "Display name of new assignee (only in extended mode)",
                          "type": "string"
                        },
                        "old_assignee": {
                          "description": "Previous assignee user ID",
                          "type": "string"
                        },
                        "old_assignee_name": {
                          "description": "Display name of old assignee (only in extended mode)",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              ],
              "type": "object"
            },
            "type": "array"
          },
          "filters": {
            "description": "Applied filters",
            "type": "object"
          },
          "info_level": {
            "description": "Always 'extended' for this response type",
            "enum": [
              "extended"
            ],
            "type": "string"
          },
          "statistics": {
            "description": "Activity statistics summary",
            "type": "object"
          },
          "total_count": {
            "description": "Total number of activities",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "AddCreditCardRequest": {
        "description": "The card is tokenised client-side with Stripe.js into a PaymentMethod\n(`pm_…`). Send its id as `token` (what the web app sends) or as\n`paymentMethodId` — the two are aliases and one of them is required.\n",
        "properties": {
          "billingAddress": {
            "properties": {
              "city": {
                "type": "string"
              },
              "country": {
                "type": "string"
              },
              "line1": {
                "type": "string"
              },
              "line2": {
                "type": "string"
              },
              "postalCode": {
                "type": "string"
              },
              "state": {
                "type": "string"
              }
            },
            "type": "object"
          },
          "cardholderName": {
            "description": "Name on the card",
            "type": "string"
          },
          "email": {
            "description": "Email address for receipts",
            "type": "string"
          },
          "paymentMethodId": {
            "description": "Alias of `token` — the Stripe PaymentMethod id (`pm_…`). One of the two is required.",
            "type": "string"
          },
          "setAsDefault": {
            "default": false,
            "description": "Whether to set this as the default payment method",
            "type": "boolean"
          },
          "token": {
            "description": "Stripe PaymentMethod id (`pm_…`) from Stripe.js. Alias of `paymentMethodId`; one of the two is required.",
            "type": "string"
          }
        },
        "required": [
          "cardholderName"
        ],
        "type": "object"
      },
      "AddIncidentTeamMemberRequest": {
        "example": {
          "role": "Responder",
          "user_id": "user-456"
        },
        "properties": {
          "role": {
            "description": "Role to assign to the new team member.",
            "enum": [
              "Owner",
              "Manager",
              "Responder",
              "Observer"
            ],
            "type": "string"
          },
          "user_id": {
            "description": "ID of the user to add to the team (a string of at most 200 characters; anything else is a 400 `validation_error`).",
            "maxLength": 200,
            "type": "string"
          }
        },
        "required": [
          "user_id",
          "role"
        ],
        "type": "object"
      },
      "AddOrganizationMemberRequest": {
        "example": {
          "role": "Member",
          "user_id": "user-456"
        },
        "properties": {
          "role": {
            "description": "Role to assign to the new organization member (cannot assign Owner role).",
            "enum": [
              "Manager",
              "Member"
            ],
            "type": "string"
          },
          "user_id": {
            "description": "ID of the user to add to the organization. A longer one names nobody (400 `invalid_request`).",
            "maxLength": 200,
            "type": "string"
          }
        },
        "required": [
          "user_id",
          "role"
        ],
        "type": "object"
      },
      "AddPhoneMethodRequest": {
        "properties": {
          "phone_number": {
            "description": "Phone number in E.164 format",
            "example": "+1234567890",
            "pattern": "^\\+[1-9]\\d{1,14}$",
            "type": "string"
          }
        },
        "required": [
          "phone_number"
        ],
        "type": "object"
      },
      "AddPhoneMethodResponse": {
        "properties": {
          "active": {
            "description": "Present when the method already had a verified number: that number keeps paging, with the method's order, delay and retries, until the new one is verified, which then takes its place.",
            "properties": {
              "enabled": {
                "example": true,
                "type": "boolean"
              },
              "number": {
                "example": "+1234567890",
                "type": "string"
              },
              "verified": {
                "example": true,
                "type": "boolean"
              }
            },
            "required": [
              "number",
              "verified",
              "enabled"
            ],
            "type": "object"
          },
          "caller_number": {
            "description": "The number the verification call comes from, to show so the user picks up.",
            "example": "+15550000000",
            "type": "string"
          },
          "expires_in_seconds": {
            "description": "How long the code works, from now.",
            "example": 300,
            "type": "integer"
          },
          "message": {
            "description": "Success message",
            "example": "Phone number added. Verification call initiated.",
            "type": "string"
          },
          "phone": {
            "properties": {
              "delay_seconds": {
                "description": "Delay before notification",
                "example": 0,
                "type": "integer"
              },
              "enabled": {
                "description": "Whether notifications are enabled",
                "example": false,
                "type": "boolean"
              },
              "number": {
                "description": "Phone number",
                "example": "+1234567890",
                "type": "string"
              },
              "order": {
                "description": "Order in notification sequence",
                "example": 2,
                "type": "integer"
              },
              "verified": {
                "description": "Whether the phone number is verified",
                "example": false,
                "type": "boolean"
              }
            },
            "required": [
              "number",
              "verified",
              "order",
              "delay_seconds",
              "enabled"
            ],
            "type": "object"
          },
          "phone_number": {
            "description": "The number being called with its code.",
            "example": "+1234567899",
            "type": "string"
          },
          "verification": {
            "properties": {
              "call_sid": {
                "description": "Identifier of the verification call",
                "example": "CA123456789abcdef",
                "type": "string"
              },
              "expires_in_seconds": {
                "description": "Time until verification expires",
                "example": 300,
                "type": "integer"
              },
              "method": {
                "description": "Verification method used",
                "example": "voice_call",
                "type": "string"
              }
            },
            "required": [
              "method",
              "expires_in_seconds"
            ],
            "type": "object"
          }
        },
        "required": [
          "message",
          "phone_number",
          "expires_in_seconds",
          "caller_number",
          "phone",
          "verification"
        ],
        "type": "object"
      },
      "AddSmsMethodRequest": {
        "properties": {
          "phone_number": {
            "description": "Phone number in E.164 format",
            "example": "+1234567890",
            "pattern": "^\\+[1-9]\\d{1,14}$",
            "type": "string"
          }
        },
        "required": [
          "phone_number"
        ],
        "type": "object"
      },
      "AddSmsMethodResponse": {
        "properties": {
          "active": {
            "description": "Present when the method already had a verified number: that number keeps paging, with the method's order, delay and retries, until the new one is verified, which then takes its place.",
            "properties": {
              "enabled": {
                "example": true,
                "type": "boolean"
              },
              "number": {
                "example": "+1234567890",
                "type": "string"
              },
              "verified": {
                "example": true,
                "type": "boolean"
              }
            },
            "required": [
              "number",
              "verified",
              "enabled"
            ],
            "type": "object"
          },
          "expires_in_seconds": {
            "description": "How long the code works, from now.",
            "example": 600,
            "type": "integer"
          },
          "message": {
            "description": "Success message",
            "example": "SMS number added. Verification code sent.",
            "type": "string"
          },
          "phone_number": {
            "description": "The number the code was texted to.",
            "example": "+34600001122",
            "type": "string"
          },
          "sms": {
            "properties": {
              "enabled": {
                "description": "Whether notifications are enabled",
                "example": false,
                "type": "boolean"
              },
              "number": {
                "description": "SMS number",
                "example": "+1234567890",
                "type": "string"
              },
              "order": {
                "description": "Order in notification sequence",
                "example": 2,
                "type": "integer"
              },
              "verified": {
                "description": "Whether the SMS number is verified",
                "example": false,
                "type": "boolean"
              }
            },
            "required": [
              "number",
              "verified",
              "order",
              "enabled"
            ],
            "type": "object"
          },
          "verification": {
            "properties": {
              "expires_in_seconds": {
                "description": "Time until verification expires",
                "example": 600,
                "type": "integer"
              },
              "method": {
                "description": "Verification method used",
                "example": "sms",
                "type": "string"
              }
            },
            "required": [
              "method",
              "expires_in_seconds"
            ],
            "type": "object"
          }
        },
        "required": [
          "message",
          "phone_number",
          "expires_in_seconds",
          "sms",
          "verification"
        ],
        "type": "object"
      },
      "AlertAction": {
        "example": {
          "action_id": "action-123",
          "action_type": "acknowledge",
          "note": "Investigating the issue",
          "performed_by": "user-456",
          "performed_by_name": "Jane Smith",
          "timestamp": "2023-10-15T10:35:00Z"
        },
        "properties": {
          "action_id": {
            "description": "Unique identifier for the action.",
            "format": "uuid",
            "type": "string"
          },
          "action_type": {
            "description": "Type of action performed.",
            "enum": [
              "acknowledge",
              "close",
              "reassign",
              "escalate",
              "note",
              "snooze"
            ],
            "type": "string"
          },
          "details": {
            "description": "Additional details specific to the action type.",
            "type": "object"
          },
          "note": {
            "description": "Optional note or comment about the action.",
            "type": "string"
          },
          "performed_by": {
            "description": "User ID who performed the action.",
            "type": "string"
          },
          "performed_by_name": {
            "description": "Display name of the user who performed the action.",
            "type": "string"
          },
          "timestamp": {
            "description": "When the action was performed.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "AlertActivity": {
        "example": {
          "activity_id": "act-123-456",
          "activity_type": "alertAction",
          "actor": "user-456",
          "actor_name": "Jane Smith",
          "alert_id": "alert-789",
          "description": "Alert acknowledged via mobile app",
          "details": {
            "action": "acknowledged",
            "note": "Investigating the issue",
            "source": "mobile-app"
          },
          "is_recent": false,
          "status": "completed",
          "time_ago_minutes": 125,
          "timestamp": "2023-10-15T10:35:00Z"
        },
        "properties": {
          "activity_id": {
            "description": "Unique identifier for this activity.",
            "format": "uuid",
            "type": "string"
          },
          "activity_type": {
            "description": "Type of activity performed.",
            "enum": [
              "system",
              "alertAction",
              "alertRecipient",
              "notification",
              "integration"
            ],
            "type": "string"
          },
          "actor": {
            "description": "Who performed this activity (user ID, 'System', integration name).",
            "type": "string"
          },
          "actor_name": {
            "description": "Display name of the actor.",
            "type": "string"
          },
          "alert_id": {
            "description": "ID of the alert this activity belongs to.",
            "format": "uuid",
            "type": "string"
          },
          "description": {
            "description": "Human-readable description of what happened.",
            "type": "string"
          },
          "details": {
            "description": "Structured metadata about this activity.\nWhen activities endpoint is called with info=extended, user IDs in details are enriched with corresponding names\n(a member of the organization by name, else the ID itself):\n- old_assignee → adds old_assignee_name\n- new_assignee → adds new_assignee_name\n- assigned_to → adds assigned_to_name\n- assigned_by → adds assigned_by_name\n",
            "properties": {
              "action": {
                "description": "Specific action performed (e.g., 'acknowledged', 'assigned', 'unassigned', 'severity_changed', 'status_changed_to_open'; 'acknowledgement_note' is a note sent with a repeated acknowledgement, recorded on the timeline without any change to the alert). Two entries record what a service disruption received for an alert with the same `alias`, written once the service recovers: 'degraded_alert_merged' (an alert received during the disruption was counted on this one, which was open by then: `degraded_alert_id` is the id its create answered, `count` its occurrences) and 'degraded_occurrence_dropped' (occurrences received for this alert during the disruption while it was still open; it was closed before the service recovered, so they were not counted. An occurrence received after the alert had been closed becomes a new alert instead).",
                "type": "string"
              },
              "degraded": {
                "description": "On an entry written for something received during a service disruption (`alert_created`, `alert_deduplicated`, `degraded_alert_merged`, `degraded_occurrence_dropped`): `received_at`, when it was received. The entry itself is written once the service recovers.",
                "properties": {
                  "received_at": {
                    "format": "date-time",
                    "type": "string"
                  }
                },
                "type": "object"
              },
              "new_assignee": {
                "description": "New assignee user ID (if applicable)",
                "type": "string"
              },
              "new_assignee_name": {
                "description": "New assignee display name (only in extended mode)",
                "type": "string"
              },
              "new_severity": {
                "description": "New severity (if applicable)",
                "type": "string"
              },
              "new_status": {
                "description": "New status (if applicable)",
                "type": "string"
              },
              "note": {
                "description": "The note sent with the action, when there was one.",
                "type": "string"
              },
              "old_assignee": {
                "description": "Previous assignee user ID (if applicable)",
                "type": "string"
              },
              "old_assignee_name": {
                "description": "Previous assignee display name (only in extended mode)",
                "type": "string"
              },
              "old_severity": {
                "description": "Previous severity (if applicable)",
                "type": "string"
              },
              "old_status": {
                "description": "Previous status (if applicable)",
                "type": "string"
              },
              "reason": {
                "description": "Why the alert changed when the actor did not ask for that change itself: `acknowledged` on the assignment a repeated acknowledgement makes (the alert had been reassigned since; an acknowledgement assigns the alert to the one who acknowledges). On `routing_suppressed`, and with `routing: suppressed` on `alert_deduplicated` and `degraded_alert_merged`, why the alert was not routed: `team_disabled`, `free_tier_paused`, or, for what a service disruption received with an integration key no longer valid then, `integration_key_revoked`, `integration_key_expired`, `integration_key_reissued` (the request carried the key's previous secret) and `integration_key_unknown` (the key no longer exists).",
                "type": "string"
              },
              "source": {
                "description": "Source of the action (e.g., 'web_ui', 'api', 'voice', 'mobile')",
                "type": "string"
              }
            },
            "type": "object"
          },
          "display_time": {
            "description": "Localized timestamp string",
            "type": "string"
          },
          "formatted_description": {
            "description": "Human-readable formatted description of the activity",
            "type": "string"
          },
          "integration_id": {
            "description": "ID of integration that performed this activity.",
            "nullable": true,
            "type": "string"
          },
          "is_recent": {
            "description": "Whether activity occurred within the last hour",
            "type": "boolean"
          },
          "metadata": {
            "description": "When the entry was recorded.",
            "properties": {
              "created_at": {
                "description": "The entry's `timestamp`.",
                "format": "date-time",
                "type": "string"
              },
              "logged_at": {
                "description": "When the event was reported, as reported; absent on some entries. The entry's `timestamp` is this instant, or a millisecond or so later when another entry of the alert has the same one.",
                "type": "string"
              }
            },
            "type": "object"
          },
          "notification_channel": {
            "description": "Channel used for notifications (if applicable).",
            "enum": [
              "email",
              "voice",
              "sms",
              "push",
              "mobile",
              "webhook",
              "integration"
            ],
            "nullable": true,
            "type": "string"
          },
          "related_rule": {
            "description": "Escalation rule that triggered this activity.",
            "nullable": true,
            "type": "string"
          },
          "status": {
            "description": "Status of the activity (if applicable).",
            "enum": [
              "submitted",
              "in-progress",
              "sending",
              "skipped",
              "delivered",
              "failed",
              "completed"
            ],
            "nullable": true,
            "type": "string"
          },
          "time_ago_minutes": {
            "description": "Minutes since this activity occurred",
            "type": "integer"
          },
          "timestamp": {
            "description": "When this activity occurred.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "AlertBusyError": {
        "description": "503 `alert_busy`: the alert was being changed by another request at that\nmoment (or, on a PUT without `expected_updated_at`, its status was changed\ntwice by other requests) and this change was not applied; on a comment\nedit, the same comment was being changed. Nothing changed: send the\nrequest again after `retry_after` seconds, the same value as the\n`Retry-After` header. Never a 409, which means the alert's state settled\nthe request.\n",
        "example": {
          "error": "alert_busy",
          "error_description": "The alert is being changed by another request; retry in a moment",
          "retry_after": 1,
          "status": 503
        },
        "properties": {
          "error": {
            "enum": [
              "alert_busy"
            ],
            "type": "string"
          },
          "error_description": {
            "type": "string"
          },
          "retry_after": {
            "description": "Seconds to wait before retrying.",
            "type": "integer"
          },
          "status": {
            "example": 503,
            "type": "integer"
          }
        },
        "required": [
          "error",
          "error_description",
          "retry_after",
          "status"
        ],
        "type": "object"
      },
      "AlertComment": {
        "description": "Comment on an alert",
        "example": {
          "alert_id": "alert-789",
          "author_email": "john.doe@example.com",
          "author_id": "user-123",
          "author_name": "John Doe",
          "comment_id": "cmt-123-456",
          "content": "Investigating the issue. Will update shortly.",
          "created_at": "2023-10-15T10:35:00Z",
          "is_author": true,
          "is_edited": false,
          "mentions": [
            "user-456"
          ],
          "updated_at": "2023-10-15T10:35:00Z"
        },
        "properties": {
          "alert_id": {
            "description": "ID of the alert this comment belongs to",
            "format": "uuid",
            "type": "string"
          },
          "author_email": {
            "description": "Email address of the comment author",
            "format": "email",
            "nullable": true,
            "type": "string"
          },
          "author_id": {
            "description": "User ID of the comment author",
            "type": "string"
          },
          "author_name": {
            "description": "Display name of the comment author",
            "type": "string"
          },
          "comment_id": {
            "description": "Unique identifier for the comment",
            "format": "uuid",
            "type": "string"
          },
          "content": {
            "description": "Comment content (Markdown supported)",
            "type": "string"
          },
          "created_at": {
            "description": "When the comment was created",
            "format": "date-time",
            "type": "string"
          },
          "is_author": {
            "description": "True when the requesting user wrote the comment (read-only; `author_id` is the stable user id, not the Cognito sub, so compare with this flag rather than client-side).",
            "type": "boolean"
          },
          "is_edited": {
            "default": false,
            "description": "Whether the comment has been edited",
            "type": "boolean"
          },
          "mentions": {
            "description": "Array of user IDs mentioned in the comment",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "updated_at": {
            "description": "When the comment was last updated",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "comment_id",
          "alert_id",
          "author_id",
          "author_name",
          "content",
          "created_at",
          "updated_at"
        ],
        "type": "object"
      },
      "AlertCommentListResponse": {
        "description": "Response containing a list of comments",
        "example": {
          "alert_id": "alert-789",
          "comments": [
            {
              "alert_id": "alert-789",
              "author_id": "user-123",
              "author_name": "John Doe",
              "comment_id": "cmt-123-456",
              "content": "Investigating the issue.",
              "created_at": "2023-10-15T10:35:00Z",
              "is_edited": false,
              "updated_at": "2023-10-15T10:35:00Z"
            }
          ],
          "pagination": {
            "has_more": false,
            "limit": 50,
            "sort_order": "desc",
            "total_count": 1
          }
        },
        "properties": {
          "alert_id": {
            "description": "ID of the alert these comments belong to",
            "format": "uuid",
            "type": "string"
          },
          "comments": {
            "description": "Array of comments",
            "items": {
              "$ref": "#/components/schemas/AlertComment"
            },
            "type": "array"
          },
          "pagination": {
            "properties": {
              "has_more": {
                "description": "Whether there are more comments to fetch",
                "type": "boolean"
              },
              "limit": {
                "description": "Maximum number of comments per page",
                "type": "integer"
              },
              "next_token": {
                "description": "Token for fetching the next page",
                "nullable": true,
                "type": "string"
              },
              "sort_order": {
                "description": "Sort order used",
                "enum": [
                  "asc",
                  "desc"
                ],
                "type": "string"
              },
              "total_count": {
                "description": "Number of comments in this response",
                "type": "integer"
              }
            },
            "type": "object"
          }
        },
        "type": "object"
      },
      "AlertCommentResponse": {
        "description": "Response containing a single comment",
        "example": {
          "comment": {
            "alert_id": "alert-789",
            "author_id": "user-123",
            "author_name": "John Doe",
            "comment_id": "cmt-123-456",
            "content": "Investigating the issue.",
            "created_at": "2023-10-15T10:35:00Z",
            "is_edited": false,
            "updated_at": "2023-10-15T10:35:00Z"
          },
          "message": "Comment created successfully"
        },
        "properties": {
          "comment": {
            "$ref": "#/components/schemas/AlertComment"
          },
          "message": {
            "description": "Success message",
            "type": "string"
          }
        },
        "type": "object"
      },
      "AlertOperationResponse": {
        "description": "Response wrapper for alert update and acknowledge operations",
        "example": {
          "acknowledged_at": "2023-10-15T10:35:00Z",
          "acknowledged_by": "user-123",
          "acknowledged_by_name": "John Doe",
          "alert": {
            "alert_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
            "severity": "P2",
            "status": "acknowledged",
            "title": "High CPU Usage on web-server-01"
          },
          "alert_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
          "assigned_to": "user-123",
          "message": "Alert acknowledged successfully",
          "source": "api",
          "status": "acknowledged"
        },
        "properties": {
          "acknowledged_at": {
            "description": "When the alert was acknowledged",
            "format": "date-time",
            "type": "string"
          },
          "acknowledged_by": {
            "description": "User ID who acknowledged the alert",
            "type": "string"
          },
          "acknowledged_by_name": {
            "description": "Display name of user who acknowledged the alert",
            "type": "string"
          },
          "alert": {
            "$ref": "#/components/schemas/TeamAlert",
            "description": "Updated alert object"
          },
          "alert_id": {
            "description": "ID of the affected alert",
            "format": "uuid",
            "type": "string"
          },
          "assigned_to": {
            "description": "User ID the alert is assigned to: the caller, whom every acknowledgement assigns it to (a repeat assigns it back when the alert was reassigned meanwhile), the same as `alert.assignee`.",
            "type": "string"
          },
          "message": {
            "description": "Success or status message",
            "type": "string"
          },
          "note": {
            "description": "Optional note provided with the operation",
            "nullable": true,
            "type": "string"
          },
          "source": {
            "description": "Source of the operation (api, voice, email, etc.)",
            "type": "string"
          },
          "status": {
            "description": "Current alert status",
            "enum": [
              "open",
              "acknowledged",
              "closed"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "ApiKeyCreateRequest": {
        "description": "A personal API key acts with its owner's full account: there are no narrower scopes, and a non-empty `permissions` object is refused with 400 `validation_error` rather than stored as a restriction that would never apply.",
        "example": {
          "name": "CI/CD Bot Token",
          "ttl_days": 30
        },
        "properties": {
          "name": {
            "description": "Friendly name for the key",
            "maxLength": 100,
            "type": "string"
          },
          "ttl_days": {
            "description": "Days until expiration (omit for no expiry)",
            "maximum": 3650,
            "minimum": 1,
            "type": "integer"
          }
        },
        "required": [
          "name"
        ],
        "type": "object"
      },
      "ApiKeyCreateResponse": {
        "example": {
          "expires_at": "2024-03-16T14:30:00Z",
          "key_id": "123e4567-e89b-12d3-a456-426614174000",
          "key_secret": "ghp_1234567890abcdefghijklmnopqrstuvwxyz"
        },
        "properties": {
          "expires_at": {
            "description": "Expiration date and time of the key, if set.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "key_id": {
            "description": "Unique identifier for the key.",
            "format": "uuid",
            "type": "string"
          },
          "key_secret": {
            "description": "The generated key secret.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "ApiKeyItem": {
        "properties": {
          "created_at": {
            "description": "Date and time when the key was created.",
            "format": "date-time",
            "type": "string"
          },
          "expires_at": {
            "description": "Expiration date and time of the key, if set.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "key_id": {
            "description": "Unique identifier for the key.",
            "format": "uuid",
            "type": "string"
          },
          "name": {
            "description": "Friendly name for the key.",
            "type": "string"
          },
          "revoked": {
            "description": "Whether the key has been revoked.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "ApiKeyList": {
        "example": {
          "keys": [],
          "pagination": {
            "has_more": false,
            "limit": 20,
            "next_token": null
          }
        },
        "properties": {
          "keys": {
            "description": "List of api keys.",
            "items": {
              "$ref": "#/components/schemas/ApiKeyItem"
            },
            "type": "array"
          },
          "pagination": {
            "$ref": "#/components/schemas/PaginationMetadata"
          }
        },
        "type": "object"
      },
      "AssignSeatRequest": {
        "properties": {
          "user_id": {
            "description": "The user id of a member of the organization",
            "maxLength": 128,
            "minLength": 1,
            "type": "string"
          }
        },
        "required": [
          "user_id"
        ],
        "type": "object"
      },
      "AuthConfig": {
        "description": "The Cognito configuration the apps start from, and where to use it. The pool and client ids are fixed for a deployment of the service: should one change, Cognito refuses the old one (`ResourceNotFoundException`), so read this configuration again. `region` and `primaryRegion` can change at any time: re-read them at the start of every sign-in and after an authentication failure that is not a wrong code. Cacheable for 60 seconds.",
        "properties": {
          "cliClientId": {
            "type": "string"
          },
          "mobileClientId": {
            "type": "string"
          },
          "primaryRegion": {
            "description": "The Region that takes writes (sign-up, attribute changes). Normally equal to `region`; while they differ, sign-up and profile writes are unavailable and the clients say so.",
            "example": "us-east-1",
            "type": "string"
          },
          "region": {
            "description": "The Region to authenticate against (InitiateAuth, the token refresh). It can change while `userPoolId` stays the same.",
            "example": "us-east-1",
            "type": "string"
          },
          "userPoolId": {
            "description": "The user pool. Its id does not change with `region`, so its prefix need not be `region`.",
            "example": "us-east-1_XXXXXXXXX",
            "type": "string"
          },
          "webClientId": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "BillingProfile": {
        "properties": {
          "accumulated_usage_usd": {
            "description": "Running total of unbilled usage charges for the current period",
            "type": "number"
          },
          "billing_status": {
            "description": "Current billing status: `free` (no card, never billed), `active`, `grace` (a charge failed within\nthe last 7 days; retried daily), `suspended` (frozen for non-payment after the grace), `overdue`\n(legacy value of a failed usage invoice, treated like `grace`).\n",
            "enum": [
              "free",
              "active",
              "grace",
              "suspended",
              "overdue"
            ],
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "grace_ends_at": {
            "description": "While `billing_status` is `grace` or `suspended` — when the payment grace ends / ended",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "grace_invoice_id": {
            "description": "While in grace — the failed usage invoice that opened it (null for a seat renewal)",
            "nullable": true,
            "type": "string"
          },
          "grace_kind": {
            "enum": [
              "seat_renewal",
              "usage_invoice"
            ],
            "nullable": true,
            "type": "string"
          },
          "included": {
            "$ref": "#/components/schemas/IncludedAllowance"
          },
          "last_billed_period": {
            "description": "Last successfully billed period (YYYY-MM format)",
            "nullable": true,
            "type": "string"
          },
          "organization_id": {
            "description": "Organization ID",
            "type": "string"
          },
          "plan": {
            "description": "The organization's plan; should it ever differ from the organization's `plan`, the organization's is authoritative",
            "enum": [
              "free",
              "paid"
            ],
            "type": "string"
          },
          "plan_limits": {
            "additionalProperties": true,
            "description": "The effective limits of the plan (`null` = unlimited); should they ever differ from the organization's `plan_limits`, the organization's are authoritative",
            "type": "object"
          },
          "seat_price_usd": {
            "description": "Monthly price of one seat in USD (the price `purchaseSeats` charges); omitted when it cannot be determined",
            "type": "number"
          },
          "trust_tier": {
            "description": "Trust tier level (1=new org $50 cap, 2=$100, 3=$200, 4=$500, 5=unlimited)",
            "maximum": 5,
            "minimum": 1,
            "type": "integer"
          },
          "updated_at": {
            "format": "date-time",
            "type": "string"
          },
          "usage_credit_limit": {
            "description": "Current usage credit limit in USD (null for tier 5 = unlimited)",
            "nullable": true,
            "type": "number"
          }
        },
        "type": "object"
      },
      "ConfirmationMismatchError": {
        "description": "The `confirm` field is not the account's e-mail address (400).",
        "example": {
          "error": "confirmation_mismatch",
          "error_description": "Type the e-mail address of your account to confirm the deletion.",
          "status": 400
        },
        "properties": {
          "error": {
            "enum": [
              "confirmation_mismatch",
              "validation_error",
              "invalid_json"
            ],
            "type": "string"
          },
          "error_description": {
            "type": "string"
          },
          "status": {
            "example": 400,
            "type": "integer"
          }
        },
        "required": [
          "error",
          "error_description",
          "status"
        ],
        "type": "object"
      },
      "ContactRequest": {
        "properties": {
          "company": {
            "example": "Acme Inc.",
            "maxLength": 100,
            "type": "string"
          },
          "company_website": {
            "description": "Honeypot field — must be empty. Non-empty submissions are silently dropped.",
            "type": "string"
          },
          "email": {
            "example": "jane@company.com",
            "format": "email",
            "maxLength": 254,
            "type": "string"
          },
          "message": {
            "maxLength": 5000,
            "minLength": 10,
            "type": "string"
          },
          "name": {
            "example": "Jane Doe",
            "maxLength": 100,
            "type": "string"
          },
          "turnstile_token": {
            "description": "Cloudflare Turnstile token from the widget on the site.",
            "type": "string"
          }
        },
        "required": [
          "name",
          "email",
          "message"
        ],
        "type": "object"
      },
      "ContactResponse": {
        "properties": {
          "status": {
            "example": "ok",
            "type": "string"
          }
        },
        "type": "object"
      },
      "CreateAlertCommentRequest": {
        "description": "Request to create a new comment on an alert",
        "example": {
          "content": "Investigating the issue. @user-456 please take a look.",
          "mentions": [
            "user-456"
          ]
        },
        "properties": {
          "content": {
            "description": "Comment content (Markdown supported)",
            "maxLength": 5000,
            "minLength": 1,
            "type": "string"
          },
          "mentions": {
            "description": "Array of user IDs to mention in the comment",
            "items": {
              "type": "string"
            },
            "maxItems": 20,
            "type": "array"
          }
        },
        "required": [
          "content"
        ],
        "type": "object"
      },
      "CreateEscalationPolicyRequest": {
        "example": {
          "description": "For critical production alerts",
          "enabled": true,
          "escalation_rules": [
            {
              "delay_minutes": 0,
              "target": {
                "target_id": "schedule-primary-oncall",
                "type": "schedule"
              }
            }
          ],
          "name": "Immediate Response Team",
          "repeat_policy": {
            "enabled": true,
            "max_repeats": 3,
            "repeat_after_seconds": 3600
          }
        },
        "properties": {
          "description": {
            "description": "Description of this escalation policy",
            "maxLength": 500,
            "type": "string"
          },
          "enabled": {
            "default": true,
            "description": "Whether this policy is active. A boolean; any other value (\"nope\", \"false\") is a 400 `validation_error`.",
            "type": "boolean"
          },
          "escalation_rules": {
            "description": "Escalation steps in order",
            "items": {
              "$ref": "#/components/schemas/EscalationRuleRequest"
            },
            "minItems": 1,
            "type": "array"
          },
          "name": {
            "description": "Human-readable name for the policy, stored trimmed and unique in the team: a name another policy of the team has is a 400 `validation_error` (`Escalation policy name '<name>' is already used in this team`)",
            "maxLength": 100,
            "minLength": 1,
            "type": "string"
          },
          "repeat_policy": {
            "description": "Absent or `null`, no repeat (`enabled` false, `repeat_after_seconds` 3600, `max_repeats` null).",
            "nullable": true,
            "properties": {
              "enabled": {
                "default": false,
                "type": "boolean"
              },
              "max_repeats": {
                "minimum": 1,
                "type": "integer"
              },
              "repeat_after_seconds": {
                "default": 3600,
                "minimum": 1,
                "type": "integer"
              }
            },
            "type": "object"
          }
        },
        "required": [
          "name",
          "escalation_rules"
        ],
        "type": "object"
      },
      "CreateIncidentTeamRequest": {
        "example": {
          "description": "Primary team responsible for production incidents and alerts",
          "enabled": true,
          "name": "Production Support Team"
        },
        "properties": {
          "description": {
            "description": "Description of the team's purpose and responsibilities.",
            "maxLength": 500,
            "type": "string"
          },
          "enabled": {
            "default": true,
            "description": "Whether the team should be active upon creation (a disabled team pages nobody).",
            "type": "boolean"
          },
          "name": {
            "description": "Name of the incident response team (alphanumeric, spaces, hyphens, underscores only).",
            "maxLength": 100,
            "minLength": 1,
            "pattern": "^[a-zA-Z0-9\\s\\-_]+$",
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "type": "object"
      },
      "CreateInvitationRequest": {
        "example": {
          "email": "teammate@example.com",
          "role": "Member"
        },
        "properties": {
          "email": {
            "description": "Email address to invite (stored lower-cased; a `+tag` local part is accepted). The invitee must sign in — or create the account — with this exact address to accept.",
            "format": "email",
            "maxLength": 254,
            "type": "string"
          },
          "role": {
            "description": "Role granted on acceptance. Owners can invite Managers and Members; Managers can invite Members only.",
            "enum": [
              "Manager",
              "Member"
            ],
            "type": "string"
          }
        },
        "required": [
          "email",
          "role"
        ],
        "type": "object"
      },
      "CreateOrganizationRequest": {
        "example": {
          "acquisition": {
            "gclid": "Cj0KCQ",
            "landing_path": "/start",
            "utm_source": "google"
          },
          "description": "Technology company focused on software development",
          "enabled": true,
          "name": "acme-corp",
          "plan": "free",
          "terms_version": "tos-2026-09"
        },
        "properties": {
          "acquisition": {
            "$ref": "#/components/schemas/Acquisition"
          },
          "description": {
            "description": "Description of the organization's purpose: at most 500 characters once trimmed, stored trimmed (null or absent is none). Anything else is a 400, as on update.",
            "maxLength": 500,
            "nullable": true,
            "type": "string"
          },
          "enabled": {
            "default": true,
            "description": "Whether the organization should be active upon creation (default true). A real boolean: \"false\" or 0 is a 400, never read as true.",
            "type": "boolean"
          },
          "name": {
            "description": "Name of the organization (URL-compliant alphanumeric, hyphens, underscores only). Unique regardless of case: no two organizations bear \"Acme\" and \"acme\" (409 `organization_already_exists`). The case given is the one kept.",
            "maxLength": 100,
            "minLength": 1,
            "pattern": "^[a-zA-Z0-9\\-_]+$",
            "type": "string"
          },
          "payment_method_token": {
            "description": "Stripe PaymentMethod id (pm_xxx) from Stripe.js. Required on the Pro plan: the\ncard is attached to the organization's Stripe customer and confirmed with an\noff-session SetupIntent. Nothing is charged at creation — the owner's seat is\ncomplimentary on every plan. After a `402 payment_requires_action`, confirm the\nSetupIntent client-side and send the same token again: the customer it is\nattached to and the SetupIntent (idempotent on the payment method) are reused.\n",
            "type": "string"
          },
          "plan": {
            "description": "The plan to create the organization on. Omitted: `free` while the free tier is\nopen (`free_tier_enabled` of GET /v1/billing/plans), `paid` otherwise. `free` needs no card (one free\norganization per owner, at most three created per user in 30 days); `paid` needs\n`payment_method_token`.\n",
            "enum": [
              "free",
              "paid"
            ],
            "type": "string"
          },
          "terms_version": {
            "description": "The Terms of Service version the owner accepted; stored with `terms_accepted_at`.",
            "maxLength": 32,
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "type": "object"
      },
      "CreateRoutingRuleRequest": {
        "example": {
          "conditions": {
            "logical_operator": "and",
            "rules": [
              {
                "field": "severity",
                "operator": "in",
                "value": [
                  "P1",
                  "P2"
                ]
              },
              {
                "field": "tags.environment",
                "operator": "equals",
                "value": "production"
              }
            ]
          },
          "description": "Route all P1/P2 alerts from production to immediate escalation",
          "enabled": true,
          "name": "Critical Production Alerts",
          "priority": 1,
          "target": {
            "target_id": "policy-789",
            "type": "escalation_policy"
          }
        },
        "properties": {
          "conditions": {
            "description": "At most 256 KB as stored (UTF-8 JSON of `logical_operator` and each condition's `field`, `operator` and `value`), else 400 `validation_error` (\"Conditions take <n> KB; at most 256 KB\").",
            "properties": {
              "logical_operator": {
                "description": "How to combine multiple conditions (AND = all must match, OR = any can match)",
                "enum": [
                  "and",
                  "or"
                ],
                "type": "string"
              },
              "rules": {
                "description": "Individual matching conditions; an empty list matches every alert, whatever `logical_operator` says",
                "items": {
                  "$ref": "#/components/schemas/RoutingCondition"
                },
                "maxItems": 20,
                "minItems": 0,
                "type": "array"
              }
            },
            "required": [
              "logical_operator",
              "rules"
            ],
            "type": "object"
          },
          "description": {
            "description": "Description of what this rule does",
            "maxLength": 500,
            "type": "string"
          },
          "enabled": {
            "default": true,
            "description": "Whether this rule is active. A boolean; any other value (\"no\", 0, \"false\") is a 400 `validation_error`.",
            "type": "boolean"
          },
          "name": {
            "description": "Human-readable name for the rule",
            "maxLength": 100,
            "minLength": 1,
            "type": "string"
          },
          "priority": {
            "description": "Priority order (1 = highest priority)",
            "maximum": 999,
            "minimum": 1,
            "type": "integer"
          },
          "target": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RoutingTarget"
              },
              {
                "properties": {
                  "target_name": {
                    "description": "Name of the target (read-only, auto-populated)",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            ],
            "description": "Who/what to notify when this rule matches"
          }
        },
        "required": [
          "name",
          "priority",
          "conditions",
          "target"
        ],
        "type": "object"
      },
      "CreateScheduleOverrideRequest": {
        "description": "The override replaces whoever the schedule's rotations would put on call for the whole period; it may not overlap another override of the schedule.",
        "example": {
          "end_time": "2023-10-21T09:00:00Z",
          "start_time": "2023-10-20T09:00:00Z",
          "user_id": "user-456",
          "user_name": "Jane Smith"
        },
        "properties": {
          "end_time": {
            "description": "When the override ends, a date-time as `start_time`, after it and at most a year later.",
            "format": "date-time",
            "type": "string"
          },
          "start_time": {
            "description": "When the override starts: an RFC 3339 date-time with an offset (`Z` or `±HH:MM`), in the years 1970–9999; any other shape or a day that does not exist is a 400 `validation_error`. Stored in UTC with milliseconds.",
            "format": "date-time",
            "type": "string"
          },
          "user_id": {
            "description": "User ID who will be on-call during the override.",
            "type": "string"
          },
          "user_name": {
            "description": "Display name of the override user, stored as sent.",
            "type": "string"
          }
        },
        "required": [
          "start_time",
          "end_time",
          "user_id"
        ],
        "type": "object"
      },
      "CreateSupportTicketResponse": {
        "properties": {
          "message": {
            "$ref": "#/components/schemas/SupportMessage"
          },
          "notice": {
            "description": "Why the priority was clamped (only when it was)",
            "type": "string"
          },
          "priority_clamped": {
            "description": "`true` when a high/urgent priority was clamped to normal (Free plan)",
            "type": "boolean"
          },
          "ticket": {
            "$ref": "#/components/schemas/SupportTicket"
          }
        },
        "required": [
          "ticket",
          "message"
        ],
        "type": "object"
      },
      "CreateTeamScheduleRequest": {
        "example": {
          "description": "Primary on-call schedule for production alerts",
          "enabled": true,
          "name": "primary-oncall",
          "rotations": [],
          "timezone": "America/New_York"
        },
        "properties": {
          "description": {
            "description": "Optional description of the schedule.",
            "maxLength": 500,
            "type": "string"
          },
          "enabled": {
            "default": true,
            "description": "Whether the schedule should be active. A boolean; any other value (\"false\", 0) is a 400 `validation_error`.",
            "type": "boolean"
          },
          "name": {
            "description": "Name of the schedule (alphanumeric, dots, underscores, dashes only).",
            "maxLength": 100,
            "minLength": 1,
            "pattern": "^[a-zA-Z0-9._-]+$",
            "type": "string"
          },
          "rotations": {
            "description": "Initial rotations for the schedule (an array; any other value is a 400 `validation_error`).",
            "items": {
              "$ref": "#/components/schemas/RotationCreateRequest"
            },
            "type": "array"
          },
          "timezone": {
            "description": "IANA timezone for the schedule (e.g., \"America/New_York\", \"Asia/Kolkata\", \"UTC\"); primary names and their aliases (e.g., \"Asia/Calcutta\") are both accepted and stored as given.",
            "type": "string"
          }
        },
        "required": [
          "name",
          "timezone"
        ],
        "type": "object"
      },
      "DeleteAccountRequest": {
        "description": "The confirmation of the deletion. `confirm` must be the account's e-mail address.",
        "example": {
          "confirm": "jane@example.com",
          "reason": "Moving to another tool.",
          "via": "web"
        },
        "properties": {
          "confirm": {
            "description": "The account's e-mail address, compared trimmed and case-insensitively.",
            "format": "email",
            "maxLength": 254,
            "type": "string"
          },
          "reason": {
            "description": "Optional, what the user typed.",
            "maxLength": 500,
            "nullable": true,
            "type": "string"
          },
          "via": {
            "description": "Which client asked; `api` when omitted.",
            "enum": [
              "web",
              "mobile",
              "api"
            ],
            "type": "string"
          }
        },
        "required": [
          "confirm"
        ],
        "type": "object"
      },
      "DeleteAccountResponse": {
        "description": "The state of the account after the request (also what a second call on a pending account answers).",
        "properties": {
          "account": {
            "$ref": "#/components/schemas/AccountState"
          },
          "api_keys_revoked": {
            "description": "The user's personal API keys were revoked (and stay revoked after a restore).",
            "type": "boolean"
          },
          "email": {
            "format": "email",
            "type": "string"
          },
          "organizations_left": {
            "description": "The organizations the user left at the request (a restore does not rejoin them).",
            "items": {
              "$ref": "#/components/schemas/MembershipSummary"
            },
            "type": "array"
          },
          "organizations_scheduled": {
            "description": "The organizations the user owns, now scheduled for deletion with the account.",
            "items": {
              "$ref": "#/components/schemas/ScheduledOrganization"
            },
            "type": "array"
          },
          "user_id": {
            "type": "string"
          }
        },
        "required": [
          "user_id",
          "email",
          "account",
          "organizations_scheduled",
          "organizations_left"
        ],
        "type": "object"
      },
      "DeleteAlertCommentResponse": {
        "description": "Response for comment deletion",
        "example": {
          "deleted_comment": {
            "alert_id": "alert-789",
            "comment_id": "cmt-123-456",
            "deleted_at": "2023-10-15T11:00:00Z",
            "deleted_by": "user-123"
          },
          "message": "Comment deleted successfully"
        },
        "properties": {
          "deleted_comment": {
            "properties": {
              "alert_id": {
                "format": "uuid",
                "type": "string"
              },
              "comment_id": {
                "format": "uuid",
                "type": "string"
              },
              "deleted_at": {
                "format": "date-time",
                "type": "string"
              },
              "deleted_by": {
                "type": "string"
              }
            },
            "type": "object"
          },
          "message": {
            "description": "Success message",
            "type": "string"
          }
        },
        "type": "object"
      },
      "DeleteAlertResponse": {
        "description": "Response for alert deletion operations",
        "example": {
          "deleted_alert": {
            "alert_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
            "deleted_at": "2023-10-15T11:00:00Z",
            "deleted_by": "usr_01HXYZ0000000000000000000",
            "organization_name": "acme-corp",
            "severity": "P2",
            "status": "closed",
            "team_id": "team-123",
            "team_name": "platform-team",
            "title": "High CPU Usage on web-server-01"
          },
          "message": "Alert deleted successfully"
        },
        "properties": {
          "deleted_alert": {
            "description": "Summary of the deleted alert",
            "properties": {
              "alert_id": {
                "description": "ID of the deleted alert",
                "format": "uuid",
                "type": "string"
              },
              "deleted_at": {
                "description": "When the alert was deleted",
                "format": "date-time",
                "type": "string"
              },
              "deleted_by": {
                "description": "User ID who deleted the alert",
                "type": "string"
              },
              "organization_name": {
                "description": "Organization name",
                "type": "string"
              },
              "severity": {
                "description": "Severity of the deleted alert",
                "type": "string"
              },
              "status": {
                "description": "Status of the alert when deleted",
                "type": "string"
              },
              "team_id": {
                "description": "Team ID the alert belonged to",
                "type": "string"
              },
              "team_name": {
                "description": "Team name",
                "type": "string"
              },
              "title": {
                "description": "Title of the deleted alert",
                "type": "string"
              }
            },
            "type": "object"
          },
          "message": {
            "description": "Success message for the deletion",
            "type": "string"
          }
        },
        "type": "object"
      },
      "DeleteOrganizationRequest": {
        "description": "The confirmation of the deletion. `confirm` must be the organization's name.",
        "example": {
          "confirm": "acme",
          "reason": "Moving to another tool."
        },
        "properties": {
          "confirm": {
            "description": "Must equal the `organizationName` of the path",
            "type": "string"
          },
          "reason": {
            "description": "Optional feedback, what the owner typed: why the organization is deleted. HTML is stripped and the text trimmed; it is recorded with the deletion in the audit trail.",
            "maxLength": 500,
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "confirm"
        ],
        "type": "object"
      },
      "Deployment": {
        "properties": {
          "commit_sha": {
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "deployment_id": {
            "description": "`dep_` and 32 hexadecimal characters.",
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "environment": {
            "type": "string"
          },
          "external_id": {
            "type": "string"
          },
          "finished_at": {
            "format": "date-time",
            "type": "string"
          },
          "kind": {
            "enum": [
              "deploy",
              "rollback",
              "config",
              "infra",
              "feature_flag"
            ],
            "type": "string"
          },
          "previous_version": {
            "type": "string"
          },
          "recorded_by": {
            "description": "Who recorded it first.",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "nullable": true,
                "type": "string"
              },
              "type": {
                "enum": [
                  "integration_key",
                  "user"
                ],
                "type": "string"
              }
            },
            "type": "object"
          },
          "repository": {
            "type": "string"
          },
          "service": {
            "type": "string"
          },
          "started_at": {
            "format": "date-time",
            "type": "string"
          },
          "status": {
            "description": "`unknown`: recorded as `started` and never closed after 24 hours.",
            "enum": [
              "started",
              "succeeded",
              "failed",
              "rolled_back",
              "unknown"
            ],
            "type": "string"
          },
          "tags": {
            "additionalProperties": {
              "type": "string"
            },
            "type": "object"
          },
          "team_id": {
            "type": "string"
          },
          "triggered_by": {
            "type": "string"
          },
          "updated_at": {
            "format": "date-time",
            "type": "string"
          },
          "updated_by": {
            "description": "Who changed it last, present once it was updated (the same shape as `recorded_by`).",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "nullable": true,
                "type": "string"
              },
              "type": {
                "enum": [
                  "integration_key",
                  "user"
                ],
                "type": "string"
              }
            },
            "type": "object"
          },
          "url": {
            "type": "string"
          },
          "version": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "DeploymentList": {
        "properties": {
          "deployments": {
            "items": {
              "$ref": "#/components/schemas/Deployment"
            },
            "type": "array"
          },
          "pagination": {
            "description": "A page holds at most `limit` deployments and stops earlier when it reaches about 4 MB. With filters a page can hold fewer, or none, while `has_more` is true: follow `next_token` until it is null.",
            "properties": {
              "has_more": {
                "type": "boolean"
              },
              "limit": {
                "type": "integer"
              },
              "next_token": {
                "nullable": true,
                "type": "string"
              }
            },
            "type": "object"
          }
        },
        "type": "object"
      },
      "DeviceInfo": {
        "description": "Informational. Stored when it is a JSON object of at most 2048 characters, else stored empty; it never makes a registration fail.",
        "properties": {
          "appVersion": {
            "description": "App version",
            "example": "1.0.0",
            "type": "string"
          },
          "deviceName": {
            "description": "Device name set by user. Names the registration when no device_name is sent or stored, cut to 100 characters.",
            "example": "John's iPhone",
            "type": "string"
          },
          "model": {
            "description": "Device model name",
            "example": "iPhone 15 Pro",
            "type": "string"
          },
          "osVersion": {
            "description": "Operating system version",
            "example": "17.2.1",
            "type": "string"
          }
        },
        "type": "object"
      },
      "DeviceListResponse": {
        "properties": {
          "data": {
            "properties": {
              "devices": {
                "items": {
                  "$ref": "#/components/schemas/DeviceRegistration"
                },
                "type": "array"
              }
            },
            "required": [
              "devices"
            ],
            "type": "object"
          },
          "success": {
            "example": true,
            "type": "boolean"
          }
        },
        "required": [
          "success",
          "data"
        ],
        "type": "object"
      },
      "DeviceRegistration": {
        "properties": {
          "created_at": {
            "description": "When the device was registered",
            "example": "2024-06-24T22:00:00.000Z",
            "format": "date-time",
            "type": "string"
          },
          "delay_seconds": {
            "description": "Delay before sending notification (max 15 min)",
            "example": 60,
            "maximum": 900,
            "minimum": 0,
            "type": "integer"
          },
          "device_id": {
            "description": "Unique device identifier",
            "example": "550e8400-e29b-41d4-a716-446655440000",
            "type": "string"
          },
          "device_name": {
            "description": "Human-readable device name",
            "example": "iPhone 15 Pro",
            "type": "string"
          },
          "enabled": {
            "description": "Whether notifications are enabled for this device",
            "example": true,
            "type": "boolean"
          },
          "order": {
            "description": "Order in notification sequence (1 is reserved for email)",
            "example": 2,
            "minimum": 2,
            "type": "integer"
          },
          "platform": {
            "description": "Mobile platform",
            "enum": [
              "ios",
              "android"
            ],
            "example": "ios",
            "type": "string"
          },
          "updated_at": {
            "description": "When the device settings were last updated",
            "example": "2024-06-24T22:00:00.000Z",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "device_id",
          "device_name",
          "platform",
          "order",
          "delay_seconds",
          "enabled"
        ],
        "type": "object"
      },
      "ErrorResponse": {
        "example": {
          "error": "invalid_request",
          "error_description": "The provided project name is invalid.",
          "field": "project_name",
          "status": 400
        },
        "properties": {
          "error": {
            "description": "Error code or identifier.",
            "type": "string"
          },
          "error_description": {
            "description": "Human-readable description of the error.",
            "type": "string"
          },
          "field": {
            "description": "Field related to the error, if applicable.",
            "format": "uuid",
            "type": "string"
          },
          "status": {
            "description": "HTTP status code associated with the error.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "EscalationPolicy": {
        "example": {
          "created_at": "2023-10-15T14:30:00Z",
          "created_by": "user-123",
          "description": "For critical production alerts requiring immediate attention",
          "enabled": true,
          "escalation_rules": [
            {
              "delay_minutes": 0,
              "target": {
                "target_id": "schedule-primary-oncall",
                "type": "schedule"
              }
            },
            {
              "delay_minutes": 15,
              "target": {
                "target_id": "user-123",
                "type": "user"
              }
            }
          ],
          "href": "/v1/organizations/acme/incident-management/platform/escalation-policies/policy-123e4567-e89b-12d3-a456-426614174000",
          "name": "Immediate Response Team",
          "policy_id": "policy-123e4567-e89b-12d3-a456-426614174000",
          "repeat_policy": {
            "enabled": true,
            "max_repeats": 3,
            "repeat_after_seconds": 3600
          },
          "team_id": "team-456",
          "updated_at": "2023-10-15T14:30:00Z"
        },
        "properties": {
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "created_by": {
            "description": "User ID who created the policy",
            "type": "string"
          },
          "description": {
            "description": "Description of this escalation policy",
            "type": "string"
          },
          "enabled": {
            "description": "Whether this policy is active",
            "type": "boolean"
          },
          "escalation_rules": {
            "description": "Escalation steps in order",
            "items": {
              "$ref": "#/components/schemas/EscalationRule"
            },
            "type": "array"
          },
          "href": {
            "description": "Link to this escalation policy resource",
            "type": "string"
          },
          "name": {
            "description": "Human-readable name for the policy",
            "type": "string"
          },
          "policy_id": {
            "description": "Unique identifier for the escalation policy",
            "format": "uuid",
            "type": "string"
          },
          "repeat_policy": {
            "description": "What to do after all steps are completed",
            "properties": {
              "enabled": {
                "description": "Whether to repeat the escalation",
                "type": "boolean"
              },
              "max_repeats": {
                "description": "Maximum number of repeats (null = infinite)",
                "minimum": 1,
                "type": "integer"
              },
              "repeat_after_seconds": {
                "description": "Seconds to wait before repeating",
                "minimum": 1,
                "type": "integer"
              }
            },
            "type": "object"
          },
          "team_id": {
            "description": "ID of the incident team this policy belongs to",
            "format": "uuid",
            "type": "string"
          },
          "updated_at": {
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "EscalationPolicyList": {
        "example": {
          "escalation_policies": [],
          "pagination": {
            "has_more": false,
            "limit": 50,
            "next_token": null
          }
        },
        "properties": {
          "escalation_policies": {
            "items": {
              "$ref": "#/components/schemas/EscalationPolicy"
            },
            "type": "array"
          },
          "pagination": {
            "$ref": "#/components/schemas/PaginationMetadata"
          }
        },
        "type": "object"
      },
      "EscalationRule": {
        "description": "One step of a policy, as stored: `delay_minutes`, `target` and `rule_id`, nothing else. A step pages its one target; steps with the same `delay_minutes` are paged together.",
        "example": {
          "delay_minutes": 0,
          "rule_id": "8a3c2f9e-5b1d-4c7a-9e2f-1d6b3a8c4e70",
          "target": {
            "target_id": "schedule-primary-oncall",
            "target_name": "Primary on-call",
            "type": "schedule"
          }
        },
        "properties": {
          "delay_minutes": {
            "description": "Minutes to wait before this step triggers (0 = immediate)",
            "minimum": 0,
            "type": "integer"
          },
          "rule_id": {
            "description": "ID of the step. An update keeps it when the request sends the `rule_id` of a step of the stored policy; any other step gets a new one.",
            "type": "string"
          },
          "target": {
            "$ref": "#/components/schemas/EscalationRuleTarget",
            "description": "Who/what to notify in this step"
          }
        },
        "required": [
          "delay_minutes",
          "target",
          "rule_id"
        ],
        "type": "object"
      },
      "EscalationRuleRequest": {
        "description": "Only `delay_minutes`, `target` and `rule_id` are read; any other key is ignored and never stored, except `targets`, which is refused with 400 `validation_error`: a step pages its one target (give each target its own step; steps with the same `delay_minutes` are paged together).",
        "example": {
          "delay_minutes": 0,
          "target": {
            "target_id": "schedule-primary-oncall",
            "type": "schedule"
          }
        },
        "properties": {
          "delay_minutes": {
            "minimum": 0,
            "type": "integer"
          },
          "rule_id": {
            "description": "On an update, the `rule_id` of a step of the stored policy, to keep it; ignored otherwise (a new ID is given).",
            "type": "string"
          },
          "target": {
            "$ref": "#/components/schemas/EscalationTargetRequest"
          }
        },
        "required": [
          "delay_minutes",
          "target"
        ],
        "type": "object"
      },
      "EscalationRuleTarget": {
        "example": {
          "target_id": "user-123",
          "target_name": "Ana Torres",
          "type": "user"
        },
        "properties": {
          "target_id": {
            "description": "ID of the target (user ID, team ID, schedule ID), or the e-mail address, webhook URL or Slack incoming-webhook URL. Checked when the policy is saved: see `EscalationTargetRequest`. A `webhook` or `slack_channel` URL is the destination's credential: it is returned in full only to a caller who may edit the team's policies (an organization or team Owner or Manager) and masked to everyone else, as the scheme, the host and the last characters (`https://hooks.slack.com/…x7Qz`, with `target_masked: true`).",
            "type": "string"
          },
          "target_masked": {
            "description": "Present (true) on a `webhook` or `slack_channel` target whose URL is masked for this caller, who may not edit the policy. A masked URL sent back on create or update is a 400.",
            "readOnly": true,
            "type": "boolean"
          },
          "target_name": {
            "description": "Name of the user, team or schedule when the policy was saved, set by the API (a name sent in the request is ignored). Absent for an e-mail address or a URL, and for a target without a name.",
            "readOnly": true,
            "type": "string"
          },
          "type": {
            "description": "Type of escalation target",
            "enum": [
              "user",
              "team",
              "schedule",
              "schedule_next",
              "schedule_previous",
              "email",
              "webhook",
              "slack_channel"
            ],
            "type": "string"
          }
        },
        "required": [
          "type",
          "target_id"
        ],
        "type": "object"
      },
      "EscalationTargetRequest": {
        "description": "Only `type` and `target_id` are read; `target_name` is set by the API.",
        "example": {
          "target_id": "user-123",
          "type": "user"
        },
        "properties": {
          "target_id": {
            "description": "Checked when the policy is saved, else 400 `validation_error`: a `user` must be a member of the organization and a `team` a team of the organization (\"… is not a member of this organization\", \"… is not part of this organization\", whether or not the ID exists elsewhere), a schedule an enabled schedule of the team; an `email` is an address (at most 254 characters), a `webhook` an http(s) URL on its scheme's default port (443 for https, 80 for http: a URL naming another port is refused) and a `slack_channel` a Slack incoming-webhook URL (https://hooks.slack.com/services/…), both at most 2048 characters, never a masked one (`https://host/…abcd`, as a caller who may not edit the policy reads it); an ID is at most 200 characters. A `webhook` or `slack_channel` step is a Pro feature (included at no charge): on a plan without the `webhook` channel the policy is refused with `402 plan_feature_required` (`feature: webhook`).",
            "maxLength": 2048,
            "type": "string"
          },
          "type": {
            "description": "A `webhook` target is sent an HTTP POST of a JSON document (`event: alert.escalation`) and a `slack_channel` target a Slack message (Slack ignores the header below). Every such POST carries an `Idempotency-Key` header, a bare UUID (as Stripe sends it, not a quoted structured string) naming the page: the alert, the escalation, the round, the step and the URL. The JSON document repeats it as `idempotency_key`. A page delivered more than once (a retry after a timeout, a 408, 409, 429 or 5xx, a step run again) carries the same key, so a receiver may answer 2xx to a key it has already processed and drop the repeat. The same key also comes again after the receiver's own refusal (below). Deduplicate on the key alone: the body of a repeat may differ (`sent_at`, the alert's fields as of that delivery). A repeat can come about 30 minutes later (retries), up to a day later after an outage, and, in a rare recovery, up to 14 days later: keep keys at least a day. A repeat round, another step or a new escalation of the alert is another page with another key; a different key does not prove a different page (a re-route of the alert can page the first step of a running escalation again under a new key), and keys carry no order or time. The key is not a secret: deduplicate on it, and check the signature (below) before you trust a request.\n\n**Signatures** (Standard Webhooks, https://www.standardwebhooks.com). Every `webhook` POST also carries `webhook-id` (the same value as `Idempotency-Key`) and `webhook-timestamp` (the attempt's Unix time in seconds). Once a team Owner or Manager created the endpoint's signing secret (`POST …/webhook-endpoints/signing-secret`), it carries `webhook-signature` too: one or more space-separated `v1,<base64 signature>`, each the HMAC-SHA256 of `<webhook-id>.<webhook-timestamp>.<raw body>` keyed with the base64-decoded bytes after `whsec_` in the secret. During a rotation two signatures are sent, one per valid secret: accept the request when any of them matches. Verify the raw body bytes as received, compare in constant time, and reject a timestamp more than 5 minutes from your clock. The Standard Webhooks libraries verify these headers as they are. An endpoint without a secret is sent unsigned; Slack targets are never signed. A delivery the receiver refuses (any answer other than 2xx) is sent again about a minute later, three attempts in all, each with the same key and a new timestamp and signature. A receiver that enforces signatures should answer a delivery that fails verification (no `webhook-signature`, or none that matches) with a 4xx, not a 2xx. In JavaScript (Node): `expected = crypto.createHmac('sha256', Buffer.from(secret.slice(6), 'base64')).update(id + '.' + timestamp + '.' + rawBody).digest()`, then accept when one `v1,` value, base64-decoded, has the same length and `crypto.timingSafeEqual` with `expected`.",
            "enum": [
              "user",
              "team",
              "schedule",
              "schedule_next",
              "schedule_previous",
              "email",
              "webhook",
              "slack_channel"
            ],
            "type": "string"
          }
        },
        "required": [
          "type",
          "target_id"
        ],
        "type": "object"
      },
      "FreeOrgLimitError": {
        "description": "One free organization per owner (409 `free_org_limit`): a second free\norganization by the same owner, more than three free organizations created\nby one user in 30 days, or a transfer to someone who already owns a free\norganization. `organization_name` names the free organization the owner\nalready has when there is one.\n",
        "example": {
          "error": "free_org_limit",
          "error_description": "You already own the free organization 'acme'. Upgrade it to the Pro plan or create this organization on the Pro plan.",
          "organization_name": "acme",
          "status": 409
        },
        "properties": {
          "error": {
            "enum": [
              "free_org_limit"
            ],
            "type": "string"
          },
          "error_description": {
            "type": "string"
          },
          "organization_name": {
            "nullable": true,
            "type": "string"
          },
          "status": {
            "example": 409,
            "type": "integer"
          }
        },
        "required": [
          "error",
          "error_description",
          "status"
        ],
        "type": "object"
      },
      "IncidentTeam": {
        "example": {
          "created_at": "2023-10-15T14:30:00Z",
          "created_by": "user-123",
          "description": "Primary team responsible for production incidents and alerts",
          "enabled": true,
          "href": "/v1/organizations/acme-corp/incident-management/production-support",
          "member_count": 5,
          "my_role": "Owner",
          "name": "production-support",
          "organization_id": "org-123e4567-e89b-12d3-a456-426614174000",
          "team_id": "team-123e4567-e89b-12d3-a456-426614174000",
          "updated_at": "2023-10-15T14:30:00Z"
        },
        "properties": {
          "created_at": {
            "description": "Timestamp when the team was created.",
            "format": "date-time",
            "type": "string"
          },
          "created_by": {
            "description": "User ID who created the team.",
            "type": "string"
          },
          "description": {
            "description": "Description of the team's purpose and responsibilities.",
            "type": "string"
          },
          "enabled": {
            "description": "Whether the team is active. A disabled team pages nobody: its alerts are stored and not routed, and other teams' rules, escalation steps and rotations skip it. Absent means enabled.",
            "type": "boolean"
          },
          "href": {
            "description": "Resource URL for this team.",
            "type": "string"
          },
          "member_count": {
            "description": "Number of members in the team, the same in the list and in the single GET.",
            "type": "integer"
          },
          "my_role": {
            "description": "Role of the authenticated user in this team, or null if not a member.",
            "enum": [
              "Owner",
              "Manager",
              "Responder",
              "Observer"
            ],
            "nullable": true,
            "type": "string"
          },
          "name": {
            "description": "Name of the incident response team (unique within organization).",
            "type": "string"
          },
          "organization_id": {
            "description": "ID of the organization this team belongs to.",
            "format": "uuid",
            "type": "string"
          },
          "team_id": {
            "description": "Unique identifier for the incident response team.",
            "format": "uuid",
            "type": "string"
          },
          "updated_at": {
            "description": "Timestamp when the team was last updated.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "IncidentTeamList": {
        "example": {
          "pagination": {
            "has_more": false,
            "limit": 20,
            "next_token": null
          },
          "teams": []
        },
        "properties": {
          "pagination": {
            "$ref": "#/components/schemas/PaginationMetadata"
          },
          "teams": {
            "description": "List of incident response teams.",
            "items": {
              "$ref": "#/components/schemas/IncidentTeam"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "IncidentTeamMember": {
        "example": {
          "added_at": "2023-10-15T14:30:00Z",
          "added_by": "user-123",
          "email": "john.doe@company.com",
          "name": "John Doe",
          "notification_preferences": {
            "email": true,
            "phone_call": false,
            "push": true,
            "sms": false
          },
          "role": "Responder",
          "team_id": "team-123e4567-e89b-12d3-a456-426614174000",
          "user_id": "user-456"
        },
        "properties": {
          "added_at": {
            "description": "Timestamp when the member was added to the team.",
            "format": "date-time",
            "type": "string"
          },
          "added_by": {
            "description": "User ID who added this member to the team.",
            "type": "string"
          },
          "email": {
            "description": "Email address of the team member.",
            "format": "email",
            "type": "string"
          },
          "name": {
            "description": "Display name of the team member.",
            "type": "string"
          },
          "notification_preferences": {
            "allOf": [
              {
                "$ref": "#/components/schemas/NotificationPreferences"
              }
            ],
            "description": "The member's own notification channels, as paging uses them: read-only here, each person sets them with PATCH /v1/user/notification-preferences. Every member answer (list, GET, POST, PUT) carries the same view; only the list adds `phone_number`."
          },
          "role": {
            "description": "Role of the member in the team.",
            "enum": [
              "Owner",
              "Manager",
              "Responder",
              "Observer"
            ],
            "type": "string"
          },
          "team_id": {
            "description": "ID of the incident response team.",
            "format": "uuid",
            "type": "string"
          },
          "user_id": {
            "description": "ID of the user.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "IncidentTeamMemberList": {
        "example": {
          "members": [],
          "pagination": {
            "has_more": false,
            "limit": 20,
            "next_token": null
          }
        },
        "properties": {
          "members": {
            "description": "List of incident team members.",
            "items": {
              "$ref": "#/components/schemas/IncidentTeamMember"
            },
            "type": "array"
          },
          "pagination": {
            "$ref": "#/components/schemas/PaginationMetadata"
          }
        },
        "type": "object"
      },
      "IncludedAllowance": {
        "description": "The allowance every organization gets at $0 each UTC calendar month (`null` = unlimited); on the Free plan it is also the cap",
        "properties": {
          "alerts": {
            "nullable": true,
            "type": "integer"
          },
          "api_calls": {
            "nullable": true,
            "type": "integer"
          },
          "email": {
            "nullable": true,
            "type": "integer"
          },
          "paid_channel": {
            "description": "The voice + SMS pool (currently 0)",
            "nullable": true,
            "type": "integer"
          },
          "push": {
            "nullable": true,
            "type": "integer"
          }
        },
        "type": "object"
      },
      "InsightAction": {
        "description": "Optional action button for the insight",
        "properties": {
          "label": {
            "description": "The button label text",
            "example": "View Alerts",
            "maxLength": 50,
            "type": "string"
          },
          "link": {
            "description": "The URL or route to navigate to",
            "example": "/acme-corp/alerts?severity=P1",
            "type": "string"
          },
          "type": {
            "description": "Whether the link is internal (relative) or external (absolute URL)",
            "enum": [
              "internal",
              "external"
            ],
            "example": "internal",
            "type": "string"
          }
        },
        "required": [
          "label",
          "link",
          "type"
        ],
        "type": "object"
      },
      "InsightFinding": {
        "description": "One finding of the weekly review: severity `warning` (needs action this\nweek) or `info`. What went well is the report's `kudos`; reviews written\nbefore October 2026 may still carry a `success` finding.\n",
        "properties": {
          "action": {
            "$ref": "#/components/schemas/InsightAction"
          },
          "evidence": {
            "description": "What the data shows, with its numbers",
            "type": "string"
          },
          "recommendation": {
            "description": "One concrete action",
            "type": "string"
          },
          "severity": {
            "$ref": "#/components/schemas/InsightSeverity"
          },
          "title": {
            "type": "string"
          },
          "why_it_matters": {
            "type": "string"
          }
        },
        "required": [
          "title",
          "severity",
          "evidence",
          "why_it_matters",
          "recommendation"
        ],
        "type": "object"
      },
      "InsightMetadata": {
        "description": "Type-specific metadata for the insight",
        "properties": {
          "affected_entities": {
            "description": "List of affected entities (team names, sources, etc.)",
            "example": [
              "Platform Team",
              "Infrastructure Team"
            ],
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "category": {
            "description": "Sub-category of the insight, such as time_pattern, volume_spike, deploy_correlation (a deployment followed by unusual alerts of its team) or change_failure_rate (a team whose deployments often fail)",
            "example": "time_pattern",
            "type": "string"
          },
          "evidence": {
            "additionalProperties": true,
            "description": "The evidence pack of a `review` insight: the week's facts (volume,\nresponse, escalation, coverage, hygiene and, when the organization\nrecords deployments, changes: the week's deployments, the 30-day\nchange failure rate and the deployments followed by unusual alerts),\nthe source of every number in the report. A count that is null is\nunknown.\n",
            "type": "object"
          },
          "metrics": {
            "additionalProperties": true,
            "description": "Supporting metrics for the insight (a time pattern's `days_seen` and a day pattern's `weeks_seen` say on how many dates it recurred)",
            "example": {
              "alerts_in_window": 84,
              "days_seen": 19,
              "peak_hour_end": 16,
              "peak_hour_start": 14,
              "percentage": 70,
              "sample_size": 120
            },
            "type": "object"
          },
          "period": {
            "additionalProperties": true,
            "description": "The review's periods (`review` insights): the current and previous 7-day windows, the 30-day window, `thin_evidence`",
            "type": "object"
          },
          "recommendation": {
            "description": "One concrete action for this insight, written by the model from the week's evidence (only on the insights found by the daily run, which also writes the `review`)",
            "example": "Add an alias to the PgBouncer alert so repeats update one alert instead of paging again.",
            "type": "string"
          },
          "report": {
            "$ref": "#/components/schemas/InsightReport"
          },
          "time_range": {
            "description": "The time range analyzed for this insight",
            "properties": {
              "end": {
                "description": "End of the analysis period",
                "format": "date-time",
                "type": "string"
              },
              "start": {
                "description": "Start of the analysis period",
                "format": "date-time",
                "type": "string"
              }
            },
            "type": "object"
          },
          "timezone": {
            "description": "The clock the insight's hours and days are expressed in (time and day patterns are computed in UTC)",
            "example": "UTC",
            "type": "string"
          }
        },
        "required": [
          "category"
        ],
        "type": "object"
      },
      "InsightReport": {
        "description": "The weekly operations review a `review` insight carries (its title is the headline, its description the summary)",
        "properties": {
          "findings": {
            "items": {
              "$ref": "#/components/schemas/InsightFinding"
            },
            "type": "array"
          },
          "kudos": {
            "description": "What went well this week, or null",
            "nullable": true,
            "type": "string"
          },
          "next_steps": {
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "findings",
          "next_steps"
        ],
        "type": "object"
      },
      "InsightSeverity": {
        "description": "The severity level of the insight",
        "enum": [
          "info",
          "warning",
          "success"
        ],
        "example": "info",
        "type": "string"
      },
      "InsightType": {
        "description": "The type of AI insight",
        "enum": [
          "pattern",
          "anomaly",
          "recommendation",
          "prediction",
          "review"
        ],
        "example": "pattern",
        "type": "string"
      },
      "IntegrationKeyCreateRequest": {
        "example": {
          "description": "Prometheus monitoring system for alerts",
          "integration_name": "prometheus",
          "ttl_days": 90
        },
        "properties": {
          "description": {
            "description": "Optional description of what this integration key is used for: a string of at most 200 characters once trimmed (stored trimmed; blank is stored as null). Any other type or a longer text is a 400 `validation_error`.",
            "maxLength": 200,
            "nullable": true,
            "type": "string"
          },
          "integration_name": {
            "description": "Name of the integration (e.g., \"prometheus\", \"cloudwatch\", \"github-actions\"): 2–50 lowercase letters, digits, hyphens and underscores, starting and ending with a letter or digit. Anything else, a non-string included, is a 400 `validation_error`.",
            "maxLength": 50,
            "minLength": 2,
            "pattern": "^[a-z0-9][a-z0-9-_]*[a-z0-9]$",
            "type": "string"
          },
          "ttl_days": {
            "description": "Days until expiration, a whole number from 1 to 365 (a string of digits is accepted). Omit it or send null for a key that never expires; any other value (`\"abc\"`, 0, true, a fraction) is a 400 `validation_error`, never a key without expiry.",
            "maximum": 365,
            "minimum": 1,
            "nullable": true,
            "type": "integer"
          }
        },
        "required": [
          "integration_name"
        ],
        "type": "object"
      },
      "IntegrationKeyCreateResponse": {
        "example": {
          "created_at": "2024-03-16T14:30:00Z",
          "expires_at": "2024-06-16T14:30:00Z",
          "href": "/v1/organizations/acme-corp/incident-management/platform-team/integration-keys/123e4567-e89b-12d3-a456-426614174000",
          "integration_name": "prometheus",
          "key_id": "123e4567-e89b-12d3-a456-426614174000",
          "key_secret": "ign_1234567890abcdefghijklmnopqrstuvwxyz",
          "team_id": "team-789"
        },
        "properties": {
          "created_at": {
            "description": "When the key was created",
            "format": "date-time",
            "type": "string"
          },
          "expires_at": {
            "description": "Expiration date and time of the key, if set",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "href": {
            "description": "URL to manage this integration key",
            "type": "string"
          },
          "integration_name": {
            "description": "Name of the integration",
            "type": "string"
          },
          "key_id": {
            "description": "Unique identifier for the integration key",
            "format": "uuid",
            "type": "string"
          },
          "key_secret": {
            "description": "The generated integration key secret (only shown once)",
            "type": "string"
          },
          "team_id": {
            "description": "ID of the team this key belongs to",
            "format": "uuid",
            "type": "string"
          }
        },
        "type": "object"
      },
      "IntegrationKeyItem": {
        "example": {
          "created_at": "2024-03-16T14:30:00Z",
          "created_by": "user-456",
          "created_by_name": "John Doe",
          "description": "Prometheus monitoring system for alerts",
          "expires_at": "2024-06-16T14:30:00Z",
          "href": "/v1/organizations/acme-corp/incident-management/platform-team/integration-keys/123e4567-e89b-12d3-a456-426614174000",
          "integration_name": "prometheus",
          "key_id": "123e4567-e89b-12d3-a456-426614174000",
          "last_used_at": "2024-03-20T09:15:00Z",
          "revoked": false,
          "revoked_at": null,
          "token_prefix": "ign_12345678"
        },
        "properties": {
          "created_at": {
            "description": "When the key was created",
            "format": "date-time",
            "type": "string"
          },
          "created_by": {
            "description": "User ID who created the key",
            "type": "string"
          },
          "created_by_name": {
            "description": "Display name of the user who created the key",
            "type": "string"
          },
          "description": {
            "description": "Description of what this integration key is used for",
            "nullable": true,
            "type": "string"
          },
          "expires_at": {
            "description": "Expiration date and time of the key, if set",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "href": {
            "description": "URL to manage this integration key",
            "type": "string"
          },
          "integration_name": {
            "description": "Name of the integration",
            "type": "string"
          },
          "key_id": {
            "description": "Unique identifier for the integration key",
            "format": "uuid",
            "type": "string"
          },
          "last_used_at": {
            "description": "When the key was last used, if tracked",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "revoked": {
            "description": "Whether the key has been revoked",
            "type": "boolean"
          },
          "revoked_at": {
            "description": "When the key was revoked, if applicable",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "token_prefix": {
            "description": "First 12 characters of the token (for identification)",
            "type": "string"
          }
        },
        "type": "object"
      },
      "IntegrationKeyList": {
        "example": {
          "integration_keys": [
            {
              "created_at": "2024-03-16T14:30:00Z",
              "created_by": "user-456",
              "created_by_name": "John Doe",
              "description": "Prometheus monitoring system",
              "expires_at": "2024-06-16T14:30:00Z",
              "href": "/v1/organizations/acme-corp/incident-management/platform-team/integration-keys/123e4567-e89b-12d3-a456-426614174000",
              "integration_name": "prometheus",
              "key_id": "123e4567-e89b-12d3-a456-426614174000",
              "revoked": false,
              "revoked_at": null,
              "token_prefix": "ign_12345678"
            }
          ],
          "organization_name": "acme-corp",
          "pagination": {
            "has_more": false,
            "limit": 50,
            "next_token": null
          },
          "team_name": "platform-team"
        },
        "properties": {
          "integration_keys": {
            "description": "List of integration keys for the team",
            "items": {
              "$ref": "#/components/schemas/IntegrationKeyItem"
            },
            "type": "array"
          },
          "pagination": {
            "$ref": "#/components/schemas/PaginationMetadata"
          }
        },
        "type": "object"
      },
      "IntegrationKeyReissueResponse": {
        "example": {
          "expires_at": "2024-06-16T14:30:00Z",
          "href": "/v1/organizations/acme-corp/incident-management/platform-team/integration-keys/123e4567-e89b-12d3-a456-426614174000",
          "integration_name": "prometheus",
          "key_id": "123e4567-e89b-12d3-a456-426614174000",
          "key_secret": "ign_abcdef1234567890ghijklmnopqrstuvwxyz",
          "reissued_at": "2024-03-20T10:00:00Z"
        },
        "properties": {
          "expires_at": {
            "description": "Expiration date and time of the key, if set",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "href": {
            "description": "URL to manage this integration key",
            "type": "string"
          },
          "integration_name": {
            "description": "Name of the integration",
            "type": "string"
          },
          "key_id": {
            "description": "Unique identifier for the integration key",
            "format": "uuid",
            "type": "string"
          },
          "key_secret": {
            "description": "The new integration key secret (only shown once)",
            "type": "string"
          },
          "reissued_at": {
            "description": "When the key was reissued",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "InvitationDetail": {
        "description": "Invitee-facing view of an invitation, returned only to the invited email address.",
        "properties": {
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "email": {
            "format": "email",
            "type": "string"
          },
          "expires_at": {
            "format": "date-time",
            "type": "string"
          },
          "invitation_id": {
            "type": "string"
          },
          "invited_by_name": {
            "type": "string"
          },
          "organization_name": {
            "description": "While the invitation is pending, the organization's name as it is now: an organization renamed since the invitation was sent is named by its new name, the one the acceptance answers. A resolved invitation (accepted, revoked, expired) keeps the name it was sent with.",
            "type": "string"
          },
          "role": {
            "enum": [
              "Manager",
              "Member"
            ],
            "type": "string"
          },
          "status": {
            "description": "`expired` once `expires_at` has passed; `revoked` by an owner or manager, or because the organization was deleted",
            "enum": [
              "pending",
              "accepted",
              "revoked",
              "expired"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "Invoice": {
        "properties": {
          "billed_usage_usd": {
            "additionalProperties": {
              "type": "number"
            },
            "description": "Usage invoices — how much of each period's usage counts as billed once this invoice is paid, by `YYYY-MM`",
            "type": "object"
          },
          "billing_period": {
            "description": "Period the invoice covers (usage charges), e.g. \"2026-09\"",
            "nullable": true,
            "type": "string"
          },
          "billing_reason": {
            "description": "Why a usage invoice was issued — `monthly_usage` (at the end of the billing period), `usage_cap_reached` (trust-tier cap hit mid-period) or `plan_downgrade` (the outstanding usage billed before a downgrade to the Free plan); absent on seat invoices",
            "nullable": true,
            "type": "string"
          },
          "created_at": {
            "description": "When the invoice was created",
            "format": "date-time",
            "type": "string"
          },
          "disputed_at": {
            "description": "When a payment dispute on this invoice was first recorded; absent when there was none",
            "format": "date-time",
            "type": "string"
          },
          "hosted_invoice_url": {
            "description": "URL to the hosted invoice page (Stripe invoices)",
            "nullable": true,
            "type": "string"
          },
          "href": {
            "description": "API endpoint for this invoice",
            "type": "string"
          },
          "invoice_id": {
            "description": "Unique identifier for the invoice",
            "type": "string"
          },
          "invoice_pdf": {
            "description": "URL to the invoice PDF (Stripe invoices)",
            "nullable": true,
            "type": "string"
          },
          "line_items": {
            "description": "Usage invoices — one line per metric with an amount, showing the used and included quantities",
            "items": {
              "properties": {
                "amount": {
                  "type": "number"
                },
                "description": {
                  "type": "string"
                },
                "quantity": {
                  "description": "The billable quantity",
                  "type": "integer"
                },
                "unit_price": {
                  "type": "number"
                }
              },
              "type": "object"
            },
            "type": "array"
          },
          "organization_id": {
            "description": "Organization this invoice belongs to",
            "type": "string"
          },
          "payment_method_id": {
            "description": "Payment method used",
            "type": "string"
          },
          "payment_provider": {
            "description": "Payment provider (stripe or invoice)",
            "type": "string"
          },
          "quantity": {
            "description": "Number of items billed",
            "type": "integer"
          },
          "refunded_amount_minor": {
            "description": "The amount refunded on this invoice's payment so far, in the currency's minor unit (cents for USD); absent when nothing was refunded",
            "type": "integer"
          },
          "refunded_at": {
            "description": "When the latest refund was recorded; absent when nothing was refunded",
            "format": "date-time",
            "type": "string"
          },
          "renewal_dates": {
            "additionalProperties": {
              "nullable": true,
              "type": "string"
            },
            "description": "Seat renewals — the `renewal_date` of each seat the invoice pays for, by seat id. A renewal is listed with `status` `pending` from just before its charge until the charge is settled; it then becomes `paid`, or goes away when nothing was charged.",
            "type": "object"
          },
          "seat_ids": {
            "description": "Seat invoices — the seats the invoice pays for",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "status": {
            "description": "Payment status of the invoice (`failed` usage invoices are retried automatically during the payment grace period and by the manual retry; `disputed` while a chargeback on its payment is open or after it was lost; back to `paid` when the dispute is won)",
            "enum": [
              "paid",
              "pending",
              "failed",
              "disputed"
            ],
            "type": "string"
          },
          "total_amount": {
            "description": "Total invoice amount",
            "type": "number"
          },
          "type": {
            "description": "Type of invoice (`plan_upgrade` is the member-seat charge of a Free → Pro upgrade)",
            "enum": [
              "seat_purchase",
              "seat_renewal",
              "usage_charges",
              "plan_upgrade"
            ],
            "type": "string"
          },
          "unit_price": {
            "description": "Price per unit",
            "type": "number"
          },
          "updated_at": {
            "description": "When the invoice was last updated",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "IssueWebhookSigningSecretRequest": {
        "properties": {
          "keep_previous_seconds": {
            "default": 86400,
            "description": "On a rotation, how long the secret it replaces keeps signing beside the new one (0: it stops at once). Deliveries already under way may use it for about a minute more.",
            "maximum": 604800,
            "minimum": 0,
            "type": "integer"
          },
          "replace_previous": {
            "default": false,
            "description": "When a previous secret still signs, true stops it at once and rotates; without it the request is refused with 409 `rotation_in_progress`.",
            "type": "boolean"
          },
          "url": {
            "description": "A `webhook` target of one of the team's escalation policies (compared trimmed and case-insensitively). Never a masked or a Slack URL.",
            "maxLength": 2048,
            "type": "string"
          }
        },
        "required": [
          "url"
        ],
        "type": "object"
      },
      "ListInvoicesResponse": {
        "properties": {
          "invoices": {
            "items": {
              "$ref": "#/components/schemas/Invoice"
            },
            "type": "array"
          },
          "pagination": {
            "properties": {
              "has_more": {
                "type": "boolean"
              },
              "limit": {
                "type": "integer"
              },
              "next_token": {
                "nullable": true,
                "type": "string"
              },
              "total": {
                "type": "integer"
              }
            },
            "type": "object"
          }
        },
        "type": "object"
      },
      "ListSeatsResponse": {
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/Seat"
            },
            "type": "array"
          },
          "limit": {
            "description": "Maximum number of seats per page",
            "type": "integer"
          },
          "next_token": {
            "nullable": true,
            "type": "string"
          },
          "summary": {
            "description": "Counts over this page's `items`.",
            "properties": {
              "active": {
                "type": "integer"
              },
              "assigned": {
                "description": "Current seats (not `cancelled`) with a user",
                "type": "integer"
              },
              "cancelled": {
                "description": "Cancelled seats in `items`, counted apart from `total`",
                "type": "integer"
              },
              "complimentary": {
                "description": "Active complimentary seats (the owner's on every plan, all five on the Free plan)",
                "type": "integer"
              },
              "pending_cancellation": {
                "type": "integer"
              },
              "reserved": {
                "description": "Unassigned seats held by pending invitations",
                "type": "integer"
              },
              "total": {
                "description": "Current seats, every status but `cancelled` (active, grace_period, pending_cancellation, suspended)",
                "type": "integer"
              },
              "unassigned": {
                "type": "integer"
              }
            },
            "type": "object"
          }
        },
        "type": "object"
      },
      "MembershipSummary": {
        "description": "An organization the user belongs to without owning it (left when the deletion is requested).",
        "properties": {
          "name": {
            "nullable": true,
            "type": "string"
          },
          "organization_id": {
            "type": "string"
          },
          "role": {
            "description": "The user's organization role (Owner, Manager or Member).",
            "example": "Member",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "organization_id",
          "name"
        ],
        "type": "object"
      },
      "NoSeatsAvailableError": {
        "description": "422 `no_seats_available` (invitations, add member, accept): no free seat.\n`plan`, `limit` and `upgrade_path` let the app offer \"buy seats\"\non Pro (`limit.max` null) or \"upgrade\" on Free.\n",
        "example": {
          "error": "no_seats_available",
          "error_description": "All 5 seats of the Free plan are in use. Upgrade to the Pro plan to add more members.",
          "limit": {
            "current": 5,
            "key": "seats",
            "max": 5
          },
          "plan": "free",
          "status": 422,
          "upgrade_path": "/org/acme/settings/plan"
        },
        "properties": {
          "error": {
            "enum": [
              "no_seats_available"
            ],
            "type": "string"
          },
          "error_description": {
            "type": "string"
          },
          "limit": {
            "properties": {
              "current": {
                "description": "Seats in use (assigned or reserved by a pending invitation); null when they could not be counted.",
                "nullable": true,
                "type": "integer"
              },
              "key": {
                "enum": [
                  "seats"
                ],
                "type": "string"
              },
              "max": {
                "description": "The plan's seat limit; null when unlimited (Pro).",
                "nullable": true,
                "type": "integer"
              }
            },
            "type": "object"
          },
          "plan": {
            "enum": [
              "free",
              "paid"
            ],
            "type": "string"
          },
          "status": {
            "example": 422,
            "type": "integer"
          },
          "upgrade_path": {
            "example": "/org/acme/settings/plan",
            "type": "string"
          }
        },
        "required": [
          "error",
          "error_description",
          "status",
          "plan",
          "limit",
          "upgrade_path"
        ],
        "type": "object"
      },
      "NotPendingDeletionError": {
        "description": "The restore is refused (409): the account is not pending deletion, so there is nothing to restore (`not_pending_deletion`), or its purge has started and it cannot be restored any more (`purge_in_progress`).",
        "example": {
          "error": "not_pending_deletion",
          "error_description": "This account is not pending deletion; there is nothing to restore.",
          "status": 409
        },
        "properties": {
          "error": {
            "enum": [
              "not_pending_deletion",
              "purge_in_progress"
            ],
            "type": "string"
          },
          "error_description": {
            "type": "string"
          },
          "status": {
            "example": 409,
            "type": "integer"
          }
        },
        "required": [
          "error",
          "error_description",
          "status"
        ],
        "type": "object"
      },
      "NotificationMethodConfig": {
        "properties": {
          "delay_seconds": {
            "description": "Delay in seconds before sending this notification (from alert creation time), whole seconds. Capped at 900. Always 0 for e-mail, which is sent at once: any other value is refused.",
            "example": 0,
            "maximum": 900,
            "minimum": 0,
            "type": "integer"
          },
          "enabled": {
            "description": "Whether this notification method is enabled",
            "example": true,
            "type": "boolean"
          },
          "order": {
            "description": "Order in which this notification method should be executed",
            "example": 1,
            "maximum": 1000,
            "minimum": 1,
            "type": "integer"
          },
          "retry_count": {
            "description": "Total number of voice call attempts (1-3) while the alert stays unacknowledged. Only applicable for voice calls; defaults to 3.",
            "example": 3,
            "maximum": 3,
            "minimum": 1,
            "type": "integer"
          }
        },
        "required": [
          "order",
          "enabled",
          "delay_seconds"
        ],
        "type": "object"
      },
      "NotificationMethodItem": {
        "properties": {
          "added_at": {
            "format": "date-time",
            "type": "string"
          },
          "contact_info": {
            "description": "E-mail address, phone number or device name (push; the same as device_name).",
            "example": "+1234567890",
            "type": "string"
          },
          "delay_seconds": {
            "description": "Delay before this method fires (from alert creation).",
            "example": 0,
            "minimum": 0,
            "type": "integer"
          },
          "device_id": {
            "description": "Registered device (push only).",
            "type": "string"
          },
          "device_name": {
            "description": "Device name (push only).",
            "type": "string"
          },
          "enabled": {
            "example": true,
            "type": "boolean"
          },
          "order": {
            "description": "Position in the notification sequence.",
            "example": 2,
            "minimum": 1,
            "type": "integer"
          },
          "pending": {
            "description": "A number added on this method while its verified number keeps paging (sms and voice only): it replaces `contact_info` once its code is entered, keeping the method's order, delay and retries. Never paged before then. Answered only until its code lapses (`expires_at`): a number whose code lapsed can no longer be verified, and adding it again sends a new code.",
            "properties": {
              "added_at": {
                "format": "date-time",
                "type": "string"
              },
              "contact_info": {
                "description": "The number being verified.",
                "example": "+1234567899",
                "type": "string"
              },
              "expires_at": {
                "description": "When its code lapses.",
                "format": "date-time",
                "type": "string"
              }
            },
            "required": [
              "contact_info",
              "expires_at"
            ],
            "type": "object"
          },
          "platform": {
            "description": "Device platform (push only).",
            "type": "string"
          },
          "required": {
            "description": "True for the e-mail method, which cannot be removed.",
            "type": "boolean"
          },
          "retry_count": {
            "description": "Total voice-call attempts while the alert stays unacknowledged (voice only).",
            "maximum": 3,
            "minimum": 1,
            "type": "integer"
          },
          "type": {
            "description": "Channel of this method.",
            "enum": [
              "email",
              "push",
              "sms",
              "voice"
            ],
            "type": "string"
          },
          "verified": {
            "description": "Whether the phone / SMS number is verified (sms and voice only).",
            "type": "boolean"
          },
          "verified_at": {
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "type",
          "contact_info",
          "order",
          "enabled",
          "delay_seconds"
        ],
        "type": "object"
      },
      "NotificationPreferences": {
        "example": {
          "email": true,
          "escalation_delay_minutes": 15,
          "phone_call": false,
          "phone_number": "+1234567890",
          "push": true,
          "sms": false
        },
        "properties": {
          "email": {
            "default": true,
            "description": "Whether to receive email notifications.",
            "type": "boolean"
          },
          "escalation_delay_minutes": {
            "default": 15,
            "description": "Minutes to wait before escalating to this user.",
            "maximum": 60,
            "minimum": 0,
            "type": "integer"
          },
          "phone_call": {
            "default": false,
            "description": "Whether to receive phone call notifications.",
            "type": "boolean"
          },
          "phone_number": {
            "description": "Phone number for SMS and voice notifications.",
            "type": "string"
          },
          "push": {
            "default": true,
            "description": "Whether to receive push notifications.",
            "type": "boolean"
          },
          "sms": {
            "default": false,
            "description": "Whether to receive SMS notifications.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "OnCallInfo": {
        "example": {
          "next_handover": "2023-10-16T09:00:00Z",
          "on_call_participants": [],
          "schedule_id": "123e4567-e89b-42d3-a456-426614174000",
          "schedule_name": "primary-oncall",
          "time": "2023-10-15T14:30:00Z"
        },
        "properties": {
          "next_handover": {
            "description": "When the next handover occurs: the earliest instant after `time` at which a shift starts or ends (a rotation's `end_time`, an override, another rotation starting count too, however far ahead). Null when nothing changes ahead, for example the only participant of a rotation with no `end_time`, who stays on call.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "on_call_participants": {
            "description": "The participants on call at `time`.",
            "items": {
              "$ref": "#/components/schemas/OnCallParticipant"
            },
            "type": "array"
          },
          "schedule_id": {
            "description": "ID of the schedule.",
            "format": "uuid",
            "type": "string"
          },
          "schedule_name": {
            "description": "Name of the schedule.",
            "type": "string"
          },
          "time": {
            "description": "The instant the answer is for, in UTC: `at_time` as sent, or the moment of the request when it is absent.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "OnCallParticipant": {
        "example": {
          "id": "user-123",
          "is_override": false,
          "name": "John Doe",
          "rotation_id": "3f2c8a1e-6b4d-4e8a-9c1f-5d7e2a9b0c34",
          "rotation_name": "weekday-rotation",
          "shift_end": "2023-10-16T09:00:00Z",
          "type": "user"
        },
        "properties": {
          "id": {
            "description": "ID of the participant.",
            "type": "string"
          },
          "is_override": {
            "description": "Whether this participant is from an override.",
            "type": "boolean"
          },
          "name": {
            "description": "Display name of the participant, from its own record: for a user, the account's name (else its e-mail) when the user is a member of the organization; for a team, its name when a team of the organization; for an escalation policy, its name when a policy of the team. Else the id, never a name stored with the participant.",
            "type": "string"
          },
          "rotation_id": {
            "description": "ID of the rotation (null for overrides).",
            "format": "uuid",
            "nullable": true,
            "type": "string"
          },
          "rotation_name": {
            "description": "Name of the rotation (null for overrides).",
            "nullable": true,
            "type": "string"
          },
          "shift_end": {
            "description": "When this participant's current shift ends. Null when it does not end within the next 35 days (longer for long or weighted rotations), for example the only participant of a rotation, who stays on call.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "type": {
            "description": "Type of participant.",
            "enum": [
              "user",
              "team",
              "escalation",
              "override"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "Organization": {
        "description": "The organization as GET /organizations (each row), GET, PUT and PATCH\n…/transfer-ownership and POST /organizations answer it: the fields below for\nthe requesting user (the single GET adds `plan_usage` and `limits_reached`).\nOnly these fields are answered.\n",
        "example": {
          "access_state": "active",
          "activated_at": "2023-10-16T09:12:00Z",
          "created_at": "2023-10-15T14:30:00Z",
          "description": "Technology company focused on software development",
          "enabled": true,
          "href": "/v1/organizations/acme-corp",
          "is_owner": true,
          "limits_reached": [
            "seats",
            "teams"
          ],
          "member_count": 5,
          "my_role": "Owner",
          "name": "acme-corp",
          "organization_id": "org-123e4567-e89b-12d3-a456-426614174000",
          "owner_user_id": "user-123",
          "plan": "free",
          "plan_changed_at": "2023-10-15T14:30:00Z",
          "plan_limits": {
            "ai_insights": false,
            "alerts_per_day": 1000,
            "alerts_per_minute": 30,
            "alerts_per_month": 100,
            "channels": [
              "email",
              "push"
            ],
            "escalation_policies": 2,
            "escalation_repeat": false,
            "escalation_steps": 3,
            "invitations_per_day": 10,
            "paid_channel_pool": 0,
            "routing_rules": 3,
            "schedules": 2,
            "seats": 5,
            "support_priority": "normal",
            "teams": 1,
            "webhook_deliveries_per_day": 2000
          },
          "plan_usage": {
            "alerts_per_month": {
              "max": 100,
              "resets_at": "2023-11-01T00:00:00.000Z",
              "used": 63
            },
            "escalation_policies": {
              "max": 2,
              "used": 1
            },
            "routing_rules": {
              "max": 3,
              "used": 2
            },
            "schedules": {
              "max": 2,
              "used": 0
            },
            "seats": {
              "max": 5,
              "used": 5
            },
            "teams": {
              "max": 1,
              "used": 1
            }
          },
          "seat_status": "active",
          "updated_at": "2023-10-15T14:30:00Z",
          "upgraded_at": null
        },
        "properties": {
          "access_state": {
            "description": "Whether the requesting user can use the organization right now (read-only):\n`pending_deletion` — the organization is scheduled for deletion with its\nowner's account: its members are locked out,\nnothing is paged or billed, and only the owner can keep it, by restoring\ntheir account or transferring ownership; `deletion_scheduled_at` and\n`deletion_purge_at` say when. It wins over `frozen`;\n`frozen` — the organization is frozen for an unpaid balance; reported to\neveryone, the owner included (the owner still reaches Billing to settle it);\n`suspended` — the requesting user's own seat in this organization is\nsuspended (`seat_status` is `suspended`); `active` otherwise. Frozen,\nsuspended and pending-deletion organizations are still listed so clients\ncan explain why they are unavailable. Denied requests carry the matching\n`organization_frozen` / `seat_suspended` / `organization_pending_deletion`\nreason in the 403 body. A member who holds no usable seat is denied too\n(`seat_cancelled` with only cancelled seats, `seat_unassigned` without a\nseat), while this field reads `active` and `seat_status` `cancelled` or\nnull.\n",
            "enum": [
              "active",
              "frozen",
              "suspended",
              "pending_deletion"
            ],
            "type": "string"
          },
          "acquisition": {
            "$ref": "#/components/schemas/Acquisition"
          },
          "activated_at": {
            "description": "When the first alert of the organization was routed (free organizations).",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "created_at": {
            "description": "Timestamp when the organization was created.",
            "format": "date-time",
            "type": "string"
          },
          "deletion_purge_at": {
            "description": "When the organization and everything in it will be deleted, unless the owner\nrestores their account or transfers ownership first. Set with\n`deletion_scheduled_at`.\n",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "deletion_scheduled_at": {
            "description": "Set when the organization is scheduled for deletion with its owner's account\n(`access_state` is `pending_deletion`). Cleared when\nthe owner restores their account or transfers ownership.\n",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "description": {
            "description": "Description of the organization's purpose.",
            "type": "string"
          },
          "enabled": {
            "description": "Whether the organization is currently active.",
            "type": "boolean"
          },
          "frozen_at": {
            "description": "Set when the organization is in a billing-suspended state because\nthe owner's seat renewal failed three times. Non-owner members\nand integration keys are denied API access while this is set; the\nowner retains access to billing endpoints so they can settle the\nbalance via POST /billing/retry-renewal.\n",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "frozen_reason": {
            "description": "Why the organization is frozen: `payment_failed` (a charge still failed when\nits grace period ended), `payment_disputed` (a payment was disputed), the\nlegacy `owner_seat_suspended`, or — for a freeze by IgniteOps staff — the\nstaff's note to the owner, which everyone else reads as `administrative`.\n",
            "nullable": true,
            "type": "string"
          },
          "href": {
            "description": "The organization's path in this API (`/v1/organizations/{name}`).",
            "type": "string"
          },
          "is_owner": {
            "description": "True iff the requesting user is the organization owner. Computed\nper-request from `owner_user_id == requesting_user_id`. Use this\nto gate owner-only UI (billing, retry-renewal, transfer ownership)\ninstead of comparing identifiers client-side — the JWT carries\nthe Cognito sub while `owner_user_id` is the resolved stable user\nid, so a client-side comparison would not match.\n",
            "type": "boolean"
          },
          "limits_reached": {
            "description": "Single GET only: the `plan_usage` keys whose `used` reached `max`. Empty for\npaid organizations and when the usage could not be read.\n",
            "items": {
              "enum": [
                "seats",
                "teams",
                "routing_rules",
                "escalation_policies",
                "schedules",
                "alerts_per_month"
              ],
              "type": "string"
            },
            "type": "array"
          },
          "manual_retries_used": {
            "description": "Counter for the once-per-suspension retry-renewal endpoint. Reset to 0 each time the org enters the frozen state.",
            "nullable": true,
            "type": "integer"
          },
          "member_count": {
            "description": "Number of members in the organization.",
            "type": "integer"
          },
          "my_role": {
            "description": "The requesting user's membership role in this organization (read-only).",
            "enum": [
              "Owner",
              "Manager",
              "Member"
            ],
            "nullable": true,
            "type": "string"
          },
          "name": {
            "description": "Name of the organization.",
            "type": "string"
          },
          "organization_id": {
            "description": "Unique identifier for the organization.",
            "format": "uuid",
            "type": "string"
          },
          "owner_user_id": {
            "description": "User ID of the organization owner (handles billing).",
            "type": "string"
          },
          "plan": {
            "description": "The organization's plan.\n",
            "enum": [
              "free",
              "paid"
            ],
            "type": "string"
          },
          "plan_changed_at": {
            "description": "When the organization last changed plan (its creation for a new one).",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "plan_limits": {
            "$ref": "#/components/schemas/PlanLimits"
          },
          "plan_usage": {
            "$ref": "#/components/schemas/PlanUsage"
          },
          "seat_status": {
            "description": "Status of the requesting user's seat in this organization (read-only), or\nnull when they hold no seat or the seat could not be read. When the user\nholds several seats here, the most restrictive live one is reported\n(suspended, then grace_period, pending_cancellation, active, cancelled).\n",
            "enum": [
              "active",
              "pending_cancellation",
              "grace_period",
              "cancelled",
              "suspended"
            ],
            "nullable": true,
            "type": "string"
          },
          "terms_accepted_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "terms_version": {
            "description": "The Terms of Service version accepted when the organization was created.",
            "nullable": true,
            "type": "string"
          },
          "updated_at": {
            "description": "Timestamp when the organization was last updated.",
            "format": "date-time",
            "type": "string"
          },
          "upgraded_at": {
            "description": "When a free organization moved to the Pro plan, when it did.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          }
        },
        "type": "object"
      },
      "OrganizationInvitation": {
        "properties": {
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "email": {
            "description": "Invited email address.",
            "format": "email",
            "type": "string"
          },
          "expires_at": {
            "description": "Pending invitations expire 7 days after creation.",
            "format": "date-time",
            "type": "string"
          },
          "invitation_id": {
            "description": "Unique invitation identifier.",
            "type": "string"
          },
          "invited_by": {
            "description": "User ID of the inviter.",
            "type": "string"
          },
          "invited_by_name": {
            "description": "Display name of the inviter.",
            "type": "string"
          },
          "organization_id": {
            "description": "ID of the organization.",
            "type": "string"
          },
          "organization_name": {
            "description": "Name of the organization as it is called now (the list answers the name of the path); an invitation sent before a rename is not left with the old name.",
            "type": "string"
          },
          "role": {
            "description": "Role granted on acceptance.",
            "enum": [
              "Manager",
              "Member"
            ],
            "type": "string"
          },
          "status": {
            "description": "Invitation status.",
            "enum": [
              "pending",
              "accepted",
              "revoked",
              "expired"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "OrganizationList": {
        "example": {
          "organizations": [
            {
              "created_at": "2026-01-05T12:00:00.000Z",
              "description": "Acme Corp incident response",
              "enabled": true,
              "member_count": 2,
              "name": "acme-corp",
              "organization_id": "org-123e4567-e89b-12d3-a456-426614174000",
              "owner_user_id": "user-123",
              "updated_at": "2026-01-06T09:30:00.000Z"
            }
          ],
          "pagination": {
            "has_more": false,
            "limit": 50,
            "next_token": null
          }
        },
        "properties": {
          "organizations": {
            "description": "List of organizations.",
            "items": {
              "$ref": "#/components/schemas/Organization"
            },
            "type": "array"
          },
          "pagination": {
            "$ref": "#/components/schemas/PaginationMetadata"
          }
        },
        "type": "object"
      },
      "OrganizationMember": {
        "description": "The same in the list, GET, PUT (role change) and POST (direct add) answers: the documented attributes of the membership, with the name and e-mail of the member's account.",
        "example": {
          "added_at": "2023-10-15T14:30:00Z",
          "added_by": {
            "href": "/v1/organizations/acme/members/user-123",
            "user_id": "user-123"
          },
          "email": "john.doe@company.com",
          "href": "/v1/organizations/acme/members/user-456",
          "is_me": false,
          "name": "John Doe",
          "organization_id": "org-123e4567-e89b-12d3-a456-426614174000",
          "role": "Member",
          "user_id": "user-456"
        },
        "properties": {
          "added_at": {
            "description": "Timestamp when the member was added to the organization.",
            "format": "date-time",
            "type": "string"
          },
          "added_by": {
            "description": "Who added this member to the organization (null when the record does not say).",
            "nullable": true,
            "properties": {
              "href": {
                "type": "string"
              },
              "user_id": {
                "type": "string"
              }
            },
            "type": "object"
          },
          "email": {
            "description": "Email address of the organization member.",
            "format": "email",
            "type": "string"
          },
          "href": {
            "description": "This member's resource.",
            "type": "string"
          },
          "is_me": {
            "description": "Server-computed: whether this member is the calling user. Clients must use this (or the list response's current_user_role) for self/permission checks — the JWT sub does not match the internal user_id, so the caller cannot be identified client-side.",
            "type": "boolean"
          },
          "name": {
            "description": "Display name of the organization member.",
            "type": "string"
          },
          "organization_id": {
            "description": "ID of the organization.",
            "format": "uuid",
            "type": "string"
          },
          "role": {
            "description": "Role of the member in the organization.",
            "enum": [
              "Owner",
              "Manager",
              "Member"
            ],
            "type": "string"
          },
          "user_id": {
            "description": "ID of the user.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "OrganizationMetricsResponse": {
        "description": "Pre-calculated metrics for an entire organization (aggregated from all teams)",
        "example": {
          "age_minutes": 15,
          "is_stale": false,
          "metrics": {
            "acknowledged_alerts": 10,
            "busiest_teams": [
              {
                "alert_count": 200,
                "team_id": "team-123",
                "team_name": "Platform Team"
              },
              {
                "alert_count": 150,
                "team_id": "team-456",
                "team_name": "Infrastructure Team"
              }
            ],
            "by_severity": {
              "P1": 50,
              "P2": 100,
              "P3": 200,
              "P4": 100,
              "P5": 50
            },
            "closed_alerts": 475,
            "mtta_hours": 0.2,
            "mtta_minutes": 12,
            "mttr_hours": 1.5,
            "mttr_minutes": 90,
            "open_alerts": 15,
            "team_count": 5,
            "total_alerts": 500
          },
          "organization_id": "org-123",
          "organization_name": "acme-corp",
          "updated_at": "2024-12-30T10:00:00Z"
        },
        "properties": {
          "age_minutes": {
            "type": "integer"
          },
          "calculated_at": {
            "format": "date-time",
            "type": "string"
          },
          "is_stale": {
            "type": "boolean"
          },
          "metrics": {
            "properties": {
              "acknowledged_alerts": {
                "type": "integer"
              },
              "busiest_teams": {
                "description": "Top 10 teams by alert volume",
                "items": {
                  "properties": {
                    "alert_count": {
                      "type": "integer"
                    },
                    "team_id": {
                      "type": "string"
                    },
                    "team_name": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                },
                "type": "array"
              },
              "by_severity": {
                "properties": {
                  "P1": {
                    "type": "integer"
                  },
                  "P2": {
                    "type": "integer"
                  },
                  "P3": {
                    "type": "integer"
                  },
                  "P4": {
                    "type": "integer"
                  },
                  "P5": {
                    "type": "integer"
                  }
                },
                "type": "object"
              },
              "closed_alerts": {
                "type": "integer"
              },
              "mtta_hours": {
                "nullable": true,
                "type": "number"
              },
              "mtta_minutes": {
                "description": "Organization-wide mean time to acknowledge",
                "nullable": true,
                "type": "integer"
              },
              "mttr_hours": {
                "nullable": true,
                "type": "number"
              },
              "mttr_minutes": {
                "description": "Organization-wide mean time to resolve",
                "nullable": true,
                "type": "integer"
              },
              "open_alerts": {
                "type": "integer"
              },
              "team_count": {
                "description": "Number of teams in organization",
                "type": "integer"
              },
              "top_sources": {
                "description": "Top 10 alert sources across organization",
                "items": {
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "source": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                },
                "type": "array"
              },
              "total_alerts": {
                "description": "Total alerts across all teams",
                "type": "integer"
              }
            },
            "type": "object"
          },
          "organization_id": {
            "description": "Organization identifier",
            "format": "uuid",
            "type": "string"
          },
          "organization_name": {
            "description": "Organization name",
            "type": "string"
          },
          "updated_at": {
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "OwnedOrganizationSummary": {
        "description": "An organization the user owns, which is deleted with the account.",
        "example": {
          "deletion_scheduled_at": null,
          "member_count": 4,
          "name": "acme",
          "organization_id": "0f1a4c9e-7b5d-4d3e-9a2b-1c2d3e4f5a6b",
          "plan": "free"
        },
        "properties": {
          "deletion_scheduled_at": {
            "description": "Set once the organization is scheduled for deletion with the account; null otherwise.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "member_count": {
            "nullable": true,
            "type": "integer"
          },
          "name": {
            "nullable": true,
            "type": "string"
          },
          "organization_id": {
            "type": "string"
          },
          "plan": {
            "enum": [
              "free",
              "paid"
            ],
            "type": "string"
          }
        },
        "required": [
          "organization_id",
          "name",
          "plan"
        ],
        "type": "object"
      },
      "PaginationMetadata": {
        "example": {
          "has_more": true,
          "limit": 50,
          "next_token": "KsQgyVO076A"
        },
        "properties": {
          "has_more": {
            "description": "Whether there are more items available",
            "type": "boolean"
          },
          "has_previous": {
            "description": "Whether there are previous pages (page-based only)",
            "nullable": true,
            "type": "boolean"
          },
          "limit": {
            "description": "Maximum number of items per page/batch",
            "type": "integer"
          },
          "next_token": {
            "description": "Token for next batch of results (cursor-based only)",
            "nullable": true,
            "type": "string"
          },
          "page": {
            "description": "Current page number (page-based only)",
            "nullable": true,
            "type": "integer"
          },
          "total": {
            "description": "Total number of items (page-based only)",
            "nullable": true,
            "type": "integer"
          },
          "total_pages": {
            "description": "Total number of pages (page-based only)",
            "nullable": true,
            "type": "integer"
          },
          "type": {
            "description": "Type of pagination (cursor or page-based)",
            "enum": [
              "cursor",
              "page"
            ],
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "limit",
          "has_more"
        ],
        "type": "object"
      },
      "Participant": {
        "example": {
          "frequency": 1,
          "id": "user-123",
          "name": "John Doe",
          "type": "user"
        },
        "properties": {
          "frequency": {
            "default": 1,
            "description": "How many consecutive rotations this participant gets (for weighted rotation).",
            "type": "integer"
          },
          "id": {
            "description": "ID of the user, team, or escalation policy (absent for 'none' type).",
            "type": "string"
          },
          "name": {
            "description": "The name sent for a `user` participant, as stored. A `team` or `escalation` participant has none (an older one may still carry the name a client sent): who-is-on-call and the iCal feed name it from its own record.",
            "type": "string"
          },
          "type": {
            "description": "Type of participant ('none' means nobody is on-call).",
            "enum": [
              "user",
              "team",
              "escalation",
              "none"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "ParticipantCreateRequest": {
        "example": {
          "frequency": 1,
          "id": "user-123",
          "type": "user"
        },
        "properties": {
          "frequency": {
            "default": 1,
            "description": "How many consecutive rotations this participant gets. Any other key is not stored.",
            "maximum": 100,
            "minimum": 1,
            "type": "integer"
          },
          "id": {
            "description": "ID of the user, team, or escalation policy (omit for 'none' type). A user must be a member of the team, a team a team of the organization, an escalation policy a policy of this team (a disabled one is accepted: it pages nobody until enabled).",
            "maxLength": 200,
            "type": "string"
          },
          "name": {
            "description": "Optional display name of a `user` participant, stored with it: the iCal feed shows it for a user who is no longer a member of the organization. Ignored for the other types (who-is-on-call and the iCal feed name a team or a policy from its own record).",
            "maxLength": 200,
            "type": "string"
          },
          "type": {
            "description": "Type of participant.",
            "enum": [
              "user",
              "team",
              "escalation",
              "none"
            ],
            "type": "string"
          }
        },
        "required": [
          "type"
        ],
        "type": "object"
      },
      "PaymentMethod": {
        "description": "A payment method on file — snake_case identifiers next to camelCase\nfields. This is the wire shape of `addCreditCard` (201),\n`listPaymentMethods` (`paymentMethods[]`) and `setDefaultPaymentMethod`\n(200). `providerData.customerId` is left out of the `addCreditCard` and\n`setDefaultPaymentMethod` answers. The customer API adds Stripe cards only: an\n`invoice` method (pay by invoice, NET terms) is arranged with IgniteOps,\nnever added through this API, and is listed like any other.\n",
        "properties": {
          "addedBy": {
            "description": "User ID who added this payment method (audit trail)",
            "type": "string"
          },
          "billingAddress": {
            "description": "Billing address as entered when the card was added (may be empty)",
            "properties": {
              "city": {
                "type": "string"
              },
              "country": {
                "type": "string"
              },
              "line1": {
                "type": "string"
              },
              "line2": {
                "type": "string"
              },
              "postalCode": {
                "type": "string"
              },
              "state": {
                "type": "string"
              }
            },
            "type": "object"
          },
          "createdAt": {
            "description": "When the payment method was added",
            "format": "date-time",
            "type": "string"
          },
          "details": {
            "additionalProperties": true,
            "description": "Card summary (Stripe) or invoicing details (invoice)",
            "properties": {
              "brand": {
                "description": "Card brand (visa, mastercard, …)",
                "type": "string"
              },
              "cardholderName": {
                "description": "Name on the card",
                "type": "string"
              },
              "companyName": {
                "description": "Invoice payment methods — company name",
                "type": "string"
              },
              "expirationMonth": {
                "description": "Card expiration month",
                "type": "integer"
              },
              "expirationYear": {
                "description": "Card expiration year",
                "type": "integer"
              },
              "last4": {
                "description": "Last 4 digits of the card",
                "type": "string"
              },
              "paymentTerms": {
                "description": "Invoice payment methods — payment terms in days",
                "type": "integer"
              }
            },
            "type": "object"
          },
          "isDefault": {
            "description": "Whether this is the default payment method",
            "type": "boolean"
          },
          "organization_id": {
            "description": "Organization ID this payment method belongs to",
            "type": "string"
          },
          "payment_method_id": {
            "description": "Unique identifier of the payment method record (not the Stripe id — that is `providerData.paymentMethodId`)",
            "type": "string"
          },
          "provider": {
            "description": "Payment provider",
            "enum": [
              "stripe",
              "invoice"
            ],
            "type": "string"
          },
          "providerData": {
            "additionalProperties": true,
            "description": "Provider references; secrets are never returned",
            "properties": {
              "customerId": {
                "description": "Stripe customer id (`listPaymentMethods` only)",
                "type": "string"
              },
              "email": {
                "description": "E-mail address given to the provider for receipts",
                "type": "string"
              },
              "paymentMethodId": {
                "description": "Stripe PaymentMethod id (`pm_…`)",
                "type": "string"
              }
            },
            "type": "object"
          },
          "status": {
            "description": "`active` while usable; `detached` once Stripe reports the card removed",
            "type": "string"
          },
          "type": {
            "description": "`credit_card` for a Stripe card (other Stripe PaymentMethod types are passed through as-is), `invoice` for pay-by-invoice",
            "type": "string"
          },
          "updatedAt": {
            "description": "When the payment method was last updated",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "payment_method_id",
          "organization_id",
          "provider",
          "type",
          "isDefault",
          "status",
          "createdAt"
        ],
        "type": "object"
      },
      "PlanFeatureError": {
        "description": "A feature is not included in the organization's plan (402 `plan_feature_required`):\nbuying seats (`seats`, `POST …/billing/seats`) and repeating an escalation policy\n(`escalation_repeat`) on Free. The other values of `feature` are reserved: AI\ninsights answer `200 { insights: [], locked: true, reason: 'plan_feature_required',\nfeature: 'ai_insights', upgrade_path }` and phone calls / SMS are skipped when the\nalert is paged (recorded on the alert timeline), never refused with a 402.\n",
        "example": {
          "error": "plan_feature_required",
          "error_description": "AI insights is not included in the Free plan. Upgrade to enable it.",
          "feature": "ai_insights",
          "plan": "free",
          "status": 402,
          "upgrade_path": "/org/acme/settings/plan"
        },
        "properties": {
          "error": {
            "enum": [
              "plan_feature_required"
            ],
            "type": "string"
          },
          "error_description": {
            "type": "string"
          },
          "feature": {
            "enum": [
              "seats",
              "escalation_repeat",
              "ai_insights",
              "voice",
              "sms"
            ],
            "type": "string"
          },
          "plan": {
            "enum": [
              "free",
              "paid"
            ],
            "type": "string"
          },
          "status": {
            "example": 402,
            "type": "integer"
          },
          "upgrade_path": {
            "example": "/org/acme/settings/plan",
            "type": "string"
          }
        },
        "required": [
          "error",
          "error_description",
          "status",
          "plan",
          "feature",
          "upgrade_path"
        ],
        "type": "object"
      },
      "PlanLimitError": {
        "description": "A count cap of the organization's plan was reached (402 `plan_limit_reached`):\nthe resource cannot be created until the organization upgrades (or, on Pro,\nthe cap is unlimited and this error never occurs). `limit.max` is null when\nthe cap is unlimited; `upgrade_path` is the app route of the Plan page.\n",
        "example": {
          "error": "plan_limit_reached",
          "error_description": "Free plan limit reached: 1 team(s). Upgrade to add more.",
          "limit": {
            "current": 1,
            "key": "teams",
            "max": 1
          },
          "plan": "free",
          "status": 402,
          "upgrade_path": "/org/acme/settings/plan"
        },
        "properties": {
          "error": {
            "enum": [
              "plan_limit_reached"
            ],
            "type": "string"
          },
          "error_description": {
            "type": "string"
          },
          "limit": {
            "properties": {
              "current": {
                "description": "How many the organization (or the team, for per-team caps) already has; null when it could not be counted.",
                "nullable": true,
                "type": "integer"
              },
              "key": {
                "enum": [
                  "seats",
                  "teams",
                  "routing_rules",
                  "escalation_policies",
                  "escalation_steps",
                  "schedules",
                  "alerts_per_month",
                  "invitations_per_day"
                ],
                "type": "string"
              },
              "max": {
                "description": "The cap; null when unlimited.",
                "nullable": true,
                "type": "integer"
              }
            },
            "required": [
              "key",
              "max"
            ],
            "type": "object"
          },
          "plan": {
            "enum": [
              "free",
              "paid"
            ],
            "type": "string"
          },
          "status": {
            "example": 402,
            "type": "integer"
          },
          "upgrade_path": {
            "example": "/org/acme/settings/plan",
            "type": "string"
          }
        },
        "required": [
          "error",
          "error_description",
          "status",
          "plan",
          "limit",
          "upgrade_path"
        ],
        "type": "object"
      },
      "PlanLimits": {
        "additionalProperties": true,
        "description": "The effective limits of the organization's plan: the plan's limits, with\nany adjustment made for this organization. `null` means unlimited.\n",
        "properties": {
          "ai_insights": {
            "type": "boolean"
          },
          "alerts_per_day": {
            "nullable": true,
            "type": "integer"
          },
          "alerts_per_minute": {
            "nullable": true,
            "type": "integer"
          },
          "alerts_per_month": {
            "description": "Over the cap a free organization's alerts are stored, not routed.",
            "nullable": true,
            "type": "integer"
          },
          "channels": {
            "description": "The delivery channels the plan includes: e-mail and push on Free; `webhook` (an escalation step that posts to Slack or to a URL, at no charge), `sms` and `voice` on Pro.",
            "items": {
              "enum": [
                "email",
                "push",
                "webhook",
                "sms",
                "voice"
              ],
              "type": "string"
            },
            "type": "array"
          },
          "escalation_policies": {
            "description": "Per team.",
            "nullable": true,
            "type": "integer"
          },
          "escalation_repeat": {
            "type": "boolean"
          },
          "escalation_steps": {
            "description": "Per policy.",
            "nullable": true,
            "type": "integer"
          },
          "invitations_per_day": {
            "nullable": true,
            "type": "integer"
          },
          "paid_channel_pool": {
            "nullable": true,
            "type": "integer"
          },
          "routing_rules": {
            "description": "Per team.",
            "nullable": true,
            "type": "integer"
          },
          "schedules": {
            "description": "Per team.",
            "nullable": true,
            "type": "integer"
          },
          "seats": {
            "nullable": true,
            "type": "integer"
          },
          "support_priority": {
            "enum": [
              "normal",
              "high"
            ],
            "type": "string"
          },
          "teams": {
            "nullable": true,
            "type": "integer"
          },
          "webhook_deliveries_per_day": {
            "nullable": true,
            "type": "integer"
          }
        },
        "type": "object"
      },
      "PlanUsage": {
        "description": "Single GET only, free organizations only (an empty object on Pro): how much of\neach capped resource the organization uses. The per-team keys report the busiest\nteam, since the caps are per team. Empty when the counts could not be read.\n",
        "properties": {
          "alerts_per_month": {
            "properties": {
              "max": {
                "nullable": true,
                "type": "integer"
              },
              "resets_at": {
                "description": "The first instant of the next UTC month.",
                "format": "date-time",
                "type": "string"
              },
              "used": {
                "description": "Alerts created this UTC month, routed and suppressed.",
                "type": "integer"
              }
            },
            "required": [
              "used",
              "max",
              "resets_at"
            ],
            "type": "object"
          },
          "escalation_policies": {
            "$ref": "#/components/schemas/PlanUsageEntry"
          },
          "routing_rules": {
            "$ref": "#/components/schemas/PlanUsageEntry"
          },
          "schedules": {
            "$ref": "#/components/schemas/PlanUsageEntry"
          },
          "seats": {
            "$ref": "#/components/schemas/PlanUsageEntry"
          },
          "teams": {
            "$ref": "#/components/schemas/PlanUsageEntry"
          }
        },
        "type": "object"
      },
      "PlanUsageEntry": {
        "properties": {
          "max": {
            "description": "The cap; null when unlimited.",
            "nullable": true,
            "type": "integer"
          },
          "used": {
            "type": "integer"
          }
        },
        "required": [
          "used",
          "max"
        ],
        "type": "object"
      },
      "PlansResponse": {
        "description": "The public price list and plan limits",
        "properties": {
          "currency": {
            "type": "string"
          },
          "free_tier_enabled": {
            "description": "Whether POST /organizations without a card creates a free organization right now",
            "type": "boolean"
          },
          "included": {
            "$ref": "#/components/schemas/IncludedAllowance"
          },
          "limits": {
            "properties": {
              "free": {
                "additionalProperties": true,
                "description": "The Free plan's current limits (`null` = unlimited)",
                "type": "object"
              },
              "paid": {
                "additionalProperties": true,
                "type": "object"
              }
            },
            "type": "object"
          },
          "seat_price_usd": {
            "description": "Monthly price of one member seat on the Pro plan (the owner's seat is free)",
            "type": "number"
          },
          "signups_paused": {
            "description": "Free sign-ups are paused — POST /organizations on the Free plan answers 503 `signups_paused` (paid creation keeps working)",
            "type": "boolean"
          },
          "usage_prices": {
            "description": "Price per unit beyond the included allowance on the Pro plan (e-mail, push and API calls are 0; voice and SMS are priced per delivery at carrier cost plus markup, so their entry here is 0)",
            "properties": {
              "alerts_created": {
                "type": "number"
              },
              "api_calls": {
                "type": "number"
              },
              "emails_sent": {
                "type": "number"
              },
              "push_notifications_sent": {
                "type": "number"
              },
              "sms_sent": {
                "type": "number"
              },
              "voice_calls": {
                "type": "number"
              }
            },
            "type": "object"
          }
        },
        "type": "object"
      },
      "PrimaryRegionUnavailableError": {
        "description": "503 `primary_region_unavailable`: the service could not complete the request\nright now (billing, the organization create, rename, delete and ownership\ntransfer, the account deletion and restore). Either it did not run, or it\ndid not answer in time and may have completed (`error_description` says\nwhich), so read the current state before repeating it. The `Retry-After`\nheader carries the same number of seconds as `retry_after` (a browser\ncalling the API cross-origin can read only the body).\n",
        "example": {
          "error": "primary_region_unavailable",
          "error_description": "This request could not be completed right now. Try again in 5 seconds.",
          "retry_after": 5,
          "status": 503
        },
        "properties": {
          "error": {
            "enum": [
              "primary_region_unavailable"
            ],
            "type": "string"
          },
          "error_description": {
            "type": "string"
          },
          "retry_after": {
            "description": "Seconds to wait before retrying.",
            "type": "integer"
          },
          "status": {
            "example": 503,
            "type": "integer"
          }
        },
        "required": [
          "error",
          "error_description",
          "status",
          "retry_after"
        ],
        "type": "object"
      },
      "PurchaseSeatsRequest": {
        "properties": {
          "paymentMethodId": {
            "description": "Payment method to use, defaults to organization default",
            "nullable": true,
            "type": "string"
          },
          "quantity": {
            "description": "Number of seats to purchase",
            "minimum": 1,
            "type": "integer"
          }
        },
        "required": [
          "quantity"
        ],
        "type": "object"
      },
      "PurchaseSeatsResponse": {
        "properties": {
          "idempotent_replay": {
            "description": "Present and `true` on a 200 replay — a purchase with this Idempotency-Key already completed, so the original invoice's seats are returned and nothing was charged or created again",
            "type": "boolean"
          },
          "message": {
            "type": "string"
          },
          "order": {
            "properties": {
              "amount": {
                "type": "number"
              },
              "order_id": {
                "description": "The Idempotency-Key of the purchase",
                "type": "string"
              },
              "payment_method": {
                "description": "The payment method charged (a summary, not the full PaymentMethod record)",
                "properties": {
                  "details": {
                    "additionalProperties": true,
                    "type": "object"
                  },
                  "id": {
                    "description": "payment_method_id of the record",
                    "type": "string"
                  },
                  "provider": {
                    "enum": [
                      "stripe",
                      "invoice"
                    ],
                    "type": "string"
                  },
                  "type": {
                    "type": "string"
                  }
                },
                "type": "object"
              },
              "quantity": {
                "type": "integer"
              },
              "renews_at": {
                "format": "date-time",
                "type": "string"
              }
            },
            "type": "object"
          },
          "purchased_seats": {
            "items": {
              "$ref": "#/components/schemas/Seat"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "QaLoginConfig": {
        "description": "Validation-account login policy. Exposes no credentials — only which addresses use the password challenge and whether the flow is on.",
        "properties": {
          "accounts": {
            "description": "Lower-cased email addresses allowed to sign in with a password. Empty when disabled.",
            "example": [],
            "items": {
              "format": "email",
              "type": "string"
            },
            "type": "array"
          },
          "enabled": {
            "description": "False when the flow is switched off or the allow-list is empty.",
            "example": false,
            "type": "boolean"
          },
          "label": {
            "description": "Badge the clients show while a validation account is signing in.",
            "example": "Validation Account",
            "type": "string"
          },
          "notice": {
            "description": "Explanatory copy shown alongside the password field.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "RateLimitError": {
        "description": "Too many requests for the organization's plan (429): the alert ingestion\nbuckets (`rate_limited`), invitations (`invitation_rate_limited`), phone\nverification (`verification_rate_limited`) and upgrade requests\n(`upgrade_request_rate_limited`). The `Retry-After` header carries the same\nnumber of seconds as `retry_after`.\n",
        "example": {
          "error": "invitation_rate_limited",
          "error_description": "This organization's plan allows 10 invitations per day. Try again tomorrow.",
          "limit": 10,
          "retry_after": 43200,
          "scope": "day",
          "status": 429
        },
        "properties": {
          "error": {
            "enum": [
              "rate_limited",
              "invitation_rate_limited",
              "verification_rate_limited",
              "upgrade_request_rate_limited"
            ],
            "type": "string"
          },
          "error_description": {
            "type": "string"
          },
          "limit": {
            "description": "The bucket's size.",
            "nullable": true,
            "type": "integer"
          },
          "retry_after": {
            "description": "Seconds until the bucket resets.",
            "type": "integer"
          },
          "scope": {
            "description": "Which bucket was full.",
            "enum": [
              "minute",
              "day"
            ],
            "nullable": true,
            "type": "string"
          },
          "status": {
            "example": 429,
            "type": "integer"
          }
        },
        "required": [
          "error",
          "error_description",
          "status",
          "retry_after"
        ],
        "type": "object"
      },
      "RecordDeploymentRequest": {
        "example": {
          "environment": "production",
          "external_id": "9123456789-1-checkout-api",
          "repository": "acme/checkout-api",
          "service": "checkout-api",
          "status": "succeeded",
          "version": "2.14.0"
        },
        "properties": {
          "commit_sha": {
            "maxLength": 64,
            "type": "string"
          },
          "description": {
            "maxLength": 2000,
            "type": "string"
          },
          "environment": {
            "default": "production",
            "maxLength": 50,
            "type": "string"
          },
          "external_id": {
            "description": "One id per deployment attempt and per service (for example `<run id>-<attempt>-<service>`). The same value again updates this deployment instead of recording another; with another `service` or `environment` it is refused (409 `external_id_conflict`). Required when `status` is `started`, so that a later request can close it, and what makes a resend safe. Refused when it contains `<` or `>`.",
            "maxLength": 200,
            "type": "string"
          },
          "finished_at": {
            "description": "Now by default once the status is not `started`; refused with `started`. A value up to 5 minutes before the deployment's start (clock skew between machines) is accepted and stored as the start; an earlier one is refused. At most a day ahead, at most 400 days back.",
            "format": "date-time",
            "type": "string"
          },
          "kind": {
            "default": "deploy",
            "enum": [
              "deploy",
              "rollback",
              "config",
              "infra",
              "feature_flag"
            ],
            "type": "string"
          },
          "previous_version": {
            "maxLength": 100,
            "type": "string"
          },
          "repository": {
            "maxLength": 200,
            "type": "string"
          },
          "service": {
            "description": "The service deployed.",
            "maxLength": 100,
            "type": "string"
          },
          "started_at": {
            "description": "RFC 3339 with an offset; `finished_at` when only that is given, else now. At most a day ahead, at most 400 days back.",
            "format": "date-time",
            "type": "string"
          },
          "status": {
            "default": "succeeded",
            "enum": [
              "started",
              "succeeded",
              "failed",
              "rolled_back"
            ],
            "type": "string"
          },
          "tags": {
            "additionalProperties": {
              "maxLength": 500,
              "type": "string"
            },
            "maxProperties": 20,
            "propertyNames": {
              "maxLength": 100
            },
            "type": "object"
          },
          "triggered_by": {
            "maxLength": 254,
            "type": "string"
          },
          "url": {
            "description": "An http or https link to the run.",
            "maxLength": 2048,
            "type": "string"
          },
          "version": {
            "maxLength": 100,
            "type": "string"
          }
        },
        "required": [
          "service",
          "version"
        ],
        "type": "object"
      },
      "RegisterDeviceRequest": {
        "properties": {
          "delay_seconds": {
            "description": "Delay in seconds before sending push notification, whole seconds (default 0, or the device's stored delay)",
            "example": 60,
            "maximum": 900,
            "minimum": 0,
            "type": "integer"
          },
          "device_id": {
            "description": "Unique device identifier",
            "example": "550e8400-e29b-41d4-a716-446655440000",
            "maxLength": 256,
            "type": "string"
          },
          "device_info": {
            "$ref": "#/components/schemas/DeviceInfo"
          },
          "device_name": {
            "description": "Human-readable device name for notification preferences (the bounds of the device settings)",
            "example": "My iPhone",
            "maxLength": 100,
            "minLength": 1,
            "type": "string"
          },
          "device_token": {
            "description": "FCM token (Android) or APNs token (iOS) from Firebase, at most 1024 bytes",
            "example": "fK8x9vZnRJ-abc123def456...",
            "maxLength": 1024,
            "type": "string"
          },
          "platform": {
            "description": "Mobile platform",
            "enum": [
              "ios",
              "android"
            ],
            "example": "ios",
            "type": "string"
          },
          "previous_device_id": {
            "description": "The id this installation registered under before, when the app moves to a new device_id. The registration under the previous id (same platform only) is removed; if the new id has no registration yet, it first takes over that one's name, order, delay and enabled state.",
            "example": "m3k9x2-4f8a1c7d2e-9b6c3a1f5e",
            "type": "string"
          }
        },
        "required": [
          "device_token",
          "platform",
          "device_id"
        ],
        "type": "object"
      },
      "RegisterDeviceResponse": {
        "properties": {
          "data": {
            "properties": {
              "delay_seconds": {
                "description": "Delay before sending notification",
                "example": 60,
                "type": "integer"
              },
              "device_name": {
                "description": "Device name",
                "example": "My iPhone",
                "type": "string"
              },
              "message": {
                "example": "Device registered successfully",
                "type": "string"
              },
              "order": {
                "description": "Order in notification sequence",
                "example": 2,
                "type": "integer"
              },
              "registration_id": {
                "description": "Unique registration identifier",
                "example": "user123:device456",
                "type": "string"
              }
            },
            "required": [
              "registration_id",
              "message"
            ],
            "type": "object"
          },
          "success": {
            "example": true,
            "type": "boolean"
          }
        },
        "required": [
          "success",
          "data"
        ],
        "type": "object"
      },
      "RemoveMethodResponse": {
        "properties": {
          "message": {
            "description": "Success message",
            "example": "Phone number removed successfully",
            "type": "string"
          }
        },
        "required": [
          "message"
        ],
        "type": "object"
      },
      "ReorderNotificationMethodsRequest": {
        "properties": {
          "methods": {
            "description": "Array of notification methods with their new order and delay settings. Each method once: email, sms and voice at most once each, each push device once.",
            "example": [
              {
                "order": 1,
                "type": "email"
              },
              {
                "delay_seconds": 60,
                "device_id": "dev-001",
                "order": 2,
                "type": "push"
              },
              {
                "delay_seconds": 300,
                "order": 3,
                "type": "sms"
              },
              {
                "delay_seconds": 120,
                "order": 4,
                "retry_count": 3,
                "type": "voice"
              }
            ],
            "items": {
              "properties": {
                "delay_seconds": {
                  "description": "Delay before sending notification (0-900 whole seconds); 0 or absent for email",
                  "example": 60,
                  "maximum": 900,
                  "minimum": 0,
                  "type": "integer"
                },
                "device_id": {
                  "description": "Device ID (required for push type)",
                  "example": "550e8400-e29b-41d4-a716-446655440000",
                  "maxLength": 256,
                  "type": "string"
                },
                "order": {
                  "description": "Order in notification sequence (email must be 1)",
                  "example": 1,
                  "maximum": 1000,
                  "minimum": 1,
                  "type": "integer"
                },
                "retry_count": {
                  "description": "Total number of voice call attempts (1-3). Only accepted for type voice; when omitted the stored value is kept.",
                  "example": 3,
                  "maximum": 3,
                  "minimum": 1,
                  "type": "integer"
                },
                "type": {
                  "description": "Type of notification method",
                  "enum": [
                    "email",
                    "push",
                    "sms",
                    "voice"
                  ],
                  "example": "email",
                  "type": "string"
                }
              },
              "required": [
                "type",
                "order"
              ],
              "type": "object"
            },
            "maxItems": 100,
            "minItems": 1,
            "type": "array"
          }
        },
        "required": [
          "methods"
        ],
        "type": "object"
      },
      "RestoreAccountResponse": {
        "description": "The state of the account after a restore.",
        "properties": {
          "account": {
            "$ref": "#/components/schemas/AccountState"
          },
          "api_keys_revoked": {
            "description": "Always true — the personal API keys revoked at the request stay revoked.",
            "type": "boolean"
          },
          "email": {
            "format": "email",
            "type": "string"
          },
          "organizations_left": {
            "description": "The organizations left at the request, which are not rejoined.",
            "items": {
              "$ref": "#/components/schemas/MembershipSummary"
            },
            "type": "array"
          },
          "organizations_restored": {
            "description": "The organizations un-scheduled by this restore, back exactly as they were.",
            "items": {
              "$ref": "#/components/schemas/OwnedOrganizationSummary"
            },
            "type": "array"
          },
          "user_id": {
            "type": "string"
          }
        },
        "required": [
          "user_id",
          "email",
          "account",
          "organizations_restored",
          "organizations_left"
        ],
        "type": "object"
      },
      "RetryPaymentResponse": {
        "properties": {
          "already_unfrozen": {
            "description": "Nothing was outstanding",
            "type": "boolean"
          },
          "invoices_paid": {
            "description": "Usage invoices paid",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "message": {
            "type": "string"
          },
          "seat_ids": {
            "description": "Member seats renewed",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "unfrozen": {
            "description": "Whether this call lifted the freeze. False when a payment dispute is still open, when another charge is still outstanding, or when the freeze was lifted or changed meanwhile: `message` says which.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "Rotation": {
        "description": "A rotation is stored with the fields below only (participants and time restrictions in their own shapes); any other key in the request is dropped.",
        "example": {
          "end_time": null,
          "name": "weekday-rotation",
          "participants": [],
          "rotation_id": "3f2c8a1e-6b4d-4e8a-9c1f-5d7e2a9b0c34",
          "start_time": "2023-10-16T09:00:00Z",
          "time_restrictions": [],
          "type": "daily"
        },
        "properties": {
          "end_time": {
            "description": "When this rotation ends (optional, absent or null = indefinite), in UTC as `start_time`.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "length": {
            "description": "Length of rotation in whole minutes (only for custom type), on the schedule's wall clock.",
            "type": "integer"
          },
          "name": {
            "description": "Optional name for the rotation.",
            "type": "string"
          },
          "participants": {
            "description": "List of participants in rotation order.",
            "items": {
              "$ref": "#/components/schemas/Participant"
            },
            "type": "array"
          },
          "rotation_id": {
            "description": "Unique identifier for the rotation, also the UID of its iCal events. Set by the API; an update that sends it back keeps it (see RotationCreateRequest).",
            "format": "uuid",
            "type": "string"
          },
          "start_time": {
            "description": "When this rotation starts, in UTC with milliseconds (2026-10-05T09:00:00.000Z), whatever offset it was sent with. Its local time in the schedule's timezone is the hand-over time, kept through daylight-saving changes; on a day the clocks skip it, the hand-over happens when they jump (02:30 → 03:00), and on a day they repeat it, at its first occurrence.",
            "format": "date-time",
            "type": "string"
          },
          "time_restrictions": {
            "description": "Time periods when this rotation is active (e.g., business hours only).",
            "items": {
              "$ref": "#/components/schemas/TimeRestriction"
            },
            "type": "array"
          },
          "type": {
            "description": "Type of rotation: daily (every day), weekly (every 7 days) or custom (every `length` minutes). Rotations run on the schedule's wall clock: every hand-over happens at the local time of `start_time` in the schedule's timezone, kept through daylight-saving changes (the shift that spans a change is an hour shorter or longer).",
            "enum": [
              "daily",
              "weekly",
              "custom"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "RotationCreateRequest": {
        "example": {
          "participants": [],
          "start_time": "2023-10-16T09:00:00Z",
          "type": "daily"
        },
        "properties": {
          "end_time": {
            "description": "When this rotation ends (optional; absent or null = never), a date-time as `start_time`, after it.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "length": {
            "description": "Length of rotation in whole minutes (required for custom type). A string (\"90\"), a fraction or any other type is a 400 `validation_error`.",
            "maximum": 525600,
            "minimum": 30,
            "type": "integer"
          },
          "name": {
            "description": "Optional name for the rotation.",
            "maxLength": 100,
            "type": "string"
          },
          "participants": {
            "description": "List of participants in rotation order.",
            "items": {
              "$ref": "#/components/schemas/ParticipantCreateRequest"
            },
            "minItems": 1,
            "type": "array"
          },
          "rotation_id": {
            "description": "On update only: the `rotation_id` of one of the schedule's rotations keeps that id (and the UID of the rotation's iCal events, so a subscribed calendar keeps following them), wherever the rotation moves in the list. Any other value, or none, gets a new id, as on create. One id sent on two rotations is a 400 `validation_error`.",
            "type": "string"
          },
          "start_time": {
            "description": "When this rotation starts: an RFC 3339 date-time with an offset (`Z` or `±HH:MM`, seconds and a fraction optional), in the years 1970–9999. Any other shape (a number, \"0\", a time without an offset, a bare date) or a day that does not exist (February 30) is a 400 `validation_error`. Stored in UTC with milliseconds. Its local time in the schedule's timezone is the hand-over time, kept through daylight-saving changes (see Rotation).",
            "format": "date-time",
            "type": "string"
          },
          "time_restrictions": {
            "description": "Time periods when this rotation is active (absent or null = at all hours).",
            "items": {
              "$ref": "#/components/schemas/TimeRestriction"
            },
            "nullable": true,
            "type": "array"
          },
          "type": {
            "description": "Type of rotation: daily, weekly or custom (every `length` minutes), on the schedule's wall clock (see Rotation).",
            "enum": [
              "daily",
              "weekly",
              "custom"
            ],
            "type": "string"
          }
        },
        "required": [
          "type",
          "start_time",
          "participants"
        ],
        "type": "object"
      },
      "RotationInProgressError": {
        "description": "409 `rotation_in_progress`: the previous secret still signs. Send `replace_previous: true` to stop it now.",
        "properties": {
          "error": {
            "type": "string"
          },
          "error_description": {
            "type": "string"
          },
          "previous_expires_at": {
            "description": "When the previous secret stops signing.",
            "format": "date-time",
            "type": "string"
          },
          "status": {
            "type": "integer"
          }
        },
        "type": "object"
      },
      "RoutingCondition": {
        "example": {
          "field": "tags.environment",
          "operator": "equals",
          "value": "production"
        },
        "properties": {
          "field": {
            "description": "The alert attribute to match: `severity`, `source`, `title`, `message`, `description`, `assignee`, `alias`, `entity`, `integration_name`, `count`, `tags.<key>` or `custom_fields.<key>` (key at most 100 characters). `status` and `priority` are refused (400 `validation_error`): rules are evaluated on open alerts only, so a condition on `status` matches every alert or none, and alerts have no priority (P1-P5 is the severity).",
            "example": "tags.environment",
            "type": "string"
          },
          "operator": {
            "description": "How to compare the field value. `at_least` and `at_most` compare severities, on `severity` only (P1 is the most severe): `at_least P3` matches P1, P2 and P3, and `at_most P4` matches P4 and P5; on any other field they are a 400 `validation_error`.",
            "enum": [
              "equals",
              "not_equals",
              "in",
              "not_in",
              "contains",
              "not_contains",
              "starts_with",
              "ends_with",
              "regex",
              "exists",
              "not_exists",
              "at_least",
              "at_most"
            ],
            "type": "string"
          },
          "value": {
            "description": "Value(s) to compare against: a list for `in` / `not_in`, nothing for `exists` / `not_exists`, a severity (`P1`-`P5`) for `at_least` / `at_most`. The comparison is strict, so a value must have the type the attribute holds, else a 400 `validation_error` naming the condition: strings for the text attributes and for every tag (an alert's tags are text, `\"true\"` rather than `true`), `P1`-`P5` for `severity`, whole numbers for `count` (which takes `equals`, `not_equals`, `in`, `not_in` and `exists` only), and any string, number or boolean for a custom field, which keeps the JSON type it was sent with. `not_exists` is refused on the attributes every alert has (`severity`, `source`, `title`, `message`, `description`, `count`). A regex pattern is at most 200 characters, compiled without flags, and may not nest quantifiers that can match the same text (such as `(a+)+`, `((a+))+` or `(?:(?:\\w\\s?)+\\s)+`), repeat a group whose alternatives can split the same text in more than one way (such as `(a|aa)+`; `(warn|warning)+` is fine), chain optional parts or alternatives that read the same text in more than 256 ways (such as `a?a?a?…aaa` or `(a|a)(a|a)…`) or use backreferences. It is tested against the first 10 000 characters of the alert field; a test that takes longer than 100 ms counts as no match",
            "oneOf": [
              {
                "maxLength": 500,
                "type": "string"
              },
              {
                "items": {
                  "oneOf": [
                    {
                      "maxLength": 500,
                      "type": "string"
                    },
                    {
                      "type": "number"
                    },
                    {
                      "type": "boolean"
                    }
                  ]
                },
                "maxItems": 50,
                "type": "array"
              },
              {
                "type": "number"
              },
              {
                "type": "boolean"
              }
            ]
          }
        },
        "required": [
          "field",
          "operator",
          "value"
        ],
        "type": "object"
      },
      "RoutingRule": {
        "example": {
          "conditions": {
            "logical_operator": "and",
            "rules": [
              {
                "field": "severity",
                "operator": "in",
                "value": [
                  "P1",
                  "P2"
                ]
              },
              {
                "field": "tags.environment",
                "operator": "equals",
                "value": "production"
              }
            ]
          },
          "created_at": "2023-10-15T14:30:00Z",
          "created_by": "user-123",
          "description": "Route all P1/P2 alerts from production to immediate escalation",
          "enabled": true,
          "href": "/v1/organizations/acme/incident-management/platform/routing-rules/rule-123e4567-e89b-12d3-a456-426614174000",
          "name": "Critical Production Alerts",
          "priority": 1,
          "rule_id": "rule-123e4567-e89b-12d3-a456-426614174000",
          "target": {
            "target_id": "policy-789",
            "target_name": "Immediate Response Team",
            "type": "escalation_policy"
          },
          "team_id": "team-456",
          "updated_at": "2023-10-15T14:30:00Z"
        },
        "properties": {
          "conditions": {
            "description": "Conditions that must match for this rule to apply",
            "properties": {
              "logical_operator": {
                "default": "and",
                "description": "How to combine multiple conditions",
                "enum": [
                  "and",
                  "or"
                ],
                "type": "string"
              },
              "rules": {
                "description": "Individual matching conditions; an empty list matches every alert, whatever `logical_operator` says",
                "items": {
                  "$ref": "#/components/schemas/RoutingCondition"
                },
                "maxItems": 20,
                "minItems": 0,
                "type": "array"
              }
            },
            "type": "object"
          },
          "created_at": {
            "description": "When the rule was created",
            "format": "date-time",
            "type": "string"
          },
          "created_by": {
            "description": "User ID who created the rule",
            "type": "string"
          },
          "description": {
            "description": "Description of what this rule does",
            "type": "string"
          },
          "enabled": {
            "description": "Whether this rule is active",
            "type": "boolean"
          },
          "escalation_policy_id": {
            "deprecated": true,
            "description": "Legacy: only on a rule saved before targets existed, which pages this escalation policy (it still applies). Rules are created and answered with `target`; read that first.",
            "type": "string"
          },
          "escalation_policy_name": {
            "deprecated": true,
            "description": "Legacy, with `escalation_policy_id`: the policy's name when that rule was saved. `target.target_name` replaces it.",
            "type": "string"
          },
          "href": {
            "description": "Link to this routing rule resource",
            "type": "string"
          },
          "name": {
            "description": "Human-readable name for the rule",
            "type": "string"
          },
          "priority": {
            "description": "Priority order (1 = highest priority, evaluated first)",
            "maximum": 999,
            "minimum": 1,
            "type": "integer"
          },
          "rule_id": {
            "description": "Unique identifier for the routing rule",
            "format": "uuid",
            "type": "string"
          },
          "target": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RoutingTarget"
              },
              {
                "properties": {
                  "target_name": {
                    "description": "Display name of the target (read-only, resolved when the rule is saved)",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            ],
            "description": "Who receives the alert when the rule matches (as stored; see CreateRoutingRuleRequest). Absent only on a rule saved before targets existed and never retargeted since, which carries `escalation_policy_id` instead."
          },
          "team_id": {
            "description": "ID of the incident team this rule belongs to",
            "format": "uuid",
            "type": "string"
          },
          "updated_at": {
            "description": "When the rule was last updated",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "RoutingRuleList": {
        "example": {
          "pagination": {
            "has_more": false,
            "limit": 50,
            "next_token": null
          },
          "routing_rules": []
        },
        "properties": {
          "pagination": {
            "$ref": "#/components/schemas/PaginationMetadata"
          },
          "routing_rules": {
            "items": {
              "$ref": "#/components/schemas/RoutingRule"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "RoutingTarget": {
        "example": {
          "target_id": "policy-789",
          "type": "escalation_policy"
        },
        "properties": {
          "target_id": {
            "description": "ID of the target (policy ID, user ID, team ID, schedule ID, etc.). Checked when the rule is saved: the policy or schedule must exist for the team and the user or team must belong to the organization, else the request is refused with 400 `validation_error` (\"… not found\" for a policy or schedule; \"… is not a member of this organization\" for a user and \"… is not part of this organization\" for a team, whether or not the ID exists elsewhere). A disabled policy or schedule is accepted; an alert that matches the rule then skips it, and its timeline records it. A direct target (schedule, user, team) that pages nobody when an alert matches falls back to notifying the whole team.",
            "type": "string"
          },
          "type": {
            "description": "Type of routing target. `team` pages every member of a team (the rule's own or another one of the organization, by team ID); `other_team` is the same for another team. A rule stored with a type outside this list is skipped when alerts are routed.",
            "enum": [
              "escalation_policy",
              "schedule",
              "schedule_next",
              "schedule_previous",
              "team",
              "other_team",
              "user"
            ],
            "type": "string"
          }
        },
        "required": [
          "type",
          "target_id"
        ],
        "type": "object"
      },
      "ScheduleInUseError": {
        "description": "The schedule is not deleted while escalation policies or routing rules of its team page it.",
        "example": {
          "error": "schedule_in_use",
          "error_description": "Cannot delete the schedule because it is referenced by escalation policies: Primary; routing rules: Database alerts. Point them at another target first.",
          "referencing_policies": [
            "Primary"
          ],
          "referencing_rules": [
            "Database alerts"
          ],
          "status": 400
        },
        "properties": {
          "error": {
            "enum": [
              "schedule_in_use"
            ],
            "type": "string"
          },
          "error_description": {
            "type": "string"
          },
          "referencing_policies": {
            "description": "The names of the team's escalation policies with a step on the schedule (enabled or not).",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "referencing_rules": {
            "description": "The names of the team's routing rules aimed at the schedule (enabled or not).",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "status": {
            "example": 400,
            "type": "integer"
          }
        },
        "type": "object"
      },
      "ScheduleList": {
        "example": {
          "filters": {
            "active_only": null,
            "name": null
          },
          "pagination": {
            "has_more": false,
            "limit": 100,
            "next_token": null,
            "type": "cursor"
          },
          "schedules": []
        },
        "properties": {
          "filters": {
            "description": "Applied filters for the schedule list",
            "properties": {
              "active_only": {
                "description": "Filter to show only active schedules",
                "nullable": true,
                "type": "boolean"
              },
              "name": {
                "description": "Filter by schedule name",
                "nullable": true,
                "type": "string"
              }
            },
            "type": "object"
          },
          "pagination": {
            "$ref": "#/components/schemas/PaginationMetadata"
          },
          "schedules": {
            "description": "List of team schedules.",
            "items": {
              "$ref": "#/components/schemas/TeamSchedule"
            },
            "type": "array"
          }
        },
        "required": [
          "schedules",
          "pagination"
        ],
        "type": "object"
      },
      "ScheduleOverride": {
        "description": "A temporary override of a schedule: for the whole period it replaces whoever the schedule's rotations would put on call.",
        "example": {
          "created_at": "2023-10-15T14:30:00Z",
          "created_by": "user-123",
          "end_time": "2023-10-21T09:00:00.000Z",
          "override_id": "c56a4180-65aa-42ec-a945-5fd21dec0538",
          "schedule_id": "123e4567-e89b-42d3-a456-426614174000",
          "start_time": "2023-10-20T09:00:00.000Z",
          "user_id": "user-456",
          "user_name": "Jane Smith"
        },
        "properties": {
          "created_at": {
            "description": "When the override was created.",
            "format": "date-time",
            "type": "string"
          },
          "created_by": {
            "description": "User ID who created the override.",
            "type": "string"
          },
          "end_time": {
            "description": "When the override ends, in UTC with milliseconds (2026-10-14T10:00:00.000Z).",
            "format": "date-time",
            "type": "string"
          },
          "override_id": {
            "description": "Unique identifier for the override.",
            "format": "uuid",
            "type": "string"
          },
          "schedule_id": {
            "description": "ID of the schedule this override applies to.",
            "format": "uuid",
            "type": "string"
          },
          "start_time": {
            "description": "When the override starts, in UTC with milliseconds (2026-10-14T09:00:00.000Z).",
            "format": "date-time",
            "type": "string"
          },
          "user_id": {
            "description": "User ID who will be on-call during the override.",
            "type": "string"
          },
          "user_name": {
            "description": "Display name of the override user.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "ScheduleOverrideList": {
        "example": {
          "overrides": [],
          "pagination": {
            "has_more": false,
            "next_token": null,
            "total": 0
          }
        },
        "properties": {
          "overrides": {
            "description": "List of schedule overrides.",
            "items": {
              "$ref": "#/components/schemas/ScheduleOverride"
            },
            "type": "array"
          },
          "pagination": {
            "$ref": "#/components/schemas/PaginationMetadata"
          }
        },
        "type": "object"
      },
      "ScheduledOrganization": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OwnedOrganizationSummary"
          },
          {
            "properties": {
              "deletion_purge_at": {
                "description": "The same instant as the owner's `deletion_purge_at`.",
                "format": "date-time",
                "nullable": true,
                "type": "string"
              }
            },
            "type": "object"
          }
        ]
      },
      "Seat": {
        "properties": {
          "assigned_at": {
            "description": "When the seat was assigned to a user",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "assigned_by": {
            "description": "User who assigned the seat",
            "nullable": true,
            "type": "string"
          },
          "cancelled_at": {
            "description": "When the seat was cancelled, or for a `pending_cancellation` seat when the cancel was asked; absent on a seat that was never cancelled",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "created_at": {
            "description": "When the seat was purchased",
            "format": "date-time",
            "type": "string"
          },
          "grace_period_ends": {
            "description": "End of the grace period after a failed renewal (status grace_period)",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "href": {
            "description": "API endpoint for this seat",
            "type": "string"
          },
          "is_complimentary": {
            "default": false,
            "description": "A complimentary seat never renews and carries no payment method, renewal date or invoice: the owner's\nseat on every plan and the five seats of a free organization. `renews_at` is null; `cancelSeat` refuses\nit (`400 complimentary_seat`); the upgrade converts the assigned / reserved member seats in place.\n",
            "type": "boolean"
          },
          "is_owner_seat": {
            "default": false,
            "description": "Whether this is the organization owner's mandatory seat (cannot be unassigned, reassigned, or cancelled). Complimentary on every plan.",
            "type": "boolean"
          },
          "last_renewed_at": {
            "description": "When the seat was last renewed successfully",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "order_id": {
            "description": "Order ID this seat belongs to",
            "type": "string"
          },
          "organization_id": {
            "description": "Organization that owns this seat",
            "type": "string"
          },
          "renews_at": {
            "description": "Next renewal date for the seat; null for a complimentary seat",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "reserved_by_invitation": {
            "description": "Invitation id holding this seat for an invitee who has not accepted yet",
            "nullable": true,
            "type": "string"
          },
          "seat_id": {
            "description": "Unique identifier for the seat",
            "type": "string"
          },
          "status": {
            "description": "Current status of the seat.\n- active: paid through current billing period\n- grace_period: most recent renewal attempt failed; retries are scheduled\n- suspended: max renewal retries exhausted; access revoked for that member. The\n  organization itself is frozen (frozen_reason payment_failed) only when a charge is\n  still failing after the 7-day payment grace; the owner settles it with\n  POST /billing/retry-payment.\n- pending_cancellation: user-initiated cancel, will become cancelled at next renewal\n- cancelled: terminal — no longer billed and not visible to the user\n",
            "enum": [
              "active",
              "pending_cancellation",
              "grace_period",
              "cancelled",
              "suspended"
            ],
            "type": "string"
          },
          "suspended_at": {
            "description": "When the seat was suspended (status suspended)",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "suspension_reason": {
            "description": "Why the seat was suspended (currently always `payment_failed`)",
            "nullable": true,
            "type": "string"
          },
          "user_id": {
            "description": "User assigned to this seat, null if unassigned",
            "nullable": true,
            "type": "string"
          }
        },
        "type": "object"
      },
      "SendNotificationResponse": {
        "properties": {
          "data": {
            "properties": {
              "failed": {
                "description": "Number of devices that failed to receive",
                "example": 0,
                "type": "integer"
              },
              "sent": {
                "description": "Number of devices that received the notification",
                "example": 2,
                "type": "integer"
              },
              "total": {
                "description": "Total devices attempted",
                "example": 2,
                "type": "integer"
              }
            },
            "required": [
              "sent",
              "failed",
              "total"
            ],
            "type": "object"
          },
          "success": {
            "example": true,
            "type": "boolean"
          }
        },
        "required": [
          "success",
          "data"
        ],
        "type": "object"
      },
      "ServiceUnavailableError": {
        "description": "503 `service_unavailable`. Send the request again after `retry_after`\nseconds, the same value as the `Retry-After` header. Two cases answer\nit:\n\n- the request could not be authorized at that moment, before any\n  operation ran (`retry_after` 5): nothing was done. Any authenticated\n  operation can answer it;\n- during a service disruption, a change to an alert could not be\n  confirmed: an acknowledgement (by the API or an e-mail link), an\n  update, a close, a reopen or a delete (`retry_after` 30). The change\n  may already have been made: sent again, an acknowledgement then\n  answers as already acknowledged and a delete as not found. Creating\n  alerts goes on during a disruption (see `degraded` on the create's\n  answer).\n",
        "example": {
          "error": "service_unavailable",
          "error_description": "The request could not be authorized right now. Retry in a few seconds.",
          "retry_after": 5,
          "status": 503
        },
        "properties": {
          "error": {
            "enum": [
              "service_unavailable"
            ],
            "type": "string"
          },
          "error_description": {
            "type": "string"
          },
          "retry_after": {
            "description": "Seconds to wait before retrying.",
            "type": "integer"
          },
          "status": {
            "example": 503,
            "type": "integer"
          }
        },
        "required": [
          "error",
          "error_description",
          "retry_after",
          "status"
        ],
        "type": "object"
      },
      "Status": {
        "example": {
          "message": "ok"
        },
        "properties": {
          "message": {
            "description": "Status message.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "StoredNotificationMethod": {
        "description": "A notification method as stored.",
        "properties": {
          "added_at": {
            "format": "date-time",
            "type": "string"
          },
          "contact_info": {
            "description": "The e-mail address or the phone number.",
            "type": "string"
          },
          "delay_seconds": {
            "maximum": 900,
            "minimum": 0,
            "type": "integer"
          },
          "enabled": {
            "type": "boolean"
          },
          "order": {
            "minimum": 1,
            "type": "integer"
          },
          "retry_count": {
            "description": "Total voice-call attempts (phone only, when stored).",
            "maximum": 3,
            "minimum": 1,
            "type": "integer"
          },
          "verified": {
            "description": "Whether the number is verified (phone and sms only).",
            "type": "boolean"
          },
          "verified_at": {
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "contact_info",
          "order",
          "enabled",
          "delay_seconds"
        ],
        "type": "object"
      },
      "SuccessResponse": {
        "properties": {
          "message": {
            "example": "Operation completed successfully",
            "type": "string"
          },
          "success": {
            "example": true,
            "type": "boolean"
          }
        },
        "required": [
          "success",
          "message"
        ],
        "type": "object"
      },
      "SupportMessage": {
        "properties": {
          "attachments": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "author_kind": {
            "enum": [
              "customer",
              "staff",
              "system"
            ],
            "type": "string"
          },
          "author_user_id": {
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "message_id": {
            "type": "string"
          },
          "ticket_id": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "SupportTicket": {
        "properties": {
          "assigned_staff_id": {
            "nullable": true,
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "created_by_kind": {
            "enum": [
              "customer",
              "staff"
            ],
            "type": "string"
          },
          "created_by_user_id": {
            "type": "string"
          },
          "last_message_at": {
            "format": "date-time",
            "type": "string"
          },
          "organization_id": {
            "type": "string"
          },
          "organization_plan": {
            "description": "The organization's plan when the ticket was opened",
            "enum": [
              "free",
              "paid"
            ],
            "type": "string"
          },
          "priority": {
            "description": "Clamped to normal on the Free plan (best-effort support)",
            "enum": [
              "low",
              "normal",
              "high",
              "urgent"
            ],
            "type": "string"
          },
          "requested_priority": {
            "description": "The priority the customer asked for when it was clamped",
            "enum": [
              "high",
              "urgent"
            ],
            "type": "string"
          },
          "status": {
            "enum": [
              "open",
              "pending",
              "resolved",
              "closed"
            ],
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "tags": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "ticket_id": {
            "type": "string"
          },
          "updated_at": {
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "TeamAlert": {
        "description": "An alert as the list, GET and PUT answer it, with its computed fields.",
        "example": {
          "alert_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
          "alias": "cpu-high-web-server-01",
          "assignee": "user-123",
          "assignee_name": "John Doe",
          "count": 1,
          "created_at": "2023-10-15T10:30:00Z",
          "description": "CPU utilization has exceeded 90% for the past 5 minutes",
          "entity": "web-server-01",
          "project_id": "proj-123",
          "severity": "P2",
          "source": "monitoring-system",
          "status": "open",
          "tags": {
            "datacenter": "us-east-1",
            "environment": "production",
            "service": "web-server"
          },
          "title": "High CPU Usage on web-server-01",
          "updated_at": "2023-10-15T10:30:00Z"
        },
        "properties": {
          "acknowledged_at": {
            "description": "Timestamp when the alert was acknowledged (cleared when the alert is reopened).",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "acknowledged_by": {
            "description": "Stable user id of the acknowledger.",
            "nullable": true,
            "type": "string"
          },
          "acknowledged_by_name": {
            "description": "Display name of the acknowledger (read-only, resolved on read): a member of the organization by name, else the `acknowledged_by` value itself.",
            "nullable": true,
            "type": "string"
          },
          "actions": {
            "description": "Actions taken on this alert.",
            "items": {
              "$ref": "#/components/schemas/AlertAction"
            },
            "type": "array"
          },
          "age_days": {
            "description": "Days since alert creation",
            "minimum": 0,
            "type": "integer"
          },
          "age_hours": {
            "description": "Hours since alert creation",
            "minimum": 0,
            "type": "integer"
          },
          "alert_id": {
            "description": "Unique identifier for the alert.",
            "format": "uuid",
            "type": "string"
          },
          "alert_url": {
            "description": "URL to view more details about the alert.",
            "type": "string"
          },
          "alias": {
            "description": "Unique alias for alert deduplication.",
            "type": "string"
          },
          "assignee": {
            "description": "User ID of the person assigned to this alert.",
            "type": "string"
          },
          "assignee_name": {
            "description": "Display name of the assigned person (read-only, resolved on read): the account's name, else its e-mail, when the assignee is a member of the organization; else the `assignee` value itself.",
            "type": "string"
          },
          "closed_at": {
            "description": "Timestamp when the alert was closed (cleared when the alert is reopened).",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "closed_by": {
            "description": "Stable user id of whoever closed the alert (cleared when the alert is reopened).",
            "nullable": true,
            "type": "string"
          },
          "count": {
            "default": 1,
            "description": "Number of times this alert has been triggered (for deduplication).",
            "minimum": 1,
            "type": "integer"
          },
          "created_at": {
            "description": "Timestamp when the alert was created.",
            "format": "date-time",
            "type": "string"
          },
          "created_by": {
            "description": "Who created the alert: the stable user id of the signed-in user who\ncreated it from the console, or `webhook` for an alert received\nthrough an integration key.\n",
            "type": "string"
          },
          "created_by_name": {
            "description": "Display name of the user who created the alert from the console (absent for webhook alerts).",
            "type": "string"
          },
          "custom_fields": {
            "description": "Extensible custom data for the alert.",
            "type": "object"
          },
          "degraded": {
            "description": "Present when the alert was received during a service disruption and listed once the service recovered (read-only); its timeline's `alert_created` entry carries the same field. Unless its routing was suppressed — its team disabled, the plan's monthly quota used up, or an integration key that was no longer valid when the alert was received (see `routing_suppressed` and `alert_quota_exceeded` on its timeline) — its escalation's first step that reaches someone was paged at once and again once the service recovered, the later steps following as usual. An alert created by a request whose answer was lost during the disruption, but whose create went through, is listed without it.",
            "properties": {
              "received_at": {
                "description": "When the alert was received.",
                "format": "date-time",
                "type": "string"
              }
            },
            "readOnly": true,
            "type": "object"
          },
          "description": {
            "description": "Detailed description of the alert.",
            "type": "string"
          },
          "duplicate_count": {
            "description": "Number of duplicate occurrences",
            "minimum": 0,
            "type": "integer"
          },
          "entity": {
            "description": "Specific entity or component that triggered the alert.",
            "type": "string"
          },
          "escalation_policy": {
            "description": "ID of the escalation policy to follow.",
            "type": "string"
          },
          "href": {
            "description": "Canonical URL for the alert resource",
            "format": "uri",
            "type": "string"
          },
          "integration_id": {
            "description": "ID of the integration that created this alert.",
            "type": "string"
          },
          "is_acknowledged": {
            "description": "Whether alert status is 'acknowledged'",
            "type": "boolean"
          },
          "is_duplicate": {
            "description": "Whether this alert is a duplicate (count > 1)",
            "type": "boolean"
          },
          "is_open": {
            "description": "Whether alert status is 'open'",
            "type": "boolean"
          },
          "is_overdue": {
            "description": "Whether alert is overdue (older than 24 hours and still open)",
            "type": "boolean"
          },
          "is_resolved": {
            "description": "Whether alert status is 'closed'",
            "type": "boolean"
          },
          "last_occurrence_at": {
            "description": "When the last repeated occurrence (alias deduplication) was received.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "project_id": {
            "description": "ID of the project this alert belongs to.",
            "format": "uuid",
            "type": "string"
          },
          "routing_completed_at": {
            "description": "When the alert's routing finished (read-only): an escalation took the alert, the routing reached no escalation, or a routing that did not finish was found with nobody to page (the alert no longer open, the team missing or disabled) or was given up (`routing_outcome` says how). Absent while the alert is being routed, a retried routing included, and for an alert that was never routed (a disabled team, the quota). A repeated occurrence or a reopen that routes the alert again clears it until that routing finishes.",
            "format": "date-time",
            "nullable": true,
            "readOnly": true,
            "type": "string"
          },
          "routing_outcome": {
            "description": "How the last routing of the alert ended (read-only): `escalated` (an escalation took it), `already_escalating` (an escalation was already running), `not_routed:team_disabled`, `not_routed:team_missing`, `not_routed:alert_<status>` (the alert was no longer open when it was routed, or when a routing that did not finish was to be retried), `recovery_failed` (a routing that did not finish was given up after several retries: nobody was paged; the timeline carries `routing_recovery_failed`).",
            "nullable": true,
            "readOnly": true,
            "type": "string"
          },
          "severity": {
            "description": "Severity level (P1=Critical, P2=High, P3=Medium, P4=Low, P5=Informational).",
            "enum": [
              "P1",
              "P2",
              "P3",
              "P4",
              "P5"
            ],
            "type": "string"
          },
          "severity_color": {
            "description": "UI color code for severity display",
            "enum": [
              "#d73027",
              "#fc8d59",
              "#fee08b",
              "#d9ef8b",
              "#91bfdb"
            ],
            "type": "string"
          },
          "severity_level": {
            "description": "Numeric severity level (1=P1/Critical, 2=P2/High, 3=P3/Medium, 4=P4/Low, 5=P5/Informational)",
            "maximum": 5,
            "minimum": 1,
            "type": "integer"
          },
          "source": {
            "description": "Source system or service that generated the alert.",
            "type": "string"
          },
          "status": {
            "description": "Current status of the alert (there is no other state).",
            "enum": [
              "open",
              "acknowledged",
              "closed"
            ],
            "type": "string"
          },
          "tags": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Key-value pairs for alert categorization and filtering.",
            "type": "object"
          },
          "time_since_created": {
            "description": "Minutes since alert creation",
            "minimum": 0,
            "type": "integer"
          },
          "time_since_updated": {
            "description": "Minutes since last update",
            "minimum": 0,
            "type": "integer"
          },
          "title": {
            "description": "Short summary/title of the alert.",
            "type": "string"
          },
          "updated_at": {
            "description": "Timestamp when the alert was last updated.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "TeamInUseError": {
        "description": "The team is not deleted while other teams' escalation policies or routing rules page it.",
        "example": {
          "error": "team_in_use",
          "error_description": "Cannot delete the team because other teams page it (escalation policies: web / Primary; routing rules: web / Database alerts). Point them at another target first.",
          "referencing_policies": [
            {
              "policy_id": "policy-1",
              "policy_name": "Primary",
              "team_id": "team-1",
              "team_name": "web"
            }
          ],
          "referencing_rules": [
            {
              "rule_id": "rule-1",
              "rule_name": "Database alerts",
              "team_id": "team-1",
              "team_name": "web"
            }
          ],
          "status": 400
        },
        "properties": {
          "error": {
            "enum": [
              "team_in_use"
            ],
            "type": "string"
          },
          "error_description": {
            "type": "string"
          },
          "referencing_policies": {
            "description": "The other teams' escalation policies with a step aimed at the team (enabled or not).",
            "items": {
              "properties": {
                "policy_id": {
                  "type": "string"
                },
                "policy_name": {
                  "type": "string"
                },
                "team_id": {
                  "type": "string"
                },
                "team_name": {
                  "type": "string"
                }
              },
              "type": "object"
            },
            "type": "array"
          },
          "referencing_rules": {
            "description": "The other teams' routing rules aimed at the team (enabled or not).",
            "items": {
              "properties": {
                "rule_id": {
                  "type": "string"
                },
                "rule_name": {
                  "type": "string"
                },
                "team_id": {
                  "type": "string"
                },
                "team_name": {
                  "type": "string"
                }
              },
              "type": "object"
            },
            "type": "array"
          },
          "status": {
            "example": 400,
            "type": "integer"
          }
        },
        "type": "object"
      },
      "TeamMetricsResponse": {
        "description": "Pre-calculated metrics for a specific team",
        "example": {
          "age_minutes": 15,
          "is_stale": false,
          "metrics": {
            "last_30_days": {
              "avg_alerts_per_day": 1.5,
              "total_alerts": 45
            },
            "overall": {
              "acknowledged_alerts": 3,
              "by_severity": {
                "P1": 10,
                "P2": 30,
                "P3": 60,
                "P4": 40,
                "P5": 10
              },
              "closed_alerts": 142,
              "mtta_hours": 0.25,
              "mtta_minutes": 15,
              "mttr_hours": 2,
              "mttr_minutes": 120,
              "open_alerts": 5,
              "top_sources": [
                {
                  "count": 80,
                  "source": "prometheus"
                },
                {
                  "count": 50,
                  "source": "cloudwatch"
                }
              ],
              "total_alerts": 150
            }
          },
          "organization_name": "acme-corp",
          "team_id": "team-123",
          "team_name": "Platform Team",
          "updated_at": "2024-12-30T10:00:00Z"
        },
        "properties": {
          "age_minutes": {
            "description": "Age of metrics in minutes",
            "type": "integer"
          },
          "calculated_at": {
            "description": "When metrics were calculated",
            "format": "date-time",
            "type": "string"
          },
          "is_stale": {
            "description": "Whether metrics are older than 1 hour",
            "type": "boolean"
          },
          "metrics": {
            "properties": {
              "last_30_days": {
                "description": "Metrics for the last 30 days",
                "type": "object"
              },
              "last_7_days": {
                "description": "Metrics for the last 7 days",
                "properties": {
                  "avg_alerts_per_day": {
                    "type": "number"
                  },
                  "busiest_day": {
                    "nullable": true,
                    "properties": {
                      "count": {
                        "type": "integer"
                      },
                      "date": {
                        "type": "string"
                      }
                    },
                    "type": "object"
                  },
                  "period_days": {
                    "type": "integer"
                  },
                  "total_alerts": {
                    "type": "integer"
                  }
                },
                "type": "object"
              },
              "last_90_days": {
                "description": "Metrics for the last 90 days",
                "type": "object"
              },
              "overall": {
                "description": "Overall metrics across all alerts",
                "properties": {
                  "acknowledged_alerts": {
                    "description": "Number of acknowledged alerts",
                    "type": "integer"
                  },
                  "by_severity": {
                    "description": "Alert counts by severity level",
                    "properties": {
                      "P1": {
                        "type": "integer"
                      },
                      "P2": {
                        "type": "integer"
                      },
                      "P3": {
                        "type": "integer"
                      },
                      "P4": {
                        "type": "integer"
                      },
                      "P5": {
                        "type": "integer"
                      }
                    },
                    "type": "object"
                  },
                  "by_source": {
                    "additionalProperties": {
                      "type": "integer"
                    },
                    "description": "Alert counts by source system",
                    "type": "object"
                  },
                  "closed_alerts": {
                    "description": "Number of closed/resolved alerts",
                    "type": "integer"
                  },
                  "mtta_hours": {
                    "description": "Mean time to acknowledge in hours",
                    "nullable": true,
                    "type": "number"
                  },
                  "mtta_minutes": {
                    "description": "Mean time to acknowledge in minutes",
                    "nullable": true,
                    "type": "integer"
                  },
                  "mtta_seconds": {
                    "description": "Mean time to acknowledge in seconds",
                    "nullable": true,
                    "type": "integer"
                  },
                  "mttr_hours": {
                    "description": "Mean time to resolve in hours",
                    "nullable": true,
                    "type": "number"
                  },
                  "mttr_minutes": {
                    "description": "Mean time to resolve in minutes",
                    "nullable": true,
                    "type": "integer"
                  },
                  "mttr_seconds": {
                    "description": "Mean time to resolve in seconds",
                    "nullable": true,
                    "type": "integer"
                  },
                  "open_alerts": {
                    "description": "Number of open alerts",
                    "type": "integer"
                  },
                  "top_sources": {
                    "description": "Top 10 alert sources by frequency",
                    "items": {
                      "properties": {
                        "count": {
                          "type": "integer"
                        },
                        "source": {
                          "type": "string"
                        }
                      },
                      "type": "object"
                    },
                    "type": "array"
                  },
                  "total_alerts": {
                    "description": "Total number of alerts",
                    "type": "integer"
                  }
                },
                "type": "object"
              }
            },
            "type": "object"
          },
          "organization_name": {
            "description": "Organization name",
            "type": "string"
          },
          "team_id": {
            "description": "Team identifier",
            "format": "uuid",
            "type": "string"
          },
          "team_name": {
            "description": "Team name",
            "type": "string"
          },
          "updated_at": {
            "description": "When metrics were last updated",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "TeamSchedule": {
        "example": {
          "created_at": "2023-10-15T14:30:00Z",
          "created_by": "user-123",
          "description": "Primary on-call schedule for production alerts",
          "enabled": true,
          "name": "primary-oncall",
          "rotations": [],
          "schedule_id": "123e4567-e89b-42d3-a456-426614174000",
          "team_id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
          "timezone": "America/New_York",
          "updated_at": "2023-10-15T14:30:00Z"
        },
        "properties": {
          "created_at": {
            "description": "Timestamp when the schedule was created.",
            "format": "date-time",
            "type": "string"
          },
          "created_by": {
            "description": "User ID who created the schedule.",
            "type": "string"
          },
          "description": {
            "description": "Optional description of the schedule.",
            "type": "string"
          },
          "enabled": {
            "description": "Whether the schedule is currently active (a disabled schedule pages nobody).",
            "type": "boolean"
          },
          "name": {
            "description": "Name of the schedule (alphanumeric, dots, underscores, dashes only).",
            "type": "string"
          },
          "rotations": {
            "description": "List of rotations that make up this schedule.",
            "items": {
              "$ref": "#/components/schemas/Rotation"
            },
            "type": "array"
          },
          "schedule_id": {
            "description": "Unique identifier for the schedule.",
            "format": "uuid",
            "type": "string"
          },
          "team_id": {
            "description": "ID of the incident team this schedule belongs to.",
            "format": "uuid",
            "type": "string"
          },
          "timezone": {
            "description": "IANA timezone for the schedule (e.g., \"America/New_York\", \"Asia/Kolkata\", \"UTC\"); primary names and their aliases (e.g., \"Asia/Calcutta\") are both accepted and stored as given.",
            "type": "string"
          },
          "updated_at": {
            "description": "Timestamp when the schedule was last updated.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "TestNotificationRequest": {
        "properties": {
          "badge": {
            "description": "Badge count to display (iOS)",
            "example": 1,
            "type": "integer"
          },
          "body": {
            "default": "This is a test notification",
            "description": "Notification body",
            "example": "This is a test notification",
            "type": "string"
          },
          "category": {
            "description": "Notification category (iOS)",
            "example": "ALERT_ACTIONS",
            "type": "string"
          },
          "channelId": {
            "description": "Android notification channel ID",
            "example": "alerts-high",
            "type": "string"
          },
          "data": {
            "description": "Additional data to include in the notification",
            "example": {
              "timestamp": "2024-06-24T22:00:00.000Z",
              "type": "test"
            },
            "type": "object"
          },
          "priority": {
            "default": "high",
            "description": "Notification priority",
            "enum": [
              "max",
              "high",
              "default",
              "low",
              "min"
            ],
            "example": "high",
            "type": "string"
          },
          "sound": {
            "description": "Sound to play (platform-specific)",
            "example": "default",
            "type": "string"
          },
          "title": {
            "default": "Test Notification",
            "description": "Notification title",
            "example": "Test Notification",
            "type": "string"
          }
        },
        "type": "object"
      },
      "TestWebhookEndpointRequest": {
        "properties": {
          "url": {
            "description": "A `webhook` target of one of the team's escalation policies, as for the signing secret, or a Slack incoming-webhook URL one of them posts to (a `slack_channel` target, or a `webhook` one on hooks.slack.com).",
            "maxLength": 2048,
            "type": "string"
          }
        },
        "required": [
          "url"
        ],
        "type": "object"
      },
      "TestWebhookEndpointResult": {
        "description": "What the endpoint answered to the test request: a POST of `{ \"event\": \"webhook.test\", \"idempotency_key\", \"sent_at\", \"organization\", \"team\" }` with the delivery headers and, when the endpoint has a secret, its signatures.",
        "properties": {
          "delivered": {
            "description": "True when the endpoint answered 2xx.",
            "type": "boolean"
          },
          "error": {
            "description": "Null when delivered. `blocked_destination`: the host resolves to an address IgniteOps never contacts (private, loopback, link-local, documentation and other special-purpose addresses), or the URL names a port other than its scheme's default (443 for https, 80 for http); `unresolvable_host`: the host does not exist; `dns_error`: its lookup failed and may pass later; `timeout`: no answer in 10 seconds; `connection_failed`: no connection; `http_error`: an answer other than 2xx (redirects are not followed).",
            "nullable": true,
            "type": "string"
          },
          "http_status": {
            "nullable": true,
            "type": "integer"
          },
          "signed": {
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "TimeRestriction": {
        "example": {
          "end_day": "friday",
          "end_time": "17:00",
          "start_day": "monday",
          "start_time": "09:00"
        },
        "properties": {
          "end_day": {
            "description": "Ending day of the week.",
            "enum": [
              "monday",
              "tuesday",
              "wednesday",
              "thursday",
              "friday",
              "saturday",
              "sunday"
            ],
            "type": "string"
          },
          "end_time": {
            "description": "Ending time in HH:MM format (24-hour).",
            "pattern": "^([01]?[0-9]|2[0-3]):[0-5][0-9]$",
            "type": "string"
          },
          "start_day": {
            "description": "Starting day of the week.",
            "enum": [
              "monday",
              "tuesday",
              "wednesday",
              "thursday",
              "friday",
              "saturday",
              "sunday"
            ],
            "type": "string"
          },
          "start_time": {
            "description": "Starting time in HH:MM format (24-hour).",
            "pattern": "^([01]?[0-9]|2[0-3]):[0-5][0-9]$",
            "type": "string"
          }
        },
        "type": "object"
      },
      "TourPreference": {
        "description": "Guided product tour state persisted by the web app.",
        "properties": {
          "status": {
            "description": "new = never started; snoozed = \"maybe later\" / left mid-way (offered again next visit); in_progress = running; completed = finished; dismissed = \"don't show this again\".\n",
            "enum": [
              "new",
              "snoozed",
              "in_progress",
              "completed",
              "dismissed"
            ],
            "type": "string"
          },
          "stepIndex": {
            "description": "Zero-based step to resume from",
            "example": 4,
            "minimum": 0,
            "type": "integer"
          },
          "team": {
            "description": "Team the tour was configured with",
            "example": "platform",
            "maxLength": 100,
            "nullable": true,
            "type": "string"
          },
          "updatedAt": {
            "description": "Client-side timestamp of the last change (used to reconcile with local state)",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "version": {
            "description": "Tour content version the state refers to",
            "example": 1,
            "minimum": 1,
            "type": "integer"
          }
        },
        "required": [
          "version",
          "status",
          "stepIndex"
        ],
        "type": "object"
      },
      "TransferOwnershipRequest": {
        "example": {
          "new_owner_user_id": "user-789"
        },
        "properties": {
          "new_owner_user_id": {
            "description": "User ID of the existing organization member who will become the new owner. A longer one names nobody (400 `invalid_request`).",
            "maxLength": 200,
            "type": "string"
          }
        },
        "required": [
          "new_owner_user_id"
        ],
        "type": "object"
      },
      "UpdateAlertCommentRequest": {
        "description": "Request to update an existing comment",
        "example": {
          "content": "Updated: Issue resolved by restarting the service."
        },
        "properties": {
          "content": {
            "description": "Updated comment content (Markdown supported)",
            "maxLength": 5000,
            "minLength": 1,
            "type": "string"
          }
        },
        "required": [
          "content"
        ],
        "type": "object"
      },
      "UpdateDeviceSettingsRequest": {
        "properties": {
          "delay_seconds": {
            "description": "Delay before sending notification (max 15 min)",
            "example": 60,
            "maximum": 900,
            "minimum": 0,
            "type": "integer"
          },
          "device_name": {
            "description": "Human-readable device name",
            "example": "My iPhone Personal",
            "maxLength": 100,
            "minLength": 1,
            "type": "string"
          },
          "enabled": {
            "description": "Whether to send notifications to this device",
            "example": true,
            "type": "boolean"
          },
          "order": {
            "description": "Order in notification sequence (1 is reserved for email)",
            "example": 2,
            "maximum": 1000,
            "minimum": 2,
            "type": "integer"
          }
        },
        "type": "object"
      },
      "UpdateDeviceSettingsResponse": {
        "properties": {
          "data": {
            "$ref": "#/components/schemas/DeviceRegistration"
          },
          "success": {
            "example": true,
            "type": "boolean"
          }
        },
        "required": [
          "success",
          "data"
        ],
        "type": "object"
      },
      "UpdateEscalationPolicyRequest": {
        "example": {
          "enabled": false
        },
        "properties": {
          "description": {
            "maxLength": 500,
            "type": "string"
          },
          "enabled": {
            "description": "A boolean; any other value is a 400 `validation_error`.",
            "type": "boolean"
          },
          "escalation_rules": {
            "items": {
              "$ref": "#/components/schemas/EscalationRuleRequest"
            },
            "minItems": 1,
            "type": "array"
          },
          "name": {
            "description": "Stored trimmed and unique in the team, as on create: a name another policy of the team has is a 400 `validation_error` (`Escalation policy name '<name>' is already used in this team`). Checked only when the trimmed name changes, so sending the policy's own name always passes",
            "maxLength": 100,
            "minLength": 1,
            "type": "string"
          },
          "repeat_policy": {
            "description": "Absent, the policy keeps its repeat; `null` sets the default of a policy created without one: no repeat (`enabled` false, `repeat_after_seconds` 3600, `max_repeats` null).",
            "nullable": true,
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "max_repeats": {
                "minimum": 1,
                "type": "integer"
              },
              "repeat_after_seconds": {
                "minimum": 1,
                "type": "integer"
              }
            },
            "type": "object"
          }
        },
        "type": "object"
      },
      "UpdateIncidentTeamMemberRequest": {
        "description": "Only `role`. A member's notification channels are their own, set with PUT /v1/user/notification-preferences: `notification_preferences` here is a 400 `validation_error` and nothing is written.",
        "example": {
          "role": "Manager"
        },
        "properties": {
          "role": {
            "description": "New role for the team member. Team Owners and the organization owner may assign any role, including Owner (teams support multiple owners), and may demote another Owner — but never the last one. Managers may only change Responders/Observers, and only to Responder or Observer.",
            "enum": [
              "Owner",
              "Manager",
              "Responder",
              "Observer"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "UpdateIncidentTeamRequest": {
        "example": {
          "description": "Updated team description",
          "enabled": false
        },
        "properties": {
          "description": {
            "description": "Description of the team's purpose and responsibilities, stored trimmed; null clears it.",
            "maxLength": 500,
            "nullable": true,
            "type": "string"
          },
          "enabled": {
            "description": "Whether the team is active. `false` switches the team off; a disabled team pages nobody.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "UpdateNotificationPreferencesRequest": {
        "description": "Only the sections sent are written: a number left out is kept as it is (a pending one included). A number is removed with DELETE …/phone or …/sms.",
        "properties": {
          "email": {
            "allOf": [
              {
                "$ref": "#/components/schemas/NotificationMethodConfig"
              },
              {
                "description": "Email notification configuration (mandatory). E-mail is always the first method, enabled and sent at once: order 1, enabled true and delay_seconds 0, else 400.",
                "example": {
                  "delay_seconds": 0,
                  "enabled": true,
                  "order": 1
                },
                "type": "object"
              }
            ]
          },
          "phone": {
            "allOf": [
              {
                "$ref": "#/components/schemas/NotificationMethodConfig"
              },
              {
                "description": "Phone notification configuration (optional, only if phone is already added and verified)",
                "example": {
                  "delay_seconds": 0,
                  "enabled": true,
                  "order": 2,
                  "retry_count": 3
                },
                "type": "object"
              }
            ]
          },
          "sms": {
            "allOf": [
              {
                "$ref": "#/components/schemas/NotificationMethodConfig"
              },
              {
                "description": "SMS notification configuration (optional, only if SMS is already added and verified)",
                "example": {
                  "delay_seconds": 300,
                  "enabled": true,
                  "order": 3
                },
                "type": "object"
              }
            ]
          }
        },
        "required": [
          "email"
        ],
        "type": "object"
      },
      "UpdateNotificationPreferencesResponse": {
        "description": "What is stored after the update: e-mail as it is sent (order 1, enabled, delay 0) and every stored number, the ones the request left out included.",
        "properties": {
          "email": {
            "$ref": "#/components/schemas/StoredNotificationMethod"
          },
          "phone": {
            "$ref": "#/components/schemas/StoredNotificationMethod"
          },
          "sms": {
            "$ref": "#/components/schemas/StoredNotificationMethod"
          },
          "user_id": {
            "type": "string"
          }
        },
        "required": [
          "user_id",
          "email"
        ],
        "type": "object"
      },
      "UpdateOrganizationMemberRequest": {
        "example": {
          "role": "Manager"
        },
        "properties": {
          "role": {
            "description": "New role for the organization member (cannot change to/from Owner role).",
            "enum": [
              "Manager",
              "Member"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "UpdateOrganizationRequest": {
        "example": {
          "description": "Updated organization description",
          "enabled": false,
          "name": "updated-org-name"
        },
        "properties": {
          "description": {
            "description": "Description of the organization's purpose: at most 500 characters once trimmed, stored trimmed; null clears it. Anything else is a 400, as on create.",
            "maxLength": 500,
            "nullable": true,
            "type": "string"
          },
          "enabled": {
            "description": "Whether the organization is active. A real boolean; \"false\" or 0 is a 400, never read as true.",
            "type": "boolean"
          },
          "name": {
            "description": "Name of the organization (URL-compliant alphanumeric, hyphens, underscores only). Unique regardless of case: no two organizations bear \"Acme\" and \"acme\" (409 `organization_already_exists`). The case given is the one kept.",
            "maxLength": 100,
            "minLength": 1,
            "pattern": "^[a-zA-Z0-9\\-_]+$",
            "type": "string"
          }
        },
        "type": "object"
      },
      "UpdateRoutingRuleRequest": {
        "example": {
          "enabled": false,
          "priority": 5
        },
        "properties": {
          "conditions": {
            "description": "As on create, at most 256 KB as stored.",
            "properties": {
              "logical_operator": {
                "enum": [
                  "and",
                  "or"
                ],
                "type": "string"
              },
              "rules": {
                "description": "Individual matching conditions; an empty list matches every alert, whatever `logical_operator` says",
                "items": {
                  "$ref": "#/components/schemas/RoutingCondition"
                },
                "maxItems": 20,
                "minItems": 0,
                "type": "array"
              }
            },
            "type": "object"
          },
          "description": {
            "maxLength": 500,
            "type": "string"
          },
          "enabled": {
            "description": "A boolean; any other value is a 400 `validation_error`.",
            "type": "boolean"
          },
          "name": {
            "maxLength": 100,
            "minLength": 1,
            "type": "string"
          },
          "priority": {
            "maximum": 999,
            "minimum": 1,
            "type": "integer"
          },
          "target": {
            "$ref": "#/components/schemas/RoutingTarget"
          }
        },
        "type": "object"
      },
      "UpdateTeamAlertRequest": {
        "description": "Partial update of an alert. Only the fields present are changed; `tags`\nand `custom_fields` are **replaced as a whole** (send the complete map).\nEvery change is recorded as an activity; a `notes` value sent together\nwith a change is attached to that change's activity (status first, then\nassignee, severity, tags, custom fields), a `notes` value on its own is\nrecorded as an update of the `notes` field. Setting `status` to `open`\nclears `acknowledged_at` / `acknowledged_by`. There is no\n`unacknowledged` status: reopen with `status: open` and, to drop the\nacknowledger's assignment, `assignee: null`.\n",
        "example": {
          "expected_updated_at": "2026-09-24T02:12:07.000Z",
          "notes": "Root cause: disk full on api-prod-3",
          "status": "closed"
        },
        "properties": {
          "assignee": {
            "description": "User ID (`usr_…`, the stable id returned by the members endpoints) to assign the alert to; `null` unassigns. A new assignee must be a member of the organization: anyone else is a 400 `validation_error`, the same answer for an id that is no account. An assignee sent unchanged is not checked again.",
            "nullable": true,
            "type": "string"
          },
          "custom_fields": {
            "additionalProperties": true,
            "description": "The complete new custom-field map (replaces the existing custom fields; values keep their JSON type).",
            "type": "object"
          },
          "expected_updated_at": {
            "description": "Optimistic concurrency: the `updated_at` the client last read. When the\nalert changed since then the update is rejected with `409 alert_modified`\nand the current alert is returned in the error body, so a whole-map\n`tags` / `custom_fields` write never overwrites a concurrent edit.\n",
            "format": "date-time",
            "type": "string"
          },
          "notes": {
            "description": "Note recorded with this update (stored on the alert and on the change's activity).",
            "maxLength": 2000,
            "type": "string"
          },
          "severity": {
            "description": "New severity level.",
            "enum": [
              "P1",
              "P2",
              "P3",
              "P4",
              "P5"
            ],
            "type": "string"
          },
          "status": {
            "description": "New status for the alert.",
            "enum": [
              "open",
              "acknowledged",
              "closed"
            ],
            "type": "string"
          },
          "tags": {
            "additionalProperties": {
              "maxLength": 500,
              "type": "string"
            },
            "description": "The complete new tag map (replaces the existing tags): keys of 1 to 100 characters, at most 20000 characters as JSON, else a 400 `validation_error`. Stored whole, and every tag is found by the `tags` filter of the alerts list, as on create. A value sent as a number or a boolean is stored as its text (`5` as `\"5\"`, `true` as `\"true\"`), as on create, which is what routing rules compare.",
            "maxProperties": 100,
            "type": "object"
          }
        },
        "type": "object"
      },
      "UpdateTeamScheduleRequest": {
        "example": {
          "enabled": false,
          "name": "updated-schedule"
        },
        "properties": {
          "description": {
            "description": "Description of the schedule.",
            "maxLength": 500,
            "type": "string"
          },
          "enabled": {
            "description": "Whether the schedule is active. A boolean; any other value is a 400 `validation_error`.",
            "type": "boolean"
          },
          "name": {
            "description": "Name of the schedule.",
            "maxLength": 100,
            "minLength": 1,
            "pattern": "^[a-zA-Z0-9._-]+$",
            "type": "string"
          },
          "rotations": {
            "description": "The complete new list of rotations: it replaces the stored one. Send each kept rotation's `rotation_id` back so it keeps its id.",
            "items": {
              "$ref": "#/components/schemas/RotationCreateRequest"
            },
            "type": "array"
          },
          "timezone": {
            "description": "Timezone for the schedule.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "UpdateUserPreferencesRequest": {
        "description": "Sections to merge. Set a section to null to remove it.",
        "minProperties": 1,
        "properties": {
          "tour": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TourPreference"
              }
            ],
            "nullable": true
          }
        },
        "type": "object"
      },
      "UpgradePaymentError": {
        "properties": {
          "client_secret": {
            "description": "payment_requires_action — confirm it with Stripe.js, then retry with the same Idempotency-Key",
            "type": "string"
          },
          "error": {
            "enum": [
              "payment_failed",
              "payment_requires_action"
            ],
            "type": "string"
          },
          "error_description": {
            "type": "string"
          },
          "intent_type": {
            "enum": [
              "payment_intent",
              "setup_intent"
            ],
            "type": "string"
          },
          "status": {
            "type": "integer"
          }
        },
        "type": "object"
      },
      "UpgradePlanRequest": {
        "properties": {
          "billingAddress": {
            "properties": {
              "city": {
                "type": "string"
              },
              "country": {
                "type": "string"
              },
              "line1": {
                "type": "string"
              },
              "line2": {
                "type": "string"
              },
              "postalCode": {
                "type": "string"
              },
              "state": {
                "type": "string"
              }
            },
            "type": "object"
          },
          "cardholderName": {
            "type": "string"
          },
          "email": {
            "description": "E-mail for the Stripe customer and receipts (defaults to the owner's)",
            "type": "string"
          },
          "payment_method_token": {
            "description": "Stripe PaymentMethod id (`pm_…`) from Stripe.js",
            "type": "string"
          },
          "terms_version": {
            "description": "The Terms version the owner accepted with the charge consent (stored as terms_version / terms_accepted_at)",
            "type": "string"
          }
        },
        "required": [
          "payment_method_token",
          "cardholderName"
        ],
        "type": "object"
      },
      "UpgradePlanResponse": {
        "properties": {
          "amount_charged_usd": {
            "type": "number"
          },
          "billing_profile": {
            "$ref": "#/components/schemas/BillingProfile"
          },
          "idempotent_replay": {
            "description": "Present and true when the same Idempotency-Key was replayed",
            "type": "boolean"
          },
          "invoice_id": {
            "description": "The `plan_upgrade` invoice, null when nothing was charged",
            "nullable": true,
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "organization": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Organization"
              }
            ],
            "description": "The organization after the upgrade as GET /organizations/{name} answers it (without plan_usage / limits_reached), never the stored record. A replay answers it as it stands now; null when it could not be read (the upgrade is done all the same).",
            "nullable": true
          },
          "plan": {
            "enum": [
              "paid"
            ],
            "type": "string"
          },
          "seats_converted": {
            "type": "integer"
          },
          "seats_dropped": {
            "type": "integer"
          },
          "upgraded_at": {
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "UpgradeQuoteMember": {
        "properties": {
          "amount_usd": {
            "type": "number"
          },
          "email": {
            "nullable": true,
            "type": "string"
          },
          "included": {
            "description": "true for the owner's seat (never charged)",
            "type": "boolean"
          },
          "name": {
            "nullable": true,
            "type": "string"
          },
          "reserved_by_invitation": {
            "description": "The pending invitation holding the seat",
            "nullable": true,
            "type": "string"
          },
          "seat_id": {
            "nullable": true,
            "type": "string"
          },
          "user_id": {
            "nullable": true,
            "type": "string"
          }
        },
        "type": "object"
      },
      "UpgradeQuoteResponse": {
        "properties": {
          "amount_usd": {
            "description": "seats_to_convert × unit_price_usd; 0 for a solo owner",
            "type": "number"
          },
          "currency": {
            "type": "string"
          },
          "members": {
            "description": "The owner first (included), then every seat that will be converted and charged",
            "items": {
              "$ref": "#/components/schemas/UpgradeQuoteMember"
            },
            "type": "array"
          },
          "plan": {
            "enum": [
              "free"
            ],
            "type": "string"
          },
          "seats_to_convert": {
            "description": "Complimentary member seats with a user or a pending invitation",
            "type": "integer"
          },
          "unassigned_seats_dropped": {
            "description": "Unassigned complimentary seats deleted by the upgrade",
            "type": "integer"
          },
          "unit_price_usd": {
            "type": "number"
          }
        },
        "type": "object"
      },
      "UpgradeRequest": {
        "description": "What the member ran into; every field is optional.",
        "example": {
          "limit_key": "teams",
          "message": "We need a second team for the night shift."
        },
        "properties": {
          "feature": {
            "description": "The Pro-only feature the member needs (`webhook`: escalation steps that post to Slack or a webhook).",
            "enum": [
              "seats",
              "escalation_repeat",
              "ai_insights",
              "voice",
              "sms",
              "webhook"
            ],
            "type": "string"
          },
          "limit_key": {
            "description": "The plan limit the member hit.",
            "enum": [
              "seats",
              "teams",
              "routing_rules",
              "escalation_policies",
              "escalation_steps",
              "schedules",
              "alerts_per_month",
              "invitations_per_day"
            ],
            "type": "string"
          },
          "message": {
            "description": "A note for the owner (HTML is stripped).",
            "maxLength": 280,
            "type": "string"
          }
        },
        "type": "object"
      },
      "UpgradeRequestAccepted": {
        "example": {
          "limit_key": "teams",
          "message": "The owner of acme has been asked to upgrade the organization.",
          "organization_name": "acme",
          "owner_notified": true,
          "upgrade_path": "/org/acme/settings/plan"
        },
        "properties": {
          "feature": {
            "type": "string"
          },
          "limit_key": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "organization_name": {
            "type": "string"
          },
          "owner_notified": {
            "description": "False when the owner asked themselves (nothing is sent).",
            "type": "boolean"
          },
          "upgrade_path": {
            "example": "/org/acme/settings/plan",
            "type": "string"
          }
        },
        "type": "object"
      },
      "UsageChargeDetail": {
        "description": "Per-metric charge breakdown for a billing period.\n\nTwo pricing models coexist:\n\n- **Flat-rate metrics** (`alerts_created`, `emails_sent`,\n  `push_notifications_sent`, `api_calls`): `unit_price` is a positive\n  number and `amount = count * unit_price`. The price is fixed and\n  configured server-side.\n- **Variable-priced metrics** (`voice_calls`, `sms_sent`): `unit_price`\n  is `null` because each delivery is priced individually based on its\n  destination. `amount` is the sum of per-delivery cost for the period\n  and may grow or be revised as final delivery costs are confirmed\n  (typically within minutes, occasionally up to 24h for international\n  destinations).\n",
        "properties": {
          "amount": {
            "description": "Total charge for this metric in USD.\nFor flat-rate metrics this is `count * unit_price` and grows\nlinearly with usage. For variable-priced metrics this is the sum\nof per-delivery cost and may increase or be revised within the\nperiod as final delivery costs are confirmed.\n",
            "type": "number"
          },
          "billable": {
            "description": "`max(0, used - included)` — 0 on the Free plan, where nothing is charged",
            "type": "integer"
          },
          "count": {
            "description": "Number of units consumed in the period (kept; same as `used`)",
            "type": "integer"
          },
          "included": {
            "description": "Units included at $0 this period (`null` = unlimited); voice and SMS share one pool",
            "nullable": true,
            "type": "integer"
          },
          "unit_price": {
            "description": "Price per unit in USD for flat-rate metrics (0 for e-mail, push and API calls, which are only\ncounted). `null` for variable-priced metrics (`voice_calls`, `sms_sent`) where each delivery has\nits own provider-reported cost.\n",
            "nullable": true,
            "type": "number"
          },
          "used": {
            "description": "Number of units consumed in the period",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "UsageChargesResponse": {
        "description": "Per-period usage charge summary for an organization. Returns one row\nper tracked metric. Voice and SMS rows are priced per individual\ndelivery and may update during the period as final delivery costs\nare confirmed. The other four metrics use fixed per-event pricing.\n",
        "properties": {
          "charges": {
            "description": "Per-metric charge breakdown",
            "properties": {
              "alerts_created": {
                "$ref": "#/components/schemas/UsageChargeDetail"
              },
              "api_calls": {
                "$ref": "#/components/schemas/UsageChargeDetail"
              },
              "emails_sent": {
                "$ref": "#/components/schemas/UsageChargeDetail"
              },
              "push_notifications_sent": {
                "$ref": "#/components/schemas/UsageChargeDetail"
              },
              "sms_sent": {
                "$ref": "#/components/schemas/UsageChargeDetail"
              },
              "voice_calls": {
                "$ref": "#/components/schemas/UsageChargeDetail"
              }
            },
            "type": "object"
          },
          "currency": {
            "description": "Currency code (always USD)",
            "type": "string"
          },
          "included": {
            "$ref": "#/components/schemas/IncludedAllowance"
          },
          "organization_id": {
            "type": "string"
          },
          "period": {
            "description": "Billing period in YYYY-MM format",
            "type": "string"
          },
          "plan": {
            "enum": [
              "free",
              "paid"
            ],
            "type": "string"
          },
          "pool": {
            "description": "The voice + SMS pool of the period",
            "properties": {
              "included": {
                "nullable": true,
                "type": "integer"
              },
              "priced": {
                "description": "Deliveries priced beyond it",
                "type": "integer"
              },
              "used": {
                "description": "Deliveries taken from the pool",
                "type": "integer"
              }
            },
            "type": "object"
          },
          "total_amount": {
            "description": "Total estimated charges for the period in USD (0 on the Free plan). Sum of all per-metric\n`amount` values, rounded to 2 decimals (individual `amount` values\nare rounded to 4). Do not validate the total against the sum of the\nparts client-side — small rounding differences are expected.\n",
            "type": "number"
          },
          "would_have_cost_usd": {
            "description": "Free plan only — what the same usage would cost on the Pro plan beyond the allowance (the upgrade nudge)",
            "type": "number"
          }
        },
        "type": "object"
      },
      "UsageMetric": {
        "properties": {
          "count": {
            "description": "Total count for the metric in this period",
            "type": "integer"
          },
          "last_updated": {
            "description": "When this metric was last updated",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          }
        },
        "type": "object"
      },
      "UsageResponse": {
        "properties": {
          "included": {
            "$ref": "#/components/schemas/IncludedAllowance"
          },
          "organization_id": {
            "description": "Organization ID",
            "type": "string"
          },
          "period": {
            "description": "Billing period in YYYY-MM format",
            "type": "string"
          },
          "plan": {
            "enum": [
              "free",
              "paid"
            ],
            "type": "string"
          },
          "usage": {
            "description": "Usage metrics for the period",
            "properties": {
              "alerts_created": {
                "$ref": "#/components/schemas/UsageMetric"
              },
              "api_calls": {
                "$ref": "#/components/schemas/UsageMetric"
              },
              "emails_sent": {
                "$ref": "#/components/schemas/UsageMetric"
              },
              "push_notifications_sent": {
                "$ref": "#/components/schemas/UsageMetric"
              },
              "sms_sent": {
                "$ref": "#/components/schemas/UsageMetric"
              },
              "voice_calls": {
                "$ref": "#/components/schemas/UsageMetric"
              }
            },
            "type": "object"
          }
        },
        "type": "object"
      },
      "UserMe": {
        "description": "The signed-in user's account, as `GET /v1/users/me` answers it.",
        "properties": {
          "account": {
            "$ref": "#/components/schemas/AccountState"
          },
          "created_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "email": {
            "format": "email",
            "type": "string"
          },
          "memberships": {
            "items": {
              "$ref": "#/components/schemas/MembershipSummary"
            },
            "type": "array"
          },
          "name": {
            "nullable": true,
            "type": "string"
          },
          "owned_organizations": {
            "items": {
              "$ref": "#/components/schemas/OwnedOrganizationSummary"
            },
            "type": "array"
          },
          "user_id": {
            "description": "The stable user id (`usr_…`).",
            "example": "usr_01HZX7K2M9Q4R8T6V3W5Y7A9B1",
            "type": "string"
          }
        },
        "required": [
          "user_id",
          "email",
          "account",
          "owned_organizations",
          "memberships"
        ],
        "type": "object"
      },
      "UserNotificationPreferences": {
        "description": "The user's notification methods as one ordered list (e-mail first and always present, then registered push devices, SMS and voice when added).",
        "example": {
          "methods": [
            {
              "contact_info": "user@example.com",
              "delay_seconds": 0,
              "enabled": true,
              "order": 1,
              "required": true,
              "type": "email"
            },
            {
              "contact_info": "+1234567890",
              "delay_seconds": 300,
              "enabled": true,
              "order": 2,
              "type": "sms",
              "verified": true
            }
          ],
          "user_id": "usr_01HXYZ0000000000000000000"
        },
        "properties": {
          "methods": {
            "items": {
              "$ref": "#/components/schemas/NotificationMethodItem"
            },
            "type": "array"
          },
          "user_id": {
            "description": "User ID",
            "example": "usr_01HXYZ0000000000000000000",
            "type": "string"
          }
        },
        "required": [
          "user_id",
          "methods"
        ],
        "type": "object"
      },
      "UserPreferences": {
        "properties": {
          "tour": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TourPreference"
              }
            ],
            "nullable": true
          },
          "updated_at": {
            "description": "Server-side timestamp of the last update",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          }
        },
        "type": "object"
      },
      "UserSearchHit": {
        "description": "A person who shares an organization with the caller (GET /v1/users).",
        "properties": {
          "email": {
            "example": "ana@example.com",
            "format": "email",
            "type": "string"
          },
          "name": {
            "description": "Full name (empty when the account has none)",
            "example": "Ana Torres",
            "type": "string"
          },
          "user_id": {
            "description": "Stable user id, as POST /v1/organizations/{organizationName}/members and the schedule overrides take it",
            "example": "usr_2kq8Xb1mT0aRZ9yP4cVnLw",
            "type": "string"
          }
        },
        "required": [
          "user_id",
          "name",
          "email"
        ],
        "type": "object"
      },
      "UserSearchResponse": {
        "properties": {
          "pagination": {
            "properties": {
              "has_more": {
                "type": "boolean"
              },
              "limit": {
                "description": "Page size",
                "type": "integer"
              },
              "next_token": {
                "description": "Pass as `next_token` to read the next page; null on the last one",
                "nullable": true,
                "type": "string"
              },
              "total": {
                "description": "Number of people matching, over every page",
                "type": "integer"
              }
            },
            "required": [
              "total",
              "limit",
              "has_more",
              "next_token"
            ],
            "type": "object"
          },
          "users": {
            "items": {
              "$ref": "#/components/schemas/UserSearchHit"
            },
            "type": "array"
          }
        },
        "required": [
          "users",
          "pagination"
        ],
        "type": "object"
      },
      "VerifyMethodRequest": {
        "properties": {
          "verification_code": {
            "description": "6-digit verification code",
            "example": "123456",
            "pattern": "^\\d{6}$",
            "type": "string"
          }
        },
        "required": [
          "verification_code"
        ],
        "type": "object"
      },
      "VerifyMethodResponse": {
        "properties": {
          "message": {
            "description": "Success message",
            "example": "Phone number verified successfully",
            "type": "string"
          },
          "phone": {
            "properties": {
              "enabled": {
                "description": "Whether notifications are enabled",
                "example": true,
                "type": "boolean"
              },
              "number": {
                "description": "Verified phone number",
                "example": "+1234567890",
                "type": "string"
              },
              "verified": {
                "description": "Verification status",
                "example": true,
                "type": "boolean"
              }
            },
            "type": "object"
          },
          "sms": {
            "properties": {
              "enabled": {
                "description": "Whether notifications are enabled",
                "example": true,
                "type": "boolean"
              },
              "number": {
                "description": "Verified SMS number",
                "example": "+1234567890",
                "type": "string"
              },
              "verified": {
                "description": "Verification status",
                "example": true,
                "type": "boolean"
              }
            },
            "type": "object"
          }
        },
        "required": [
          "message"
        ],
        "type": "object"
      },
      "WebhookAlertCreateResponse": {
        "properties": {
          "alert_id": {
            "description": "Unique identifier for the created alert",
            "format": "uuid",
            "type": "string"
          },
          "alias": {
            "description": "The alias the alert is deduplicated by.",
            "nullable": true,
            "type": "string"
          },
          "assigned_to": {
            "description": "Stable user id the alert is assigned to (the creator by default when a signed-in user created it)",
            "nullable": true,
            "type": "string"
          },
          "count": {
            "description": "Occurrences absorbed by this alert (1 for a new alert).",
            "type": "integer"
          },
          "created_at": {
            "description": "When the alert was created",
            "format": "date-time",
            "type": "string"
          },
          "created_by": {
            "description": "The creator's stable user id, or `webhook` for an integration key",
            "type": "string"
          },
          "deduplicated": {
            "description": "`true` when the request matched an existing alert by `alias` (200,\nnot 201): the alert's `count` was incremented and, when it is open\nwith no escalation running, it was routed again.\n",
            "type": "boolean"
          },
          "degraded": {
            "description": "`true` when the alert was accepted during a service disruption, on a\n201 and on a deduplicated occurrence's 200. Absent on every other\nanswer.\n\nOn a 201 (a new alert): it was paged at once — its escalation's first\nstep that reaches someone, whatever that step's delay — unless\n`routing` says `suppressed`. It is listed once the service recovers:\nuntil then reading it answers 404. Its first step is then paged\nagain and the later steps follow as usual, unless its routing is\nsuppressed by then (its team disabled and not enabled again within\nminutes, the plan's monthly quota used up, or an integration key\nthat was no longer valid when the alert was received, unless a\nlater request with the same `alias` and a valid key came for it). If an open alert with the same `alias` exists by\nthen, the new alert is counted on that one instead and is listed\nunder that one's id.\n\nOn a 200 (an occurrence): `alert_id` names the alert it was counted\non, and `count` is the occurrences received during the disruption.\n\nWhen that alert existed before the disruption, it is readable as\nusual, and the occurrence was paged only when that alert was open.\nIts count is updated once the service recovers, and it is routed\nagain then only if it is still open and its escalation has ended. If\nit has been closed by then, the occurrence is not counted when it was\nreceived before the close, and becomes a new alert of its own, under\na new id, when it was received after it.\n\nWhen that alert was itself accepted during the disruption (an earlier\nrequest with the same `alias`, answered with a 201), reading it\nanswers 404 until the service recovers, as after that 201, and it is\nlisted then as that 201 describes, with this occurrence counted. The\noccurrence pages nobody, unless that earlier request's routing was\nsuppressed and this one's is not (the team enabled again in between,\nfor instance): it is then paged at once, and the alert is routed\nonce the service recovers, unless its routing is suppressed by\nthen.\n",
            "type": "boolean"
          },
          "href": {
            "description": "Canonical URL for the alert resource",
            "format": "uri",
            "type": "string"
          },
          "integration_name": {
            "description": "Name of the integration that created the alert (absent when a signed-in user created it)",
            "type": "string"
          },
          "message": {
            "description": "Success message",
            "type": "string"
          },
          "organization_name": {
            "description": "Organization name",
            "type": "string"
          },
          "quota": {
            "description": "Free plan only: the month's alert quota after this alert. `used` may\nexceed `limit` (the suppressed alerts are counted); `resets_at` is the\nfirst instant of the next UTC month. The same numbers travel in the\n`X-IgniteOps-Quota` header.\n",
            "example": {
              "limit": 100,
              "plan": "free",
              "resets_at": "2026-10-01T00:00:00.000Z",
              "used": 101
            },
            "properties": {
              "limit": {
                "description": "The month's allowance (null when unlimited)",
                "nullable": true,
                "type": "integer"
              },
              "plan": {
                "enum": [
                  "free",
                  "paid"
                ],
                "type": "string"
              },
              "resets_at": {
                "format": "date-time",
                "type": "string"
              },
              "used": {
                "description": "Alerts created this month, suppressed ones included. Absent (and the header not sent) when the quota could not be determined.",
                "type": "integer"
              }
            },
            "type": "object"
          },
          "routing": {
            "description": "Whether the alert's processing (routing and paging) was started.\n`suppressed`: the alert is stored but nobody is paged — the team is\ndisabled, a Free plan organization is over its monthly quota, or the\nfree tier's routing is paused (see `routing_reason`). On a\ndeduplicated occurrence (200) the field is present only when a\nre-route was suppressed.\n",
            "enum": [
              "started",
              "suppressed"
            ],
            "type": "string"
          },
          "routing_reason": {
            "description": "Why the routing was suppressed (only with `routing: suppressed`): `team_disabled` (a disabled team pages nobody), `quota_exceeded`, `free_tier_paused`.",
            "enum": [
              "team_disabled",
              "quota_exceeded",
              "free_tier_paused"
            ],
            "type": "string"
          },
          "severity": {
            "description": "Alert severity level",
            "enum": [
              "P1",
              "P2",
              "P3",
              "P4",
              "P5"
            ],
            "type": "string"
          },
          "source": {
            "description": "Source system that created the alert",
            "type": "string"
          },
          "status": {
            "description": "Current alert status (same values as TeamAlert.status)",
            "enum": [
              "open",
              "acknowledged",
              "closed"
            ],
            "type": "string"
          },
          "team_id": {
            "description": "Team that received the alert",
            "format": "uuid",
            "type": "string"
          },
          "team_name": {
            "description": "Team name",
            "type": "string"
          },
          "title": {
            "description": "Alert title",
            "type": "string"
          },
          "updated_at": {
            "description": "Last change (a repeated occurrence updates it).",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "WebhookCreateAlertRequest": {
        "description": "Text longer than a field's `maxLength` is clipped to it, not refused: an\nalert from a monitoring tool must still page someone. `alias`,\n`assignee`, `source` and `entity` are bounded at 256 characters. The\nconsole's Create alert form carries the same caps.\n",
        "properties": {
          "alert_url": {
            "description": "URL link for more details about the alert (http or https; anything else is dropped)",
            "format": "uri",
            "maxLength": 2048,
            "type": "string"
          },
          "alias": {
            "description": "Unique alias for deduplication (optional)",
            "maxLength": 256,
            "type": "string"
          },
          "assignee": {
            "description": "Stable user id (`usr_…`) to assign the alert to. For a signed-in user\nthe default is the creator; send `null` to leave the alert unassigned.\nA signed-in user may assign a member of the organization only: anyone\nelse is a 400 `validation_error`, the same answer for an id that is no\naccount. An integration key assigns nobody unless it names one, and\nits value is kept as sent, shown by name only when it is a member of\nthe organization.\n",
            "maxLength": 256,
            "nullable": true,
            "type": "string"
          },
          "custom_fields": {
            "additionalProperties": true,
            "description": "Additional custom data fields (keys up to 100 characters; beyond 20000\ncharacters as JSON the map is replaced by `{ \"_truncated\": true }`)\n",
            "maxProperties": 50,
            "type": "object"
          },
          "entity": {
            "description": "Entity or resource affected (optional)",
            "maxLength": 256,
            "type": "string"
          },
          "message": {
            "description": "Detailed description of the alert",
            "maxLength": 10000,
            "type": "string"
          },
          "severity": {
            "default": "P3",
            "description": "Priority level of the alert",
            "enum": [
              "P1",
              "P2",
              "P3",
              "P4",
              "P5"
            ],
            "type": "string"
          },
          "source": {
            "description": "Source system or service generating the alert. Optional: left out (or\nblank), it is the integration's name for an alert sent with an\nintegration key (the key's `integration_name`) and `manual` for one a\nsigned-in user creates. Routing rules can match on it.\n",
            "maxLength": 256,
            "type": "string"
          },
          "tags": {
            "additionalProperties": {
              "maxLength": 500,
              "type": "string"
            },
            "description": "Key-value pairs for categorizing alerts (keys up to 100 characters; a longer key is dropped, values are clipped to 500). Never refused for their total size, and every tag is found by the `tags` filter of the alerts list. A value sent as a number or a boolean is stored as its text (`5` as `\"5\"`, `true` as `\"true\"`), which is what routing rules compare.",
            "maxProperties": 50,
            "type": "object"
          },
          "title": {
            "description": "Brief title of the alert",
            "maxLength": 500,
            "type": "string"
          }
        },
        "required": [
          "title",
          "message"
        ],
        "type": "object"
      },
      "WebhookEndpoint": {
        "properties": {
          "signing": {
            "$ref": "#/components/schemas/WebhookSigningState"
          },
          "url": {
            "description": "The endpoint's URL as the policy holds it; masked (`https://host/…abcd`, with `url_masked: true`) for a caller who may not edit the team's policies.",
            "type": "string"
          },
          "url_masked": {
            "description": "Present and true when `url` is masked.",
            "type": "boolean"
          },
          "used_by": {
            "description": "The policy steps that post to this URL; match an entry to a step by `rule_id`.",
            "items": {
              "properties": {
                "policy_id": {
                  "type": "string"
                },
                "policy_name": {
                  "nullable": true,
                  "type": "string"
                },
                "rule_id": {
                  "nullable": true,
                  "type": "string"
                }
              },
              "type": "object"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "WebhookEndpointList": {
        "description": "The team's webhook endpoints: every distinct `webhook` URL of its escalation policies (Slack URLs left out), with the steps that use it and whether its deliveries are signed.",
        "properties": {
          "webhook_endpoints": {
            "items": {
              "$ref": "#/components/schemas/WebhookEndpoint"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "WebhookSigningSecret": {
        "description": "The endpoint's new signing secret. It is shown only in this answer, and in the answer to a retry with the same `Idempotency-Key` within 24 hours: add it to the receiver now. Deliveries start carrying its signature within about a minute (exceptionally later), so keep accepting the previous secret until `previous_expires_at`: during a rotation every delivery is signed with each valid secret. On an endpoint that had no secret, enforce signatures on the receiver only once a test request answers `signed: true`.",
        "properties": {
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "generation": {
            "type": "integer"
          },
          "previous_expires_at": {
            "description": "When the secret this one replaced stops signing; null when none still signs.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "secret": {
            "description": "`whsec_` followed by the base64 of 32 random bytes.",
            "type": "string"
          },
          "url_masked": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "WebhookSigningState": {
        "description": "Whether the endpoint's deliveries are signed. Never the secret.",
        "properties": {
          "created_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "enabled": {
            "description": "True once a signing secret exists for the endpoint.",
            "type": "boolean"
          },
          "generation": {
            "description": "The current secret's number, 1 for the first, one more on each rotation.",
            "nullable": true,
            "type": "integer"
          },
          "previous_expires_at": {
            "description": "While a rotation keeps the previous secret valid, when it stops signing.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "rotated_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          }
        },
        "type": "object"
      }
    },
    "securitySchemes": {
      "IgniteOpsAuth": {
        "description": "One `Authorization` header, three kinds of credential, told apart by their prefix:\n\n- `Authorization: Bearer <Cognito ID token>`: a signed-in user (the console and the apps). Only the ID token: a Cognito access token is a 403 `invalid_token`, and so is an ID token whose e-mail is not verified or belongs to no account.\n- `Authorization: PAT <personal API key>`: a personal API key from Account → API keys. It acts as its owner, except on the account's own settings, notification methods, devices and credentials (`api_key_not_allowed`).\n- `Authorization: ign_…`: a team's integration key, sent as is, with no prefix. It is the credential of the monitoring tool that creates alerts for that team and of the pipeline that records its deployments, and it can do nothing else: create a key per tool or pipeline and keep it as a secret there, so each can be revoked alone.\n\nThere is no `X-Api-Key` or `X-Integration-Key` header (a request without `Authorization` is a 401), and a key behind the wrong prefix (`Bearer ign_…`) is a 403 `invalid_token`.\n",
        "in": "header",
        "name": "Authorization",
        "type": "apiKey"
      }
    }
  },
  "info": {
    "contact": {
      "email": "support@igniteops.io",
      "name": "IgniteOps Support",
      "url": "https://igniteops.io/support"
    },
    "description": "The IgniteOps API: alerts in from any tool that can post a JSON webhook, routed to whoever is on call and paged by push, e-mail, SMS and phone call until someone acknowledges. Everything the console does is here too: organizations, teams and members, routing rules, escalation policies, on-call schedules, integration keys, notification methods, support and billing.\n\n## Authentication\n\nEvery request carries one `Authorization` header. Its prefix says which credential it is:\n\n| Credential | Header | Acts as |\n|---|---|---|\n| Cognito ID token (the console and the apps) | `Authorization: Bearer <id token>` | The signed-in user |\n| Personal API key (Account → API keys) | `Authorization: PAT <key>` | Its owner, except on the account's own settings, notification methods, devices and credentials |\n| Integration key (`ign_…`, created per team under the team's Integration keys tab) | `Authorization: ign_…`, the key as is, no prefix | The team's monitoring tool and pipeline, on `POST …/{teamName}/alerts` and `POST …/{teamName}/deployments` |\n\nA key behind the wrong prefix (`Bearer ign_…`) is a 403 `invalid_token`, and a request without an `Authorization` header (a key sent in any other header) a 401. A Cognito access token is not a credential either (403 `invalid_token`): send the ID token.\n\nA request that could not be authorized at that moment is a 503 `service_unavailable` with a `Retry-After` header, on any authenticated operation: nothing was done, send it again after that many seconds.\n\nDuring a service disruption, alerts sent with an integration key or from the console are still accepted and paged (the answer carries `degraded: true`); a key revoked shortly before or during the disruption may be accepted for that time (see the integration key's revoke operation), and so may a member removed, or a seat suspended, shortly before or during it, whose alerts from the console are then routed as usual once the service recovers. A request with a personal API key is answered 503 `service_unavailable` until the service recovers (`Retry-After: 5`), and so is a change to an existing alert (an acknowledgement, an update, a delete; `Retry-After: 30`).\n\n## Your first alert\n\n```\ncurl -X POST https://api.igniteops.io/v1/organizations/{organizationName}/incident-management/{teamName}/alerts \\\n  -H \"Authorization: ign_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{ \"title\": \"Database connection pool exhausted\", \"message\": \"PgBouncer reports 100% pool utilisation.\", \"source\": \"datadog\", \"severity\": \"P1\", \"alias\": \"db-pool-exhausted\" }'\n```\n\n`title` and `message` are required, `source` defaults to the integration key's name, `severity` is `P1` to `P5`, and repeats with the same `alias` collapse into one alert. The `201` body says whether routing `started` or was `suppressed` (by the plan's quota, or because the team is disabled: a disabled team pages nobody).\n\n## Recording deployments from your pipeline\n\nRecord each deployment with the same integration key, so the AI insights can tell what changed before an alert burst. One request when the deploy starts and one when it ends, with the same `external_id`: the second updates the first. Use one `external_id` per attempt and per service: a re-run of a job is a new attempt, and a job that deploys several services records each one under its own id. A repeat of an `external_id` with another `service` or `environment` is refused with 409 `external_id_conflict`.\n\n```\nprintf 'Authorization: %s\\n' \"$IGNITEOPS_KEY\" | curl --fail-with-body --retry 3 --retry-delay 5 -X POST https://api.igniteops.io/v1/organizations/{organizationName}/incident-management/{teamName}/deployments \\\n  -H @- \\\n  -H \"Content-Type: application/json\" \\\n  -d '{ \"service\": \"checkout-api\", \"version\": \"2.14.0\", \"status\": \"succeeded\", \"external_id\": \"9123456789-1-checkout-api\" }'\n```\n\n(curl reads the line `Authorization: ign_…` from its standard input, so the key never appears on a command line nor in a file.)\n\nIn GitHub Actions, around the step that deploys: the start before it, the end only when it ran, with its own outcome (a run cancelled or failed before the deploy records nothing, and a run cancelled during it stays `started`). Give each pipeline a key of its own, created for this team and kept as a repository or environment secret, so one can be revoked without stopping the others. The key and every value reach the script through `env:`, the key goes to curl on its standard input and the body is built by `jq`, so neither the secret nor a value is pasted into a command line or the JSON. A refusal (a plan limit, a renamed team, a field the API refuses) never fails the deploy: the steps continue on error and leave a warning, and only a transient failure (a timeout, 408, 429, 5xx) is retried, which the `external_id` makes safe:\n\n```\n- name: Record the deployment start\n  continue-on-error: true\n  env:\n    IGNITEOPS_KEY: ${{ secrets.IGNITEOPS_INTEGRATION_KEY }}\n    VERSION: ${{ github.sha }}\n    EXTERNAL_ID: ${{ github.run_id }}-${{ github.run_attempt }}-checkout-api\n    RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\n  run: |\n    jq -n --arg version \"$VERSION\" --arg external_id \"$EXTERNAL_ID\" --arg url \"$RUN_URL\" \\\n      '{service: \"checkout-api\", version: $version, status: \"started\", external_id: $external_id, url: $url}' > \"$RUNNER_TEMP/deployment-body\"\n    printf 'Authorization: %s\\n' \"$IGNITEOPS_KEY\" | curl -sS --fail-with-body --retry 3 --retry-delay 5 -X POST \"https://api.igniteops.io/v1/organizations/acme/incident-management/platform/deployments\" \\\n      -H @- -H \"Content-Type: application/json\" --data @\"$RUNNER_TEMP/deployment-body\" \\\n      || { echo; echo \"::warning::The deployment start was not recorded in IgniteOps\"; }\n\n- name: Deploy\n  id: deploy\n  run: make deploy\n\n- name: Record the deployment end\n  if: always() && (steps.deploy.outcome == 'success' || steps.deploy.outcome == 'failure')\n  continue-on-error: true\n  env:\n    IGNITEOPS_KEY: ${{ secrets.IGNITEOPS_INTEGRATION_KEY }}\n    VERSION: ${{ github.sha }}\n    STATUS: ${{ steps.deploy.outcome == 'success' && 'succeeded' || 'failed' }}\n    EXTERNAL_ID: ${{ github.run_id }}-${{ github.run_attempt }}-checkout-api\n  run: |\n    jq -n --arg version \"$VERSION\" --arg status \"$STATUS\" --arg external_id \"$EXTERNAL_ID\" \\\n      '{service: \"checkout-api\", version: $version, status: $status, external_id: $external_id}' > \"$RUNNER_TEMP/deployment-body\"\n    printf 'Authorization: %s\\n' \"$IGNITEOPS_KEY\" | curl -sS --fail-with-body --retry 3 --retry-delay 5 -X POST \"https://api.igniteops.io/v1/organizations/acme/incident-management/platform/deployments\" \\\n      -H @- -H \"Content-Type: application/json\" --data @\"$RUNNER_TEMP/deployment-body\" \\\n      || { echo; echo \"::warning::The deployment end was not recorded in IgniteOps\"; }\n```\n\n`service` and `version` are required; `status` is `started`, `succeeded` (the default), `failed` or `rolled_back`, and only moves forward. A later request for the same deployment keeps its `environment` and `kind` unless it sends them.\n",
    "license": {
      "name": "IgniteOps Commercial License 1.0",
      "url": "https://igniteops.io/license"
    },
    "title": "IgniteOps API",
    "version": "1.0"
  },
  "openapi": "3.0.1",
  "paths": {
    "/status": {
      "get": {
        "description": "Retrieves the status of services.",
        "operationId": "getStatus",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Status"
                }
              }
            },
            "description": "Success"
          },
          "503": {
            "content": {
              "application/json": {
                "example": {
                  "message": "unavailable"
                },
                "schema": {
                  "$ref": "#/components/schemas/Status"
                }
              }
            },
            "description": "The service is unavailable"
          }
        },
        "security": [],
        "summary": "Gets status of services",
        "tags": [
          "Status"
        ]
      }
    },
    "/v1/auth/config": {
      "get": {
        "description": "Returns the Cognito pool and client ids and the Region to authenticate against (`region`; `primaryRegion` is the one that takes writes). The two Regions can change: re-read this endpoint at the start of every sign-in and after an authentication failure that is not a wrong code. Public endpoint, no auth required; cacheable for 60 seconds.",
        "operationId": "getAuthConfig",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthConfig"
                }
              }
            },
            "description": "Success"
          }
        },
        "security": [],
        "summary": "Get auth configuration and the Region to use",
        "tags": [
          "Auth"
        ]
      }
    },
    "/v1/auth/qa": {
      "get": {
        "description": "Returns the list of validation (app store review) accounts that sign in with a password instead of an emailed OTP, and whether that flow is currently enabled. Public endpoint, no auth required. Contains no credentials: the password is handed to the reviewer out of band.",
        "operationId": "getQaLoginConfig",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QaLoginConfig"
                }
              }
            },
            "description": "Success"
          }
        },
        "security": [],
        "summary": "Get validation-account login policy",
        "tags": [
          "Auth"
        ]
      }
    },
    "/v1/billing/plans": {
      "get": {
        "description": "Public and cacheable (`Cache-Control: public, max-age=300`): the seat price, the usage rate card,\nthe allowance every organization gets each month, the limits of the Free and Pro plans\n(`null` = unlimited) and whether free organizations can be created right now.\n",
        "operationId": "getPlans",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlansResponse"
                }
              }
            },
            "description": "The plans",
            "headers": {
              "Cache-Control": {
                "description": "public, max-age=300",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "500": {
            "description": "Server error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not answer this request right now. Retry after `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [],
        "summary": "The plans, their limits, the included allowance and the price list (public)",
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/contact": {
      "post": {
        "description": "Public endpoint for the marketing-site contact (lead) form. Stores the lead, notifies the team, and sends the prospect an auto-reply.",
        "operationId": "submitContact",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactResponse"
                }
              }
            },
            "description": "Accepted"
          },
          "400": {
            "description": "Invalid request"
          },
          "403": {
            "description": "Bot challenge failed"
          },
          "429": {
            "description": "Too many requests"
          }
        },
        "security": [],
        "summary": "Submit a contact-form message",
        "tags": [
          "Contact"
        ]
      }
    },
    "/v1/invitations": {
      "get": {
        "description": "The invitations addressed to the authenticated user's e-mail address that are still waiting for them, newest first (at most 50): each as GET /v1/invitations/{invitationId} answers it, with `status: pending`. An expired invitation, and one whose organization was deleted (it reads as `revoked` there), is left out; an organization renamed since the invitation was sent is named as it is now. Only invitations sent to the caller's own address are read; accept one with POST /v1/invitations/{invitationId}/accept. An empty list when there is none.\n",
        "operationId": "listMyInvitations",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "invitations": {
                      "items": {
                        "$ref": "#/components/schemas/InvitationDetail"
                      },
                      "maxItems": 50,
                      "type": "array"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The caller's pending invitations"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "An integration key, or an account pending deletion (`account_pending_deletion`)"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "List the caller's pending invitations (invitee)",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/v1/invitations/{invitationId}": {
      "get": {
        "description": "Fetch an invitation addressed to the authenticated user, for the acceptance page. Returns 404 unless the caller is signed in with the invited email address. An invitation whose organization was deleted reads as `revoked`.\n",
        "operationId": "getInvitation",
        "parameters": [
          {
            "description": "The invitation id (`inv-<uuid>`). A longer one names no invitation (404 `invitation_not_found`).",
            "in": "path",
            "name": "invitationId",
            "required": true,
            "schema": {
              "maxLength": 200,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvitationDetail"
                }
              }
            },
            "description": "Invitation details"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Invitation not found"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get invitation details (invitee)",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/v1/invitations/{invitationId}/accept": {
      "post": {
        "description": "Accept an invitation addressed to the authenticated user. Assigns the reserved seat and creates the organization membership. The caller must be signed in with the invited email address.\n",
        "operationId": "acceptInvitation",
        "parameters": [
          {
            "description": "The invitation id (`inv-<uuid>`). A longer one names no invitation (404 `invitation_not_found`).",
            "in": "path",
            "name": "invitationId",
            "required": true,
            "schema": {
              "maxLength": 200,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "description": "`Invitation accepted`, or `You are already a member of this organization` for a member added directly meanwhile (no role then)",
                      "type": "string"
                    },
                    "organization_name": {
                      "description": "The organization's name as it is now, also when it was renamed after the invitation was sent: the name to open it by",
                      "type": "string"
                    },
                    "role": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Invitation accepted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Invitation not found"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`invitation_not_pending` - the invitation is already accepted or revoked, or its organization was deleted (the invitation is revoked then), or it was revoked or expired while the acceptance was written (the membership is taken back)"
          },
          "410": {
            "description": "Invitation has expired"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NoSeatsAvailableError"
                }
              }
            },
            "description": "No available seats in the organization (`no_seats_available` with the plan's seat limit; `upgrade_path` names the organization as it is now)"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Accept an invitation",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/v1/notifications/devices": {
      "get": {
        "description": "Get all registered devices with their notification settings for the authenticated user",
        "operationId": "listNotificationDevices",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceListResponse"
                }
              }
            },
            "description": "List of registered devices"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "List registered devices",
        "tags": [
          "Push Notifications"
        ]
      }
    },
    "/v1/notifications/devices/{deviceId}": {
      "delete": {
        "description": "Remove a device registration to stop receiving push notifications",
        "operationId": "unregisterNotificationDevice",
        "parameters": [
          {
            "description": "Device identifier to unregister",
            "in": "path",
            "name": "deviceId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            },
            "description": "Device unregistered successfully"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Invalid request"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Device not found"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Unregister device from push notifications",
        "tags": [
          "Push Notifications"
        ]
      },
      "put": {
        "description": "Update the order, delay, name, and enabled status for a device",
        "operationId": "updateNotificationDevice",
        "parameters": [
          {
            "description": "Device identifier to update",
            "in": "path",
            "name": "deviceId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateDeviceSettingsRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateDeviceSettingsResponse"
                }
              }
            },
            "description": "Device settings updated successfully"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Invalid request"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Device not found"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Update device notification settings",
        "tags": [
          "Push Notifications"
        ]
      }
    },
    "/v1/notifications/register-device": {
      "post": {
        "description": "Register a mobile device with FCM/APNs token for receiving push notifications",
        "operationId": "registerNotificationDevice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterDeviceRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RegisterDeviceResponse"
                }
              }
            },
            "description": "Device registered successfully"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`INVALID_JSON`: the body does not parse or is no JSON object. `INVALID_REQUEST`: a required field is missing or malformed (a `device_id` that is no string of at most 256 characters, a `platform` other than ios or android, a `device_token` that is no token or longer than 1024 bytes, a `device_name` that is no non-empty string of at most 100 characters, a `delay_seconds` that is no integer from 0 to 900); `message` says which."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Register device for push notifications",
        "tags": [
          "Push Notifications"
        ]
      }
    },
    "/v1/notifications/test": {
      "post": {
        "description": "Send a test push notification to all registered devices for the authenticated user",
        "operationId": "sendTestNotification",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TestNotificationRequest"
              }
            }
          },
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendNotificationResponse"
                }
              }
            },
            "description": "Test notification sent successfully"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Invalid request"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Send test push notification",
        "tags": [
          "Push Notifications"
        ]
      }
    },
    "/v1/organizations": {
      "get": {
        "description": "Retrieve all organizations the user is a member of, each with the caller's\n`my_role`, `access_state` (active | frozen | suspended) and `seat_status`.\n",
        "operationId": "listOrganizations",
        "parameters": [
          {
            "description": "Filter organizations by name",
            "in": "query",
            "name": "name",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Maximum number of organizations to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "The `pagination.next_token` this listing returned to the caller. Any other value, another user's included, is a 400 `invalid_request`.",
            "in": "query",
            "name": "next_token",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationList"
                }
              }
            },
            "description": "List of organizations"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`invalid_request`: a `next_token` this listing did not return to the caller"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get all organizations",
        "tags": [
          "Organizations"
        ]
      },
      "post": {
        "description": "Create a new organization with the user as owner",
        "operationId": "createOrganization",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateOrganizationRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Organization"
                }
              }
            },
            "description": "Organization created successfully"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Bad Request - `invalid_request` (among others a `description` that is no string), `reserved_name`, `invalid_plan` (plan is not free or paid) or `payment_method_required` (the Pro plan without a token, or the Free plan while the free tier is closed)"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "content": {
              "application/json": {
                "example": {
                  "client_secret": "seti_1AbCdEf_secret_XyZ",
                  "error": "payment_requires_action",
                  "error_description": "Your bank asks you to authenticate this card before it can be used.",
                  "intent_type": "setup_intent",
                  "status": 402
                },
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    },
                    {
                      "properties": {
                        "client_secret": {
                          "description": "Only with `payment_requires_action`.",
                          "type": "string"
                        },
                        "intent_type": {
                          "description": "Only with `payment_requires_action`; a creation always confirms a SetupIntent.",
                          "enum": [
                            "setup_intent"
                          ],
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Payment Required (Pro plan) - `payment_method_invalid` (the card could not be attached), `payment_failed` (the card could not be confirmed) or `payment_requires_action` (the bank asks for authentication: the body carries the SetupIntent `client_secret` and `intent_type: setup_intent`; nothing was written; confirm it client-side with Stripe.js (`stripe.handleNextAction`) and submit the same request again with the same `payment_method_token`: the first attempt's customer and SetupIntent are reused)"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/FreeOrgLimitError"
                    }
                  ]
                }
              }
            },
            "description": "Conflict - `organization_already_exists` (an organization bears this name in any case: names are unique regardless of case, and of two simultaneous creations of one name only one succeeds), or `free_org_limit` (the owner already owns a free organization, named in `organization_name`, or created three free organizations in the last 30 days)"
          },
          "500": {
            "description": "Server Error"
          },
          "502": {
            "description": "Payment provider unavailable"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                    }
                  ]
                }
              }
            },
            "description": "Service Unavailable - `signups_paused` (free sign-ups are paused for a while; paid creation keeps working), or `primary_region_unavailable` (the service could not complete the creation right now: retry after `Retry-After` seconds; the organization may have been created, so read it before repeating the request)",
            "headers": {
              "Retry-After": {
                "description": "`primary_region_unavailable` only: seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Create a new organization",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/v1/organizations/{organizationName}": {
      "delete": {
        "description": "Delete an organization (owner only). A paid organization first settles what it owes: the failed usage invoices are retried and the usage not billed yet is charged on the default card (an amount below the Stripe minimum is written off; member seats are cancelled, never charged). When that charge fails nothing is deleted (409 `billing_unsettled`). The organization's pending invitations are then revoked, before anything is deleted: their invitees read them as `revoked` and can no longer accept them. The organization and its memberships go at once, then the seats are cancelled and the billing profile closed, and everything else under the organization is purged in the background: its teams with their alerts, schedules, routing rules, escalation policies, integration keys and members, its stored cards and Stripe customer (Stripe keeps the invoices), usage counters, insights and support tickets. Nothing can reach that data meanwhile: an integration key of a deleted organization is refused with `invalid_token`.",
        "operationId": "deleteOrganization",
        "parameters": [
          {
            "description": "The URL-compliant name of the organization",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeleteOrganizationRequest"
              }
            }
          },
          "description": "The organization's name, repeated as `confirm`. A DELETE that reaches\nthis route by accident (a client that resolved a `..` team or alert\nsegment one level up, a mistyped path) carries none and deletes\nnothing; the console's danger zone sends what the person typed.\n`reason` is the owner's optional feedback, recorded with the\ndeletion in the audit trail.\n",
          "required": true
        },
        "responses": {
          "204": {
            "description": "Organization deleted successfully"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Bad Request - `confirmation_required` (the body does not repeat the organization's name as `confirm`), `validation_error` (a `reason` that is not a string or is over 500 characters; nothing is deleted) or `invalid_json`"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Not Found"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Conflict - `billing_unsettled` (a paid organization owes what the body's `outstanding_usd` says and the charge on its card failed: nothing was deleted; update the payment method and try again) or `billing_settlement_in_progress` (another deletion is settling it)"
          },
          "500": {
            "description": "Server Error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                    }
                  ]
                }
              }
            },
            "description": "Service Unavailable - `billing_unavailable` (the settlement of a paid organization could not run; nothing was deleted), or `primary_region_unavailable` (the service could not complete the deletion right now: retry after `Retry-After` seconds; the deletion may have happened, so read the organization before repeating the request)",
            "headers": {
              "Retry-After": {
                "description": "`primary_region_unavailable` only: seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Delete an organization",
        "tags": [
          "Organizations"
        ]
      },
      "get": {
        "description": "Retrieve details of a specific organization",
        "operationId": "getOrganization",
        "parameters": [
          {
            "description": "The URL-compliant name of the organization",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Organization"
                }
              }
            },
            "description": "Organization details"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get a specific organization",
        "tags": [
          "Organizations"
        ]
      },
      "put": {
        "description": "Update organization properties (organization owner only). The 200 answers the organization as GET /v1/organizations/{organizationName} does right after it (`my_role`, `access_state`, `seat_status`, `is_owner`, the plan fields, `href`), without the single GET's `plan_usage` and `limits_reached`.",
        "operationId": "updateOrganization",
        "parameters": [
          {
            "description": "The URL-compliant name of the organization",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateOrganizationRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Organization"
                }
              }
            },
            "description": "Organization updated successfully (the Organization, without `plan_usage` and `limits_reached`)"
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Not Found - `organization_not_found`, also when the organization is deleted while the update runs, or, for a rename, renamed by another request meanwhile (nothing is written)"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Conflict - `organization_already_exists`: another organization bears the new `name` in any case (names are unique regardless of case; a rename that only changes the case of the organization's own name is accepted), or took it while this rename ran. Nothing is written."
          },
          "500": {
            "description": "Server Error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not complete this request right now. Retry after `Retry-After` seconds; the change may have been made, so read the current state before repeating it.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Update an organization",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/v1/organizations/{organizationName}/ai-insights": {
      "get": {
        "description": "Retrieve AI-generated insights for an organization. Insights include pattern detection,\nanomaly detection, performance recommendations, and predictions based on alert data analysis.\n",
        "operationId": "getAIInsights",
        "parameters": [
          {
            "description": "The URL-compliant name of the organization",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Maximum number of insights to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 10,
              "maximum": 50,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Filter insights by type",
            "in": "query",
            "name": "type",
            "required": false,
            "schema": {
              "enum": [
                "pattern",
                "anomaly",
                "recommendation",
                "prediction",
                "review"
              ],
              "type": "string"
            }
          },
          {
            "description": "Filter insights by severity",
            "in": "query",
            "name": "severity",
            "required": false,
            "schema": {
              "enum": [
                "info",
                "warning",
                "success"
              ],
              "type": "string"
            }
          },
          {
            "description": "Include insights that have been dismissed by users",
            "in": "query",
            "name": "include_dismissed",
            "required": false,
            "schema": {
              "default": false,
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AIInsightsResponse"
                }
              }
            },
            "description": "List of AI insights. For a Free plan organization the list is empty\nand the response carries `locked: true`, `reason: plan_feature_required`\nand `upgrade_path` (AI insights are a Pro plan feature).\n"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unauthorized - Authentication required"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Forbidden - User does not have access to this organization"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Organization not found"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Internal server error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "AI insights service temporarily unavailable"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get AI-generated insights",
        "tags": [
          "AI Insights"
        ]
      }
    },
    "/v1/organizations/{organizationName}/ai-insights/{insightId}/dismiss": {
      "post": {
        "description": "Mark an AI insight as dismissed. Dismissed insights will not appear in the default\ninsight list unless explicitly requested via the include_dismissed parameter.\n",
        "operationId": "dismissAIInsight",
        "parameters": [
          {
            "description": "The URL-compliant name of the organization",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier of the insight to dismiss",
            "in": "path",
            "name": "insightId",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Insight dismissed successfully"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unauthorized - Authentication required"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Forbidden - User does not have access to this organization"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Insight or organization not found"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Internal server error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Dismiss an AI insight",
        "tags": [
          "AI Insights"
        ]
      }
    },
    "/v1/organizations/{organizationName}/billing/invoices": {
      "get": {
        "description": "List all invoices for an organization with optional filters",
        "operationId": "listInvoices",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Filter by invoice status",
            "in": "query",
            "name": "status",
            "schema": {
              "enum": [
                "paid",
                "pending",
                "failed"
              ],
              "type": "string"
            }
          },
          {
            "description": "Start of date range (ISO 8601)",
            "in": "query",
            "name": "from_date",
            "schema": {
              "format": "date-time",
              "maxLength": 64,
              "type": "string"
            }
          },
          {
            "description": "End of date range (ISO 8601)",
            "in": "query",
            "name": "to_date",
            "schema": {
              "format": "date-time",
              "maxLength": 64,
              "type": "string"
            }
          },
          {
            "description": "Maximum number of invoices to return",
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Pagination token from previous response",
            "in": "query",
            "name": "next_token",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListInvoicesResponse"
                }
              }
            },
            "description": "List of invoices"
          },
          "400": {
            "description": "`invalid_request`: `limit` is not an integer from 1 to 100, `from_date` or `to_date` is longer than 64 characters, `from_date` is later than `to_date`, or `next_token` is not a token this listing returned for these dates"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "error_description": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Server error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not answer this request right now. Retry after `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "List invoices",
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/organizations/{organizationName}/billing/payment-methods": {
      "get": {
        "description": "List all payment methods for an organization",
        "operationId": "listPaymentMethods",
        "parameters": [
          {
            "description": "Organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "paymentMethods": {
                      "items": {
                        "$ref": "#/components/schemas/PaymentMethod"
                      },
                      "type": "array"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Successful operation"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Organization not found"
          },
          "500": {
            "description": "Server error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not answer this request right now. Retry after `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "List payment methods",
        "tags": [
          "Billing"
        ]
      },
      "post": {
        "description": "Add a credit card payment method to the organization",
        "operationId": "addCreditCard",
        "parameters": [
          {
            "description": "Organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddCreditCardRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentMethod"
                }
              }
            },
            "description": "Payment method created"
          },
          "400": {
            "description": "`invalid_json`: the body is no JSON object; otherwise the token and the cardholder name are required, or the card was refused (`error`: `Invalid card information`; `error_description` says what to do)"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Error message",
                      "type": "string"
                    },
                    "message": {
                      "description": "Error details",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Server error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not complete this request right now. Retry after `Retry-After` seconds; the change may have been made, so read the current state before repeating it.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Add credit card",
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/organizations/{organizationName}/billing/payment-methods/{paymentMethodId}": {
      "delete": {
        "description": "Delete a payment method from the organization",
        "operationId": "deletePaymentMethod",
        "parameters": [
          {
            "description": "Organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Payment method ID",
            "in": "path",
            "name": "paymentMethodId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Payment method deleted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Error message",
                      "type": "string"
                    },
                    "message": {
                      "description": "Error details",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Payment method not found"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Error message",
                      "type": "string"
                    },
                    "message": {
                      "description": "Error details",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Server error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not complete this request right now. Retry after `Retry-After` seconds; the change may have been made, so read the current state before repeating it.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Delete payment method",
        "tags": [
          "Billing"
        ]
      },
      "put": {
        "description": "Set a payment method as the default for the organization",
        "operationId": "setDefaultPaymentMethod",
        "parameters": [
          {
            "description": "Organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Payment method ID",
            "in": "path",
            "name": "paymentMethodId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentMethod"
                }
              }
            },
            "description": "Default payment method updated"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Error message",
                      "type": "string"
                    },
                    "message": {
                      "description": "Error details",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Payment method not found"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Error message",
                      "type": "string"
                    },
                    "message": {
                      "description": "Error details",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Server error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not complete this request right now. Retry after `Retry-After` seconds; the change may have been made, so read the current state before repeating it.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Set default payment method",
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/organizations/{organizationName}/billing/profile": {
      "get": {
        "description": "Get the billing profile for the organization including trust tier and usage credit limit",
        "operationId": "getBillingProfile",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingProfile"
                }
              }
            },
            "description": "Billing profile"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "error_description": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Billing profile not found"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "error_description": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Server error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not answer this request right now. Retry after `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get billing profile",
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/organizations/{organizationName}/billing/retry-payment": {
      "post": {
        "description": "When the organization is frozen for non-payment (`frozen_reason: payment_failed`, or the legacy\n`owner_seat_suspended`), the owner can call this once per freeze — typically after updating the card —\nto re-run every outstanding charge: the failed usage invoices and the suspended / grace member seats.\nWhen everything is paid the organization is unfrozen, unless a payment dispute is still open. Failed charges are otherwise retried\nautomatically every day during the 7-day payment grace and, once frozen, whenever the card on file\nchanges (for 30 days).\n",
        "operationId": "retryPayment",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RetryPaymentResponse"
                }
              }
            },
            "description": "Everything outstanding was paid, or nothing was (`already_unfrozen`). `unfrozen` says whether this call lifted the freeze. When it is false, `message` says why: a payment dispute is still open (the organization stays frozen until it is closed), a charge is still outstanding (it stays frozen until that is paid), or the freeze was lifted or changed meanwhile (if the organization is still suspended, contact support)."
          },
          "207": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "failed": {
                      "items": {
                        "additionalProperties": true,
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "invoices_paid": {
                      "items": {
                        "type": "string"
                      },
                      "type": "array"
                    },
                    "message": {
                      "type": "string"
                    },
                    "successful": {
                      "description": "Seat ids renewed",
                      "items": {
                        "type": "string"
                      },
                      "type": "array"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Some charges were collected, some still fail; the organization stays frozen."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Nothing could be collected (`error: payment_failed`); the retry is spent. A seat whose charge went through but whose renewal could not be completed answers 503 instead, whatever else failed: the seat is completed later without a second charge, and the retry is normally given back (when it cannot be, a later call answers 429 and the seat is still completed)."
          },
          "403": {
            "description": "Forbidden — only the organization owner can retry."
          },
          "404": {
            "description": "Organization not found."
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Nothing to retry. `error` is `not_frozen` (the organization is not frozen) or\n`administrative_freeze`: the organization was suspended by an administrator, or because a payment\nwas disputed. A card retry cannot lift either; contact support to restore access (`error_description`\nsays which).\n"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The single manual retry of this freeze has already been used (`error: retry_already_used`)."
          },
          "500": {
            "description": "Server error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not complete this request right now. Retry after `Retry-After` seconds; the change may have been made, so read the current state before repeating it.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Manually retry every outstanding charge (owner only, once per freeze)",
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/organizations/{organizationName}/billing/retry-renewal": {
      "post": {
        "deprecated": true,
        "description": "The former name of the manual retry: same behaviour and responses as `retryPayment`. Clients should\nmove to `/billing/retry-payment`; this alias will be removed.\n",
        "operationId": "retryRenewal",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RetryPaymentResponse"
                }
              }
            },
            "description": "Everything paid; the organization is unfrozen, unless a payment dispute is still open (`unfrozen` false)."
          },
          "207": {
            "description": "Partial success."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Nothing could be collected."
          },
          "403": {
            "description": "Forbidden — only the organization owner can retry."
          },
          "404": {
            "description": "Organization not found."
          },
          "409": {
            "description": "not_frozen | administrative_freeze"
          },
          "429": {
            "description": "retry_already_used"
          },
          "500": {
            "description": "Server error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not complete this request right now. Retry after `Retry-After` seconds; the change may have been made, so read the current state before repeating it.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Deprecated alias of POST …/billing/retry-payment",
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/organizations/{organizationName}/billing/seats": {
      "get": {
        "description": "Retrieves all seats for the organization",
        "operationId": "listSeats",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "status",
            "schema": {
              "enum": [
                "active",
                "pending_cancellation",
                "grace_period",
                "cancelled",
                "suspended"
              ],
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "assigned",
            "schema": {
              "enum": [
                "true",
                "false"
              ],
              "type": "string"
            }
          },
          {
            "description": "Maximum number of seats to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "in": "query",
            "name": "next_token",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListSeatsResponse"
                }
              }
            },
            "description": "List of seats"
          },
          "400": {
            "description": "`invalid_request`: `limit` is not an integer from 1 to 100, or `next_token` is not a token this listing returned"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Error message",
                      "type": "string"
                    },
                    "message": {
                      "description": "Error details",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Not found"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not answer this request right now. Retry after `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "List organization seats",
        "tags": [
          "Billing"
        ]
      },
      "post": {
        "description": "Purchase new seats for the organization (owner only). Clients must send a unique Idempotency-Key header per logical purchase; retries with the same key are safe and will not result in duplicate charges.",
        "operationId": "purchaseSeats",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Unique idempotency key, 1 to 100 visible ASCII characters (UUID recommended). Required so a network retry of the same purchase does not produce a second charge. Reusing the same key returns the original outcome (or 409 if still in flight).",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "format": "uuid",
              "maxLength": 100,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PurchaseSeatsRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PurchaseSeatsResponse"
                }
              }
            },
            "description": "Idempotent replay — a purchase with this Idempotency-Key already completed. The\noriginal invoice's seats are returned with `idempotent_replay` set to true; nothing\nis charged or created again.\n"
          },
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PurchaseSeatsResponse"
                }
              }
            },
            "description": "Seats purchased successfully"
          },
          "400": {
            "description": "`invalid_json`: no body, or one that is no JSON object; otherwise an invalid quantity, a missing Idempotency-Key header or one that is not 1 to 100 visible ASCII characters, or no valid payment method on file. `payment_failed`: the charge did not go through, so no seats were added (`error_description` says what to do)."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanFeatureError"
                }
              }
            },
            "description": "The organization is on the Free plan (five complimentary seats, no card) — seats are bought on the Pro plan (`error: plan_feature_required`, `feature: seats`, `upgrade_path`)."
          },
          "403": {
            "description": "Forbidden"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A purchase with this Idempotency-Key is already in progress. The client should wait and retry, not generate a new key."
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not complete this request right now. Retry after `Retry-After` seconds; the change may have been made, so read the current state before repeating it.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Purchase seats (idempotent — requires Idempotency-Key header)",
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/organizations/{organizationName}/billing/seats/assign": {
      "post": {
        "description": "Assign an available seat to a member of the organization. Only a member can hold a seat: any other user id — an account that is not a member, or an id that is no account — is refused with the same 404 `member_not_found`, and nothing is assigned. A person joins through an invitation, which reserves a seat and assigns it on acceptance.",
        "operationId": "assignSeat",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AssignSeatRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "seat": {
                      "$ref": "#/components/schemas/Seat"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Seat assigned"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Error code",
                      "type": "string"
                    },
                    "error_description": {
                      "description": "What is wrong",
                      "type": "string"
                    },
                    "message": {
                      "description": "Error details (the older refusals)",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "`invalid_json`: the body is no JSON object. `invalid_request`: `user_id` is not a non-empty string of at most 128 characters. The other refusals (`message`): the member already holds an active seat (with that `seat`), or no seat is free."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`member_not_found`: the user id is not a member of the organization (whether or not it is an account)."
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not complete this request right now. Retry after `Retry-After` seconds; the change may have been made, so read the current state before repeating it.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Assign seat to user",
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/organizations/{organizationName}/billing/seats/{seatId}": {
      "delete": {
        "description": "Schedule seat for cancellation at end of billing period (owner only)",
        "operationId": "cancelSeat",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "seatId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Seat"
                }
              }
            },
            "description": "Seat cancelled"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The seat cannot be cancelled — the owner's seat, a seat already cancelled or pending cancellation, a seat reserved by a pending invitation, or a complimentary seat (`error: complimentary_seat` — the seat never renews; unassign the member instead)."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Error message",
                      "type": "string"
                    },
                    "message": {
                      "description": "Error details",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Not found"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not complete this request right now. Retry after `Retry-After` seconds; the change may have been made, so read the current state before repeating it.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Cancel seat",
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/organizations/{organizationName}/billing/seats/{seatId}/unassign": {
      "post": {
        "description": "Unassign a seat from a user (never the owner's seat). The member keeps their membership but no longer reaches the organization: every request answers 403 `access_denied` with the reason `seat_unassigned` until a seat is assigned to them again.",
        "operationId": "unassignSeat",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "seatId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Seat"
                }
              }
            },
            "description": "Seat unassigned"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Error message",
                      "type": "string"
                    },
                    "message": {
                      "description": "Error details",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Not found"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not complete this request right now. Retry after `Retry-After` seconds; the change may have been made, so read the current state before repeating it.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Unassign seat",
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/organizations/{organizationName}/billing/upgrade": {
      "post": {
        "description": "Puts the card on file (the Stripe customer is created with the owner's e-mail, name and billing\naddress), charges the complimentary member seats with a user or a pending invitation (one seat\nprice each, invoice type `plan_upgrade`) and converts them in place; a solo owner confirms the card\nwith a SetupIntent and pays nothing today. The owner's seat stays complimentary; unassigned\ncomplimentary seats are deleted. Effective on the next request.\n\nA card that needs authentication answers `402 payment_requires_action` with a `client_secret`\n(`intent_type` payment_intent | setup_intent): confirm it with Stripe.js and retry the same call\nwith the same `Idempotency-Key` (a solo owner's completed SetupIntent authentication is honoured on\nthe retry). Nothing is converted until the charge succeeds; a decline leaves the organization on the\nFree plan and the same key accepts another card. The seat charge belongs to the organization's\npending upgrade rather than to the key, so a later attempt with any `Idempotency-Key` (after a\ndecline, or after a failure past the charge) reuses the completed charge and finishes the\nconversion: the seats are never charged twice. The month's usage so far is priced once at the\nupgrade, so only usage after the upgrade is billed.\nReplaying a completed key answers the original 200.\n",
        "operationId": "upgradePlan",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "One key per upgrade attempt, 1 to 100 visible ASCII characters (keep it across the 3-D Secure retry; after a decline the same key accepts another card, and a fresh key never charges the seats a second time).",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "format": "uuid",
              "maxLength": 100,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpgradePlanRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpgradePlanResponse"
                }
              }
            },
            "description": "Upgraded (or the replay of a completed upgrade, `idempotent_replay: true`)"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Missing Idempotency-Key (or one that is not 1 to 100 visible ASCII characters), payment_method_token or cardholderName"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpgradePaymentError"
                }
              }
            },
            "description": "`payment_failed` (the card was declined or could not be saved; the organization stays free) or\n`payment_requires_action` (`client_secret`, `intent_type`; nothing converted yet).\n"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Organization not found"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`already_paid`, `organization_frozen`, or `upgrade_in_progress` (the same key is still being processed)."
          },
          "500": {
            "description": "Server error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not complete this request right now. Retry after `Retry-After` seconds; the change may have been made, so read the current state before repeating it.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Upgrade the organization to the Pro plan (owner only, requires Idempotency-Key)",
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/organizations/{organizationName}/billing/upgrade-quote": {
      "get": {
        "description": "The owner's seat is included at $0 and stays complimentary; every complimentary member seat with a\nuser or a pending invitation is charged one seat price and converted in place by the upgrade;\nunassigned complimentary seats are deleted at upgrade and are not listed. A line names its\nholder (`email`, `name`) only when they are a member of the organization.\n",
        "operationId": "getUpgradeQuote",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpgradeQuoteResponse"
                }
              }
            },
            "description": "The quote"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Organization not found"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The organization is already on the Pro plan (`error: already_paid`)."
          },
          "500": {
            "description": "Server error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not answer this request right now. Retry after `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "What the upgrade to the Pro plan converts and costs (owner only)",
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/organizations/{organizationName}/billing/usage": {
      "get": {
        "description": "Get usage metrics for the organization for a given billing period",
        "operationId": "getUsage",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Billing period in YYYY-MM format (defaults to current month)",
            "in": "query",
            "name": "period",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageResponse"
                }
              }
            },
            "description": "Usage data for the organization"
          },
          "400": {
            "description": "`invalid_request`: `period` is not a month as YYYY-MM"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "error_description": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Server error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not answer this request right now. Retry after `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get usage data",
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/organizations/{organizationName}/billing/usage-charges": {
      "get": {
        "description": "Returns the per-metric usage charge breakdown for the organization\nin the given billing period (defaults to the current month).\n\nVoice (`voice_calls`) and SMS (`sms_sent`) are priced per individual\ndelivery based on its destination. For these two metrics `unit_price`\nis `null` and `amount` is the sum of per-delivery cost for the\nperiod. Values may update or grow during the period as final\ndelivery costs are confirmed — typically within minutes,\noccasionally up to 24h for international destinations.\n\nThe other four metrics (`alerts_created`, `emails_sent`,\n`push_notifications_sent`, `api_calls`) use flat per-event pricing;\ntheir `unit_price` is always a number and their `amount` grows\nlinearly with `count`.\n\nSee `UsageChargesResponse` and `UsageChargeDetail` for the response\nshape and field-level semantics.\n",
        "operationId": "calculateUsageCharges",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Billing period in YYYY-MM format (defaults to current month)",
            "in": "query",
            "name": "period",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageChargesResponse"
                }
              }
            },
            "description": "Calculated usage charges"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "error_description": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Server error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not answer this request right now. Retry after `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Calculate usage charges",
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/organizations/{organizationName}/deployments": {
      "get": {
        "description": "Every team's deployments, newest first (the organization's change timeline). Every member of the organization may read them.",
        "operationId": "listOrganizationDeployments",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only this service's deployments.",
            "in": "query",
            "name": "service",
            "required": false,
            "schema": {
              "maxLength": 100,
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "environment",
            "required": false,
            "schema": {
              "maxLength": 50,
              "type": "string"
            }
          },
          {
            "description": "The status as answered (`unknown` is a `started` deployment never closed after 24 hours).",
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "enum": [
                "started",
                "succeeded",
                "failed",
                "rolled_back",
                "unknown"
              ],
              "type": "string"
            }
          },
          {
            "description": "Deployments that started at or after this time (RFC 3339).",
            "in": "query",
            "name": "from",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Deployments that started at or before this time (RFC 3339).",
            "in": "query",
            "name": "to",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "The `next_token` of the previous page of this list; any other value is a 400.",
            "in": "query",
            "name": "next_token",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeploymentList"
                }
              }
            },
            "description": "A page of deployments"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`validation_error`: a filter or `next_token` this list does not accept"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "List the organization's deployments",
        "tags": [
          "Incident Management - Deployments"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management": {
      "get": {
        "description": "Retrieve all incident teams that the user has access to within the organization",
        "operationId": "listIncidentTeams",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Filter teams by name",
            "in": "query",
            "name": "name",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Filter teams by creator user ID",
            "in": "query",
            "name": "created_by",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Maximum number of teams to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "The `next_token` of the previous page of this list, for this organization. Any other value (another organization's, another list's, a malformed one) is a 400 `invalid_request`.",
            "in": "query",
            "name": "next_token",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncidentTeamList"
                }
              }
            },
            "description": "List of incident teams"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`invalid_request`: `limit` outside 1..100, or a `next_token` this list did not answer for this organization"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get all incident teams for an organization",
        "tags": [
          "Incident Management - Teams"
        ]
      },
      "post": {
        "description": "Create a new incident management team within the organization",
        "operationId": "createIncidentTeam",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateIncidentTeamRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncidentTeam"
                }
              }
            },
            "description": "Team created successfully"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanLimitError"
                }
              }
            },
            "description": "`plan_limit_reached`: the organization's plan caps the number of teams (`limit.key`\n`teams`, `limit.max`, `limit.current`); `upgrade_path` is the Plan page.\n"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Create a new incident team",
        "tags": [
          "Incident Management - Teams"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}": {
      "delete": {
        "description": "Delete a team, its members, its integration keys (a deleted team's key is refused with `invalid_token`), and its schedules with every override on them, within the organization. The team also leaves every rotation of the organization's other schedules that names it as a `team` participant (an emptied rotation is kept), so no schedule shows a deleted team as on call. A schedule changed by another request at the same moment is not overwritten; a failure is a 500 and leaves the team in place for a retry. A team that another team's escalation policy (a `team` step) or routing rule (a `team` / `other_team` target) pages, enabled or not, is not deleted: 400 `team_in_use` names them, and nothing changes. The team's own policies and rules do not count. Once the team is gone, the rest of it — its alerts with their activities, comments, acknowledge links and deliveries (the escalation of an open one stopped first), its routing rules and escalation policies — is purged in the background; nothing can reach it meanwhile.",
        "operationId": "deleteIncidentTeam",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Team deleted successfully"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeamInUseError"
                }
              }
            },
            "description": "`team_in_use`: other teams page this team. `referencing_policies` and `referencing_rules` name them; point them at another target, then delete again."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Delete an incident team",
        "tags": [
          "Incident Management - Teams"
        ]
      },
      "get": {
        "description": "Retrieve details of a specific incident team within the organization",
        "operationId": "getIncidentTeam",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncidentTeam"
                }
              }
            },
            "description": "Team details"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get a specific incident team",
        "tags": [
          "Incident Management - Teams"
        ]
      },
      "put": {
        "description": "Update team properties within the organization. Allowed for organization Owners/Managers and team Owners; team Managers cannot change the team record. `enabled: false` switches the team off, and a disabled team pages nobody: its alerts are stored and not routed (`routing: suppressed`, `routing_reason: team_disabled`), an escalation already running stops at its next step, and the other teams' routing rules, escalation steps and rotations that name it skip it (`team_disabled` on their alerts' timelines). `enabled: true` pages as before.",
        "operationId": "updateIncidentTeam",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateIncidentTeamRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncidentTeam"
                }
              }
            },
            "description": "Team updated successfully"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Update an incident team",
        "tags": [
          "Incident Management - Teams"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/alerts": {
      "get": {
        "description": "Retrieve all alerts for the specified incident team within the organization",
        "operationId": "listTeamAlerts",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Filter alerts by status. Accepts a single status or a\ncomma-separated list of statuses (e.g. `open`,\n`open,acknowledged`). Valid values: `open`, `acknowledged`,\n`closed`. At most 20 values of at most 100 characters each\n(as `severity`), else a 400 `invalid_request`.\n\nWhen this parameter is omitted, alerts in any status are\nreturned **except** `closed` alerts. To include closed alerts\nin the results, pass `closed` explicitly (either alone or as\npart of the comma-separated list, e.g.\n`status=open,acknowledged,closed`).\n",
            "example": "open,acknowledged",
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Filter alerts by severity level",
            "in": "query",
            "name": "severity",
            "required": false,
            "schema": {
              "enum": [
                "P1",
                "P2",
                "P3",
                "P4",
                "P5"
              ],
              "type": "string"
            }
          },
          {
            "description": "Filter alerts by assignee user ID",
            "in": "query",
            "name": "assignee",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Filter alerts by source system",
            "in": "query",
            "name": "source",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Filter alerts by tags: comma-separated terms, each matched case-insensitively inside an alert's `key:value` tags (`env:prod`, `prod`); an alert must match every term. Terms are trimmed and lower-cased, and empty or repeated ones are ignored. More than 20 different terms, or a term of more than 601 characters (the longest `key:value`: a 100-character key and a 500-character value), is a 400 `invalid_request`.",
            "in": "query",
            "name": "tags",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only alerts created at or after this ISO 8601 date/timestamp (e.g. 2026-07-01 or 2026-07-01T12:00:00Z)",
            "in": "query",
            "name": "created_after",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only alerts created at or before this ISO 8601 date/timestamp. The\nbounds compare as text, so a plain date is the start of its day\n(`2026-07-01` ends before July 1st; send `2026-07-01T23:59:59.999Z`\nto include it). With `created_after`, it must not be earlier than\n`created_after`: an inverted range is a 400 `invalid_request`.\n",
            "in": "query",
            "name": "created_before",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Sort alerts by field. `status` and `severity` sort by their value\nas text (`acknowledged`, `closed`, `open`; `P1` to `P5`); alerts\nwith the same value come in no particular order. Every filter\ncombines with every sort.\n",
            "in": "query",
            "name": "sort_by",
            "required": false,
            "schema": {
              "default": "created_at",
              "enum": [
                "created_at",
                "updated_at",
                "severity",
                "status",
                "count"
              ],
              "type": "string"
            }
          },
          {
            "description": "Sort order (asc or desc)",
            "in": "query",
            "name": "sort_order",
            "required": false,
            "schema": {
              "default": "desc",
              "enum": [
                "asc",
                "desc"
              ],
              "type": "string"
            }
          },
          {
            "description": "Maximum number of alerts to return per page",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Token for pagination to get the next set of results: the\n`next_token` of the previous page, sent with the same filters and\nsort. It is opaque; a token this listing did not issue is a 400\n`invalid_request`.\n",
            "in": "query",
            "name": "next_token",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "alerts": {
                      "items": {
                        "$ref": "#/components/schemas/TeamAlert"
                      },
                      "type": "array"
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/PaginationMetadata"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "List of alerts for the team"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get all alerts for a team",
        "tags": [
          "Incident Management - Alerts"
        ]
      },
      "post": {
        "description": "Create a new alert for the incident team. Two callers are accepted:\n\n- **An integration key** (the webhook of a monitoring tool), sent as\n  `Authorization: ign_…`, the key as is with no prefix: the key\n  must belong to the team, `source` defaults to the key's\n  `integration_name`, the alert records\n  `created_by: webhook` and the integration's name, and is assigned to\n  nobody unless the body names an `assignee`.\n- **A signed-in user** (the console's \"Create alert\"; a Cognito token as\n  `Authorization: Bearer <id token>` or a personal API key as\n  `Authorization: PAT <key>`): any member of the organization, for any of its\n  teams, whatever their team role — a customer call, a bad deploy,\n  something a teammate spotted, or a way to exercise the routing and\n  escalation the team configured. `source` defaults to `manual`, the\n  alert records the user as `created_by` (and `created_by_name`) and is\n  **assigned to the creator** unless the body says otherwise\n  (`assignee: null` leaves it unassigned; another assignee must be a\n  member of the organization, else 400 `validation_error`). A caller\n  outside the organization gets 403 `access_denied`.\n\nBoth go through the same plan limits (rate limit, monthly quota), the\nsame deduplication by `alias`, the same timeline entry (`alert_created`,\nwith the user or the integration as its actor) and the same routing and\npaging: a manual alert is routed and escalated exactly like one from a\nwebhook. A **disabled team** (`enabled: false`) pages nobody: its alerts\nare stored and not routed (`routing: suppressed`, `routing_reason:\nteam_disabled`), never refused.\n\n**During a service disruption** alerts are still accepted and paged:\nthe answer (201, or 200 for an occurrence of an alert with the same\n`alias`) carries `degraded: true`. A new alert's escalation's first\nstep that reaches someone is paged at once, and the alert is listed\nonce the service recovers, when that first step is paged again unless\nits routing is suppressed by then; an occurrence is counted on its\nalert then (see `degraded` for both answers). A change to an alert (an\nacknowledgement, an update, a delete) answers 503 `service_unavailable`\nuntil then.\n",
        "operationId": "createAlertViaWebhook",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreateAlertRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookAlertCreateResponse"
                }
              }
            },
            "description": "Alert stored. `routing` says whether its processing (routing and\npaging) was started: `started` on every paid alert and on a free alert\nwithin the monthly quota; `suppressed` when the team is disabled\n(`routing_reason` `team_disabled`: the timeline records\n`routing_suppressed`, nobody is paged and no unit of the monthly quota\nis taken), when a Free plan organization has used its monthly alert\nallowance (`quota_exceeded`) or while the free tier's routing is paused\n(`free_tier_paused`) — a quota-suppressed alert is then on the alert\nlist with `quota_status: over_limit`, the timeline records\n`alert_quota_exceeded` and nobody is paged until the quota resets. A\nFree plan answer also carries `quota` and the `X-IgniteOps-Quota`\nheader; neither the quota nor a disabled team is ever a 4xx.\n`degraded: true`: accepted during a service disruption (see\n`degraded`), without `quota` and without the header; the quota is\ncounted once the service recovers.\n",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Bad Request: `invalid_json` (the body is not JSON), `validation_error`\n(missing title, `tags` not an object, an object where a string is expected).\n"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`access_denied`: a user outside the organization, or an integration key on any\norganization's path but its own (reason `forbidden`, the same whether that\norganization exists, is frozen or is scheduled for deletion). `forbidden` when\nthe integration key does not belong to the team in the path.\n"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Request body exceeds maximum size of 256KB"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitError"
                }
              }
            },
            "description": "`rate_limited`: a Free plan organization is over its ingestion rate\n(30 alerts per minute, 1,000 per day); nothing was stored. Retry after\n`Retry-After` seconds (`scope` says whether the minute or the day\nbucket is full). Pro organizations are never rate limited here.\n",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying",
                "schema": {
                  "example": 17,
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The alert was stored but its routing could not be started after three\nattempts (`routing_failed`, with `alert_id`). The alert\nrecords `alert_unrouted`; **retry the request**: a retry with the same\n`alias` is absorbed by the existing alert and routes it. During a\nservice disruption the same answer means the alert was accepted but not\npaged, and nothing is recorded on its timeline: retry the request all\nthe same.\n"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AlertBusyError"
                    },
                    {
                      "$ref": "#/components/schemas/ServiceUnavailableError"
                    }
                  ]
                }
              }
            },
            "description": "Nothing was recorded; retry after `Retry-After` seconds (the same value\nas `retry_after` in the body):\n- `alert_busy`: the alert this occurrence counts on was being changed by\n  another request at the same moment. The retry counts the occurrence.\n- `service_unavailable`: the request could not be authorized at that\n  moment, with `Retry-After: 5`. The retry is an ordinary request.\n",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "example": 1,
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Create alert",
        "tags": [
          "Incident Management - Alerts",
          "Webhooks"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/alerts/{alertId}": {
      "delete": {
        "description": "Delete a closed alert of the team, for good. Its comments, activity history, acknowledge links and delivery records are deleted with it, in the background right after the answer: no API reads them once the alert is gone. Close the alert first.",
        "operationId": "deleteAlert",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the alert",
            "in": "path",
            "name": "alertId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteAlertResponse"
                }
              }
            },
            "description": "Alert deleted successfully"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Only closed alerts can be deleted (one reopened meanwhile is not deleted)"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AlertBusyError"
                    },
                    {
                      "$ref": "#/components/schemas/ServiceUnavailableError"
                    }
                  ]
                }
              }
            },
            "description": "`alert_busy`: the alert was being changed by another request at the same\nmoment, and nothing was deleted. Retry after `Retry-After` seconds.\n`service_unavailable`: the request could not be authorized at that\nmoment and nothing was done, with `Retry-After: 5`; or, with\n`Retry-After: 30`, the delete could not be confirmed during a service\ndisruption: it may already have been made, and sent again it then\nanswers 404.\n",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "example": 1,
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Delete an alert",
        "tags": [
          "Incident Management - Alerts"
        ]
      },
      "get": {
        "description": "Retrieve details of a specific alert within the organization team",
        "operationId": "getAlert",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the alert",
            "in": "path",
            "name": "alertId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeamAlert"
                }
              }
            },
            "description": "Alert details"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get a specific alert",
        "tags": [
          "Incident Management - Alerts"
        ]
      },
      "put": {
        "description": "Partial update of an alert (status, assignee, severity, tags, custom fields,\nnotes). Allowed for organization Owners/Managers and team Owners/Managers/\nResponders. Send `expected_updated_at` to fail with 409 instead of\noverwriting a concurrent change.\n",
        "operationId": "updateAlert",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the alert",
            "in": "path",
            "name": "alertId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateTeamAlertRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AlertOperationResponse"
                }
              }
            },
            "description": "Alert updated successfully"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The alert changed since `expected_updated_at` (error `alert_modified`; the body carries the current `alert`). Without `expected_updated_at`, a status changed meanwhile is applied over once more from the alert as it stands."
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AlertBusyError"
                    },
                    {
                      "$ref": "#/components/schemas/ServiceUnavailableError"
                    }
                  ]
                }
              }
            },
            "description": "`alert_busy`: the alert was being changed by other requests at the same\nmoment (another change, or two status changes while this one, sent\nwithout `expected_updated_at`, was being applied), and nothing was\nchanged. Retry after `Retry-After` seconds.\n`service_unavailable`: the request could not be authorized at that\nmoment and nothing was done, with `Retry-After: 5`; or, with\n`Retry-After: 30`, the change could not be confirmed during a\nservice disruption (a close or a reopen included): it may already\nhave been made, so read the alert before sending it again.\n",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "example": 1,
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Update an alert",
        "tags": [
          "Incident Management - Alerts"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/alerts/{alertId}/ack": {
      "post": {
        "description": "Acknowledge an alert and assign it to the authenticated user making the request",
        "operationId": "acknowledgeAlert",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the alert",
            "in": "path",
            "name": "alertId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "note": {
                    "description": "Optional note about the acknowledgment",
                    "example": "Investigating the issue",
                    "type": "string"
                  },
                  "source": {
                    "default": "api",
                    "description": "Source of the acknowledgment",
                    "enum": [
                      "app",
                      "mobile",
                      "voice",
                      "api"
                    ],
                    "example": "app",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AlertOperationResponse"
                }
              }
            },
            "description": "Alert acknowledged successfully: the alert is assigned to the caller\n(`assigned_to`, `alert.assignee`). An alert the caller had already\nacknowledged is answered the same way: their acknowledgement stands\nand is not recorded or counted again. An acknowledgement assigns the\nalert to the one who acknowledges, so a repeat assigns it back to the\ncaller when it was reassigned (or unassigned) meanwhile, recorded on\nthe timeline as an assignment (`assigned`, `reason: acknowledged`)\nwith the `note` sent along; a `note` sent with a repeat that changes\nnothing is recorded as a note of the acknowledgement\n(`acknowledgement_note`), never as an edit of the alert's notes.\n"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The alert is not open any more (acknowledged by someone else, or closed)."
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AlertBusyError"
                    },
                    {
                      "$ref": "#/components/schemas/ServiceUnavailableError"
                    }
                  ]
                }
              }
            },
            "description": "`alert_busy`: the alert was being changed by another request at the same\nmoment, and it is still open: nothing was acknowledged (on a repeat, the\nassignment back to the caller was not applied). Retry after\n`Retry-After` seconds (a 409 means the alert is no longer open).\n`service_unavailable`: the request could not be authorized at that\nmoment and nothing was done, with `Retry-After: 5`; or, with\n`Retry-After: 30`, the acknowledgement could not be confirmed during\na service disruption: it may already have been made, and sent again\nit then answers as already acknowledged.\n",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "example": 1,
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Acknowledge an alert",
        "tags": [
          "Incident Management - Alerts"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/alerts/{alertId}/activities": {
      "get": {
        "description": "Retrieve the activity/audit trail for a specific alert within the organization team. A webhook or Slack delivery (`notification_channel: webhook`) carries its destination masked in `recipient`, `contact_name`, the other details and the description (`https://hooks.slack.com/…x7Qz`): the URL is the destination's credential and the timeline is read by every member of the organization.",
        "operationId": "listAlertActivities",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the alert",
            "in": "path",
            "name": "alertId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Filter activities by type (the value stored on the activity)",
            "in": "query",
            "name": "activity_type",
            "required": false,
            "schema": {
              "enum": [
                "system",
                "alertAction",
                "alertRecipient",
                "notification",
                "integration",
                "escalation"
              ],
              "type": "string"
            }
          },
          {
            "description": "Filter activities by the user who performed them. Like every filter, at most 200 characters: a longer one matches nothing and is a 400 `invalid_request`, answered before anything is read.",
            "in": "query",
            "name": "actor",
            "required": false,
            "schema": {
              "maxLength": 200,
              "type": "string"
            }
          },
          {
            "description": "Filter activities by notification channel",
            "in": "query",
            "name": "notification_channel",
            "required": false,
            "schema": {
              "enum": [
                "email",
                "sms",
                "voice",
                "push",
                "webhook"
              ],
              "type": "string"
            }
          },
          {
            "description": "Filter activities by delivery status",
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "enum": [
                "queued",
                "sending",
                "sent",
                "delivered",
                "failed",
                "skipped"
              ],
              "type": "string"
            }
          },
          {
            "description": "Filter activities created after this timestamp",
            "in": "query",
            "name": "since",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Maximum number of activities to return (newest first; there is no cursor — poll with `since` for newer ones). A value below 1 is read as 1 and one above 100 as 100; the answer's `filters.limit` is the one used.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Information level: 'basic' returns activity data as-is, 'extended' enriches user IDs with names (e.g., assignee changes show both old_assignee_name and new_assignee_name). Any other value is a 400 `invalid_request`, answered before anything is read.",
            "in": "query",
            "name": "info",
            "required": false,
            "schema": {
              "default": "basic",
              "enum": [
                "basic",
                "extended"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "basic": {
                    "summary": "Basic mode (default)",
                    "value": {
                      "activities": [
                        {
                          "activity_id": "act-123",
                          "activity_type": "alertAction",
                          "alert_id": "alert-789",
                          "details": {
                            "action": "assigned",
                            "new_assignee": "user-789",
                            "old_assignee": "user-456"
                          }
                        }
                      ],
                      "info_level": "basic",
                      "statistics": {
                        "total": 15
                      },
                      "total_count": 15
                    }
                  },
                  "extended": {
                    "summary": "Extended mode (with user names)",
                    "value": {
                      "activities": [
                        {
                          "activity_id": "act-456",
                          "activity_type": "alertAction",
                          "alert_id": "alert-789",
                          "details": {
                            "action": "assigned",
                            "new_assignee": "user-789",
                            "new_assignee_name": "Bob Johnson",
                            "old_assignee": "user-456",
                            "old_assignee_name": "Jane Smith"
                          }
                        }
                      ],
                      "info_level": "extended",
                      "statistics": {
                        "total": 15
                      },
                      "total_count": 15
                    }
                  }
                },
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ActivityList"
                    },
                    {
                      "$ref": "#/components/schemas/ActivityListExtended"
                    }
                  ]
                }
              }
            },
            "description": "List of activities for the alert.\nReturns ActivityList when info=basic (default).\nReturns ActivityListExtended when info=extended (with user names).\n"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Bad Request - `invalid_request`: `info` is neither `basic` nor `extended`, or a filter (`activity_type`, `actor`, `notification_channel`, `status`, `since`) is over 200 characters"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get alert activities",
        "tags": [
          "Incident Management - Alerts"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/alerts/{alertId}/comments": {
      "get": {
        "description": "Retrieve all comments for a specific alert with pagination support",
        "operationId": "listAlertComments",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the alert",
            "in": "path",
            "name": "alertId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Maximum number of comments to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Sort order (asc for oldest first, desc for newest first)",
            "in": "query",
            "name": "sort_order",
            "required": false,
            "schema": {
              "default": "desc",
              "enum": [
                "asc",
                "desc"
              ],
              "type": "string"
            }
          },
          {
            "description": "Token for pagination to get the next set of results: the `pagination.next_token` of the previous page of this alert's comments. Any other value (another alert's, an edited one) is a 400 `invalid_request`.",
            "in": "query",
            "name": "next_token",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AlertCommentListResponse"
                }
              }
            },
            "description": "List of comments"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "List comments on an alert",
        "tags": [
          "Incident Management - Alerts",
          "Comments"
        ]
      },
      "post": {
        "description": "Add a new comment to an alert. Supports Markdown formatting and user mentions. The body is a JSON object; anything else (`null`, an array) is a 400 `bad_request`.",
        "operationId": "createAlertComment",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the alert",
            "in": "path",
            "name": "alertId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAlertCommentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AlertCommentResponse"
                }
              }
            },
            "description": "Comment created successfully"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Create a comment on an alert",
        "tags": [
          "Incident Management - Alerts",
          "Comments"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/alerts/{alertId}/comments/{commentId}": {
      "delete": {
        "description": "Delete a comment. Only the comment author can delete their own comments.",
        "operationId": "deleteAlertComment",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the alert",
            "in": "path",
            "name": "alertId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the comment",
            "in": "path",
            "name": "commentId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteAlertCommentResponse"
                }
              }
            },
            "description": "Comment deleted successfully"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Delete a comment",
        "tags": [
          "Incident Management - Alerts",
          "Comments"
        ]
      },
      "put": {
        "description": "Update an existing comment. Only the comment author can edit their own comments. The body is a JSON object; anything else (`null`, an array) is a 400 `bad_request`. The write applies only while the comment is still there and still the caller's: a comment deleted while the request ran is a 404 `not_found`, and is not brought back.",
        "operationId": "updateAlertComment",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the alert",
            "in": "path",
            "name": "alertId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the comment",
            "in": "path",
            "name": "commentId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateAlertCommentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AlertCommentResponse"
                }
              }
            },
            "description": "Comment updated successfully"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AlertBusyError"
                    },
                    {
                      "$ref": "#/components/schemas/ServiceUnavailableError"
                    }
                  ]
                }
              }
            },
            "description": "`alert_busy`: the comment was being changed by another request at the same\nmoment, and nothing was changed. Retry after `Retry-After` seconds.\n`service_unavailable`: the request could not be authorized at that\nmoment and nothing was done, with `Retry-After: 5`.\n",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "example": 1,
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Update a comment",
        "tags": [
          "Incident Management - Alerts",
          "Comments"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/deployments": {
      "get": {
        "description": "The team's deployments, newest first. Every member of the organization may read them.",
        "operationId": "listDeployments",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only this service's deployments.",
            "in": "query",
            "name": "service",
            "required": false,
            "schema": {
              "maxLength": 100,
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "environment",
            "required": false,
            "schema": {
              "maxLength": 50,
              "type": "string"
            }
          },
          {
            "description": "The status as answered (`unknown` is a `started` deployment never closed after 24 hours).",
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "enum": [
                "started",
                "succeeded",
                "failed",
                "rolled_back",
                "unknown"
              ],
              "type": "string"
            }
          },
          {
            "description": "Deployments that started at or after this time (RFC 3339).",
            "in": "query",
            "name": "from",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Deployments that started at or before this time (RFC 3339).",
            "in": "query",
            "name": "to",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "The `next_token` of the previous page of this list; any other value is a 400.",
            "in": "query",
            "name": "next_token",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeploymentList"
                }
              }
            },
            "description": "A page of deployments"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`validation_error`: a filter or `next_token` this list does not accept"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`team_not_found`: the organization has no team of this name"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "List the team's deployments",
        "tags": [
          "Incident Management - Deployments"
        ]
      },
      "post": {
        "description": "Records a deployment of the team, from its pipeline (the team's integration key, `Authorization: ign_…`)\nor by hand (a member with the Responder role or above). With an `external_id`, the same deployment\nsent again (the job's end after its start) updates the recorded one and answers 200; its `status` only\nmoves forward (`started` to `succeeded`, `failed` or `rolled_back`; `succeeded` to `rolled_back`).\nWithout one, every request records a new deployment (201). Use one `external_id` per attempt and per\nservice: the same `external_id` with another `service` or `environment` is refused (409\n`external_id_conflict`). A later request keeps the recorded `environment` and `kind` unless it sends\nthem. A request from anyone but the recorder (another key, another member), unless it comes from a team\nor organization Owner or Manager, moves `status` and `finished_at` and fills only the fields still empty:\n`service`, `version`, `previous_version`, `environment`, `kind`, `triggered_by`, `description`,\n`repository`, `commit_sha`, `url` and `tags` keep what the recorder sent. An integration key records only its own team's deployments. On the Free plan an organization\nrecords at most 100 new deployments a day.\n",
        "operationId": "recordDeployment",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RecordDeploymentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deployment"
                }
              }
            },
            "description": "The same `external_id` again, updated"
          },
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deployment"
                }
              }
            },
            "description": "The deployment, recorded"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`validation_error`: a field missing, out of bounds or of the wrong type (`errors` lists each)"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanLimitError"
                }
              }
            },
            "description": "`plan_limit_reached` (`limit.key` `deployments_per_day`): the Free plan's 100 new deployments a day per organization"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`forbidden`: an integration key of another team"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`team_not_found`: the organization has no team of this name"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`invalid_transition`: the deployment's status cannot move back (`current_status`, `requested_status`); `external_id_conflict`: the `external_id` already names a deployment of another service or environment (`service`, `environment`); `conflict`: another request changed it at the same time"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceUnavailableError"
                }
              }
            },
            "description": "`service_unavailable`: the deployment could not be confirmed and may have been recorded. With an `external_id`, send the same request again after `retry_after` seconds (`Retry-After`): it lands on the same deployment. Without one, a resend may record it twice.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Record a deployment",
        "tags": [
          "Incident Management - Deployments"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/deployments/{deploymentId}": {
      "delete": {
        "description": "Deletes one deployment of the team. An organization or team Owner or Manager only.",
        "operationId": "deleteDeployment",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "deploymentId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`team_not_found`, or `not_found`: no such deployment in this team"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Delete a deployment",
        "tags": [
          "Incident Management - Deployments"
        ]
      },
      "get": {
        "description": "One deployment of the team. Every member of the organization may read it.",
        "operationId": "getDeployment",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "deploymentId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deployment"
                }
              }
            },
            "description": "The deployment"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`team_not_found`, or `not_found`: no such deployment in this team"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get a deployment",
        "tags": [
          "Incident Management - Deployments"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/escalation-policies": {
      "get": {
        "description": "Retrieve all escalation policies for the specified incident team within the organization. The URL of a `webhook` or `slack_channel` step is returned in full only to a caller who may edit the team's policies (an organization or team Owner or Manager); anyone else reads it masked, with `target_masked: true`.",
        "operationId": "listEscalationPolicies",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Filter by enabled status",
            "in": "query",
            "name": "enabled",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "description": "Maximum number of escalation policies to return per page. A value above 100 is read as 100 and a negative one as 1 (0 or a value that is not a number as the default, 50); the answer's `pagination.limit` is the one used.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "The `next_token` of the previous page of this list, for this team. Any other value (another team's, another list's, a malformed one) is a 400 `validation_error`.",
            "in": "query",
            "name": "next_token",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EscalationPolicyList"
                }
              }
            },
            "description": "List of escalation policies for the team"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`validation_error`: a `next_token` this list did not answer for this team"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`team_not_found`: the organization has no team of this name"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get all escalation policies for a team",
        "tags": [
          "Incident Management - Escalation Policies"
        ]
      },
      "post": {
        "description": "Create a new escalation policy for the incident team within the organization",
        "operationId": "createEscalationPolicy",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateEscalationPolicyRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EscalationPolicy"
                }
              }
            },
            "description": "Escalation policy created successfully"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/PlanLimitError"
                    },
                    {
                      "$ref": "#/components/schemas/PlanFeatureError"
                    }
                  ]
                }
              }
            },
            "description": "`plan_limit_reached`: the organization's plan caps the number of escalation\npolicies per team (`limit.key` `escalation_policies`) or the number of steps\nper policy (`escalation_steps`); `plan_feature_required` (`feature`\n`escalation_repeat`): the plan has no repeating policies; (`feature` `webhook`):\na step posts to Slack or to a webhook and the plan has no outbound webhooks\n(Free). `upgrade_path` is the Plan page.\n"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`team_not_found`: the organization has no team of this name"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Create a new escalation policy",
        "tags": [
          "Incident Management - Escalation Policies"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/escalation-policies/{policyId}": {
      "delete": {
        "description": "Delete an escalation policy within the organization team. 400 `policy_in_use` while any routing rule targets it or any schedule of the team names it in a rotation, enabled or not (a deleted policy would leave that slot shown as on call, paging nobody): `referencing_rules` and `referencing_schedules` list their names.",
        "operationId": "deleteEscalationPolicy",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the escalation policy",
            "in": "path",
            "name": "policyId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Escalation policy deleted successfully"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`team_not_found` (the organization has no team of this name) or `not_found` (the team has no escalation policy with this ID; an ID over 200 characters names none)"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Delete an escalation policy",
        "tags": [
          "Incident Management - Escalation Policies"
        ]
      },
      "get": {
        "description": "Retrieve details of a specific escalation policy within the organization team. The URL of a `webhook` or `slack_channel` step is returned in full only to a caller who may edit the policy (an organization or team Owner or Manager); anyone else reads it masked, with `target_masked: true`.",
        "operationId": "getEscalationPolicy",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the escalation policy",
            "in": "path",
            "name": "policyId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EscalationPolicy"
                }
              }
            },
            "description": "Escalation policy details"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`team_not_found` (the organization has no team of this name) or `not_found` (the team has no escalation policy with this ID; an ID over 200 characters names none)"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get a specific escalation policy",
        "tags": [
          "Incident Management - Escalation Policies"
        ]
      },
      "put": {
        "description": "Update an escalation policy within the organization team. A new `name` is unique in the team, also between two requests at once (400 `validation_error`). A request that sends a `name` is written only while the policy still bears the name it read, and a rename only on the version it read: one renamed (for a rename, changed) by another request meanwhile is a 400 `validation_error`, and nothing is written.",
        "operationId": "updateEscalationPolicy",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the escalation policy",
            "in": "path",
            "name": "policyId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateEscalationPolicyRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EscalationPolicy"
                }
              }
            },
            "description": "Escalation policy updated successfully"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/PlanLimitError"
                    },
                    {
                      "$ref": "#/components/schemas/PlanFeatureError"
                    }
                  ]
                }
              }
            },
            "description": "`plan_limit_reached`: the organization's plan caps the number of steps per\npolicy (`limit.key` `escalation_steps`); `plan_feature_required` (`feature`\n`escalation_repeat`): the plan has no repeating policies; (`feature` `webhook`):\na step posts to Slack or to a webhook and the plan has no outbound webhooks\n(Free). `upgrade_path` is the Plan page.\n"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`team_not_found` (the organization has no team of this name) or `not_found` (the team has no escalation policy with this ID; an ID over 200 characters names none)"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Update an escalation policy",
        "tags": [
          "Incident Management - Escalation Policies"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/integration-keys": {
      "get": {
        "description": "Retrieve all integration keys for the specified incident team within the organization",
        "operationId": "listIntegrationKeys",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Maximum number of integration keys to return per page. Below 1 is a 400 `validation_error`; above 100 is read as 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "The `next_token` of the previous page of this list, for this team. Any other value (another team's, another list's, a malformed one) is a 400 `invalid_pagination_token`.",
            "in": "query",
            "name": "next_token",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationKeyList"
                }
              }
            },
            "description": "Success"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "List integration keys for a team",
        "tags": [
          "Incident Management - Integration Keys"
        ]
      },
      "post": {
        "description": "Creates an integration key for the team's webhook and returns the secret (`ign_…`) once.\nThe monitoring tool sends it as the `Authorization` header value, as is (no `Bearer`),\non `POST /v1/organizations/{organizationName}/incident-management/{teamName}/alerts`.\n`ttl_days` sets an expiry of 1–365 days; omitted or null, the key never expires. Any other\n`ttl_days`, a `description` that is not a string of at most 200 characters, or an\n`integration_name` outside its pattern is refused with 400 `validation_error`.\n",
        "operationId": "createIntegrationKey",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IntegrationKeyCreateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationKeyCreateResponse"
                }
              }
            },
            "description": "Integration key created"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Create an integration key for a team",
        "tags": [
          "Incident Management - Integration Keys"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/integration-keys/{keyId}": {
      "delete": {
        "description": "Revokes the specified integration key so it can no longer be used for webhook authentication. A key already revoked, by an earlier request or by one at the same moment, is a 409 `already_revoked` and keeps the first revocation's `revoked_at` and `revoked_by`.\nA revoke holds at once, with one exception: during a service disruption, a key revoked shortly before or during it may still be accepted to create alerts until the service recovers, each such alert paging its escalation's first step once (the create answers `degraded: true`). Once the service has recovered, an alert received with a key that was already revoked or expired at that moment, or that no longer exists, is kept and not routed again (`routing_suppressed` on its timeline, with the reason); an occurrence such a key sent is counted on its alert, which it does not route again (the reason is on that alert's `alert_deduplicated` entry). A key whose team is deleted goes with its team's alerts. The same holds for a reissued key's previous secret, whichever reissues followed; and a request with the new secret may then be answered 503 `service_unavailable` until the service recovers (send it again after `Retry-After`).",
        "operationId": "revokeIntegrationKey",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The integration key ID to revoke",
            "in": "path",
            "name": "keyId",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Integration key revoked successfully"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`already_revoked`: the key is already revoked"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Revoke an integration key",
        "tags": [
          "Incident Management - Integration Keys"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/integration-keys/{keyId}/reissue": {
      "post": {
        "description": "Generates a new secret for the specified integration key and returns it. The previous secret stops working at once, with the exception the revoke operation describes: during a service disruption that begins shortly after the reissue, the previous secret may still be accepted to create alerts until the service recovers (their routing is suppressed once it has), and the new one may be answered 503 `service_unavailable` (send it again after `Retry-After`).",
        "operationId": "reissueIntegrationKey",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The integration key ID to reissue",
            "in": "path",
            "name": "keyId",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationKeyReissueResponse"
                }
              }
            },
            "description": "New integration key secret issued"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Reissue an integration key",
        "tags": [
          "Incident Management - Integration Keys"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/members": {
      "get": {
        "description": "Retrieve all members of an incident team within the organization",
        "operationId": "listTeamMembers",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Filter members by team role",
            "in": "query",
            "name": "role",
            "required": false,
            "schema": {
              "enum": [
                "Owner",
                "Manager",
                "Responder",
                "Observer"
              ],
              "type": "string"
            }
          },
          {
            "description": "Maximum number of team members to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "The `next_token` of the previous page of this list, for this team. Any other value (another team's, another list's, a malformed one) is a 400 `invalid_request`.",
            "in": "query",
            "name": "next_token",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncidentTeamMemberList"
                }
              }
            },
            "description": "List of team members"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`invalid_request`: `limit` outside 1..100, an unknown `role` filter, or a `next_token` this list did not answer for this team"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get team members",
        "tags": [
          "Incident Management - Teams"
        ]
      },
      "post": {
        "description": "Add a new member to the incident team within the organization",
        "operationId": "addTeamMember",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddIncidentTeamMemberRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncidentTeamMember"
                }
              }
            },
            "description": "Member added successfully"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Add team member",
        "tags": [
          "Incident Management - Teams"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/members/{userId}": {
      "delete": {
        "description": "Remove a member from the incident team. The owner tier (organization owner,\nteam Owners) may remove anyone but the last team Owner; the manager tier\n(team Managers, organization Managers) may only remove Responders and\nObservers (403 `forbidden` otherwise). The member also leaves the team's\non-call: they are taken out of every rotation of the team's schedules and\ntheir current and future overrides on them are deleted (past ones are\nkept as history). Their organization membership, alerts, escalation\npolicy and routing rule targets are left as they are.\n",
        "operationId": "removeTeamMember",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the user. A longer one than 200 characters names nobody (404 `member_not_found`).",
            "in": "path",
            "name": "userId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Member removed successfully"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Remove team member",
        "tags": [
          "Incident Management - Teams"
        ]
      },
      "get": {
        "description": "Retrieve details of a specific team member within the organization",
        "operationId": "getTeamMember",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the user. A longer one than 200 characters names nobody (404 `member_not_found`).",
            "in": "path",
            "name": "userId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncidentTeamMember"
                }
              }
            },
            "description": "Team member details"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get team member details",
        "tags": [
          "Incident Management - Teams"
        ]
      },
      "put": {
        "description": "Update team member role or other properties within the organization",
        "operationId": "updateTeamMember",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the user. A longer one than 200 characters names nobody (404 `member_not_found`).",
            "in": "path",
            "name": "userId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateIncidentTeamMemberRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncidentTeamMember"
                }
              }
            },
            "description": "Member updated successfully"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`validation_error`: no `role`, or `notification_preferences` (each person sets their own channels with PATCH /v1/user/notification-preferences; nothing is written); `cannot_demote_last_owner`; `invalid_json`"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Update team member",
        "tags": [
          "Incident Management - Teams"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/metrics": {
      "get": {
        "description": "Retrieve pre-calculated metrics for a specific team including MTTA, MTTR, alert frequency, and top sources",
        "operationId": "getTeamMetrics",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeamMetricsResponse"
                }
              }
            },
            "description": "Team metrics"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get team alert metrics",
        "tags": [
          "Incident Management - Metrics"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/routing-rules": {
      "get": {
        "description": "Retrieve all routing rules for the specified incident team within the organization",
        "operationId": "listRoutingRules",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Filter by enabled status",
            "in": "query",
            "name": "enabled",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "description": "Minimum priority to include (a whole number; anything else is a 400 `validation_error`)",
            "in": "query",
            "name": "priority_min",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "Maximum priority to include (a whole number; anything else is a 400 `validation_error`)",
            "in": "query",
            "name": "priority_max",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "Maximum number of routing rules to return per page. A value above 100 is read as 100 and a negative one as 1 (0 or a value that is not a number as the default, 50); the answer's `pagination.limit` is the one used.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "The `next_token` of the previous page of this list, for this team. Any other value (another team's, another list's, a malformed one) is a 400 `validation_error`.",
            "in": "query",
            "name": "next_token",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoutingRuleList"
                }
              }
            },
            "description": "List of routing rules for the team"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`validation_error`: a `next_token` this list did not answer for this team, or a `priority_min` / `priority_max` that is not a whole number"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`team_not_found`: the organization has no team of this name"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get all routing rules for a team",
        "tags": [
          "Incident Management - Routing Rules"
        ]
      },
      "post": {
        "description": "Create a new routing rule for the incident team within the organization",
        "operationId": "createRoutingRule",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateRoutingRuleRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoutingRule"
                }
              }
            },
            "description": "Routing rule created successfully"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanLimitError"
                }
              }
            },
            "description": "`plan_limit_reached`: the organization's plan caps the number of routing rules per team (`limit.key`\n`routing_rules`, `limit.max`, `limit.current`); `upgrade_path` is the Plan page.\n"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`team_not_found`: the organization has no team of this name"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Create a new routing rule",
        "tags": [
          "Incident Management - Routing Rules"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/routing-rules/{ruleId}": {
      "delete": {
        "description": "Delete a routing rule within the organization team",
        "operationId": "deleteRoutingRule",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the routing rule",
            "in": "path",
            "name": "ruleId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Routing rule deleted successfully"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`team_not_found` (the organization has no team of this name) or `not_found` (the team has no routing rule with this ID; an ID over 200 characters names none)"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Delete a routing rule",
        "tags": [
          "Incident Management - Routing Rules"
        ]
      },
      "get": {
        "description": "Retrieve details of a specific routing rule within the organization team",
        "operationId": "getRoutingRule",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the routing rule",
            "in": "path",
            "name": "ruleId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoutingRule"
                }
              }
            },
            "description": "Routing rule details"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`team_not_found` (the organization has no team of this name) or `not_found` (the team has no routing rule with this ID; an ID over 200 characters names none)"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get a specific routing rule",
        "tags": [
          "Incident Management - Routing Rules"
        ]
      },
      "put": {
        "description": "Update a routing rule within the organization team",
        "operationId": "updateRoutingRule",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the routing rule",
            "in": "path",
            "name": "ruleId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateRoutingRuleRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoutingRule"
                }
              }
            },
            "description": "Routing rule updated successfully"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`team_not_found` (the organization has no team of this name) or `not_found` (the team has no routing rule with this ID; an ID over 200 characters names none)"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Update a routing rule",
        "tags": [
          "Incident Management - Routing Rules"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/schedules": {
      "get": {
        "description": "Retrieve all on-call schedules for the specified incident team within the organization",
        "operationId": "listSchedules",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Filter schedules by name",
            "in": "query",
            "name": "name",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only return active schedules",
            "in": "query",
            "name": "active_only",
            "required": false,
            "schema": {
              "default": false,
              "type": "boolean"
            }
          },
          {
            "description": "Type of pagination (cursor for \"load more\", page for numbered pages)",
            "in": "query",
            "name": "pagination_type",
            "required": false,
            "schema": {
              "default": "cursor",
              "enum": [
                "cursor",
                "page"
              ],
              "type": "string"
            }
          },
          {
            "description": "Number of items per page/batch. Below 1 is a 400 `validation_error`; above 1000 is read as 1000.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 100,
              "maximum": 1000,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Page number (for page-based pagination only). Below 1 is a 400 `validation_error`.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "The `next_token` of the previous page of this list, for this team. Any other value (another team's, a malformed one) is a 400 `invalid_request`.",
            "in": "query",
            "name": "next_token",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduleList"
                }
              }
            },
            "description": "List of schedules for the team"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Not Found - `team_not_found`: the organization has no team of this name (answered before the body is read or anything is looked up)"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get all schedules for a team",
        "tags": [
          "Incident Management - Schedules"
        ]
      },
      "post": {
        "description": "Create a new on-call schedule for the team within the organization. A `user`\nparticipant must be a member of the team, a `team` participant a team of the\norganization and an `escalation` participant a policy of this team (a disabled one\nincluded): anything else is a 400 `validation_error` naming the rotation and the\nparticipant (`Rotation <i>: …`), the same answer whether the id belongs to someone\nelse or to nobody. A temporary failure during that check is a 500, never a 400.\nA participant is stored as `type`, `id` and `frequency`; a `user` also keeps the\n`name` sent, a `team` or an `escalation` participant does not (who-is-on-call and\nthe iCal feed name it from its own record). A schedule's name is unique in the\nteam, also between two\nrequests at once: the second is a 409 `conflict`.\n",
        "operationId": "createSchedule",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTeamScheduleRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeamSchedule"
                }
              }
            },
            "description": "Schedule created successfully"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanLimitError"
                }
              }
            },
            "description": "`plan_limit_reached`: the organization's plan caps the number of schedules per team (`limit.key`\n`schedules`, `limit.max`, `limit.current`); `upgrade_path` is the Plan page.\n"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Not Found - `team_not_found`: the organization has no team of this name (answered before the body is read or anything is looked up)"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`conflict`: the team has a schedule of this name"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Create a new schedule",
        "tags": [
          "Incident Management - Schedules"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/schedules/{scheduleId}": {
      "delete": {
        "description": "Delete a schedule and all associated overrides within the organization team. A schedule that one of the team's escalation policies pages (a `schedule`, `schedule_next` or `schedule_previous` step) or one of its routing rules is aimed at, enabled or not, is not deleted: 400 `schedule_in_use` names them in `referencing_policies` and `referencing_rules`, and nothing changes. A step on a deleted schedule would page nobody while the escalation still completes, and a rule on it would be skipped.",
        "operationId": "deleteSchedule",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the schedule (a UUID; any other value is a 404 `not_found` before anything is read)",
            "in": "path",
            "name": "scheduleId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Schedule deleted successfully"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduleInUseError"
                }
              }
            },
            "description": "`schedule_in_use`: escalation policies or routing rules of the team page this schedule. `referencing_policies` and `referencing_rules` name them; point them at another target, then delete again."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Not Found - `team_not_found`: the organization has no team of this name (answered before the body is read or anything is looked up), or `not_found`: the team has no such schedule"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Delete a schedule",
        "tags": [
          "Incident Management - Schedules"
        ]
      },
      "get": {
        "description": "Retrieve details of a specific schedule within the organization team",
        "operationId": "getSchedule",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the schedule (a UUID; any other value is a 404 `not_found` before anything is read)",
            "in": "path",
            "name": "scheduleId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeamSchedule"
                }
              }
            },
            "description": "Schedule details"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Not Found - `team_not_found`: the organization has no team of this name (answered before the body is read or anything is looked up), or `not_found`: the team has no such schedule"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get a specific schedule",
        "tags": [
          "Incident Management - Schedules"
        ]
      },
      "put": {
        "description": "Update schedule properties or rotation within the organization team. `rotations`\nreplace the stored ones and are checked as on create (a `user` must be a member of\nthe team, a `team` a team of the organization, an `escalation` participant a policy\nof this team, else 400 `validation_error` naming the rotation and the participant;\na temporary failure is a 500). A `user` the schedule already has is kept without a\ncheck, someone who has left the team since included. A `team` or `escalation`\nparticipant is checked on every save: one deleted since pages nobody, so the save\nis refused until it is removed. Participants are stored as on create. A rotation\nkeeps its `rotation_id` (the UID of its iCal events) when it sends back the id of\none of the schedule's rotations; any other id gets a new one, and one id sent on\ntwo rotations is a 400 `validation_error`. A new `name` is unique in the team, also\nbetween two requests at once (409 `conflict`); the name left behind is free at once.\nA request that sends a `name` is written only while the schedule still bears the name\nit read, and a rename only on the version it read: one renamed (for a rename, changed)\nby another request meanwhile is a 409 `conflict`, and nothing is written.\n",
        "operationId": "updateSchedule",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the schedule (a UUID; any other value is a 404 `not_found` before anything is read)",
            "in": "path",
            "name": "scheduleId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateTeamScheduleRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeamSchedule"
                }
              }
            },
            "description": "Schedule updated successfully"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Not Found - `team_not_found`: the organization has no team of this name (answered before the body is read or anything is looked up), or `not_found`: the team has no such schedule (a schedule deleted while the request ran included)"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`conflict`: the team has a schedule of this name, or the schedule was renamed (for a rename, changed) while the request ran"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Update a schedule",
        "tags": [
          "Incident Management - Schedules"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/schedules/{scheduleId}/ical": {
      "get": {
        "description": "The schedule as an iCalendar feed: one VEVENT per on-call shift, expanded\nfrom the rotations on the schedule's wall clock (type, weighted participants,\ntime restrictions in the schedule's timezone: a hand-over keeps its local time\nthrough daylight-saving changes) with overrides applied, from 24 h ago until\n`duration_months` ahead. Times are UTC; events are transparent (no\nfree/busy impact). A disabled schedule yields an empty calendar (the\nVCALENDAR with no VEVENT), whatever its rotations. A `user`\nparticipant is named only when a member of the organization; another user\nappears under the participant's stored name, else the id. A `team` (of the\norganization) or an `escalation` participant (a policy of the team) is named\nfrom its own record, anything else by its id, never by a stored name.\n",
        "operationId": "exportScheduleAsICal",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the schedule (a UUID; any other value is a 404 `not_found` before anything is read)",
            "in": "path",
            "name": "scheduleId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Number of months to include in the export, a whole number from 1 to 12 (anything else is a 400 `validation_error`). An export may hold at most 10,000 shifts: a dense rotation over many months is a 400 `export_too_large`, so ask for fewer months.",
            "in": "query",
            "name": "duration_months",
            "required": false,
            "schema": {
              "default": 3,
              "maximum": 12,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Only the shifts of this user (`me` = the requesting user, or a stable user id)",
            "in": "query",
            "name": "participant",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "text/calendar": {}
            },
            "description": "iCal file"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Bad Request - `validation_error`: `duration_months` is not a whole number from 1 to 12; or `export_too_large`: the schedule has more than 10,000 shifts from 24 h ago to `duration_months` ahead (the description gives the count); ask for fewer months. A disabled schedule is never too large: its export is the empty calendar."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Not Found - `team_not_found`: the organization has no team of this name (answered before the body is read or anything is looked up), or `not_found`: the team has no such schedule"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Export schedule as iCal",
        "tags": [
          "Incident Management - Schedules"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/schedules/{scheduleId}/overrides": {
      "get": {
        "description": "Retrieve all overrides for a specific schedule within the organization team",
        "operationId": "listScheduleOverrides",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the schedule (a UUID; any other value is a 404 `not_found` before anything is read)",
            "in": "path",
            "name": "scheduleId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Filter overrides starting at or after this time (at most 64 characters, else a 400 `validation_error`). Overrides are stored in UTC with milliseconds (2026-10-14T09:00:00.000Z) and compared as text, so send this form.",
            "in": "query",
            "name": "start_time",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Filter overrides ending before this time. With `start_time`, it\nmust not be earlier than `start_time` (the two compare as text, so\nuse the same format for both): an inverted range is a 400\n`validation_error`, and so is a value of more than 64 characters.\n",
            "in": "query",
            "name": "end_time",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduleOverrideList"
                }
              }
            },
            "description": "List of schedule overrides"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Not Found - `team_not_found`: the organization has no team of this name (answered before the body is read or anything is looked up), or `not_found`: the team has no such schedule"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get schedule overrides",
        "tags": [
          "Incident Management - Schedules"
        ]
      },
      "post": {
        "description": "Create a temporary override for the schedule within the organization team (vacation, coverage, etc.). The override replaces whoever the schedule's rotations would put on call for the whole period; it may not overlap another override of the schedule (409).",
        "operationId": "createScheduleOverride",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the schedule (a UUID; any other value is a 404 `not_found` before anything is read)",
            "in": "path",
            "name": "scheduleId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateScheduleOverrideRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduleOverride"
                }
              }
            },
            "description": "Override created successfully"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Not Found - `team_not_found`: the organization has no team of this name (answered before the body is read or anything is looked up), or `not_found`: the team has no such schedule"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Create a schedule override",
        "tags": [
          "Incident Management - Schedules"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/schedules/{scheduleId}/overrides/{overrideId}": {
      "delete": {
        "description": "Remove a schedule override within the organization team",
        "operationId": "deleteScheduleOverride",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the schedule (a UUID; any other value is a 404 `not_found` before anything is read)",
            "in": "path",
            "name": "scheduleId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the override (a UUID; any other value is a 404 `not_found` before anything is read)",
            "in": "path",
            "name": "overrideId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Override deleted successfully"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Not Found - `team_not_found`: the organization has no team of this name (answered before the body is read or anything is looked up), or `not_found`: the team has no such schedule or the schedule no such override"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Delete a schedule override",
        "tags": [
          "Incident Management - Schedules"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/schedules/{scheduleId}/who-is-on-call": {
      "get": {
        "description": "Determine who is on call now, or who was or will be on call at another instant\n(`at_time`), within the organization team. Each participant is named from its own\nrecord: a `user` (name, else e-mail) only when a member of the organization, a `team`\nonly when a team of the organization, an `escalation` participant only when a policy\nof the team. Anything else (another organization's id, a team or policy deleted\nsince, a lookup that failed) is answered with the id, never with a name stored with\nthe participant.\n",
        "operationId": "getWhoIsOnCall",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The incident team name",
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the schedule (a UUID; any other value is a 404 `not_found` before anything is read)",
            "in": "path",
            "name": "scheduleId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The instant to answer for, past or future: an RFC 3339 date-time with an offset (for example 2026-10-14T09:00:00Z or 2026-10-14T11:00:00+02:00), or a date (2026-10-14, its midnight UTC), in the years 1970–9999; defaults to the moment of the request. Anything else (a number, \"0\", a time without an offset, a day that does not exist such as February 30) is refused with 400 `validation_error`.",
            "in": "query",
            "name": "at_time",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "deprecated": true,
            "description": "Former name of `at_time`, still accepted; read only when `at_time` is absent.",
            "in": "query",
            "name": "time",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OnCallInfo"
                }
              }
            },
            "description": "Who is on call at the instant asked about"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Not Found - `team_not_found`: the organization has no team of this name (answered before the body is read or anything is looked up), or `not_found`: the team has no such schedule"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get who is on call",
        "tags": [
          "Incident Management - Schedules"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/webhook-endpoints": {
      "get": {
        "description": "Every distinct `webhook` URL of the team's escalation policies (Slack URLs left out), the steps that use it (`used_by`, by `rule_id`) and whether its deliveries are signed. Never a secret. Every member of the organization may read it; a caller who may not edit the team's policies reads each URL masked.",
        "operationId": "listWebhookEndpoints",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointList"
                }
              }
            },
            "description": "The team's webhook endpoints"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`team_not_found`: the organization has no team of this name"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "List the team's webhook endpoints and their signing",
        "tags": [
          "Incident Management - Escalation Policies"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/webhook-endpoints/signing-secret": {
      "post": {
        "description": "Creates the signing secret of one of the team's webhook endpoints, or rotates it: the new secret becomes current and the one it replaces keeps signing for `keep_previous_seconds` (both signatures are sent meanwhile). The secret is answered only here, once: store it in the receiver. A retry with the same `Idempotency-Key` within 24 hours answers the same secret (200). Deliveries to the endpoint are signed within about a minute (see `EscalationTargetRequest`, Signatures). An organization or team Owner or Manager only.",
        "operationId": "issueWebhookSigningSecret",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "A UUID, the same on every retry of this request.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IssueWebhookSigningSecretRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSigningSecret"
                }
              }
            },
            "description": "A retry with the same `Idempotency-Key`, within 24 hours and before another rotation, answers the same secret"
          },
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSigningSecret"
                }
              }
            },
            "description": "The new secret (`Cache-Control: no-store`)"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`validation_error`: no `Idempotency-Key` UUID, a body that is not a JSON object, a `url` that is missing, not http(s), longer than 2048 characters, masked or a Slack URL, or an option out of range."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanFeatureError"
                }
              }
            },
            "description": "`plan_feature_required` (`feature` `webhook`): the plan has no outbound webhooks (Free)."
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`team_not_found`: the organization has no team of this name; `not_found`: the URL is neither a webhook step nor a Slack channel step of any of the team's escalation policies."
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/RotationInProgressError"
                    }
                  ]
                }
              }
            },
            "description": "`rotation_in_progress` (with `previous_expires_at`): a previous secret still signs and the request did not ask to replace it; `conflict`: another request changed the secret first, or this Idempotency-Key was already used and its answer is no longer kept."
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceUnavailableError"
                }
              }
            },
            "description": "`service_unavailable`: the secret could not be confirmed during a service disruption; it may have been created. Retry after `retry_after` seconds (`Retry-After`) with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Create or rotate a webhook endpoint's signing secret",
        "tags": [
          "Incident Management - Escalation Policies"
        ]
      }
    },
    "/v1/organizations/{organizationName}/incident-management/{teamName}/webhook-endpoints/test": {
      "post": {
        "description": "Sends one POST of `{ \"event\": \"webhook.test\" }` to one of the team's webhook endpoints, with the delivery headers and, when it has a signing secret, its signatures, and answers what the endpoint answered. For a Slack channel of the team's policies (a `slack_channel` step, or a `webhook` step aimed at `hooks.slack.com`), it posts one test message in the layout of an alert's Slack message instead, unsigned (`signed` is false). The request is refused before it leaves when the host resolves to an address IgniteOps never contacts. Redirects are not followed; the endpoint has 10 seconds to answer. Counts against no quota and writes nothing on any alert. At most 5 tests a minute and 100 a day per organization. An organization or team Owner or Manager only.",
        "operationId": "testWebhookEndpoint",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "teamName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TestWebhookEndpointRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestWebhookEndpointResult"
                }
              }
            },
            "description": "What the endpoint answered (`delivered`, `http_status`, `signed`, `error`)"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`validation_error`: a `url` that is missing, not http(s), longer than 2048 characters or masked, or a Slack URL other than https on the default port."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanFeatureError"
                }
              }
            },
            "description": "`plan_feature_required` (`feature` `webhook`): the plan has no outbound webhooks (Free)."
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`team_not_found`: the organization has no team of this name; `not_found`: the URL is not a `webhook` step of any of the team's escalation policies."
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`rate_limited`: the organization sent 5 tests this minute or 100 today; retry after `retry_after` seconds (`Retry-After`).",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceUnavailableError"
                }
              }
            },
            "description": "`service_unavailable`: the endpoint's signing secrets could not be read, so nothing was sent; retry after `retry_after` seconds (`Retry-After`).",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Send a test request to a webhook endpoint or a Slack channel",
        "tags": [
          "Incident Management - Escalation Policies"
        ]
      }
    },
    "/v1/organizations/{organizationName}/members": {
      "get": {
        "description": "Retrieve all members of an organization",
        "operationId": "listOrganizationMembers",
        "parameters": [
          {
            "description": "The URL-compliant name of the organization",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Filter members by role",
            "in": "query",
            "name": "role",
            "required": false,
            "schema": {
              "enum": [
                "Owner",
                "Manager",
                "Member"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "current_user_role": {
                      "description": "Server-computed role of the calling user in this organization (null if the caller is not a member). Computed before any role filtering. Clients must use this for permission gating instead of matching their JWT sub against member user_ids.",
                      "enum": [
                        "Owner",
                        "Manager",
                        "Member"
                      ],
                      "nullable": true,
                      "type": "string"
                    },
                    "members": {
                      "items": {
                        "$ref": "#/components/schemas/OrganizationMember"
                      },
                      "type": "array"
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/PaginationMetadata"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "List of organization members"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get organization members",
        "tags": [
          "Organizations"
        ]
      },
      "post": {
        "description": "Add someone to the organization without an invitation (owner or manager only;\na Manager adds Members only, 403 `forbidden`). Only someone who already shares\nan organization with the caller can be added this way: the people\nGET /v1/users finds for the caller. Anyone else, and an id that\nbelongs to no account, gets 403 `invitation_required`: invite them with\nPOST .../members/invitations, and they join when they accept. The added\nperson takes a seat at once and is e-mailed who added them, with a link to\nleave the organization (DELETE .../members/{userId} with their own id).\n",
        "operationId": "addOrganizationMember",
        "parameters": [
          {
            "description": "The URL-compliant name of the organization",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddOrganizationMemberRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationMember"
                }
              }
            },
            "description": "Member added successfully"
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden: `invitation_required` (the person shares no organization with the\ncaller; invite them by e-mail instead) or `forbidden` (a Manager adding a Manager).\n"
          },
          "404": {
            "description": "Not Found - `organization_not_found`, also when the organization is deleted while the member is being added (the membership is taken back)"
          },
          "409": {
            "description": "Conflict - `member_already_exists`: the person is already a member, also when they became one while being added (their membership is not overwritten)"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NoSeatsAvailableError"
                }
              }
            },
            "description": "No available seats in the organization (`no_seats_available` with the plan's seat limit)"
          },
          "500": {
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Add organization member",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/v1/organizations/{organizationName}/members/invitations": {
      "get": {
        "description": "List pending email invitations for the organization (Owner/Manager only)",
        "operationId": "listOrganizationInvitations",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "invitations": {
                      "items": {
                        "$ref": "#/components/schemas/OrganizationInvitation"
                      },
                      "type": "array"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Pending invitations"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "List pending invitations",
        "tags": [
          "Organizations"
        ]
      },
      "post": {
        "description": "Send an email invitation to join the organization. A seat is reserved for the invitee when the invitation is created and released if it is revoked or expires (7 days). Owners can invite Managers and Members; Managers can invite Members only.\n",
        "operationId": "createOrganizationInvitation",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateInvitationRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationInvitation"
                }
              }
            },
            "description": "Invitation created and email sent"
          },
          "400": {
            "description": "Invalid request"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "409": {
            "description": "User is already a member or an invitation is already pending"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NoSeatsAvailableError"
                }
              }
            },
            "description": "No available seats to reserve for the invitee (`no_seats_available` with the plan's seat limit)"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "description": "Invitation email could not be sent"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Invite a user by email",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/v1/organizations/{organizationName}/members/invitations/{invitationId}": {
      "delete": {
        "description": "Revoke a pending invitation and release its reserved seat. Owners revoke any invitation; Managers revoke Member invitations only, as they invite Members only (403 `forbidden_for_role` for a Manager invitation).",
        "operationId": "revokeOrganizationInvitation",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The invitation id (`inv-<uuid>`). A longer one names no invitation (404 `invitation_not_found`).",
            "in": "path",
            "name": "invitationId",
            "required": true,
            "schema": {
              "maxLength": 200,
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Invitation revoked"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`access_denied` (a Member, or no access to the organization), or `forbidden_for_role`: a Manager revoking a Manager invitation"
          },
          "404": {
            "description": "Invitation not found"
          },
          "409": {
            "description": "Invitation is no longer pending"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Revoke a pending invitation",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/v1/organizations/{organizationName}/members/{userId}": {
      "delete": {
        "description": "Remove a member, or leave the organization with your own user id.\nAny member but the owner may leave; the owner never can, and nobody removes the\nowner (403 `owner_protected`; transfer ownership first). Otherwise an Owner\nremoves anyone, a Manager only Members, and a Member nobody but themselves\n(403 `forbidden_for_role`). Either way the person is offboarded from the\norganization at once: membership, seat, teams, assigned alerts, rotations,\noverrides, escalation targets and routing rules. A removed member is e-mailed\nthat they no longer have access; when someone leaves, the organization owner\nis e-mailed what their departure changed.\n",
        "operationId": "removeOrganizationMember",
        "parameters": [
          {
            "description": "The URL-compliant name of the organization",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the user. A longer one names nobody (404 `member_not_found`).",
            "in": "path",
            "name": "userId",
            "required": true,
            "schema": {
              "maxLength": 200,
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Member removed, or the caller left"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden: `owner_protected` (the target is the organization owner, the caller\nincluded) or `forbidden_for_role` (a Manager may only remove Members; a Member\nmay only remove themselves).\n"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Remove an organization member, or leave the organization",
        "tags": [
          "Organizations"
        ]
      },
      "get": {
        "description": "Retrieve details of a specific organization member",
        "operationId": "getOrganizationMember",
        "parameters": [
          {
            "description": "The URL-compliant name of the organization",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the user. A longer one names nobody (404 `member_not_found`).",
            "in": "path",
            "name": "userId",
            "required": true,
            "schema": {
              "maxLength": 200,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationMember"
                }
              }
            },
            "description": "Organization member details"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get organization member details",
        "tags": [
          "Organizations"
        ]
      },
      "put": {
        "description": "Change a member's role (owner or manager only). The owner's membership is never\nchanged here (403 `owner_protected`; use transfer-ownership) and the Owner role\nis never assigned here (400 `validation_error`); a Manager may only manage\nMembers and may only assign the Member role (403 `forbidden_for_role`).\n",
        "operationId": "updateOrganizationMember",
        "parameters": [
          {
            "description": "The URL-compliant name of the organization",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The unique identifier for the user. A longer one names nobody (404 `member_not_found`).",
            "in": "path",
            "name": "userId",
            "required": true,
            "schema": {
              "maxLength": 200,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateOrganizationMemberRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationMember"
                }
              }
            },
            "description": "Member updated successfully"
          },
          "400": {
            "description": "Bad Request: `validation_error` when the role is Owner (use transfer-ownership),\n`invalid_request` for any other invalid role or body.\n"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden: `owner_protected` (the target is the organization owner) or\n`forbidden_for_role` (a Manager may only make Members Members; only owners\nand managers may change roles).\n"
          },
          "404": {
            "description": "Not Found"
          },
          "500": {
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Update organization member",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/v1/organizations/{organizationName}/metrics/alerts": {
      "get": {
        "description": "Retrieve pre-calculated metrics for an entire organization including MTTA, MTTR, alert frequency, top sources, and busiest teams",
        "operationId": "getOrganizationMetrics",
        "parameters": [
          {
            "description": "The organization name",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationMetricsResponse"
                }
              }
            },
            "description": "Organization metrics"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get organization alert metrics",
        "tags": [
          "Incident Management - Metrics"
        ]
      }
    },
    "/v1/organizations/{organizationName}/support-tickets": {
      "get": {
        "operationId": "listOrgSupportTickets",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "maximum": 200,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "The `pagination.next_token` of the previous page of this list (any other value is a 400)",
            "in": "query",
            "name": "next_token",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of tickets for this organization"
          },
          "400": {
            "description": "`invalid_request`: `next_token` is not a token this listing returned"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "List the caller organization's support tickets",
        "tags": [
          "Support Tickets"
        ]
      },
      "post": {
        "operationId": "createSupportTicket",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "message": {
                    "maxLength": 10000,
                    "minLength": 1,
                    "type": "string"
                  },
                  "priority": {
                    "default": "normal",
                    "description": "On the Free plan (best-effort support) high and urgent are accepted but clamped to normal; the answer carries `priority_clamped` and `notice`",
                    "enum": [
                      "low",
                      "normal",
                      "high",
                      "urgent"
                    ],
                    "type": "string"
                  },
                  "subject": {
                    "maxLength": 200,
                    "minLength": 3,
                    "type": "string"
                  }
                },
                "required": [
                  "subject",
                  "message"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateSupportTicketResponse"
                }
              }
            },
            "description": "Ticket created (`ticket.organization_plan` is the plan; `priority_clamped` / `notice` when a Free plan priority was clamped)"
          },
          "400": {
            "description": "Validation error"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Open a support ticket for the caller's organization",
        "tags": [
          "Support Tickets"
        ]
      }
    },
    "/v1/organizations/{organizationName}/support-tickets/{ticketId}": {
      "get": {
        "operationId": "getOrgSupportTicket",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "ticketId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Ticket detail with customer-visible messages"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Ticket not found"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get one ticket and its customer-visible messages (internal notes hidden)",
        "tags": [
          "Support Tickets"
        ]
      }
    },
    "/v1/organizations/{organizationName}/support-tickets/{ticketId}/messages": {
      "post": {
        "operationId": "postCustomerSupportMessage",
        "parameters": [
          {
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "ticketId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "body": {
                    "maxLength": 10000,
                    "minLength": 1,
                    "type": "string"
                  }
                },
                "required": [
                  "body"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Reply posted; ticket re-opened if it was pending/resolved"
          },
          "400": {
            "description": "Validation error"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Ticket not found"
          },
          "409": {
            "description": "Ticket is closed"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Reply to a support ticket as a customer (internal=false enforced)",
        "tags": [
          "Support Tickets"
        ]
      }
    },
    "/v1/organizations/{organizationName}/transfer-ownership": {
      "patch": {
        "description": "Transfer ownership of the organization to another existing member (owner only). The 200 answers the organization as the caller sees it once the transfer is written (`is_owner` false, `my_role` Manager, the seat the swap gave them in `seat_status`), without `plan_usage` and `limits_reached`, plus `transfer_details`.",
        "operationId": "transferOrganizationOwnership",
        "parameters": [
          {
            "description": "The URL-compliant name of the organization",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TransferOwnershipRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Organization"
                    },
                    {
                      "properties": {
                        "transfer_details": {
                          "properties": {
                            "new_owner": {
                              "type": "string"
                            },
                            "previous_owner": {
                              "description": "The caller's user id.",
                              "type": "string"
                            },
                            "transferred_at": {
                              "format": "date-time",
                              "type": "string"
                            }
                          },
                          "type": "object"
                        }
                      },
                      "type": "object"
                    }
                  ]
                }
              }
            },
            "description": "Ownership transferred successfully"
          },
          "400": {
            "description": "`invalid_request`: `new_owner_user_id` is missing, is no user id (a string of at most 200 characters), is the caller, or is not a member of the organization."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden - `forbidden`: the caller is not the organization's owner (`owner_user_id`), also while a transfer to them that failed half-way still gives them the Owner role (it is rolled back within the hour)"
          },
          "404": {
            "description": "Not Found"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/FreeOrgLimitError"
                    }
                  ]
                }
              }
            },
            "description": "Conflict - `free_org_limit` (the new owner already owns a free organization, a FreeOrgLimitError with its `organization_name`), `purge_in_progress` (the purge of the organization, scheduled with its owner's account, has started, so ownership cannot be transferred any more) or `transfer_conflict` (another ownership transfer of the organization is in progress, or the owner, a membership or a seat of the transfer changed while it was being made; nothing was changed — read the organization and try again)"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unprocessable - `no_seat_for_new_owner` (the new owner holds no active seat; the owner's complimentary seat and theirs swap holders)"
          },
          "500": {
            "description": "Server Error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not complete this request right now. Retry after `Retry-After` seconds; the transfer may have been made, so read the organization before repeating it.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Transfer organization ownership",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/v1/organizations/{organizationName}/upgrade-requests": {
      "post": {
        "description": "Any member of a free organization asks its owner to move it to the Pro plan: the owner is e-mailed the reason (the limit or feature the member ran into, in words, and their message) with a link to the Plan page. One request per member and UTC day. The owner asking is answered without an e-mail.\n",
        "operationId": "requestOrganizationUpgrade",
        "parameters": [
          {
            "description": "The URL-compliant name of the organization",
            "in": "path",
            "name": "organizationName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpgradeRequest"
              }
            }
          },
          "required": false
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpgradeRequestAccepted"
                }
              }
            },
            "description": "The owner has been asked"
          },
          "400": {
            "description": "Invalid request (unknown limit_key or feature, a message over 280 characters)"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Conflict - `already_paid` (the organization is on the Pro plan)"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "Server Error"
          },
          "502": {
            "description": "The e-mail to the owner could not be sent"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Ask the owner to upgrade the organization",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/v1/service/api-keys": {
      "get": {
        "description": "Retrieves a paginated list of active api keys for the authenticated user.",
        "operationId": "listKeys",
        "parameters": [
          {
            "description": "Maximum number of API keys to return (1-100)",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "The `pagination.next_token` of the previous page of this list (any other value is a 400)",
            "in": "query",
            "name": "next_token",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyList"
                }
              }
            },
            "description": "Success"
          },
          "400": {
            "description": "`invalid_request`: `limit` is not an integer from 1 to 100, or `next_token` is not a token this list returned"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "A personal API key (`api_key_not_allowed`: keys are managed from a signed-in session only), or an account pending deletion (`account_pending_deletion`)"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "List Api Keys for the authenticated user",
        "tags": [
          "APIKeys"
        ]
      },
      "post": {
        "description": "Creates a personal API key for the authenticated user and returns the secret once, with its metadata.\nSend it as `Authorization: PAT <secret>` on every request. It acts as its owner, except on the account's\nown settings, notification methods, devices and credentials, which answer 403 `api_key_not_allowed`.\nThere are no narrower scopes: a non-empty `permissions` object is refused with 400 `validation_error`,\nand the `permissions` an older key was created with are not returned and never applied.\n",
        "operationId": "createKey",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiKeyCreateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyCreateResponse"
                }
              }
            },
            "description": "Key created"
          },
          "400": {
            "description": "Bad Request: `invalid_json`, or `validation_error` (a bad `name` or `ttl_days`, or a non-empty `permissions`)"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "A personal API key (`api_key_not_allowed`: keys are managed from a signed-in session only), or an account pending deletion (`account_pending_deletion`)"
          },
          "500": {
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Create an API Key",
        "tags": [
          "APIKeys"
        ]
      }
    },
    "/v1/service/api-keys/{key_id}": {
      "delete": {
        "description": "Revokes the specified API Key so it can no longer be used. A revoke holds at once, during a service disruption too: a request with a personal API key is then answered 503 `service_unavailable` (send it again after `Retry-After`) rather than accepted on a key's older state.",
        "operationId": "revokeKey",
        "parameters": [
          {
            "in": "path",
            "name": "key_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "A personal API key (`api_key_not_allowed`: keys are managed from a signed-in session only), or an account pending deletion (`account_pending_deletion`)"
          },
          "404": {
            "description": "`key_not_found`: the caller has no key with this id (an id over 200 characters names none)"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Revoke an API Key",
        "tags": [
          "APIKeys"
        ]
      }
    },
    "/v1/service/api-keys/{key_id}/reissue": {
      "post": {
        "description": "Generates a new secret for the specified API Key and returns it.",
        "operationId": "reissueKey",
        "parameters": [
          {
            "in": "path",
            "name": "key_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyCreateResponse"
                }
              }
            },
            "description": "New token issued"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "A personal API key (`api_key_not_allowed`: keys are managed from a signed-in session only), or an account pending deletion (`account_pending_deletion`)"
          },
          "404": {
            "description": "`key_not_found`: the caller has no key with this id (an id over 200 characters names none); `key_revoked`: the key is revoked (create a new one)"
          },
          "409": {
            "description": "`key_changed`: another reissue of this key happened at the same moment; its secret is the valid one and this request issued none (reload the keys)"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Re-issue a API Key (generate a new secret)",
        "tags": [
          "APIKeys"
        ]
      }
    },
    "/v1/user/notification-preferences": {
      "get": {
        "description": "Retrieve the authenticated user's notification preferences",
        "operationId": "getNotificationPreferences",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserNotificationPreferences"
                }
              }
            },
            "description": "User notification preferences"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserNotificationPreferences"
                }
              }
            },
            "description": "User preferences not found (returns default preferences)"
          },
          "500": {
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get user notification preferences",
        "tags": [
          "User Notification Preferences"
        ]
      },
      "patch": {
        "description": "Update the order, delay timing and enabled state of the verified phone and SMS numbers. Only the sections sent are written: a number left out of the body is kept as it is. E-mail is mandatory in the body and fixed: order 1, enabled, delay 0. The answer is what is stored.",
        "operationId": "updateNotificationPreferences",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateNotificationPreferencesRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateNotificationPreferencesResponse"
                }
              }
            },
            "description": "Notification preferences updated successfully"
          },
          "400": {
            "description": "`invalid_json` / `validation_error`: the body is no JSON object, or `validation_errors` lists what is wrong (an e-mail order other than 1, an e-mail delay other than 0 or e-mail disabled; an order that is no integer from 1 to 1000, a delay or retry count that is no integer in its range; a section that is no object; two methods at one order). `phone_not_verified` / `sms_not_verified`: only a verified number is configured here."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "`preferences_changed`: a number in the request was removed or replaced while saving; nothing was written. Reload the notification methods and try again."
          },
          "500": {
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Update notification preferences order and timing",
        "tags": [
          "User Notification Preferences"
        ]
      }
    },
    "/v1/user/notification-preferences/phone": {
      "delete": {
        "description": "Remove phone number from notification preferences",
        "operationId": "removePhoneNumber",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RemoveMethodResponse"
                }
              }
            },
            "description": "Phone number removed successfully"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Phone number not configured"
          },
          "500": {
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Remove phone number",
        "tags": [
          "User Notification Preferences"
        ]
      },
      "post": {
        "description": "Add a phone number and trigger voice verification call",
        "operationId": "addPhoneNumber",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddPhoneMethodRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddPhoneMethodResponse"
                }
              }
            },
            "description": "Phone number added and verification call initiated"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`INVALID_JSON`: the body is not valid JSON. `INVALID_PHONE_FORMAT`: `phone_number` is missing or is no E.164 string. `VALIDATION_ERROR`: the number could not be saved."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`number_already_verified`: the number is already the verified number of\nthis method; nothing is sent or counted. `too_many_numbers`: the number\nwould make more than 2 verified numbers across the phone and SMS methods\n(`verified_numbers`); the method's own verified number is not counted,\nsince the new one replaces it once verified.\n"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitError"
                }
              }
            },
            "description": "`verification_rate_limited`: the verification-call throttle (60 s\nbetween calls, 5 per 24 h, 10 per 7 days per user; `reason` is\n`RATE_LIMIT_COOLDOWN`, `RATE_LIMIT_DAILY` or `RATE_LIMIT_WEEKLY`). Retry\nafter `Retry-After` seconds.\n",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "`INTERNAL_ERROR`: the number could not be added; no call was placed after the error."
          },
          "502": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`code_not_sent`: the call could not be placed. The number is saved, unverified; ask for a new code after the 60 s wait (the attempt counts toward the limits)."
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`verification_paused`: phone calls are paused for a moment; nothing is stored, called or counted. Try again later. `verification_unavailable`: the request could not finish in time; no call was placed. Retry after `retry_after` seconds (`Retry-After`)."
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Add phone number for voice notifications",
        "tags": [
          "User Notification Preferences"
        ]
      },
      "put": {
        "description": "Verify phone number with 6-digit code from voice call",
        "operationId": "verifyPhoneNumber",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VerifyMethodRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerifyMethodResponse"
                }
              }
            },
            "description": "Phone number verified successfully"
          },
          "400": {
            "description": "`INVALID_JSON`: the body is not valid JSON. `INVALID_CODE_FORMAT`: `verification_code` is missing or is not 6 digits as a string. `INVALID_CODE`: the code does not match (`remaining_attempts`; the attempt is counted before the code is compared)."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No verification pending"
          },
          "409": {
            "description": "`NUMBER_CHANGED`: the phone number was removed, or replaced by another, since the code was sent; nothing is verified. Add the number again."
          },
          "410": {
            "description": "Verification code expired"
          },
          "429": {
            "description": "`TOO_MANY_ATTEMPTS`: three wrong codes ended this verification; request a new code."
          },
          "500": {
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Verify phone number",
        "tags": [
          "User Notification Preferences"
        ]
      }
    },
    "/v1/user/notification-preferences/reorder": {
      "put": {
        "description": "Reorder all notification methods (email, push devices, sms, voice) at once. Email must always be order 1. Of two lists saved at once, the later one wins on every item, never a mix. An sms or voice entry the account does not have (also one removed meanwhile) is a 409 and nothing is saved. A push device that is no longer registered is skipped. A failure part-way (a 500) can leave some push devices in their previous order: sending the list again completes it.",
        "operationId": "reorderNotificationMethods",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReorderNotificationMethodsRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            },
            "description": "Notification methods reordered successfully"
          },
          "400": {
            "description": "`INVALID_JSON`: the body does not parse or is no JSON object. `VALIDATION_ERROR`: the list is not valid, `details` says why (an entry that is no object or of no known type, email missing or not at order 1, an order that is no integer from 1 to 1000, an order or a method listed twice, a push entry without a `device_id` string, more than 100 entries, a `delay_seconds` that is no integer from 0 to 900 or an e-mail `delay_seconds` other than 0, a `retry_count` out of range)."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "`METHOD_NOT_FOUND`: an sms or voice entry names a number the account does not have (`details` says which); nothing was saved. `REORDER_CONFLICT`: the methods kept changing while the list was saved; nothing was saved. Reload the notification methods and try again."
          },
          "500": {
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Reorder notification methods",
        "tags": [
          "User Notification Preferences"
        ]
      }
    },
    "/v1/user/notification-preferences/sms": {
      "delete": {
        "description": "Remove SMS number from notification preferences",
        "operationId": "removeSmsNumber",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RemoveMethodResponse"
                }
              }
            },
            "description": "SMS number removed successfully"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "SMS number not configured"
          },
          "500": {
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Remove SMS number",
        "tags": [
          "User Notification Preferences"
        ]
      },
      "post": {
        "description": "Add an SMS number and trigger SMS verification",
        "operationId": "addSmsNumber",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddSmsMethodRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddSmsMethodResponse"
                }
              }
            },
            "description": "SMS number added and verification code sent"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`INVALID_JSON`: the body is not valid JSON. `INVALID_PHONE_FORMAT`: `phone_number` is missing or is no E.164 string. `sms_not_available`: a text cannot reach this number yet (+1 numbers); nothing is stored, sent or counted, and the same number can be added as a phone number, verified by a call. `VALIDATION_ERROR`: the number could not be saved."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`number_already_verified`: the number is already the verified number of\nthis method; nothing is sent or counted. `too_many_numbers`: the number\nwould make more than 2 verified numbers across the phone and SMS methods\n(`verified_numbers`); the method's own verified number is not counted,\nsince the new one replaces it once verified.\n"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The verification-code throttle, 60 s between codes, 5 per 24 hours and 10 per 7 days per user and method: `error` is `RATE_LIMIT_COOLDOWN`, `RATE_LIMIT_DAILY` or `RATE_LIMIT_WEEKLY`, with `message` and `retry_after`. Retry after `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "`INTERNAL_ERROR`: the number could not be added; no text was sent after the error."
          },
          "502": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`code_not_sent`: the text could not be sent. The number is saved, unverified; ask for a new code after the 60 s wait (the attempt counts toward the limits)."
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "`verification_paused`: text messages are paused for a moment; nothing is stored, sent or counted. Try again later. `verification_unavailable`: the request could not finish in time; no text was sent. Retry after `retry_after` seconds (`Retry-After`)."
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Add SMS number",
        "tags": [
          "User Notification Preferences"
        ]
      },
      "put": {
        "description": "Verify SMS number with 6-digit code from SMS",
        "operationId": "verifySmsNumber",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VerifyMethodRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerifyMethodResponse"
                }
              }
            },
            "description": "SMS number verified successfully"
          },
          "400": {
            "description": "`INVALID_JSON`, `INVALID_CODE_FORMAT`, or `INVALID_CODE`: the code does not match (`remaining_attempts`; the attempt is counted before the code is compared)."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No verification pending"
          },
          "409": {
            "description": "`NUMBER_CHANGED`: the SMS number was removed, or replaced by another, since the code was sent; nothing is verified. Add the number again."
          },
          "410": {
            "description": "Verification code expired"
          },
          "429": {
            "description": "`TOO_MANY_ATTEMPTS`: three wrong codes ended this verification; request a new code."
          },
          "500": {
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Verify SMS number",
        "tags": [
          "User Notification Preferences"
        ]
      }
    },
    "/v1/user/preferences": {
      "get": {
        "description": "Small, user-scoped UI preferences that follow the user across devices (currently the guided product tour state). Sections the user never saved are returned as null.\n",
        "operationId": "getUserPreferences",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserPreferences"
                }
              }
            },
            "description": "User preferences"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Get the authenticated user's UI preferences",
        "tags": [
          "Users"
        ]
      },
      "put": {
        "description": "Merges the given sections into the stored preferences. Sections not present in the body are left untouched; a section set to null is removed. Unknown sections are rejected.\n",
        "operationId": "updateUserPreferences",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateUserPreferencesRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserPreferences"
                }
              }
            },
            "description": "Updated user preferences"
          },
          "400": {
            "description": "Validation error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "description": "Server Error"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Update the authenticated user's UI preferences",
        "tags": [
          "Users"
        ]
      }
    },
    "/v1/users": {
      "get": {
        "description": "Searches the people the caller already shares an organization with: the members of every organization whose members list the caller can open (GET /v1/organizations/{organizationName}/members). An organization frozen for an unpaid balance, one scheduled for deletion with its owner's account (its owner excepted) and one where the caller's seat is suspended or cancelled are left out. The caller is a result like any other member; an account pending deletion is not. Nobody else is ever matched: an address that belongs to no one the caller shares an organization with answers an empty list whether or not it has an IgniteOps account, so the search never tells a stranger that an address has an account. Such a person joins through an invitation (POST /v1/organizations/{organizationName}/members/invitations). A person matches when their name or e-mail address contains the search string, ignoring case and accents; the exact address comes first, then the names, name words and addresses that start with it, then the rest. Names and addresses are returned in full, as the members lists show them.\n",
        "operationId": "searchUsers",
        "parameters": [
          {
            "description": "Part of a name or of an e-mail address, at least 3 characters once trimmed. A `+` sent unencoded in an address arrives as a space and is read as a `+` again.\n",
            "in": "query",
            "name": "q",
            "required": true,
            "schema": {
              "maxLength": 254,
              "minLength": 3,
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "pagination.next_token of the previous page",
            "in": "query",
            "name": "next_token",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserSearchResponse"
                }
              }
            },
            "description": "The matching people, best match first"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "An integration key, or an account pending deletion (`account_pending_deletion`)"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Find people who share an organization with the caller",
        "tags": [
          "Users"
        ]
      }
    },
    "/v1/users/me": {
      "delete": {
        "description": "Marks the account pending deletion: the user is signed out everywhere, their personal API keys are revoked, they leave every organization they do not own, and every organization they own is scheduled for deletion with them (members locked out, nothing paged, nothing billed). Thirty days later everything is purged; until then signing in and calling `POST /v1/users/me/restore` brings the account and the owned organizations back. `confirm` must equal the account's e-mail address (trimmed, case-insensitive), else `400 confirmation_mismatch`. Idempotent: a second call on a pending account answers `202` with the same state and sends nothing again. Session users only (`api_key_not_allowed` for a personal API key).\n",
        "operationId": "deleteMyAccount",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeleteAccountRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteAccountResponse"
                }
              }
            },
            "description": "The account is pending deletion"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfirmationMismatchError"
                }
              }
            },
            "description": "`confirmation_mismatch` when `confirm` is not the account's e-mail address; `validation_error` for a reason over 500 characters or an unknown `via`; `invalid_json` for a body that is not a JSON object.\n"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not complete this request right now. Retry after `Retry-After` seconds; the change may have been made, so read the current state before repeating it.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Delete the signed-in user's account (30-day grace period)",
        "tags": [
          "Account"
        ]
      },
      "get": {
        "description": "The account's deletion state (`account.status` is `active` or `pending_deletion`, with the request and purge instants and the grace period in days), the organizations the user owns (deleted with the account, each with its member count and, once scheduled, its `deletion_scheduled_at`) and the user's other memberships (left when the deletion is requested). Session users only: a personal API key answers `403 access_denied` with reason `api_key_not_allowed`. A pending account may still call this route.\n",
        "operationId": "getMe",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserMe"
                }
              }
            },
            "description": "The account"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "The signed-in user's account and what a deletion would take with it",
        "tags": [
          "Account"
        ]
      }
    },
    "/v1/users/me/restore": {
      "post": {
        "description": "Allowed only while the account is pending deletion (`409 not_pending_deletion` otherwise). Every organization still scheduled for deletion by this user is un-scheduled and comes back exactly as it was; the account is active again. The personal API keys revoked at the request stay revoked and the organizations left then are not rejoined (the response says so). Session users only.\n",
        "operationId": "restoreMyAccount",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RestoreAccountResponse"
                }
              }
            },
            "description": "The account is active again"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotPendingDeletionError"
                }
              }
            },
            "description": "`not_pending_deletion`: the account is not pending deletion, nothing to restore; `purge_in_progress`: its grace period is over and the purge of the account has started, so it cannot be restored any more"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrimaryRegionUnavailableError"
                }
              }
            },
            "description": "`primary_region_unavailable`: the service could not complete this request right now. Retry after `Retry-After` seconds; the change may have been made, so read the current state before repeating it.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (the same value as `retry_after` in the body)",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "IgniteOpsAuth": []
          }
        ],
        "summary": "Cancel the deletion of the signed-in user's account",
        "tags": [
          "Account"
        ]
      }
    }
  },
  "servers": [
    {
      "description": "Production",
      "url": "https://api.igniteops.io"
    }
  ],
  "tags": [
    {
      "description": "Health-check and miscellaneous public endpoints",
      "name": "Status"
    },
    {
      "description": "Public authentication configuration and discovery endpoints",
      "name": "Auth"
    },
    {
      "description": "User account and profile operations",
      "name": "Users"
    },
    {
      "description": "Organization lifecycle and membership operations",
      "name": "Organizations"
    },
    {
      "description": "Subscription, seat, and payment management for organizations",
      "name": "Billing"
    },
    {
      "description": "API key management for external integrations",
      "name": "APIKeys"
    },
    {
      "description": "Manage user notification settings and verification",
      "name": "User Notification Preferences"
    },
    {
      "description": "Mobile and browser push notification device management",
      "name": "Push Notifications"
    },
    {
      "description": "Alert ingestion, routing and lifecycle",
      "name": "Incident Management - Alerts"
    },
    {
      "description": "Team management and membership for incident response",
      "name": "Incident Management - Teams"
    },
    {
      "description": "Routing rules for matching alerts to teams and recipients",
      "name": "Incident Management - Routing Rules"
    },
    {
      "description": "Escalation policies that govern how unacknowledged alerts are escalated",
      "name": "Incident Management - Escalation Policies"
    },
    {
      "description": "On-call schedules and rotations",
      "name": "Incident Management - Schedules"
    },
    {
      "description": "Integration keys for external systems",
      "name": "Incident Management - Integration Keys"
    },
    {
      "description": "Deployments recorded by a team's pipeline or by hand, the change timeline next to the alerts",
      "name": "Incident Management - Deployments"
    },
    {
      "description": "Aggregate metrics for teams and organizations (MTTA, MTTR, frequency)",
      "name": "Incident Management - Metrics"
    },
    {
      "description": "Threaded comments on incident alerts",
      "name": "Comments"
    },
    {
      "description": "AI-generated incident summaries, root-cause hints, and recommendations",
      "name": "AI Insights"
    },
    {
      "description": "Inbound webhook endpoints invoked by integrations and external systems",
      "name": "Webhooks"
    }
  ]
}