{
  "openapi": "3.1.0",
  "info": {
    "title": "Bay Cloud API",
    "version": "1.0.0",
    "summary": "Ship an app, give it a database and a domain, and read the logs when it breaks.",
    "description": "The API behind the `bay` CLI, and the one an agent can call directly.\n\nEvery operation here needs a bearer token. `bay login` mints one and writes it to\n`~/.bay/config.json`; a person can also mint one at https://app.thebay.cloud/cli.\nSend it as `Authorization: Bearer <token>`.\n\nErrors are JSON, always, including from the authentication gate. The body carries\n`error` as a sentence; errors raised by the gate also carry `code` and `resolution`.\nA 402 is not a failure to relay as one: it means a plan limit was reached, and the\nbody says which in `reason`.\n\nThree operations stream rather than answer once: starting a deploy and following\nlogs are `text/event-stream`, and a repair agent's patch is `text/plain`.",
    "contact": {
      "name": "Bay",
      "url": "https://thebay.cloud/contact",
      "email": "founders@thebay.cloud"
    },
    "license": {
      "name": "AGPL-3.0-only",
      "identifier": "AGPL-3.0-only"
    }
  },
  "externalDocs": {
    "description": "Guides and CLI reference",
    "url": "https://bay.mintlify.app"
  },
  "servers": [
    {
      "url": "https://app.thebay.cloud",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "account",
      "description": "Who you are, what you are entitled to, and the tokens that prove it."
    },
    {
      "name": "apps",
      "description": "The things you have shipped."
    },
    {
      "name": "deploy",
      "description": "Shipping one."
    },
    {
      "name": "data",
      "description": "The database an app was given."
    },
    {
      "name": "access",
      "description": "Who can open an app, and at which address."
    },
    {
      "name": "diagnostics",
      "description": "What production saw, and what to do about it."
    }
  ],
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "What went wrong, in a sentence meant to be shown to a person."
          },
          "code": {
            "type": "string",
            "description": "Stable machine-readable identifier. Present on errors raised by the gate; older handlers carry only `error`."
          },
          "resolution": {
            "type": "string",
            "description": "What to do about it."
          },
          "documentation_url": {
            "type": "string",
            "format": "uri"
          },
          "reason": {
            "type": "string",
            "enum": [
              "no_account",
              "app_limit",
              "build_limit"
            ],
            "description": "On 402 only: which limit was reached."
          },
          "paywall": {
            "type": "boolean",
            "description": "On 402 only: the account has no plan at all."
          },
          "upgrade": {
            "type": "boolean",
            "description": "On 402 only: the account has a plan and has reached its limit."
          }
        }
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "A CLI token. `bay login`, or mint one at https://app.thebay.cloud/cli."
      },
      "sessionCookie": {
        "type": "apiKey",
        "in": "cookie",
        "name": "authjs.session-token",
        "description": "The dashboard's own session. Present so the browser client is described too; a machine should use the bearer token."
      }
    }
  },
  "paths": {
    "/api/account": {
      "get": {
        "tags": [
          "account"
        ],
        "operationId": "getAccount",
        "summary": "The signed-in account, its plan, and what it has used",
        "responses": {
          "200": {
            "description": "The account.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "email": {
                      "type": "string",
                      "format": "email"
                    },
                    "name": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "plan": {
                      "type": "string",
                      "description": "free, pro, or team."
                    },
                    "access": {
                      "type": "string",
                      "enum": [
                        "active",
                        "locked"
                      ]
                    },
                    "locked": {
                      "type": "boolean"
                    },
                    "usage": {
                      "type": "object",
                      "properties": {
                        "apps": {
                          "type": "integer"
                        },
                        "maxApps": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "description": "null means no limit."
                        },
                        "publicApps": {
                          "type": "integer"
                        },
                        "maxPublicApps": {
                          "type": [
                            "integer",
                            "null"
                          ]
                        },
                        "builds": {
                          "type": "integer"
                        },
                        "monthlyBuilds": {
                          "type": [
                            "integer",
                            "null"
                          ]
                        },
                        "agentRuns": {
                          "type": "integer"
                        },
                        "monthlyAgentRuns": {
                          "type": [
                            "integer",
                            "null"
                          ]
                        },
                        "periodStart": {
                          "type": "string",
                          "format": "date-time"
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "No credential, or one that is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Signed in against an account that no longer exists.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "account"
        ],
        "operationId": "renameAccount",
        "summary": "Change the display name",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Renamed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "name": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "`name` was not a string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No credential, or one that is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/account/tokens": {
      "get": {
        "tags": [
          "account"
        ],
        "operationId": "listTokens",
        "summary": "Every CLI token this account has minted",
        "responses": {
          "200": {
            "description": "The tokens. The secret itself is shown once, at creation, and never here.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tokens": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": [
                              "string",
                              "null"
                            ]
                          }
                        },
                        "additionalProperties": true
                      }
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "No credential, or one that is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "account"
        ],
        "operationId": "revokeToken",
        "summary": "Revoke one",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "revoke"
                ],
                "properties": {
                  "revoke": {
                    "type": "string",
                    "description": "The token id."
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "No token id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No credential, or one that is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No token with that id belongs to this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps": {
      "get": {
        "tags": [
          "apps"
        ],
        "operationId": "listApps",
        "summary": "Every app this account owns, including the ones still building",
        "responses": {
          "200": {
            "description": "The apps.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "apps": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "slug": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string",
                            "format": "uri"
                          },
                          "region": {
                            "type": "string"
                          },
                          "visibility": {
                            "type": "string",
                            "enum": [
                              "private",
                              "public"
                            ]
                          },
                          "deployedAt": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          }
                        },
                        "additionalProperties": true
                      }
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Not signed in. `apps` is present and empty beside the error, so a caller that renders the list does not have to branch.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "apps": {
                          "type": "array",
                          "items": {}
                        }
                      },
                      "additionalProperties": true
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{slug}": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "description": "The app's name, which is also the first label of its address.",
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]*$"
          }
        }
      ],
      "get": {
        "tags": [
          "apps"
        ],
        "operationId": "getApp",
        "summary": "One app: its address, its state, and whether a deploy is running",
        "responses": {
          "200": {
            "description": "The app.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "slug": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "repo": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "deploying": {
                      "type": "boolean"
                    },
                    "stage": {
                      "type": "string",
                      "description": "Which phase a running deploy is in; empty when none is."
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but this app belongs to somebody else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No app by that name.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{slug}/env": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "description": "The app's name, which is also the first label of its address.",
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]*$"
          }
        }
      ],
      "get": {
        "tags": [
          "apps"
        ],
        "operationId": "listEnv",
        "summary": "The names of an app's secrets — never the values",
        "description": "Values are in Secret Manager and are not readable through this API by anyone, including the owner. `note` explains an empty list that is empty for a reason other than 'there are none'.",
        "responses": {
          "200": {
            "description": "The key names.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "note": {
                      "type": "string"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but this app belongs to somebody else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "apps"
        ],
        "operationId": "setEnv",
        "summary": "Set or unset secrets",
        "description": "`set` and `unset` are alternatives, not a pair. A change reaches a running app in about ten seconds.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "set": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "KEY to value."
                  },
                  "unset": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Key names to remove."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Applied.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "note": {
                      "type": "string"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but this app belongs to somebody else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A deploy is in flight; the change would race it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Ours, not yours.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{slug}/domains": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "description": "The app's name, which is also the first label of its address.",
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]*$"
          }
        }
      ],
      "get": {
        "tags": [
          "access"
        ],
        "operationId": "listDomains",
        "summary": "Custom domains on this app, and the DNS records they need",
        "responses": {
          "200": {
            "description": "The domains.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "domains": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "hostname": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          }
                        },
                        "additionalProperties": true
                      }
                    },
                    "dns": {
                      "type": "object",
                      "additionalProperties": true,
                      "description": "The records to create, ready to read out to a registrar."
                    },
                    "allowed": {
                      "type": "boolean",
                      "description": "Whether this plan may attach one at all."
                    },
                    "visibility": {
                      "type": "string",
                      "enum": [
                        "private",
                        "public"
                      ]
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but this app belongs to somebody else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "access"
        ],
        "operationId": "attachDomain",
        "summary": "Attach a custom domain",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "hostname"
                ],
                "properties": {
                  "hostname": {
                    "type": "string"
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Attached. The certificate is issued asynchronously; poll this path until the domain reports active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "hostname": {
                      "type": "string"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Not a domain name, or one we refuse.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "This plan does not include custom domains.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but this app belongs to somebody else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Already attached, here or to another app.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "access"
        ],
        "operationId": "detachDomain",
        "summary": "Detach one",
        "parameters": [
          {
            "name": "hostname",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Detached.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but this app belongs to somebody else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{slug}/share": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "description": "The app's name, which is also the first label of its address.",
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]*$"
          }
        }
      ],
      "get": {
        "tags": [
          "access"
        ],
        "operationId": "getSharing",
        "summary": "Who can open this app",
        "responses": {
          "200": {
            "description": "The current grants and visibility.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "visibility": {
                      "type": "string",
                      "enum": [
                        "private",
                        "public"
                      ]
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but this app belongs to somebody else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "access"
        ],
        "operationId": "setSharing",
        "summary": "Make it public or private, or grant a person or a domain",
        "description": "Every app is private until this says otherwise. A grant is by email address or by email domain.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "visibility": {
                    "type": "string",
                    "enum": [
                      "private",
                      "public"
                    ]
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "domain": {
                    "type": "string"
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Applied.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Not a visibility, an address, or a domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "The account has no plan, or this would pass a sharing limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but this app belongs to somebody else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{slug}/errors": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "description": "The app's name, which is also the first label of its address.",
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]*$"
          }
        }
      ],
      "get": {
        "tags": [
          "diagnostics"
        ],
        "operationId": "listErrors",
        "summary": "Errors production actually caught, last seven days",
        "responses": {
          "200": {
            "description": "The errors.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but this app belongs to somebody else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{slug}/diagnose": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "description": "The app's name, which is also the first label of its address.",
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]*$"
          }
        }
      ],
      "post": {
        "tags": [
          "diagnostics"
        ],
        "operationId": "diagnose",
        "summary": "A fix prompt, written against what actually broke",
        "description": "With no body, it reads the last seven days of production errors and picks the one worth fixing. `healthy: true` means there was nothing to diagnose, which is an answer and not an error.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "error": {
                    "type": "string",
                    "description": "Diagnose this instead of choosing."
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The diagnosis, or a clean bill of health.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "subject",
                        "fixPrompt"
                      ],
                      "properties": {
                        "subject": {
                          "type": "string"
                        },
                        "fixPrompt": {
                          "type": "string"
                        }
                      },
                      "additionalProperties": true
                    },
                    {
                      "type": "object",
                      "required": [
                        "healthy"
                      ],
                      "properties": {
                        "healthy": {
                          "type": "boolean"
                        },
                        "message": {
                          "type": "string"
                        }
                      },
                      "additionalProperties": true
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but this app belongs to somebody else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Ours, not yours.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{slug}/patch": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "description": "The app's name, which is also the first label of its address.",
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]*$"
          }
        }
      ],
      "get": {
        "tags": [
          "diagnostics"
        ],
        "operationId": "getPatch",
        "summary": "The repair agent's diff for the last failed deploy",
        "description": "A unified diff, ready for `git apply`. Not JSON: the body is the patch.",
        "responses": {
          "200": {
            "description": "The diff.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "Not yours.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "The last deploy was not repaired by the agent, so there is no patch.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{slug}/logs/query": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "description": "The app's name, which is also the first label of its address.",
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]*$"
          }
        }
      ],
      "get": {
        "tags": [
          "diagnostics"
        ],
        "operationId": "queryLogs",
        "summary": "A page of logs",
        "parameters": [
          {
            "name": "severity",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Least severe level to include."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "A window, e.g. `1h`."
          }
        ],
        "responses": {
          "200": {
            "description": "The page, and the window it covers.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entries": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    },
                    "window": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but this app belongs to somebody else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The log backend did not answer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{slug}/logs/tail": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "description": "The app's name, which is also the first label of its address.",
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]*$"
          }
        }
      ],
      "get": {
        "tags": [
          "diagnostics"
        ],
        "operationId": "tailLogs",
        "summary": "Follow the logs",
        "description": "A stream that stays open. Read it as a stream; it does not end on its own.",
        "responses": {
          "200": {
            "description": "The stream.",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "Not yours.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{slug}/db": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "description": "The app's name, which is also the first label of its address.",
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]*$"
          }
        }
      ],
      "get": {
        "tags": [
          "data"
        ],
        "operationId": "browseDatabase",
        "summary": "Tables, or the rows of one",
        "parameters": [
          {
            "name": "table",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Omit for the table list."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The tables, or the rows.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Not a valid table name.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but this app belongs to somebody else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "data"
        ],
        "operationId": "queryDatabase",
        "summary": "Run one SELECT",
        "description": "Read-only and one statement, enforced server-side: anything that is not a single SELECT is refused rather than run.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "sql"
                ],
                "properties": {
                  "sql": {
                    "type": "string"
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The rows.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rows": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    },
                    "fields": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Not a SELECT, or more than one statement.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but this app belongs to somebody else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{slug}/git": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "description": "The app's name, which is also the first label of its address.",
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]*$"
          }
        }
      ],
      "get": {
        "tags": [
          "apps"
        ],
        "operationId": "getRepoLink",
        "summary": "The repository a push to which deploys this app",
        "responses": {
          "200": {
            "description": "The link, or `connected: false`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "connected"
                  ],
                  "properties": {
                    "connected": {
                      "type": "boolean"
                    },
                    "repo": {
                      "type": "string"
                    },
                    "branch": {
                      "type": "string"
                    },
                    "autoDeploy": {
                      "type": "boolean"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "404": {
            "description": "No app by that name, or not yours.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "apps"
        ],
        "operationId": "setRepoLink",
        "summary": "Change the branch, or turn deploy-on-push off",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "branch": {
                    "type": "string"
                  },
                  "autoDeploy": {
                    "type": "boolean"
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "connected": {
                      "type": "boolean"
                    },
                    "repo": {
                      "type": "string"
                    },
                    "branch": {
                      "type": "string"
                    },
                    "autoDeploy": {
                      "type": "boolean"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Not a branch name.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No app by that name, or not yours.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "No repository is connected to this app.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "apps"
        ],
        "operationId": "unlinkRepo",
        "summary": "Disconnect the repository",
        "responses": {
          "200": {
            "description": "Disconnected.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "404": {
            "description": "No app by that name, or not yours.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{slug}/exec": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "description": "The app's name, which is also the first label of its address.",
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]*$"
          }
        }
      ],
      "post": {
        "tags": [
          "apps"
        ],
        "operationId": "exec",
        "summary": "Run one command against a copy of the app's environment",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "command"
                ],
                "properties": {
                  "command": {
                    "type": "string"
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "What it printed, and what it exited with.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "output": {
                      "type": "string"
                    },
                    "exitCode": {
                      "type": "integer"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "No command.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but this app belongs to somebody else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Ours, not yours.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{slug}/rollback": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "description": "The app's name, which is also the first label of its address.",
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]*$"
          }
        }
      ],
      "post": {
        "tags": [
          "apps"
        ],
        "operationId": "rollback",
        "summary": "Put the previous version back in front of traffic",
        "responses": {
          "200": {
            "description": "Rolled back.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but this app belongs to somebody else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Ours, not yours.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{slug}/delete": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "description": "The app's name, which is also the first label of its address.",
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]*$"
          }
        }
      ],
      "post": {
        "tags": [
          "apps"
        ],
        "operationId": "deleteApp",
        "summary": "Delete the app, its images, its secrets and its database",
        "description": "Irreversible.",
        "responses": {
          "200": {
            "description": "Gone.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but this app belongs to somebody else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Ours, not yours.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{slug}/deploy-status": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "description": "The app's name, which is also the first label of its address.",
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]*$"
          }
        }
      ],
      "get": {
        "tags": [
          "deploy"
        ],
        "operationId": "getDeployStatus",
        "summary": "The running or last deploy, without opening a stream",
        "responses": {
          "200": {
            "description": "The deploy, or null.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deploy": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "additionalProperties": true
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but this app belongs to somebody else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/deploy": {
      "post": {
        "tags": [
          "deploy"
        ],
        "operationId": "deploy",
        "summary": "Ship, and stream the build",
        "description": "Answers `text/event-stream` and streams until the build ends. The reserved URL\narrives early and is not the app being live; the line that says live is.\n\nSource is either a git repository by URL, or an upload prepared with\n/api/deploy/upload-url.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "repo": {
                    "type": "string",
                    "description": "A git URL, or the object name of a prepared upload."
                  },
                  "slug": {
                    "type": "string",
                    "description": "Ship over an existing app. Omit to create one."
                  },
                  "secrets": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "run": {
                    "type": "string",
                    "description": "Override the production command. Omit and Bay decides."
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The build, as it happens.",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Malformed source reference.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No credential, or one that is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "No plan, or a plan limit reached. `reason` says which.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The deploy could not be started.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/deploy/preflight": {
      "post": {
        "tags": [
          "deploy"
        ],
        "operationId": "preflight",
        "summary": "Ask whether this exact source has already been shipped",
        "description": "`skip: true` means the build would produce what is already live, and the answer carries the URL.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "app": {
                    "type": "string"
                  },
                  "hash": {
                    "type": "string",
                    "description": "A hash of the source."
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Whether to skip.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "skip"
                  ],
                  "properties": {
                    "skip": {
                      "type": "boolean"
                    },
                    "slug": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "No credential, or one that is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/deploy/reserve": {
      "post": {
        "tags": [
          "deploy"
        ],
        "operationId": "reserve",
        "summary": "Take an address before the build starts",
        "description": "So that the URL can be printed immediately and answer while the build runs.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The reserved app.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "slug",
                    "url"
                  ],
                  "properties": {
                    "slug": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "name": {
                      "type": "string"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "No credential, or one that is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "No plan, or a plan limit reached. `reason` says which.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/deploy/upload-url": {
      "post": {
        "tags": [
          "deploy"
        ],
        "operationId": "uploadUrl",
        "summary": "A signed location to PUT a source archive to",
        "description": "For source that is not a git URL. PUT the archive to `url`, then start the deploy naming `object`.",
        "responses": {
          "200": {
            "description": "Where to put it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "No credential, or one that is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "An upload location could not be prepared.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  }
}
