{
  "openapi": "3.1.0",
  "info": {
    "title": "Galaxy Public API",
    "version": "1.3.0",
    "summary": "Scoped automation for Galaxy applications",
    "description": "# Getting started\n\nThe Galaxy Public API lets Professional and Custom Meteor applications automate a deliberately small set of operational actions without exposing Galaxy internal APIs.\n\n## Authentication\n\nCreate and manage Public API credentials from **Public API Access** in the Galaxy UI. Organization owners and admins may manage credentials; developers, viewers, and impersonated sessions are denied. Credential management is not part of the public API contract. Secret tokens are displayed only when created or rotated, expire after 90 days by default, and may be configured for at most 365 days. Rotation keeps the previous token valid for 15 minutes; revocation is immediate.\n\nAutomation endpoints under `/public/v1` accept only a public API credential in `Authorization: Bearer <token>`. Galaxy OAuth access tokens are not accepted on this route surface.\n\n## Scopes and application access\n\nEvery request must have both the required scope and an explicit application allowlist entry. Unlisted and cross-owner applications return `404` to avoid disclosing their existence.\n\n`public:apps:read` lists allowlisted applications and reads CPU, memory, request, and Meteor connection metrics. `public:apps:write` starts and stops allowlisted applications and requires `public:apps:read`. Start and stop match dashboard lifecycle (status and previous container count); they are not scale-to-zero. `public:domains:read` reads reduced metadata for all application domains. `public:domains:write` creates, deletes, and re-verifies only custom domains that use Galaxy-managed TLS and requires `public:domains:read`. `public:scaling:read` reads container counts, container specifications, and schedules. `public:scaling:write` changes scaling state and manages one-time or daily recurring schedules and requires `public:scaling:read`.\n\n## Safe retries\n\nDomain mutations and scaling-schedule mutations require an `Idempotency-Key` header. Reusing a key with the same operation and payload replays the completed response for 24 hours. Reusing it with a different operation or payload returns `409`.\n\n## Request IDs and rate limits\n\nAutomation responses include `X-Request-ID`. Send your own visible-ASCII identifier, up to 128 characters, to correlate requests; otherwise Galaxy generates one. Default limits are 120 requests per minute per credential and 600 requests per minute per owner. A `429` response includes `Retry-After`.\n\n## Interactive requests\n\nUse **Test Request** on an operation to call the same Galaxy environment that serves this documentation. Entered credentials remain in memory only and are cleared when the page reloads.",
    "license": {
      "name": "Proprietary",
      "identifier": "LicenseRef-Proprietary"
    }
  },
  "servers": [
    {
      "url": "/",
      "description": "Current Galaxy environment"
    }
  ],
  "tags": [
    {
      "name": "Applications",
      "description": "List the Professional and Custom Meteor applications explicitly allowlisted for a credential."
    },
    {
      "name": "Lifecycle",
      "description": "Start a stopped application or stop a running one. Stop saves the current container count and sets status to stopped. Start restores that count and sets status to running. This is not the same as setting containerCount to 0."
    },
    {
      "name": "Metrics",
      "description": "Read CPU, memory, HTTP request, and Meteor connection time series for an allowlisted application. The same Prometheus data powers the Galaxy dashboard."
    },
    {
      "name": "Domains",
      "description": "Read reduced metadata for every application domain. Mutations are limited to custom domains that use Galaxy-managed TLS."
    },
    {
      "name": "Scaling",
      "description": "Read and change the desired container count or container specification."
    },
    {
      "name": "Scaling schedules",
      "description": "Create and cancel one-time or daily recurring container-count changes. Recurring schedules use an IANA timezone; daylight-saving transition behavior is defined by the recurrence schema."
    }
  ],
  "paths": {
    "/public/v1/apps": {
      "get": {
        "operationId": "listPublicApps",
        "summary": "List allowlisted applications",
        "description": "Returns only active Professional or Custom Meteor applications explicitly assigned to this credential. Applications that become ineligible are omitted.",
        "tags": [
          "Applications"
        ],
        "security": [
          {
            "PublicApiCredential": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Allowlisted applications",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicApiAppList"
                },
                "example": {
                  "apps": [
                    {
                      "id": "57c8b005-0540-4396-b1a3-9fd2d627b5d5",
                      "name": "checkout-api",
                      "status": "running",
                      "serviceType": "meteor",
                      "regionCode": "us"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/PublicUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PublicForbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/public/v1/apps/{id}/start": {
      "post": {
        "operationId": "startPublicApp",
        "summary": "Start a stopped application",
        "description": "Starts an allowlisted stopped application using the same lifecycle as the Galaxy dashboard. Restores `previousContainerCount` (or 1 when none is saved), sets status to `running`, and scales Kubernetes accordingly. Requires `public:apps:write`. This is not a scale-from-zero substitute for Start: scaling to 0 leaves status `running` and does not save the previous count.",
        "tags": [
          "Lifecycle"
        ],
        "security": [
          {
            "PublicApiCredential": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AppId"
          },
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "204": {
            "description": "Application started",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PublicBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/PublicUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PublicForbidden"
          },
          "404": {
            "$ref": "#/components/responses/PublicNotFound"
          },
          "422": {
            "$ref": "#/components/responses/PublicValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/public/v1/apps/{id}/stop": {
      "post": {
        "operationId": "stopPublicApp",
        "summary": "Stop a running application",
        "description": "Stops an allowlisted running application using the same lifecycle as the Galaxy dashboard. Saves the current container count as `previousContainerCount`, sets status to `stopped`, and scales Kubernetes to 0 replicas. Requires `public:apps:write`. Setting `containerCount` to 0 through the scaling API is not Stop.",
        "tags": [
          "Lifecycle"
        ],
        "security": [
          {
            "PublicApiCredential": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AppId"
          },
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "204": {
            "description": "Application stopped",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PublicBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/PublicUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PublicForbidden"
          },
          "404": {
            "$ref": "#/components/responses/PublicNotFound"
          },
          "422": {
            "$ref": "#/components/responses/PublicValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/public/v1/apps/{id}/domains": {
      "get": {
        "operationId": "listPublicAppDomains",
        "summary": "List application domains",
        "description": "Returns the reduced public view of every domain for an allowlisted application, including Galaxy subdomains and domains with custom certificates. Only custom domains that use Galaxy-managed TLS can be mutated. Reading requires `public:domains:read`.",
        "tags": [
          "Domains"
        ],
        "security": [
          {
            "PublicApiCredential": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AppId"
          },
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Application domains",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicApiDomainList"
                },
                "example": {
                  "domains": [
                    {
                      "id": "a41aa1e6-0612-4d89-ac44-7e6d3d9cf18f",
                      "domain": "checkout.example.com",
                      "status": "active",
                      "domainType": "custom",
                      "isPrimary": true,
                      "tlsManaged": true,
                      "verifiedAt": "2026-07-29T09:00:00.000Z",
                      "createdAt": "2026-07-29T08:30:00.000Z",
                      "updatedAt": "2026-07-29T09:05:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/PublicUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PublicForbidden"
          },
          "404": {
            "$ref": "#/components/responses/PublicNotFound"
          },
          "422": {
            "$ref": "#/components/responses/PublicValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createPublicAppDomain",
        "summary": "Create a managed-TLS custom domain",
        "description": "Starts creation of a non-wildcard custom domain using Galaxy-managed TLS. Wildcard domains require a custom certificate; custom certificates and Galaxy subdomains are not part of the public contract. Requires `public:domains:write` and an idempotency key.",
        "tags": [
          "Domains"
        ],
        "security": [
          {
            "PublicApiCredential": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AppId"
          },
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePublicApiDomain"
              },
              "example": {
                "domainType": "custom",
                "domain": "checkout.example.com",
                "customCertificate": false
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Domain creation accepted",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicApiDomain"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PublicBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/PublicUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PublicForbidden"
          },
          "404": {
            "$ref": "#/components/responses/PublicNotFound"
          },
          "409": {
            "$ref": "#/components/responses/PublicConflict"
          },
          "422": {
            "$ref": "#/components/responses/PublicValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/public/v1/apps/{id}/domains/{domainId}": {
      "delete": {
        "operationId": "deletePublicAppDomain",
        "summary": "Delete a managed-TLS custom domain",
        "description": "Starts deletion of a custom domain that uses Galaxy-managed TLS. Repeating the same request with the same idempotency key replays the accepted response.",
        "tags": [
          "Domains"
        ],
        "security": [
          {
            "PublicApiCredential": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AppId"
          },
          {
            "$ref": "#/components/parameters/DomainId"
          },
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Domain deletion accepted",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                },
                "example": {
                  "message": "Domain deletion in progress"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PublicBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/PublicUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PublicForbidden"
          },
          "404": {
            "$ref": "#/components/responses/PublicNotFound"
          },
          "409": {
            "$ref": "#/components/responses/PublicConflict"
          },
          "422": {
            "$ref": "#/components/responses/PublicValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/public/v1/apps/{id}/domains/{domainId}/verify": {
      "post": {
        "operationId": "reverifyPublicAppDomain",
        "summary": "Re-verify a managed-TLS custom domain",
        "description": "Starts another DNS verification attempt for a custom domain using Galaxy-managed TLS. Accepts domains that failed verification, and managed-TLS domains stalled while the certificate was being provisioned.",
        "tags": [
          "Domains"
        ],
        "security": [
          {
            "PublicApiCredential": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AppId"
          },
          {
            "$ref": "#/components/parameters/DomainId"
          },
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Domain verification accepted",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                },
                "example": {
                  "message": "Domain verification initiated"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PublicBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/PublicUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PublicForbidden"
          },
          "404": {
            "$ref": "#/components/responses/PublicNotFound"
          },
          "409": {
            "$ref": "#/components/responses/PublicConflict"
          },
          "422": {
            "$ref": "#/components/responses/PublicValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/public/v1/apps/{id}/containers": {
      "get": {
        "operationId": "getPublicAppContainerCount",
        "summary": "Get desired container count",
        "description": "Returns the application's desired container count. Requires `public:scaling:read`.",
        "tags": [
          "Scaling"
        ],
        "security": [
          {
            "PublicApiCredential": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AppId"
          },
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Desired container count",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContainerCount"
                },
                "example": {
                  "containerCount": 4
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/PublicUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PublicForbidden"
          },
          "404": {
            "$ref": "#/components/responses/PublicNotFound"
          },
          "422": {
            "$ref": "#/components/responses/PublicValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "put": {
        "operationId": "updatePublicAppContainerCount",
        "summary": "Set desired container count",
        "description": "Immediately changes the application's desired container count. The application's plan and scaling rules may impose a lower limit than the contract maximum. Sending the current value is a no-op.",
        "tags": [
          "Scaling"
        ],
        "security": [
          {
            "PublicApiCredential": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AppId"
          },
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateContainerCount"
              },
              "example": {
                "containerCount": 4
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Desired container count updated",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PublicBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/PublicUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PublicForbidden"
          },
          "404": {
            "$ref": "#/components/responses/PublicNotFound"
          },
          "422": {
            "$ref": "#/components/responses/PublicValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/public/v1/apps/{id}/scaling-schedules": {
      "get": {
        "operationId": "listPublicAppScalingSchedules",
        "summary": "List recent scaling schedules",
        "description": "Returns up to 100 schedules: every pending or executing schedule, plus schedules updated during the last 30 days. Requires `public:scaling:read`.",
        "tags": [
          "Scaling schedules"
        ],
        "security": [
          {
            "PublicApiCredential": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AppId"
          },
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Recent scaling schedules",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicApiScalingScheduleList"
                },
                "example": {
                  "schedules": [
                    {
                      "id": "80dc4eb2-a266-4867-ad7e-c2e36fe69043",
                      "appId": "57c8b005-0540-4396-b1a3-9fd2d627b5d5",
                      "containerCount": 6,
                      "executeAt": "2026-07-31T18:00:00.000Z",
                      "recurrence": null,
                      "status": "pending",
                      "attemptCount": 0,
                      "lastError": null,
                      "lastExecutedAt": null,
                      "completedAt": null,
                      "canceledAt": null,
                      "failedAt": null,
                      "createdAt": "2026-07-30T14:30:00.000Z",
                      "updatedAt": "2026-07-30T14:30:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/PublicUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PublicForbidden"
          },
          "404": {
            "$ref": "#/components/responses/PublicNotFound"
          },
          "422": {
            "$ref": "#/components/responses/PublicValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createPublicAppScalingSchedule",
        "summary": "Create a scaling schedule",
        "description": "Schedules either one future desired-container-count change or a daily recurring change at a local time in an IANA timezone. Provide exactly one of `executeAt` or `recurrence`. The requested count must pass the application's current plan and scaling checks before the schedule is stored.",
        "tags": [
          "Scaling schedules"
        ],
        "security": [
          {
            "PublicApiCredential": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AppId"
          },
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePublicApiScalingSchedule"
              },
              "examples": {
                "oneTime": {
                  "summary": "One-time event",
                  "value": {
                    "containerCount": 6,
                    "executeAt": "2026-07-31T18:00:00.000Z"
                  }
                },
                "daily": {
                  "summary": "Daily scale-up",
                  "value": {
                    "containerCount": 6,
                    "recurrence": {
                      "frequency": "daily",
                      "time": "09:00",
                      "timezone": "America/New_York"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Scaling schedule created",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicApiScalingSchedule"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PublicBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/PublicUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PublicForbidden"
          },
          "404": {
            "$ref": "#/components/responses/PublicNotFound"
          },
          "409": {
            "$ref": "#/components/responses/PublicConflict"
          },
          "422": {
            "$ref": "#/components/responses/PublicValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/public/v1/apps/{id}/scaling-schedules/{scheduleId}": {
      "delete": {
        "operationId": "cancelPublicAppScalingSchedule",
        "summary": "Cancel a scaling schedule",
        "description": "Cancels a pending one-time schedule or all future occurrences of a daily schedule. Completed, executing, or failed schedules return `409`. If an occurrence is executing, retry after it returns to pending. Canceling an already canceled schedule is idempotent.",
        "tags": [
          "Scaling schedules"
        ],
        "security": [
          {
            "PublicApiCredential": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AppId"
          },
          {
            "$ref": "#/components/parameters/ScheduleId"
          },
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Canceled scaling schedule",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicApiScalingSchedule"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PublicBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/PublicUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PublicForbidden"
          },
          "404": {
            "$ref": "#/components/responses/PublicNotFound"
          },
          "409": {
            "$ref": "#/components/responses/PublicConflict"
          },
          "422": {
            "$ref": "#/components/responses/PublicValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/public/v1/apps/{id}/spec": {
      "get": {
        "operationId": "getPublicAppContainerSpec",
        "summary": "Get container specification",
        "description": "Returns the application's current container specification. Requires `public:scaling:read`.",
        "tags": [
          "Scaling"
        ],
        "security": [
          {
            "PublicApiCredential": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AppId"
          },
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Current container specification",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContainerSpecResponse"
                },
                "example": {
                  "spec": {
                    "name": "standard",
                    "displayName": "Standard",
                    "cpuCores": 1,
                    "cpuMillicores": 1000,
                    "memoryMb": 1024,
                    "serviceType": "meteor"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/PublicUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PublicForbidden"
          },
          "404": {
            "$ref": "#/components/responses/PublicNotFound"
          },
          "422": {
            "$ref": "#/components/responses/PublicValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "put": {
        "operationId": "updatePublicAppContainerSpec",
        "summary": "Set container specification",
        "description": "Immediately changes the application's container specification. Use the container-spec collection to discover valid values for allowlisted application types. Sending the current value is a no-op.",
        "tags": [
          "Scaling"
        ],
        "security": [
          {
            "PublicApiCredential": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AppId"
          },
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateContainerSpec"
              },
              "example": {
                "containerSpec": "double"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Container specification updated",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/PublicBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/PublicUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PublicForbidden"
          },
          "404": {
            "$ref": "#/components/responses/PublicNotFound"
          },
          "422": {
            "$ref": "#/components/responses/PublicValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/public/v1/apps/{id}/metrics": {
      "get": {
        "operationId": "getPublicAppMetrics",
        "summary": "Get application metrics",
        "description": "Returns CPU and memory usage plus limits, HTTP requests, and Meteor backend connections for an allowlisted application. Requires `public:apps:read`.\n\n`timeRange` selects the Prometheus lookback window (`5m` through `30d`). `aggregation` defaults to `pod`, which keys CPU and memory samples by short pod suffix; `total` averages those series across pods. Request, connection, and limit series are always totals.\n\nAn application scaled to 0 containers still returns `200`. `date` is a UTC ISO-8601 timestamp. Empty series with `degraded: false` means Prometheus had no samples in the window; historical samples from earlier in the window are returned when present. `degraded: true` means metrics collection failed (query error or unavailable Prometheus) and empty series must not be treated as zero usage. CPU is decimal cores. Memory is working-set bytes. Memory limits use SI megabytes from the container specification (`memoryMb * 1_000_000`).",
        "tags": [
          "Metrics"
        ],
        "security": [
          {
            "PublicApiCredential": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AppId"
          },
          {
            "$ref": "#/components/parameters/MetricsTimeRange"
          },
          {
            "$ref": "#/components/parameters/MetricsAggregation"
          },
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Application metrics",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicApiAppMetrics"
                },
                "example": {
                  "timeRange": "5m",
                  "aggregation": "pod",
                  "degraded": false,
                  "cpu": [
                    {
                      "date": "2023-11-14T22:13:20.000Z",
                      "7s45v": 0.12
                    }
                  ],
                  "cpuLimit": [
                    {
                      "date": "2023-11-14T22:13:20.000Z",
                      "total": 1
                    }
                  ],
                  "memoryUsage": [
                    {
                      "date": "2023-11-14T22:13:20.000Z",
                      "7s45v": 268435456
                    }
                  ],
                  "memoryLimit": [
                    {
                      "date": "2023-11-14T22:13:20.000Z",
                      "total": 1024000000
                    }
                  ],
                  "requests": [
                    {
                      "date": "2023-11-14T22:13:20.000Z",
                      "total": 42
                    }
                  ],
                  "connections": [
                    {
                      "date": "2023-11-14T22:13:20.000Z",
                      "total": 8
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/PublicUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PublicForbidden"
          },
          "404": {
            "$ref": "#/components/responses/PublicNotFound"
          },
          "422": {
            "$ref": "#/components/responses/PublicValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/public/v1/container-specs": {
      "get": {
        "operationId": "listPublicContainerSpecs",
        "summary": "List available container specifications",
        "description": "Returns active Meteor container specifications available to the credential allowlist. Requires `public:scaling:read`.",
        "tags": [
          "Scaling"
        ],
        "security": [
          {
            "PublicApiCredential": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Available container specifications",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContainerSpecList"
                },
                "example": {
                  "specs": [
                    {
                      "name": "standard",
                      "displayName": "Standard",
                      "cpuCores": 1,
                      "cpuMillicores": 1000,
                      "memoryMb": 1024,
                      "serviceType": "meteor"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/PublicUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PublicForbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "PublicApiCredential": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "gxy_v1",
        "description": "Scoped public API credential returned once by the create or rotate endpoint. OAuth access tokens are rejected on `/public/v1` routes."
      }
    },
    "parameters": {
      "AppId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Allowlisted Galaxy application identifier",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "DomainId": {
        "name": "domainId",
        "in": "path",
        "required": true,
        "description": "Application domain identifier",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "ScheduleId": {
        "name": "scheduleId",
        "in": "path",
        "required": true,
        "description": "One-time scaling schedule identifier",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "RequestId": {
        "name": "X-Request-ID",
        "in": "header",
        "required": false,
        "description": "Optional visible-ASCII correlation identifier. Galaxy generates a UUID when the value is missing or invalid.",
        "schema": {
          "type": "string",
          "maxLength": 128,
          "example": "worker-20260730-00042"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Visible-ASCII retry key scoped to the credential. Generate a unique value for each logical mutation and reuse it only when retrying the exact same operation and payload. Completed responses are replayable for 24 hours.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128
        }
      },
      "MetricsTimeRange": {
        "name": "timeRange",
        "in": "query",
        "required": true,
        "description": "Prometheus lookback window.",
        "schema": {
          "$ref": "#/components/schemas/PublicApiMetricsTimeRange"
        }
      },
      "MetricsAggregation": {
        "name": "aggregation",
        "in": "query",
        "required": false,
        "description": "Series grouping. Defaults to `pod`.",
        "schema": {
          "$ref": "#/components/schemas/PublicApiMetricsAggregation"
        }
      }
    },
    "headers": {
      "XRequestId": {
        "description": "Correlation identifier supplied by the caller or generated by Galaxy",
        "schema": {
          "type": "string",
          "maxLength": 128,
          "example": "worker-20260730-00042"
        }
      },
      "RetryAfter": {
        "description": "Seconds until the caller should retry",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      }
    },
    "responses": {
      "PublicBadRequest": {
        "description": "The request violates an operation rule, such as a missing idempotency key or an invalid scaling target",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/XRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PublicErrorResponse"
            }
          }
        }
      },
      "PublicUnauthorized": {
        "description": "Missing, invalid, expired, or revoked public API credential",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/XRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PublicErrorResponse"
            },
            "example": {
              "message": "Invalid or expired public API credential",
              "requestId": "worker-20260730-00042"
            }
          }
        }
      },
      "PublicForbidden": {
        "description": "The credential does not contain the required scope",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/XRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PublicErrorResponse"
            },
            "example": {
              "message": "Public API credential requires public:scaling:read",
              "statusCode": 403,
              "code": "E_FORBIDDEN",
              "requestId": "worker-20260730-00042"
            }
          }
        }
      },
      "PublicNotFound": {
        "description": "The application is not allowlisted, belongs to another owner, or the requested resource does not exist in the application context",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/XRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PublicErrorResponse"
            },
            "example": {
              "message": "App not found",
              "statusCode": 404,
              "code": "E_NOT_FOUND",
              "requestId": "worker-20260730-00042"
            }
          }
        }
      },
      "PublicConflict": {
        "description": "The idempotency key conflicts with another request, an equivalent request is still running, or the resource state cannot be changed",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/XRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PublicErrorResponse"
            },
            "example": {
              "message": "Idempotency key was already used with a different operation or payload",
              "statusCode": 409,
              "code": "E_CONFLICT",
              "requestId": "worker-20260730-00042"
            }
          }
        }
      },
      "PublicValidationError": {
        "description": "Request validation failed",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/XRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PublicValidationErrorResponse"
            }
          }
        }
      },
      "RateLimited": {
        "description": "The credential or owner rate limit was exceeded",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/XRequestId"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PublicErrorResponse"
            },
            "example": {
              "message": "Public API rate limit exceeded",
              "statusCode": 429,
              "code": "E_TOO_MANY_REQUESTS",
              "requestId": "worker-20260730-00042"
            }
          }
        }
      }
    },
    "schemas": {
      "PublicApiApp": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "status",
          "serviceType",
          "regionCode"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending_deployment",
              "queued",
              "building",
              "deploying",
              "running",
              "unhealthy",
              "crashed",
              "crash_looping",
              "restarting",
              "stopped",
              "suspended",
              "failed"
            ]
          },
          "serviceType": {
            "type": "string",
            "enum": [
              "meteor"
            ]
          },
          "regionCode": {
            "type": "string",
            "enum": [
              "us",
              "eu",
              "ap",
              "stg"
            ]
          }
        }
      },
      "PublicApiAppList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "apps"
        ],
        "properties": {
          "apps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicApiApp"
            }
          }
        }
      },
      "CreatePublicApiDomain": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "domainType",
          "domain",
          "customCertificate"
        ],
        "properties": {
          "domainType": {
            "type": "string",
            "const": "custom"
          },
          "domain": {
            "type": "string",
            "minLength": 3,
            "maxLength": 253,
            "pattern": "^(?!\\*\\.).+$",
            "description": "Fully qualified non-wildcard custom domain name. Wildcards require a custom certificate and are not supported."
          },
          "customCertificate": {
            "type": "boolean",
            "const": false,
            "description": "The Public API supports only Galaxy-managed TLS"
          }
        }
      },
      "PublicApiDomain": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "domain",
          "status",
          "domainType",
          "isPrimary",
          "tlsManaged",
          "verifiedAt",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "domain": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "verifying",
              "verified",
              "failed",
              "provisioning_cert",
              "cert_expired",
              "activating",
              "active",
              "deactivating",
              "inactive",
              "removing",
              "removed"
            ]
          },
          "domainType": {
            "type": "string",
            "enum": [
              "custom",
              "galaxy_custom"
            ]
          },
          "isPrimary": {
            "type": "boolean"
          },
          "tlsManaged": {
            "type": "boolean"
          },
          "verifiedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PublicApiDomainList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "domains"
        ],
        "properties": {
          "domains": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicApiDomain"
            }
          }
        }
      },
      "PublicApiMetricsTimeRange": {
        "type": "string",
        "enum": [
          "5m",
          "15m",
          "30m",
          "1h",
          "3h",
          "6h",
          "12h",
          "24h",
          "2d",
          "7d",
          "30d"
        ]
      },
      "PublicApiMetricsAggregation": {
        "type": "string",
        "enum": [
          "pod",
          "total"
        ]
      },
      "PublicApiMetricPoint": {
        "type": "object",
        "required": [
          "date"
        ],
        "properties": {
          "date": {
            "type": "string",
            "format": "date-time",
            "description": "UTC ISO-8601 sample timestamp"
          }
        },
        "additionalProperties": {
          "type": [
            "number",
            "null"
          ]
        }
      },
      "PublicApiAppMetrics": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "timeRange",
          "aggregation",
          "degraded",
          "cpu",
          "cpuLimit",
          "memoryUsage",
          "memoryLimit",
          "requests",
          "connections"
        ],
        "properties": {
          "timeRange": {
            "$ref": "#/components/schemas/PublicApiMetricsTimeRange"
          },
          "aggregation": {
            "$ref": "#/components/schemas/PublicApiMetricsAggregation"
          },
          "degraded": {
            "type": "boolean",
            "description": "True when Prometheus queries failed or metrics collection is unavailable. Empty series with degraded false means no samples in the window."
          },
          "cpu": {
            "type": "array",
            "description": "CPU usage in decimal cores. Per-pod keys are short pod suffixes when aggregation is `pod`; otherwise `total`.",
            "items": {
              "$ref": "#/components/schemas/PublicApiMetricPoint"
            }
          },
          "cpuLimit": {
            "type": "array",
            "description": "Per-container CPU limit in decimal cores from the container specification. Always keyed as `total`.",
            "items": {
              "$ref": "#/components/schemas/PublicApiMetricPoint"
            }
          },
          "memoryUsage": {
            "type": "array",
            "description": "Memory working-set usage in bytes. Per-pod keys are short pod suffixes when aggregation is `pod`; otherwise `total`.",
            "items": {
              "$ref": "#/components/schemas/PublicApiMetricPoint"
            }
          },
          "memoryLimit": {
            "type": "array",
            "description": "Per-container memory limit in bytes from the container specification (`memoryMb * 1_000_000`). Always keyed as `total`.",
            "items": {
              "$ref": "#/components/schemas/PublicApiMetricPoint"
            }
          },
          "requests": {
            "type": "array",
            "description": "HTTP requests observed at HAProxy. Always keyed as `total`.",
            "items": {
              "$ref": "#/components/schemas/PublicApiMetricPoint"
            }
          },
          "connections": {
            "type": "array",
            "description": "Active Meteor backend connections. Always keyed as `total`. Empty when Prometheus has no samples.",
            "items": {
              "$ref": "#/components/schemas/PublicApiMetricPoint"
            }
          }
        }
      },
      "ContainerCount": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "containerCount"
        ],
        "properties": {
          "containerCount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          }
        }
      },
      "UpdateContainerCount": {
        "$ref": "#/components/schemas/ContainerCount"
      },
      "ContainerSpecName": {
        "type": "string",
        "enum": [
          "tiny",
          "compact",
          "standard",
          "double",
          "quad",
          "octa",
          "dozen"
        ]
      },
      "UpdateContainerSpec": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "containerSpec"
        ],
        "properties": {
          "containerSpec": {
            "$ref": "#/components/schemas/ContainerSpecName"
          }
        }
      },
      "ContainerSpec": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "displayName",
          "cpuCores",
          "cpuMillicores",
          "memoryMb",
          "serviceType"
        ],
        "properties": {
          "name": {
            "$ref": "#/components/schemas/ContainerSpecName"
          },
          "displayName": {
            "type": "string"
          },
          "cpuCores": {
            "type": "number",
            "minimum": 0
          },
          "cpuMillicores": {
            "type": "number",
            "minimum": 0
          },
          "memoryMb": {
            "type": "number",
            "minimum": 0
          },
          "serviceType": {
            "type": "string",
            "enum": [
              "meteor"
            ]
          }
        }
      },
      "ContainerSpecResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "spec"
        ],
        "properties": {
          "spec": {
            "$ref": "#/components/schemas/ContainerSpec"
          }
        }
      },
      "ContainerSpecList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "specs"
        ],
        "properties": {
          "specs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ContainerSpec"
            }
          }
        }
      },
      "CreatePublicApiScalingSchedule": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/CreateOneTimePublicApiScalingSchedule"
          },
          {
            "$ref": "#/components/schemas/CreateDailyPublicApiScalingSchedule"
          }
        ]
      },
      "CreateOneTimePublicApiScalingSchedule": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "containerCount",
          "executeAt"
        ],
        "properties": {
          "containerCount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "executeAt": {
            "type": "string",
            "format": "date-time",
            "description": "Future execution time. Galaxy stores and returns the value in UTC."
          }
        }
      },
      "CreateDailyPublicApiScalingSchedule": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "containerCount",
          "recurrence"
        ],
        "properties": {
          "containerCount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "recurrence": {
            "$ref": "#/components/schemas/PublicApiScalingScheduleRecurrence"
          }
        }
      },
      "PublicApiScalingScheduleRecurrence": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "frequency",
          "time",
          "timezone"
        ],
        "properties": {
          "frequency": {
            "type": "string",
            "const": "daily"
          },
          "time": {
            "type": "string",
            "pattern": "^(?:[01]\\d|2[0-3]):[0-5]\\d$",
            "description": "Daily local execution time in 24-hour HH:mm format"
          },
          "timezone": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "example": "America/New_York",
            "description": "IANA timezone used to track local time. A nonexistent spring-forward time shifts forward by the DST gap; a repeated fall-back time executes once at its first occurrence."
          }
        }
      },
      "PublicApiScalingSchedule": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "appId",
          "containerCount",
          "executeAt",
          "recurrence",
          "status",
          "attemptCount",
          "lastError",
          "lastExecutedAt",
          "completedAt",
          "canceledAt",
          "failedAt",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "appId": {
            "type": "string",
            "format": "uuid"
          },
          "containerCount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "executeAt": {
            "type": "string",
            "format": "date-time",
            "description": "Next execution time for a recurring schedule, or the only execution time for a one-time schedule"
          },
          "recurrence": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PublicApiScalingScheduleRecurrence"
              },
              {
                "type": "null"
              }
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "executing",
              "completed",
              "canceled",
              "failed"
            ]
          },
          "attemptCount": {
            "type": "integer",
            "minimum": 0
          },
          "lastError": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastExecutedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Most recent successful execution, if any"
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "canceledAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "failedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PublicApiScalingScheduleList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schedules"
        ],
        "properties": {
          "schedules": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicApiScalingSchedule"
            }
          }
        }
      },
      "Message": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string"
          },
          "statusCode": {
            "type": "integer",
            "minimum": 400,
            "maximum": 599
          },
          "code": {
            "type": "string"
          },
          "requestId": {
            "type": "string"
          }
        }
      },
      "PublicErrorResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ErrorResponse"
          },
          {
            "type": "object",
            "required": [
              "requestId"
            ]
          }
        ]
      },
      "ValidationMessage": {
        "type": "object",
        "required": [
          "message",
          "field",
          "rule"
        ],
        "properties": {
          "message": {
            "type": "string"
          },
          "field": {
            "type": "string"
          },
          "rule": {
            "type": "string"
          }
        }
      },
      "ValidationErrorResponse": {
        "type": "object",
        "required": [
          "message",
          "errors"
        ],
        "properties": {
          "message": {
            "type": "string",
            "const": "Validation failure"
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ValidationMessage"
            }
          },
          "requestId": {
            "type": "string"
          }
        }
      },
      "PublicValidationErrorResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ValidationErrorResponse"
          },
          {
            "type": "object",
            "required": [
              "requestId"
            ]
          }
        ]
      }
    }
  }
}
