{
  "openapi": "3.1.0",
  "info": {
    "title": "Shorebird CodePush API",
    "version": "1.0.0",
    "description": "The Shorebird over-the-air code push API. Models and message DTOs\noriginally lived in the handwritten `shorebird_code_push_protocol`\nand `shorebird_code_push_client` packages; this spec is the\nOpenAPI representation of that surface area.\n"
  },
  "servers": [
    {
      "url": "https://api.shorebird.dev/api/v1"
    }
  ],
  "security": [
    {
      "apiKey": []
    },
    {
      "oauthJwt": []
    }
  ],
  "tags": [
    {
      "name": "apps",
      "description": "Applications and their metadata."
    },
    {
      "name": "releases",
      "description": "Release builds and their artifacts."
    },
    {
      "name": "patches",
      "description": "Over-the-air patches for releases."
    },
    {
      "name": "channels",
      "description": "Release channels."
    },
    {
      "name": "collaborators",
      "description": "App collaborators and their roles."
    },
    {
      "name": "organizations",
      "description": "Organizations and membership."
    },
    {
      "name": "users",
      "description": "Shorebird user accounts."
    },
    {
      "name": "diagnostics",
      "description": "Infrastructure diagnostics endpoints."
    },
    {
      "name": "metrics",
      "description": "Customer-facing analytics charts (MAU v2)."
    }
  ],
  "paths": {
    "/users": {
      "post": {
        "tags": [
          "users"
        ],
        "summary": "Create a new user.",
        "description": "Creates a new Shorebird user. Email is retrieved from the user's\nauth token; only the display name is provided in the body.\n",
        "operationId": "createUser",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateUserRequest"
              }
            }
          }
        },
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The newly-created user.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrivateUser"
                }
              }
            }
          }
        }
      }
    },
    "/users/me": {
      "get": {
        "tags": [
          "users"
        ],
        "summary": "Get the currently logged-in user.",
        "operationId": "getCurrentUser",
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The currently logged-in user.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrivateUser"
                }
              }
            }
          },
          "404": {
            "description": "No user is currently logged in."
          }
        }
      }
    },
    "/apps": {
      "get": {
        "tags": [
          "apps"
        ],
        "summary": "List all apps for the current account.",
        "operationId": "getApps",
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "A list of apps.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetAppsResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "apps"
        ],
        "summary": "Create a new app.",
        "operationId": "createApp",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAppRequest"
              }
            }
          }
        },
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The newly-created app.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/App"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{appId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AppId"
        }
      ],
      "get": {
        "tags": [
          "apps"
        ],
        "summary": "Get the specified app.",
        "operationId": "getApp",
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The requested app.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppMetadata"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "apps"
        ],
        "summary": "Delete the specified app.",
        "operationId": "deleteApp",
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "204": {
            "description": "The app was deleted."
          }
        }
      }
    },
    "/apps/{appId}/icon": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AppId"
        },
        {
          "name": "v",
          "in": "query",
          "required": false,
          "description": "Cache-bust token (the picked release's id), set by\n`AppMetadata.iconUrl`. Ignored by the server, which always\nserves the currently-picked icon; clients re-fetch when the\nURL changes.\n",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "apps"
        ],
        "summary": "Get the app's launcher icon as a PNG.",
        "description": "Returns the launcher icon for the most recent analyzed iOS\nrelease (or Android, when iOS has no analyzed release with an\nicon). The response is labeled `immutable` because the URL\nembedded in `AppMetadata.iconUrl` changes whenever the picked\nrelease changes. Returns 404 when no analyzed release has an\nicon.\n",
        "operationId": "getAppIcon",
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The icon bytes.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "`private, max-age=31536000, immutable`."
              }
            },
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "No analyzed release has an icon."
          }
        }
      }
    },
    "/apps/{appId}/collaborators": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AppId"
        }
      ],
      "post": {
        "tags": [
          "collaborators"
        ],
        "summary": "Add a collaborator to the app by email.",
        "operationId": "createAppCollaborator",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAppCollaboratorRequest"
              }
            }
          }
        },
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "204": {
            "description": "The collaborator was added."
          }
        }
      }
    },
    "/apps/{appId}/collaborators/{collaboratorId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AppId"
        },
        {
          "name": "collaboratorId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer",
            "format": "int64"
          }
        }
      ],
      "patch": {
        "tags": [
          "collaborators"
        ],
        "summary": "Update a collaborator's role on an app.",
        "operationId": "updateAppCollaborator",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateAppCollaboratorRequest"
              }
            }
          }
        },
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "204": {
            "description": "The collaborator was updated."
          }
        }
      }
    },
    "/apps/{appId}/channels": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AppId"
        }
      ],
      "get": {
        "tags": [
          "channels"
        ],
        "summary": "List channels for the app.",
        "operationId": "getChannels",
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The channels for the app.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Channel"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "channels"
        ],
        "summary": "Create a new channel for the app.",
        "operationId": "createChannel",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateChannelRequest"
              }
            }
          }
        },
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The newly-created channel.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Channel"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{appId}/releases": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AppId"
        }
      ],
      "get": {
        "tags": [
          "releases"
        ],
        "summary": "List releases for the app.",
        "operationId": "getReleases",
        "parameters": [
          {
            "name": "sideloadable",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "If true, only return releases that can be sideloaded."
          }
        ],
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The releases for the app.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetReleasesResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "releases"
        ],
        "summary": "Create a new release for the app.",
        "operationId": "createRelease",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateReleaseRequest"
              }
            }
          }
        },
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The newly-created release.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateReleaseResponse"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{appId}/releases/{releaseId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AppId"
        },
        {
          "$ref": "#/components/parameters/ReleaseId"
        }
      ],
      "get": {
        "tags": [
          "releases"
        ],
        "summary": "Get a specific release.",
        "operationId": "getRelease",
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The requested release.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetReleaseResponse"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "releases"
        ],
        "summary": "Update the release.",
        "operationId": "updateRelease",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateReleaseRequest"
              }
            }
          }
        },
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "204": {
            "description": "The release was updated."
          }
        }
      }
    },
    "/apps/{appId}/releases/{releaseId}/artifacts": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AppId"
        },
        {
          "$ref": "#/components/parameters/ReleaseId"
        }
      ],
      "get": {
        "tags": [
          "releases"
        ],
        "summary": "Get release artifacts.",
        "operationId": "getReleaseArtifacts",
        "parameters": [
          {
            "name": "arch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/ReleasePlatform"
            }
          }
        ],
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The artifacts for the release.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetReleaseArtifactsResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "releases"
        ],
        "summary": "Register a new release artifact and obtain an upload URL.",
        "description": "The server returns a record for the artifact plus a signed upload\nURL; clients then `POST` the artifact bytes to that URL.\n",
        "operationId": "createReleaseArtifact",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateReleaseArtifactRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/CreateReleaseArtifactRequest"
              }
            }
          }
        },
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The newly-registered release artifact.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateReleaseArtifactResponse"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{appId}/releases/{releaseId}/patches": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AppId"
        },
        {
          "$ref": "#/components/parameters/ReleaseId"
        }
      ],
      "get": {
        "tags": [
          "patches"
        ],
        "summary": "List patches for a release.",
        "operationId": "getReleasePatches",
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The patches for the release.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetReleasePatchesResponse"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{appId}/releases/{releaseId}/patches/{patchId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AppId"
        },
        {
          "$ref": "#/components/parameters/ReleaseId"
        },
        {
          "$ref": "#/components/parameters/PatchId"
        }
      ],
      "patch": {
        "tags": [
          "patches"
        ],
        "summary": "Update a patch.",
        "operationId": "updatePatch",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdatePatchRequest"
              }
            }
          }
        },
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "204": {
            "description": "The patch was updated."
          }
        }
      }
    },
    "/apps/{appId}/patches": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AppId"
        }
      ],
      "post": {
        "tags": [
          "patches"
        ],
        "summary": "Create a new patch.",
        "operationId": "createPatch",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePatchRequest"
              }
            }
          }
        },
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "201": {
            "description": "The newly-created patch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatePatchResponse"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{appId}/patches/{patchId}/artifacts": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AppId"
        },
        {
          "$ref": "#/components/parameters/PatchId"
        }
      ],
      "post": {
        "tags": [
          "patches"
        ],
        "summary": "Register a new patch artifact and obtain an upload URL.",
        "operationId": "createPatchArtifact",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePatchArtifactRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/CreatePatchArtifactRequest"
              }
            }
          }
        },
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The newly-registered patch artifact.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatePatchArtifactResponse"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{appId}/patches/promote": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AppId"
        }
      ],
      "post": {
        "tags": [
          "patches"
        ],
        "summary": "Promote a patch to a channel.",
        "operationId": "promotePatch",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PromotePatchRequest"
              }
            }
          }
        },
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "204": {
            "description": "The patch was promoted."
          }
        }
      }
    },
    "/apps/{appId}/metrics/version-distribution": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AppId"
        }
      ],
      "get": {
        "tags": [
          "metrics"
        ],
        "summary": "Distribution of currently-active devices by release version.",
        "description": "Returns the exact (non-HLL) count of currently-active devices for\nthe app, grouped by `release_version`. \"Currently active\" is\nbounded by a server-side active-device window; rows with no\nreported `release_version` are returned with `releaseVersion: null`\nand represent devices on Flutter versions too old to emit the\nfield. The result is sorted by `deviceCount` descending, then\n`releaseVersion` ascending with NULLs last.\n",
        "operationId": "getVersionDistribution",
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The version distribution for the app.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetVersionDistributionResponse"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{appId}/metrics/patch-adoption": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AppId"
        }
      ],
      "get": {
        "tags": [
          "metrics"
        ],
        "summary": "Cumulative patch-adoption curves for a release.",
        "description": "Returns, for a single release, the cumulative adoption of each patch:\nthe distinct devices running patch `>= N` (an HLL count over\n`hourly_user_sketches`) over the patch's *target* — the distinct\ndevices on the release whose platform the patch was built for. Adoption\npercentages are computed server-side and clamped to `[0.0, 1.0]`;\nclients should render them rather than recompute.\n\nValues are cumulative (\"patch `>= N`\"), so they are monotonic\nnon-increasing in `patch_number`.\n\nThe release is the `release_version` query parameter, or — when\nomitted — the app's latest release by creation date (`is_latest` flags\nthat case). The window defaults to the last 28 days and is capped at 32\ndays (the effective window is echoed in `range`). With `granularity`\nomitted, each patch carries a single full-window value (`period: null`);\nset it for a curve at that resolution.\n",
        "operationId": "getPatchAdoption",
        "parameters": [
          {
            "name": "release_version",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "The release to report on. Omitted ⇒ the app's latest release by\ncreation date.\n"
          },
          {
            "name": "start",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Window start (UTC, inclusive). Defaults to `end` minus 28 days;\nclamped so the window never exceeds 32 days.\n"
          },
          {
            "name": "end",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Window end (UTC, exclusive). Defaults to now."
          },
          {
            "name": "granularity",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "hour",
                "day",
                "week",
                "month"
              ]
            },
            "description": "Bucket resolution. Omitted ⇒ a single full-window value per patch\n(`period: null`); set ⇒ one point per bucket at this resolution.\n"
          }
        ],
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The patch-adoption data for one release.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetPatchAdoptionResponse"
                }
              }
            }
          }
        }
      }
    },
    "/patches/check": {
      "post": {
        "tags": [
          "patches"
        ],
        "summary": "Check for an available patch for an install.",
        "operationId": "patchCheck",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchCheckRequest"
              }
            }
          }
        },
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The patch check result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PatchCheckResponse"
                }
              }
            }
          }
        }
      }
    },
    "/organizations": {
      "get": {
        "tags": [
          "organizations"
        ],
        "summary": "List organizations the current user is a member of.",
        "operationId": "getOrganizationMemberships",
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The organizations the user is a member of.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetOrganizationsResponse"
                }
              }
            }
          }
        }
      }
    },
    "/organizations/{organizationId}/apps": {
      "parameters": [
        {
          "$ref": "#/components/parameters/OrganizationId"
        }
      ],
      "get": {
        "tags": [
          "organizations"
        ],
        "summary": "List apps in an organization.",
        "operationId": "getOrganizationApps",
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The apps belonging to the organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetOrganizationAppsResponse"
                }
              }
            }
          }
        }
      }
    },
    "/organizations/{organizationId}/users": {
      "parameters": [
        {
          "$ref": "#/components/parameters/OrganizationId"
        }
      ],
      "get": {
        "tags": [
          "organizations"
        ],
        "summary": "List users in an organization.",
        "operationId": "getOrganizationUsers",
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The users belonging to the organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetOrganizationUsersResponse"
                }
              }
            }
          }
        }
      }
    },
    "/diagnostics/gcp_upload": {
      "get": {
        "tags": [
          "diagnostics"
        ],
        "summary": "Get a GCP upload link for measuring upload speed.",
        "operationId": "getGcpUploadSpeedTestUrl",
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The upload URL to measure against.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "The upload URL to measure against.",
                  "required": [
                    "upload_url"
                  ],
                  "properties": {
                    "upload_url": {
                      "type": "string",
                      "description": "The GCP-signed upload URL."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/diagnostics/gcp_download": {
      "get": {
        "tags": [
          "diagnostics"
        ],
        "summary": "Get a GCP download link for measuring download speed.",
        "operationId": "getGcpDownloadSpeedTestUrl",
        "responses": {
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "200": {
            "description": "The download URL to measure against.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "The download URL to measure against.",
                  "required": [
                    "download_url"
                  ],
                  "properties": {
                    "download_url": {
                      "type": "string",
                      "description": "The GCP-signed download URL."
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "**Recommended.** An `sb_api_*` API key minted in the Shorebird\nconsole (Account > API Keys). Long-lived, no refresh needed.\n"
      },
      "oauthJwt": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "A JWT access token obtained via Shorebird's interactive OAuth\nlogin flow (`shorebird login`). Short-lived (15 min),\nauto-refreshed by the CLI using a stored refresh token.\n"
      }
    },
    "responses": {
      "Error": {
        "description": "Standard error response. Emitted for any non-2xx status where a\nbody can be decoded into an [ErrorResponse].\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    },
    "parameters": {
      "AppId": {
        "name": "appId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "The ID of the app."
      },
      "ReleaseId": {
        "name": "releaseId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer",
          "format": "int64"
        },
        "description": "The ID of the release."
      },
      "PatchId": {
        "name": "patchId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer",
          "format": "int64"
        },
        "description": "The ID of the patch."
      },
      "OrganizationId": {
        "name": "organizationId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer",
          "format": "int64"
        },
        "description": "The ID of the organization."
      }
    },
    "schemas": {
      "App": {
        "type": "object",
        "description": "The application downloaded and run on various devices/platforms.",
        "required": [
          "id",
          "display_name"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The ID of the app."
          },
          "display_name": {
            "type": "string",
            "description": "The display name of the app."
          }
        }
      },
      "AppMetadata": {
        "type": "object",
        "description": "A single app which contains zero or more releases.",
        "required": [
          "app_id",
          "display_name",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "app_id": {
            "type": "string",
            "description": "The ID of the app."
          },
          "display_name": {
            "type": "string",
            "description": "The display name of the app."
          },
          "latest_release_version": {
            "type": "string",
            "description": "The latest release version of the app."
          },
          "latest_patch_number": {
            "type": "integer",
            "format": "int64",
            "description": "The latest patch number of the app."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "The date and time the app was created."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "The date and time the app was last updated."
          },
          "platforms": {
            "type": "array",
            "description": "Every platform the app has shipped to (i.e. has at least one\nrelease artifact for). Independent of `latest_releases`:\nan app can list a platform here even if no release on that\nplatform has been analyzed yet.\n",
            "items": {
              "$ref": "#/components/schemas/ReleasePlatform"
            }
          },
          "latest_releases": {
            "type": "object",
            "description": "The latest analyzed release per platform. A platform is\nomitted when no release for that platform has been analyzed\nyet. When the latest release for a platform is not yet\nanalyzed but a previous one is, the previous release is\nreturned here and `pending_releases.{platform}` identifies\nthe unanalyzed newer release.\n",
            "additionalProperties": {
              "$ref": "#/components/schemas/LatestRelease"
            },
            "propertyNames": {
              "$ref": "#/components/schemas/ReleasePlatform"
            }
          },
          "pending_releases": {
            "type": "object",
            "description": "The newest unanalyzed release per platform, whenever one\nexists. A platform is omitted when its most recent release\nhas already been analyzed. Independent of `latest_releases`:\nboth can be present (a newer release than the analyzed one\nis being processed) or only `pending_releases` can be\npresent (no release on the platform has been analyzed yet).\n",
            "additionalProperties": {
              "$ref": "#/components/schemas/PendingRelease"
            },
            "propertyNames": {
              "$ref": "#/components/schemas/ReleasePlatform"
            }
          },
          "icon_url": {
            "type": "string",
            "description": "Server-emitted URL for the app's launcher icon, sourced from\nthe most recent analyzed iOS release (or Android, when iOS\nhas no analyzed release with an icon). Requires the same\nauth as the rest of the apps API. The URL embeds the picked\nrelease's id as a `v` query parameter so it can be cached\nindefinitely; when a different release becomes the icon\nsource the URL changes and clients re-fetch. Omitted when\nno analyzed release has an icon.\n"
          }
        }
      },
      "Channel": {
        "type": "object",
        "description": "A tag used to manage the subset of installs that receive a patch.\nBy default a \"stable\" channel is created and used by devices to\nquery for available patches.\n",
        "required": [
          "id",
          "app_id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "The ID of the channel."
          },
          "app_id": {
            "type": "string",
            "description": "The ID of the app."
          },
          "name": {
            "type": "string",
            "description": "The channel name."
          }
        }
      },
      "Release": {
        "type": "object",
        "description": "A release build of an application that is distributed to devices.\nA release can have zero or more patches applied to it.\n",
        "required": [
          "id",
          "app_id",
          "version",
          "flutter_revision",
          "platform_statuses",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "The ID of the release."
          },
          "app_id": {
            "type": "string",
            "description": "The ID of the app."
          },
          "version": {
            "type": "string",
            "description": "The version of the release."
          },
          "flutter_revision": {
            "type": "string",
            "description": "The Flutter revision used to create the release."
          },
          "flutter_version": {
            "type": "string",
            "description": "The Flutter version used to create the release. Optional\nbecause it was added later; older releases do not have it.\n"
          },
          "display_name": {
            "type": "string",
            "description": "The display name for the release."
          },
          "platform_statuses": {
            "type": "object",
            "description": "The status of the release for each platform.",
            "additionalProperties": {
              "$ref": "#/components/schemas/ReleaseStatus"
            },
            "propertyNames": {
              "$ref": "#/components/schemas/ReleasePlatform"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "The date and time the release was created."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "The date and time the release was last updated."
          },
          "notes": {
            "type": "string",
            "description": "Freeform notes associated with the release, if any."
          }
        }
      },
      "ReleaseAnalysis": {
        "type": "object",
        "description": "Analyzer-extracted metadata for a release artifact on a single\nplatform.\n",
        "required": [
          "display_name",
          "package_name",
          "min_sdk_version",
          "target_sdk_version",
          "architectures"
        ],
        "properties": {
          "display_name": {
            "type": "string",
            "description": "The user-visible application name extracted from the artifact\n(e.g. AndroidManifest `application:label`). May differ from\nthe user-curated `App.display_name`.\n"
          },
          "package_name": {
            "type": "string",
            "description": "The application package name (e.g. `com.example.app`)."
          },
          "min_sdk_version": {
            "type": "string",
            "description": "The minimum SDK level required to install the artifact\n(Android API level for android, iOS deployment target for ios).\n"
          },
          "target_sdk_version": {
            "type": "string",
            "description": "The SDK level the artifact targets (Android targetSdk for\nandroid, iOS SDK for ios).\n"
          },
          "architectures": {
            "type": "array",
            "description": "CPU architectures present in the artifact\n(e.g. `[\"arm64-v8a\", \"armeabi-v7a\"]`).\n",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "LatestRelease": {
        "type": "object",
        "description": "Per-platform projection of an analyzed release as surfaced by\n`AppMetadata.latest_releases`. Each entry corresponds to the\nmost recent analyzed release on the keying platform, so\n`analysis` is always populated and `status` is the single\nper-platform status (rather than the cross-platform\n`platform_statuses` map carried by `Release`).\n",
        "required": [
          "id",
          "version",
          "flutter_revision",
          "created_at",
          "updated_at",
          "status",
          "analysis"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "The ID of the release."
          },
          "version": {
            "type": "string",
            "description": "The version of the release."
          },
          "flutter_revision": {
            "type": "string",
            "description": "The Flutter revision used to create the release."
          },
          "flutter_version": {
            "type": "string",
            "description": "The Flutter version used to create the release. Optional\nbecause it was added later; older releases do not have it.\n"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "The date and time the release was created."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "The date and time the release was last updated."
          },
          "status": {
            "$ref": "#/components/schemas/ReleaseStatus",
            "description": "The release's status on the keying platform."
          },
          "notes": {
            "type": "string",
            "description": "Freeform notes associated with the release, if any."
          },
          "analysis": {
            "$ref": "#/components/schemas/ReleaseAnalysis",
            "description": "Analyzer-extracted metadata for this release on the keying\nplatform. Always present (entries without analysis are\nomitted from `latest_releases`).\n"
          }
        }
      },
      "PendingRelease": {
        "type": "object",
        "description": "A newer release that has been created but not yet analyzed.\nSurfaced alongside the most recent analyzed release so clients\ncan show an \"analyzing…\" indicator without losing the stable\nicon and metadata.\n",
        "required": [
          "id",
          "version",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "The ID of the pending release."
          },
          "version": {
            "type": "string",
            "description": "The version of the pending release."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "The date and time the pending release was created."
          }
        }
      },
      "ReleaseArtifact": {
        "type": "object",
        "description": "An artifact contains metadata about the contents of a specific\nrelease for a specific platform and architecture.\n",
        "required": [
          "id",
          "release_id",
          "arch",
          "platform",
          "hash",
          "size",
          "url",
          "can_sideload"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "The ID of the artifact."
          },
          "release_id": {
            "type": "integer",
            "format": "int64",
            "description": "The ID of the release."
          },
          "arch": {
            "type": "string",
            "description": "The arch of the artifact."
          },
          "platform": {
            "$ref": "#/components/schemas/ReleasePlatform",
            "description": "The platform of the artifact."
          },
          "hash": {
            "type": "string",
            "description": "The hash of the artifact."
          },
          "size": {
            "type": "integer",
            "format": "int64",
            "description": "The size of the artifact in bytes."
          },
          "url": {
            "type": "string",
            "description": "The url of the artifact."
          },
          "podfile_lock_hash": {
            "type": "string",
            "description": "sha256 of the Podfile.lock used to create the artifact (iOS only)."
          },
          "can_sideload": {
            "type": "boolean",
            "description": "Whether the artifact can be sideloaded onto a device."
          }
        }
      },
      "Patch": {
        "type": "object",
        "description": "An over-the-air update which is applied to a specific release.\nAll patches have a patch number (auto-incrementing integer) and\nmultiple patches can be published for a given release.\n",
        "required": [
          "id",
          "number"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "The unique patch identifier."
          },
          "number": {
            "type": "integer",
            "format": "int64",
            "description": "The patch number. A larger number equates to a newer patch."
          },
          "notes": {
            "type": "string",
            "description": "Freeform notes associated with the patch, if any."
          }
        }
      },
      "PatchArtifact": {
        "type": "object",
        "description": "Metadata about the contents of a specific patch for a specific\nplatform and architecture.\n",
        "required": [
          "id",
          "patch_id",
          "arch",
          "platform",
          "hash",
          "size",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "The ID of the artifact."
          },
          "patch_id": {
            "type": "integer",
            "format": "int64",
            "description": "The ID of the patch."
          },
          "arch": {
            "type": "string",
            "description": "The arch of the artifact."
          },
          "platform": {
            "$ref": "#/components/schemas/ReleasePlatform",
            "description": "The platform of the artifact."
          },
          "hash": {
            "type": "string",
            "description": "The hash of the artifact."
          },
          "size": {
            "type": "integer",
            "format": "int64",
            "description": "The size of the artifact in bytes."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "The date and time the artifact was created."
          }
        }
      },
      "ReleasePatch": {
        "type": "object",
        "description": "A patch for a given release.",
        "required": [
          "id",
          "number",
          "artifacts",
          "is_rolled_back"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "The patch id."
          },
          "number": {
            "type": "integer",
            "format": "int64",
            "description": "The patch number."
          },
          "channel": {
            "type": "string",
            "description": "The channel associated with the patch."
          },
          "artifacts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PatchArtifact"
            },
            "description": "The associated patch artifacts."
          },
          "is_rolled_back": {
            "type": "boolean",
            "description": "Whether the patch has been rolled back."
          },
          "notes": {
            "type": "string",
            "description": "Freeform notes associated with the patch, if any."
          }
        }
      },
      "ReleasePlatform": {
        "type": "string",
        "description": "A platform to which a Shorebird release can be deployed.",
        "enum": [
          "android",
          "ios",
          "linux",
          "macos",
          "windows"
        ],
        "x-enum-descriptions": [
          "Android.",
          "iOS.",
          "Linux.",
          "macOS.",
          "Windows."
        ]
      },
      "ReleaseStatus": {
        "type": "string",
        "description": "The status of a release.",
        "enum": [
          "draft",
          "active"
        ],
        "x-enum-descriptions": [
          "The release has been created, but not all platform artifacts have been uploaded.",
          "All platform artifacts have been uploaded for this release."
        ]
      },
      "Role": {
        "type": "string",
        "description": "A role that a user can have relative to an Organization or App.",
        "enum": [
          "owner",
          "admin",
          "appManager",
          "developer",
          "viewer",
          "none"
        ],
        "x-enum-descriptions": [
          "User that created the organization.",
          "Users who have permissions to manage the organization.",
          "Users who have permissions to manage an app.",
          "Users who are part of the organization but have limited permissions.",
          "Users who have read-only access to the organization.",
          "Users who are not part of the organization but have visibility into it via app collaborator permissions."
        ]
      },
      "AppCollaboratorRole": {
        "type": "string",
        "description": "A role a user can have on a specific app.",
        "enum": [
          "admin",
          "developer"
        ],
        "x-enum-descriptions": [
          "A user with this role can perform all available actions on apps, releases, patches, channels, and collaborators.",
          "A user with this role can manage releases and patches, but cannot manage collaborators or the application itself."
        ]
      },
      "AuthProvider": {
        "type": "string",
        "description": "The authentication provider used to sign in the user.",
        "enum": [
          "google",
          "microsoft",
          "shorebird"
        ],
        "x-enum-descriptions": [
          "Signed in via Google.",
          "Signed in via Microsoft.",
          "Signed in via a Shorebird-managed account."
        ]
      },
      "OrganizationType": {
        "type": "string",
        "description": "Distinguishes personal organizations (single-user) from team\norganizations (multi-user).\n",
        "enum": [
          "personal",
          "team"
        ],
        "x-enum-descriptions": [
          "A single-user organization implicitly created with each user account.",
          "A multi-user organization with collaborators."
        ]
      },
      "Organization": {
        "type": "object",
        "description": "An organization groups users and apps together. Organizations\ncan be personal (single-user) or team (multi-user).\n",
        "required": [
          "id",
          "name",
          "organization_type",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "The unique identifier for the organization."
          },
          "name": {
            "type": "string",
            "description": "The name of the organization."
          },
          "organization_type": {
            "$ref": "#/components/schemas/OrganizationType",
            "description": "The type of organization."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When this organization was created."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When this organization was last updated."
          }
        }
      },
      "OrganizationMembership": {
        "type": "object",
        "description": "An organization and the current user's role in it.",
        "required": [
          "organization",
          "role"
        ],
        "properties": {
          "organization": {
            "$ref": "#/components/schemas/Organization",
            "description": "The organization."
          },
          "role": {
            "$ref": "#/components/schemas/Role",
            "description": "The user's role in the organization."
          }
        }
      },
      "OrganizationUser": {
        "type": "object",
        "description": "A member of an organization and their role.",
        "required": [
          "user",
          "role"
        ],
        "properties": {
          "user": {
            "$ref": "#/components/schemas/PublicUser",
            "description": "The user that is a member of the organization."
          },
          "role": {
            "$ref": "#/components/schemas/Role",
            "description": "The role [user] has in the organization."
          }
        }
      },
      "PublicUser": {
        "type": "object",
        "description": "A Shorebird user with non-sensitive information only.\n",
        "required": [
          "id",
          "email"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "The user's unique identifier."
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "The user's email address."
          },
          "display_name": {
            "type": "string",
            "description": "The user's name."
          }
        }
      },
      "PrivateUser": {
        "type": "object",
        "description": "A fully-detailed user object, possibly including sensitive\ninformation. Should only be used when querying the user's own\ninformation.\n",
        "required": [
          "id",
          "email",
          "jwt_issuer"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "The unique user identifier."
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "The user's email address, as provided by the user during signup."
          },
          "display_name": {
            "type": "string",
            "description": "The user's name, as provided by the user during signup."
          },
          "stripe_customer_id": {
            "type": "string",
            "description": "The user's Stripe customer ID, if they have one."
          },
          "jwt_issuer": {
            "type": "string",
            "description": "The JWT issuer used to create the user."
          },
          "patch_overage_limit": {
            "type": "integer",
            "format": "int64",
            "description": "The maximum number of patch installs the user has agreed to\npay for as part of a pay-as-you-go plan.\n"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "description": "Standard error response body from the Shorebird CodePush API.",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "The unique error code."
          },
          "message": {
            "type": "string",
            "description": "Human-readable error message."
          },
          "details": {
            "type": "string",
            "description": "Optional details associated with the error."
          }
        }
      },
      "CreateAppRequest": {
        "type": "object",
        "description": "The request body for POST /apps.",
        "required": [
          "display_name",
          "organization_id"
        ],
        "properties": {
          "display_name": {
            "type": "string",
            "description": "The display name of the app."
          },
          "organization_id": {
            "type": "integer",
            "format": "int64",
            "description": "The id of organization that this app will belong to."
          }
        }
      },
      "CreateAppCollaboratorRequest": {
        "type": "object",
        "description": "The request body for POST /apps/{appId}/collaborators.",
        "required": [
          "email"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "The email of the collaborator to add."
          }
        }
      },
      "UpdateAppCollaboratorRequest": {
        "type": "object",
        "description": "The request body for PATCH /apps/{appId}/collaborators/{collaboratorId}.\n",
        "required": [
          "role"
        ],
        "properties": {
          "role": {
            "$ref": "#/components/schemas/AppCollaboratorRole",
            "description": "The new role for the collaborator."
          }
        }
      },
      "CreateChannelRequest": {
        "type": "object",
        "description": "The request body for POST /apps/{appId}/channels.",
        "required": [
          "channel"
        ],
        "properties": {
          "channel": {
            "type": "string",
            "description": "The channel name."
          }
        }
      },
      "CreateReleaseRequest": {
        "type": "object",
        "description": "The request body for POST /apps/{appId}/releases.",
        "required": [
          "version",
          "flutter_revision"
        ],
        "properties": {
          "version": {
            "type": "string",
            "description": "The release version."
          },
          "flutter_revision": {
            "type": "string",
            "description": "The Flutter revision used to create the release."
          },
          "flutter_version": {
            "type": "string",
            "description": "The Flutter version used to create the release.  This field is optional because it was newly added and older releases do not have this information."
          },
          "display_name": {
            "type": "string",
            "description": "The display name for the release."
          }
        }
      },
      "CreateReleaseResponse": {
        "type": "object",
        "description": "The response body for POST /apps/{appId}/releases.",
        "required": [
          "release"
        ],
        "properties": {
          "release": {
            "$ref": "#/components/schemas/Release",
            "description": "The newly-created release."
          }
        }
      },
      "UpdateReleaseRequest": {
        "type": "object",
        "description": "The request body for PATCH /apps/{appId}/releases/{releaseId}.",
        "properties": {
          "status": {
            "$ref": "#/components/schemas/ReleaseStatus",
            "description": "The desired status of the release. If provided, [platform] must also be provided If null, the status will not be updated."
          },
          "platform": {
            "$ref": "#/components/schemas/ReleasePlatform",
            "description": "The platform of the release. If provided, [status] must also be provided."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "Additional information about the command that was run to\nupdate the release and the environment it was run in.\n"
          },
          "notes": {
            "type": "string",
            "description": "Notes about the release. This is a free-form field that can be used to store additional information about the release. If null, the notes will not be updated."
          }
        }
      },
      "GetAppsResponse": {
        "type": "object",
        "description": "The response body for GET /apps.",
        "required": [
          "apps"
        ],
        "properties": {
          "apps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AppMetadata"
            },
            "description": "The list of apps."
          }
        }
      },
      "GetReleasesResponse": {
        "type": "object",
        "description": "The response body for GET /apps/{appId}/releases.",
        "required": [
          "releases"
        ],
        "properties": {
          "releases": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Release"
            },
            "description": "The list of releases for the app."
          }
        }
      },
      "GetReleaseResponse": {
        "type": "object",
        "description": "The response body for GET /apps/{appId}/releases/{releaseId}.",
        "required": [
          "release"
        ],
        "properties": {
          "release": {
            "$ref": "#/components/schemas/Release",
            "description": "The requested release."
          }
        }
      },
      "GetReleaseArtifactsResponse": {
        "type": "object",
        "description": "The response body for GET /apps/{appId}/releases/{releaseId}/artifacts.",
        "required": [
          "artifacts"
        ],
        "properties": {
          "artifacts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReleaseArtifact"
            },
            "description": "The artifacts for the release."
          }
        }
      },
      "GetReleasePatchesResponse": {
        "type": "object",
        "description": "The response to GET /apps/{appId}/releases/{releaseId}/patches.",
        "required": [
          "patches"
        ],
        "properties": {
          "patches": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReleasePatch"
            },
            "description": "List of patches."
          }
        }
      },
      "GetOrganizationsResponse": {
        "type": "object",
        "description": "The response body for GET /organizations.",
        "required": [
          "organizations"
        ],
        "properties": {
          "organizations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrganizationMembership"
            },
            "description": "Organizations that the user is a member of, as well as this user's role in each organization."
          }
        }
      },
      "GetOrganizationAppsResponse": {
        "type": "object",
        "description": "The response body for GET /organizations/{organizationId}/apps.",
        "required": [
          "apps"
        ],
        "properties": {
          "apps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AppMetadata"
            },
            "description": "The apps that belong to the organization."
          }
        }
      },
      "GetOrganizationUsersResponse": {
        "type": "object",
        "description": "The response body for GET /organizations/{organizationId}/users.",
        "required": [
          "users"
        ],
        "properties": {
          "users": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrganizationUser"
            },
            "description": "The list of users that belong to the organization, as well as their roles in the organization."
          }
        }
      },
      "CreateUserRequest": {
        "type": "object",
        "description": "The request body for POST /users. The user's email is taken\nfrom the auth token; only the display name is provided here.\n",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "The new user's display name."
          }
        }
      },
      "CreatePatchRequest": {
        "type": "object",
        "description": "The request body for POST /apps/{appId}/patches.",
        "required": [
          "release_id",
          "metadata"
        ],
        "properties": {
          "release_id": {
            "type": "integer",
            "format": "int64",
            "description": "The ID of the release."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "Additional information about the command that was run to\ncreate the patch and the environment it was run in.\n"
          }
        }
      },
      "CreatePatchResponse": {
        "type": "object",
        "description": "The response body for POST /apps/{appId}/patches. Deliberately\nnarrower than [Patch]: a freshly-created patch has no `notes`\nyet (those are set via PATCH /.../{patchId}), so this endpoint\nexposes only the identifiers the client needs to upload\nartifacts.\n",
        "required": [
          "id",
          "number"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "The unique patch identifier."
          },
          "number": {
            "type": "integer",
            "format": "int64",
            "description": "The patch number. A larger number equates to a newer patch."
          }
        }
      },
      "CreatePatchArtifactRequest": {
        "type": "object",
        "description": "Metadata for a new patch artifact. POST to\n/apps/{appId}/patches/{patchId}/artifacts and use the returned\nsigned upload URL to upload the artifact bytes separately.\n",
        "required": [
          "arch",
          "platform",
          "hash",
          "size"
        ],
        "properties": {
          "arch": {
            "type": "string",
            "description": "The arch of the artifact."
          },
          "platform": {
            "$ref": "#/components/schemas/ReleasePlatform",
            "description": "The platform of the artifact."
          },
          "hash": {
            "type": "string",
            "description": "The hash of the artifact."
          },
          "hash_signature": {
            "type": "string",
            "description": "The signature of the [hash].  Patch code signing is an opt in feature, introduced later in the life of the product, so when this field is null, the patch does not uses code signing."
          },
          "podfile_lock_hash": {
            "type": "string",
            "description": "The sha256 hash of the Podfile.lock file, if a Podfile.lock file was involved in the creation of the patch (iOS only)."
          },
          "size": {
            "type": "integer",
            "format": "int64",
            "description": "The size of the artifact in bytes."
          }
        }
      },
      "CreatePatchArtifactResponse": {
        "type": "object",
        "description": "The response body for registering a patch artifact.",
        "required": [
          "id",
          "patch_id",
          "arch",
          "platform",
          "hash",
          "size",
          "url"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "The ID of the artifact."
          },
          "patch_id": {
            "type": "integer",
            "format": "int64",
            "description": "The ID of the patch."
          },
          "arch": {
            "type": "string",
            "description": "The arch of the artifact."
          },
          "platform": {
            "$ref": "#/components/schemas/ReleasePlatform",
            "description": "The platform of the artifact."
          },
          "hash": {
            "type": "string",
            "description": "The hash of the artifact."
          },
          "size": {
            "type": "integer",
            "format": "int64",
            "description": "The size of the artifact in bytes."
          },
          "url": {
            "type": "string",
            "description": "The upload URL for the artifact."
          }
        }
      },
      "CreateReleaseArtifactRequest": {
        "type": "object",
        "description": "Metadata for a new release artifact. POST to\n/apps/{appId}/releases/{releaseId}/artifacts and use the\nreturned signed upload URL to upload the artifact bytes\nseparately.\n",
        "required": [
          "arch",
          "platform",
          "hash",
          "size",
          "filename"
        ],
        "properties": {
          "arch": {
            "type": "string",
            "description": "The arch of the artifact."
          },
          "platform": {
            "$ref": "#/components/schemas/ReleasePlatform",
            "description": "The platform of the artifact."
          },
          "hash": {
            "type": "string",
            "description": "The hash of the artifact."
          },
          "filename": {
            "type": "string",
            "description": "The name of the file."
          },
          "can_sideload": {
            "type": "boolean",
            "description": "Whether the artifact can installed and run on a device/emulator as-is."
          },
          "size": {
            "type": "integer",
            "format": "int64",
            "description": "The size of the artifact in bytes."
          },
          "podfile_lock_hash": {
            "type": "string",
            "description": "The hash of the Podfile.lock file used to create this artifact (iOS only)."
          }
        }
      },
      "CreateReleaseArtifactResponse": {
        "type": "object",
        "description": "The response body for registering a release artifact.",
        "required": [
          "id",
          "release_id",
          "arch",
          "platform",
          "hash",
          "size",
          "url"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "The ID of the artifact."
          },
          "release_id": {
            "type": "integer",
            "format": "int64",
            "description": "The ID of the release."
          },
          "arch": {
            "type": "string",
            "description": "The arch of the artifact."
          },
          "platform": {
            "$ref": "#/components/schemas/ReleasePlatform",
            "description": "The platform of the artifact."
          },
          "hash": {
            "type": "string",
            "description": "The hash of the artifact."
          },
          "size": {
            "type": "integer",
            "format": "int64",
            "description": "The size of the artifact in bytes."
          },
          "url": {
            "type": "string",
            "description": "The upload URL for the artifact."
          }
        }
      },
      "UpdatePatchRequest": {
        "type": "object",
        "description": "The request body for PATCH\n/apps/{appId}/releases/{releaseId}/patches/{patchId}.\n",
        "properties": {
          "notes": {
            "type": "string",
            "description": "Freeform notes about the patch. If null, notes are unchanged.\n"
          }
        }
      },
      "PromotePatchRequest": {
        "type": "object",
        "description": "The request body for POST /apps/{appId}/patches/promote.",
        "required": [
          "patch_id",
          "channel_id"
        ],
        "properties": {
          "patch_id": {
            "type": "integer",
            "format": "int64",
            "description": "The ID of the patch."
          },
          "channel_id": {
            "type": "integer",
            "format": "int64",
            "description": "The ID of the channel."
          }
        }
      },
      "PatchCheckRequest": {
        "type": "object",
        "description": "The request body for POST /patches/check.",
        "required": [
          "release_version",
          "platform",
          "arch",
          "app_id",
          "channel"
        ],
        "properties": {
          "release_version": {
            "type": "string",
            "description": "The release version of the app."
          },
          "patch_number": {
            "type": "integer",
            "format": "int64",
            "description": "The highest patch number the client has already downloaded.\nIf provided, the server only returns patches with a higher\nnumber. If omitted, the server returns the latest available.\n"
          },
          "patch_hash": {
            "type": "string",
            "description": "The current patch hash of the app."
          },
          "platform": {
            "$ref": "#/components/schemas/ReleasePlatform",
            "description": "The platform of the app."
          },
          "arch": {
            "type": "string",
            "description": "The architecture of the app."
          },
          "app_id": {
            "type": "string",
            "description": "The ID of the app."
          },
          "channel": {
            "type": "string",
            "description": "The channel of the app."
          },
          "client_id": {
            "type": "string",
            "description": "Unique device ID for the install, generated on device and\nunique per app. Optional for backward compatibility.\n"
          },
          "current_patch_number": {
            "type": "integer",
            "format": "int64",
            "description": "The patch number currently running on the device, if any.\nSupersedes `patch_number` for newer clients; unlike\n`patch_number`, this does not affect the server's response.\n"
          }
        }
      },
      "PatchCheckResponse": {
        "type": "object",
        "description": "The response body for POST /patches/check.",
        "required": [
          "patch_available"
        ],
        "properties": {
          "patch_available": {
            "type": "boolean",
            "description": "Whether a patch is available."
          },
          "patch": {
            "$ref": "#/components/schemas/PatchCheckMetadata",
            "description": "The patch metadata."
          },
          "rolled_back_patch_numbers": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int64"
            },
            "description": "The numbers of all patches that have been rolled back for the current release."
          }
        }
      },
      "PatchCheckMetadata": {
        "type": "object",
        "description": "Patch metadata representing the contents of a patch for a\nspecific platform and architecture.\n",
        "required": [
          "number",
          "download_url",
          "hash"
        ],
        "properties": {
          "number": {
            "type": "integer",
            "format": "int64",
            "description": "The patch number associated with the artifact."
          },
          "download_url": {
            "type": "string",
            "description": "The URL of the artifact."
          },
          "hash": {
            "type": "string",
            "description": "The hash of the artifact."
          },
          "hash_signature": {
            "type": "string",
            "description": "The signature of the `hash`."
          }
        }
      },
      "VersionDistributionEntry": {
        "type": "object",
        "description": "One bucket in a version-distribution chart: the exact count of\ncurrently-active devices on a given release version. A null\n`release_version` represents devices whose client did not emit one\n(typically very old Flutter clients).\n",
        "required": [
          "release_version",
          "device_count",
          "percentage"
        ],
        "properties": {
          "release_version": {
            "type": "string",
            "nullable": true,
            "description": "The release version for this bucket, or null for devices whose\nclient did not emit one.\n"
          },
          "device_count": {
            "type": "integer",
            "format": "int64",
            "description": "Number of currently-active devices on this release version\nwithin the active-device window.\n"
          },
          "percentage": {
            "type": "number",
            "format": "double",
            "description": "Fractional share of currently-active devices on this release\nversion, in [0.0, 1.0]. Server is the source of truth; clients\nshould render this value rather than recomputing it.\n"
          }
        }
      },
      "GetVersionDistributionResponse": {
        "type": "object",
        "description": "The response body for GET /apps/{appId}/metrics/version-distribution.\n",
        "required": [
          "entries",
          "total_devices",
          "active_window_days",
          "as_of"
        ],
        "properties": {
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VersionDistributionEntry"
            },
            "description": "One entry per release version, sorted by `device_count`\ndescending, then `release_version` ascending with NULLs last.\n"
          },
          "total_devices": {
            "type": "integer",
            "format": "int64",
            "description": "Sum of `device_count` across all entries. Convenience for\nclients; matches the server-side sum used to compute\n`percentage`.\n"
          },
          "active_window_days": {
            "type": "integer",
            "description": "The active-device window in days that bounds the query.\nHardcoded server-side in v1; tier-gated per-caller windows\nland in a follow-up.\n"
          },
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "Server's UTC timestamp at the moment the response was\nconstructed. Not a freshness indicator for the underlying\ndata, which is refreshed by an hourly scheduled query and\nmay lag by up to ~1 hour.\n"
          }
        }
      },
      "PatchAdoptionPoint": {
        "type": "object",
        "description": "One point in a patch's adoption series: the cumulative distinct\ndevices on `patch >= patch_number` (`devices`) over the patch's target\n(`target`), and their ratio (`adoption_pct`), for one bucket.\n",
        "required": [
          "period",
          "devices",
          "target",
          "adoption_pct"
        ],
        "properties": {
          "period": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "The bucket start (UTC), or null when the response is a single\nfull-window value (no granularity requested).\n"
          },
          "devices": {
            "type": "integer",
            "format": "int64",
            "description": "Distinct devices running patch `>= patch_number` on the patch's\ntarget platform(s) within this bucket (an HLL count).\n"
          },
          "target": {
            "type": "integer",
            "format": "int64",
            "description": "Distinct devices on the release whose platform the patch targets —\nthe patch's reachable denominator — within this bucket.\n"
          },
          "adoption_pct": {
            "type": "number",
            "format": "double",
            "description": "`devices / target`, in [0.0, 1.0] (0 when `target` is 0). Server\nis the source of truth; clients should render this value rather\nthan recomputing it.\n"
          }
        }
      },
      "PatchAdoptionEntry": {
        "type": "object",
        "description": "Cumulative adoption for one patch of the release. Values are\ncumulative — \"patch `>= patch_number`\" — so they are monotonic\nnon-increasing in `patch_number`.\n",
        "required": [
          "patch_number",
          "target_platforms",
          "is_rolled_back",
          "series"
        ],
        "properties": {
          "patch_number": {
            "type": "integer",
            "format": "int64",
            "description": "The patch number these cumulative values are anchored at.\n"
          },
          "target_platforms": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReleasePlatform"
            },
            "description": "The platform(s) the patch was built for (from its artifacts). The\ndenominator counts only devices on these platforms.\n"
          },
          "is_rolled_back": {
            "type": "boolean",
            "description": "Whether the patch has been rolled back."
          },
          "series": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PatchAdoptionPoint"
            },
            "description": "The adoption series. Exactly one point (`period: null`) when no\ngranularity was requested; otherwise one point per bucket, ordered\nby `period` ascending.\n"
          }
        }
      },
      "PatchAdoptionRange": {
        "type": "object",
        "description": "The effective (post-clamp) window the response covers.",
        "required": [
          "start",
          "end"
        ],
        "properties": {
          "start": {
            "type": "string",
            "format": "date-time",
            "description": "Window start (UTC, inclusive)."
          },
          "end": {
            "type": "string",
            "format": "date-time",
            "description": "Window end (UTC, exclusive)."
          }
        }
      },
      "GetPatchAdoptionResponse": {
        "type": "object",
        "description": "The response body for GET /apps/{appId}/metrics/patch-adoption. Covers\nexactly one release.\n",
        "required": [
          "release_version",
          "is_latest",
          "granularity",
          "range",
          "as_of",
          "patches"
        ],
        "properties": {
          "release_version": {
            "type": "string",
            "description": "The release version this response is for."
          },
          "is_latest": {
            "type": "boolean",
            "description": "True when the release was resolved via the \"latest release by\ncreation date\" default (no `release_version` was supplied).\n"
          },
          "granularity": {
            "type": "string",
            "nullable": true,
            "description": "The bucket resolution (`hour`, `day`, `week`, or `month`), or null\nwhen each patch carries a single full-window value.\n"
          },
          "range": {
            "$ref": "#/components/schemas/PatchAdoptionRange"
          },
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "Server's UTC timestamp at the moment the response was\nconstructed. Not a freshness indicator for the underlying\ndata, which is refreshed by an hourly scheduled query and\nmay lag by up to ~1 hour.\n"
          },
          "patches": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PatchAdoptionEntry"
            },
            "description": "One entry per patch of the release."
          }
        }
      }
    }
  }
}