{
  "openapi": "3.1.0",
  "info": {
    "title": "dudbox API",
    "version": "1",
    "description": "Deploy and run apps on dudbox. Docs for people and agents: https://dudbox.com/docs/api (Markdown: https://dudbox.com/docs/api/index.md). Fields documented here stay; a change that breaks callers goes to /api/v2."
  },
  "servers": [
    {
      "url": "https://dudbox.com"
    }
  ],
  "security": [
    {
      "token": []
    }
  ],
  "paths": {
    "/api/v1/me": {
      "get": {
        "operationId": "me",
        "summary": "Check your token",
        "tags": [
          "auth"
        ],
        "description": "The user, organization and token that this request runs as.",
        "responses": {
          "200": {
            "description": "Check your token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/templates": {
      "get": {
        "operationId": "templates",
        "summary": "List one-click apps",
        "tags": [
          "services"
        ],
        "responses": {
          "200": {
            "description": "List one-click apps",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateList"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/services": {
      "get": {
        "operationId": "services",
        "summary": "List your services",
        "tags": [
          "services"
        ],
        "responses": {
          "200": {
            "description": "List your services",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceList"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "create",
        "summary": "Add a one-click app",
        "tags": [
          "services"
        ],
        "description": "Makes the app in your project and deploys it. The answer has pending_deployment; wait for it with GET /api/v1/deployments/{id}?wait=300.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateFromTemplate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Add a one-click app",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Service"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/repos": {
      "get": {
        "operationId": "repos",
        "summary": "List GitHub repos you can import",
        "tags": [
          "services"
        ],
        "description": "The repos where you installed the dudbox GitHub App, through your GitHub login.",
        "responses": {
          "200": {
            "description": "List GitHub repos you can import",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RepoList"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/services/import": {
      "post": {
        "operationId": "import",
        "summary": "Import a GitHub repo",
        "tags": [
          "services"
        ],
        "description": "Makes a service from the repo and deploys the branch's newest commit. Every push to the branch deploys again.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ImportRepo"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Import a GitHub repo",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Service"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/services/{project}/{service}": {
      "get": {
        "operationId": "service",
        "summary": "Get one service",
        "tags": [
          "services"
        ],
        "parameters": [
          {
            "name": "project",
            "in": "path",
            "required": true,
            "description": "Project name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          },
          {
            "name": "service",
            "in": "path",
            "required": true,
            "description": "Service name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Get one service",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceDetail"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete",
        "summary": "Delete a service",
        "tags": [
          "services"
        ],
        "description": "Stops the app and deletes it. Send the service name as confirm.",
        "parameters": [
          {
            "name": "project",
            "in": "path",
            "required": true,
            "description": "Project name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          },
          {
            "name": "service",
            "in": "path",
            "required": true,
            "description": "Service name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfirmDelete"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Done. No body."
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/services/{project}/{service}/deployments": {
      "get": {
        "operationId": "deployments",
        "summary": "List deployments",
        "tags": [
          "deployments"
        ],
        "parameters": [
          {
            "name": "project",
            "in": "path",
            "required": true,
            "description": "Project name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          },
          {
            "name": "service",
            "in": "path",
            "required": true,
            "description": "Service name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List deployments",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeploymentList"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/services/{project}/{service}/redeploy": {
      "post": {
        "operationId": "redeploy",
        "summary": "Redeploy",
        "tags": [
          "deployments"
        ],
        "description": "Starts the live image again with the current env vars, with no build. A one-click app pulls its image again. Use it after you change env vars.",
        "parameters": [
          {
            "name": "project",
            "in": "path",
            "required": true,
            "description": "Project name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          },
          {
            "name": "service",
            "in": "path",
            "required": true,
            "description": "Service name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Redeploy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deployment"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/services/{project}/{service}/deploy": {
      "post": {
        "operationId": "deploy",
        "summary": "Deploy the newest commit",
        "tags": [
          "deployments"
        ],
        "description": "For a service from GitHub: builds the newest commit of its branch.",
        "parameters": [
          {
            "name": "project",
            "in": "path",
            "required": true,
            "description": "Project name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          },
          {
            "name": "service",
            "in": "path",
            "required": true,
            "description": "Service name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Deploy the newest commit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deployment"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/services/{project}/{service}/rollback": {
      "post": {
        "operationId": "rollback",
        "summary": "Roll back",
        "tags": [
          "deployments"
        ],
        "description": "Makes an older deployment live again, with no build. The body may be left out.",
        "parameters": [
          {
            "name": "project",
            "in": "path",
            "required": true,
            "description": "Project name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          },
          {
            "name": "service",
            "in": "path",
            "required": true,
            "description": "Service name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Rollback"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Roll back",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deployment"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/deployments/{id}": {
      "get": {
        "operationId": "deployment",
        "summary": "Get a deployment",
        "tags": [
          "deployments"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Deployment ID.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "wait",
            "in": "query",
            "required": false,
            "description": "Wait up to this many seconds for the deployment to be done, then answer. Default 0: answer at once.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 300
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Get a deployment",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deployment"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/deployments/{id}/log": {
      "get": {
        "operationId": "log",
        "summary": "Get the build log",
        "tags": [
          "deployments"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Deployment ID.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Skip this much text: the offset of the last answer.",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Get the build log",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeploymentLog"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/deployments/{id}/events": {
      "get": {
        "operationId": "events",
        "summary": "Follow a deployment live",
        "tags": [
          "deployments"
        ],
        "description": "Server-sent events until the deployment is done.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Deployment ID.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Skip this much build log text.",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Server-sent events: \"deployment\": A Deployment, at the start and on each status change. \"log\": New build log text (a JSON string). \"end\": The final status (a JSON string). The stream then closes.",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/deployments/{id}/cancel": {
      "post": {
        "operationId": "cancel",
        "summary": "Cancel a deployment",
        "tags": [
          "deployments"
        ],
        "description": "Stops a deployment that is still queued, building or starting. The live one stays.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Deployment ID.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cancel a deployment",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deployment"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/services/{project}/{service}/env": {
      "get": {
        "operationId": "env",
        "summary": "List env vars",
        "tags": [
          "env"
        ],
        "description": "Secret values are null here.",
        "parameters": [
          {
            "name": "project",
            "in": "path",
            "required": true,
            "description": "Project name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          },
          {
            "name": "service",
            "in": "path",
            "required": true,
            "description": "Service name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List env vars",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EnvList"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/services/{project}/{service}/env/{key}": {
      "get": {
        "operationId": "envGet",
        "summary": "Get one env var",
        "tags": [
          "env"
        ],
        "description": "With the real value, also for a secret. This is logged.",
        "parameters": [
          {
            "name": "project",
            "in": "path",
            "required": true,
            "description": "Project name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          },
          {
            "name": "service",
            "in": "path",
            "required": true,
            "description": "Service name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          },
          {
            "name": "key",
            "in": "path",
            "required": true,
            "description": "Env var name.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,127}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Get one env var",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EnvVar"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "envSet",
        "summary": "Set an env var",
        "tags": [
          "env"
        ],
        "description": "Makes or changes it. The app gets it on the next deploy: POST .../redeploy.",
        "parameters": [
          {
            "name": "project",
            "in": "path",
            "required": true,
            "description": "Project name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          },
          {
            "name": "service",
            "in": "path",
            "required": true,
            "description": "Service name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          },
          {
            "name": "key",
            "in": "path",
            "required": true,
            "description": "Env var name.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,127}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetEnv"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Set an env var",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EnvVar"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "envDelete",
        "summary": "Delete an env var",
        "tags": [
          "env"
        ],
        "description": "The app loses it on the next deploy: POST .../redeploy.",
        "parameters": [
          {
            "name": "project",
            "in": "path",
            "required": true,
            "description": "Project name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          },
          {
            "name": "service",
            "in": "path",
            "required": true,
            "description": "Service name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          },
          {
            "name": "key",
            "in": "path",
            "required": true,
            "description": "Env var name.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,127}$"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Done. No body."
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/services/{project}/{service}/logs": {
      "get": {
        "operationId": "logs",
        "summary": "Read app logs",
        "tags": [
          "logs"
        ],
        "description": "What the app wrote to stdout and stderr, from all deployments, kept 3 days. With follow=true the answer is a stream of server-sent events instead.",
        "parameters": [
          {
            "name": "project",
            "in": "path",
            "required": true,
            "description": "Project name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          },
          {
            "name": "service",
            "in": "path",
            "required": true,
            "description": "Service name.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
            }
          },
          {
            "name": "lines",
            "in": "query",
            "required": false,
            "description": "How many of the newest lines. Default 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 5000
            }
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "description": "Only lines from this time on.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "until",
            "in": "query",
            "required": false,
            "description": "Only lines before this time.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "deployment",
            "in": "query",
            "required": false,
            "description": "Only lines of this deployment.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "follow",
            "in": "query",
            "required": false,
            "description": "Keep the connection open and send new lines as server-sent events: \"line\" (a LogLine), and \"end\" once when the history is sent.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Read app logs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LogList"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/openapi.json": {
      "get": {
        "operationId": "openapi",
        "summary": "Get this API as OpenAPI 3.1",
        "tags": [
          "auth"
        ],
        "description": "No token needed.",
        "responses": {
          "200": {
            "description": "This document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": []
      }
    }
  },
  "components": {
    "securitySchemes": {
      "token": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API token (dud_…) from https://dudbox.com/account."
      }
    },
    "schemas": {
      "Commit": {
        "type": "object",
        "properties": {
          "sha": {
            "type": [
              "string",
              "null"
            ],
            "description": "Full Git commit SHA."
          },
          "message": {
            "type": [
              "string",
              "null"
            ],
            "description": "First line of the commit message."
          },
          "branch": {
            "type": [
              "string",
              "null"
            ]
          },
          "author": {
            "type": [
              "string",
              "null"
            ]
          },
          "repo": {
            "type": [
              "string",
              "null"
            ],
            "description": "GitHub owner/name."
          }
        },
        "required": [
          "sha",
          "message",
          "branch",
          "author",
          "repo"
        ],
        "additionalProperties": false,
        "description": "The commit a deployment built, when it came from Git."
      },
      "Deployment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Deployment ID. Use it with /api/v1/deployments/{id}.",
            "examples": [
              42
            ]
          },
          "service": {
            "type": "string",
            "description": "The service, as project/service.",
            "examples": [
              "o3k9x2a/uptime-kuma"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "building",
              "starting",
              "live",
              "failed",
              "superseded",
              "cancelled"
            ],
            "description": "queued, building and starting: in progress. live: it serves traffic now. failed: the build or the health check failed; the app before it stays live. superseded: it was live, a newer deployment replaced it. cancelled: stopped before it went live."
          },
          "done": {
            "type": "boolean",
            "description": "true when the status will not change any more (live, failed, superseded, cancelled)."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why it failed, in one line."
          },
          "trigger": {
            "type": [
              "string",
              "null"
            ],
            "description": "What started it, e.g. template, git, redeploy, rollback, dashboard (also this API, for now). More values may come."
          },
          "created_at": {
            "type": "string",
            "description": "When it was made.",
            "format": "date-time",
            "examples": [
              "2026-11-04T09:30:00Z"
            ]
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "examples": [
              "2026-11-04T09:30:00Z"
            ],
            "description": "When it went live, failed or was cancelled."
          },
          "commit": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Commit"
              },
              {
                "type": "null"
              }
            ]
          },
          "rollback_of": {
            "type": [
              "integer",
              "null"
            ],
            "description": "It runs the image of this deployment again (a rollback)."
          },
          "can_rollback": {
            "type": "boolean",
            "description": "Its image is still kept: POST .../rollback with {\"to\": id} makes it live again without a build."
          }
        },
        "required": [
          "id",
          "service",
          "status",
          "done",
          "error",
          "trigger",
          "created_at",
          "finished_at",
          "commit",
          "rollback_of",
          "can_rollback"
        ],
        "additionalProperties": false
      },
      "Login": {
        "type": "object",
        "properties": {
          "user": {
            "type": "string",
            "description": "The admin user name that dudbox made for this app."
          },
          "password_env": {
            "type": "string",
            "description": "The env var with the password: GET .../env/{password_env}."
          }
        },
        "required": [
          "user",
          "password_env"
        ],
        "additionalProperties": false,
        "description": "The first admin login of a one-click app."
      },
      "Usage": {
        "type": "object",
        "properties": {
          "cpu_percent": {
            "type": "number",
            "description": "CPU now, in percent of one core."
          },
          "memory_bytes": {
            "type": "integer",
            "description": "Memory now."
          }
        },
        "required": [
          "cpu_percent",
          "memory_bytes"
        ],
        "additionalProperties": false
      },
      "Service": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "project/service. Use it in paths: /api/v1/services/{project}/{service}.",
            "examples": [
              "o3k9x2a/uptime-kuma"
            ]
          },
          "project": {
            "type": "string",
            "examples": [
              "o3k9x2a"
            ]
          },
          "name": {
            "type": "string",
            "examples": [
              "uptime-kuma"
            ]
          },
          "region": {
            "type": "string",
            "description": "Where it runs.",
            "examples": [
              "eu"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "live",
              "deploying",
              "failed",
              "down",
              "new"
            ],
            "description": "live: the app runs. deploying: a deployment is queued, building or starting. failed: the newest deployment failed and nothing runs. down: the app should run but its container stopped. new: never deployed."
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "examples": [
              "https://uptime-kuma-o3k9x2a.dudbox.app"
            ],
            "description": "https://… address of a public app; null for a private one."
          },
          "domains": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "All its domains, first one is the main one."
          },
          "source": {
            "type": "string",
            "enum": [
              "template",
              "github",
              "upload"
            ],
            "description": "template: a one-click app or a database. github: built from a GitHub repo. upload: built from uploaded files."
          },
          "template": {
            "type": [
              "string",
              "null"
            ],
            "description": "The one-click template, for source template."
          },
          "repo": {
            "type": [
              "string",
              "null"
            ],
            "description": "GitHub owner/name, for source github. A push to branch deploys."
          },
          "branch": {
            "type": [
              "string",
              "null"
            ]
          },
          "live_deployment": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Deployment"
              },
              {
                "type": "null"
              }
            ]
          },
          "pending_deployment": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Deployment"
              },
              {
                "type": "null"
              }
            ]
          },
          "login": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Login"
              },
              {
                "type": "null"
              }
            ]
          },
          "usage": {
            "$ref": "#/components/schemas/Usage"
          }
        },
        "required": [
          "id",
          "project",
          "name",
          "region",
          "status",
          "url",
          "domains",
          "source",
          "template",
          "repo",
          "branch",
          "live_deployment",
          "pending_deployment",
          "login",
          "usage"
        ],
        "additionalProperties": false
      },
      "Volume": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "path": {
            "type": "string",
            "description": "Where it is mounted in the container."
          }
        },
        "required": [
          "name",
          "path"
        ],
        "additionalProperties": false
      },
      "ServiceDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "project/service. Use it in paths: /api/v1/services/{project}/{service}.",
            "examples": [
              "o3k9x2a/uptime-kuma"
            ]
          },
          "project": {
            "type": "string",
            "examples": [
              "o3k9x2a"
            ]
          },
          "name": {
            "type": "string",
            "examples": [
              "uptime-kuma"
            ]
          },
          "region": {
            "type": "string",
            "description": "Where it runs.",
            "examples": [
              "eu"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "live",
              "deploying",
              "failed",
              "down",
              "new"
            ],
            "description": "live: the app runs. deploying: a deployment is queued, building or starting. failed: the newest deployment failed and nothing runs. down: the app should run but its container stopped. new: never deployed."
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "examples": [
              "https://uptime-kuma-o3k9x2a.dudbox.app"
            ],
            "description": "https://… address of a public app; null for a private one."
          },
          "domains": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "All its domains, first one is the main one."
          },
          "source": {
            "type": "string",
            "enum": [
              "template",
              "github",
              "upload"
            ],
            "description": "template: a one-click app or a database. github: built from a GitHub repo. upload: built from uploaded files."
          },
          "template": {
            "type": [
              "string",
              "null"
            ],
            "description": "The one-click template, for source template."
          },
          "repo": {
            "type": [
              "string",
              "null"
            ],
            "description": "GitHub owner/name, for source github. A push to branch deploys."
          },
          "branch": {
            "type": [
              "string",
              "null"
            ]
          },
          "live_deployment": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Deployment"
              },
              {
                "type": "null"
              }
            ]
          },
          "pending_deployment": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Deployment"
              },
              {
                "type": "null"
              }
            ]
          },
          "login": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Login"
              },
              {
                "type": "null"
              }
            ]
          },
          "usage": {
            "$ref": "#/components/schemas/Usage"
          },
          "port": {
            "type": "integer",
            "description": "The port the app listens on."
          },
          "health_path": {
            "type": [
              "string",
              "null"
            ],
            "description": "HTTP path of the health check; null: the port must answer."
          },
          "release_command": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "npx prisma migrate deploy"
            ],
            "description": "Shell command that runs once per deploy, in the new version, before it starts (database migrations); null: none."
          },
          "root_dir": {
            "type": [
              "string",
              "null"
            ],
            "description": "Folder in the repo that is built, for source github."
          },
          "volumes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Volume"
            },
            "description": "Its saved folders, backed up every night."
          },
          "deployments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Deployment"
            },
            "description": "Recent deployments, newest first (at most 20)."
          }
        },
        "required": [
          "id",
          "project",
          "name",
          "region",
          "status",
          "url",
          "domains",
          "source",
          "template",
          "repo",
          "branch",
          "live_deployment",
          "pending_deployment",
          "login",
          "usage",
          "port",
          "health_path",
          "release_command",
          "root_dir",
          "volumes",
          "deployments"
        ],
        "additionalProperties": false
      },
      "ServiceList": {
        "type": "object",
        "properties": {
          "services": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Service"
            }
          }
        },
        "required": [
          "services"
        ],
        "additionalProperties": false
      },
      "Template": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Use it as \"template\" in POST /api/v1/services.",
            "examples": [
              "uptime-kuma"
            ]
          },
          "title": {
            "type": "string",
            "examples": [
              "Uptime Kuma"
            ]
          },
          "tagline": {
            "type": "string"
          },
          "about": {
            "type": "string"
          },
          "public": {
            "type": "boolean",
            "description": "It gets a public https address."
          },
          "login": {
            "type": "boolean",
            "description": "dudbox makes its first admin login."
          },
          "volume": {
            "type": "boolean",
            "description": "It keeps data, backed up every night."
          },
          "deployable": {
            "type": "boolean",
            "description": "POST /api/v1/services can deploy it now (it asks no questions)."
          },
          "kind": {
            "type": "string",
            "enum": [
              "app",
              "database"
            ],
            "description": "app: a one-click app. database: Postgres, MySQL or Redis, private to the apps in its project. More values may come."
          }
        },
        "required": [
          "name",
          "title",
          "tagline",
          "about",
          "public",
          "login",
          "volume",
          "deployable",
          "kind"
        ],
        "additionalProperties": false
      },
      "TemplateList": {
        "type": "object",
        "properties": {
          "templates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Template"
            }
          }
        },
        "required": [
          "templates"
        ],
        "additionalProperties": false
      },
      "DeploymentList": {
        "type": "object",
        "properties": {
          "deployments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Deployment"
            }
          }
        },
        "required": [
          "deployments"
        ],
        "additionalProperties": false
      },
      "DeploymentLog": {
        "type": "object",
        "properties": {
          "deployment_id": {
            "type": "integer"
          },
          "log": {
            "type": "string",
            "description": "Build and start log text from offset on."
          },
          "offset": {
            "type": "integer",
            "description": "Pass it as ?offset= next time to get only new text."
          },
          "done": {
            "type": "boolean",
            "description": "The deployment is done; no more text will come."
          }
        },
        "required": [
          "deployment_id",
          "log",
          "offset",
          "done"
        ],
        "additionalProperties": false
      },
      "EnvVar": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "examples": [
              "DATABASE_URL"
            ]
          },
          "value": {
            "type": [
              "string",
              "null"
            ],
            "description": "The value. null in lists when secret is true: GET .../env/{key} shows it."
          },
          "secret": {
            "type": "boolean",
            "description": "Secret values are hidden in lists and masked in logs."
          }
        },
        "required": [
          "key",
          "value",
          "secret"
        ],
        "additionalProperties": false
      },
      "EnvList": {
        "type": "object",
        "properties": {
          "env": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EnvVar"
            }
          }
        },
        "required": [
          "env"
        ],
        "additionalProperties": false
      },
      "LogLine": {
        "type": "object",
        "properties": {
          "time": {
            "type": "string",
            "description": "When the app wrote it.",
            "format": "date-time",
            "examples": [
              "2026-11-04T09:30:00Z"
            ]
          },
          "deployment": {
            "type": "integer",
            "description": "The deployment whose container wrote it."
          },
          "stream": {
            "type": "string",
            "enum": [
              "out",
              "err"
            ]
          },
          "message": {
            "type": "string"
          }
        },
        "required": [
          "time",
          "deployment",
          "stream",
          "message"
        ],
        "additionalProperties": false
      },
      "LogList": {
        "type": "object",
        "properties": {
          "lines": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LogLine"
            },
            "description": "Oldest first."
          }
        },
        "required": [
          "lines"
        ],
        "additionalProperties": false
      },
      "Repo": {
        "type": "object",
        "properties": {
          "full_name": {
            "type": "string",
            "description": "owner/name",
            "examples": [
              "acme/shop"
            ]
          },
          "private": {
            "type": "boolean"
          },
          "default_branch": {
            "type": "string"
          },
          "pushed_at": {
            "type": "string",
            "description": "Last push.",
            "format": "date-time",
            "examples": [
              "2026-11-04T09:30:00Z"
            ]
          }
        },
        "required": [
          "full_name",
          "private",
          "default_branch",
          "pushed_at"
        ],
        "additionalProperties": false
      },
      "RepoList": {
        "type": "object",
        "properties": {
          "installed": {
            "type": "boolean",
            "description": "The dudbox GitHub App is installed on at least one of your GitHub accounts."
          },
          "install_url": {
            "type": "string",
            "description": "Open this in a browser to give dudbox more repos.",
            "format": "uri"
          },
          "repos": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Repo"
            },
            "description": "Repos you can import, newest push first."
          }
        },
        "required": [
          "installed",
          "install_url",
          "repos"
        ],
        "additionalProperties": false
      },
      "Me": {
        "type": "object",
        "properties": {
          "user": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "email": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "name",
              "email"
            ],
            "additionalProperties": false
          },
          "org": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "name"
            ],
            "additionalProperties": false
          },
          "token": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "name"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "user",
          "org",
          "token"
        ],
        "additionalProperties": false
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "What went wrong, in one sentence."
          },
          "code": {
            "type": "string",
            "enum": [
              "bad_request",
              "unauthorized",
              "forbidden",
              "not_found",
              "conflict",
              "too_large",
              "rate_limited",
              "unavailable",
              "internal"
            ],
            "description": "Stable: test this in scripts, not the text of error."
          },
          "hint": {
            "type": [
              "string",
              "null"
            ],
            "description": "The next thing to try, often a call."
          }
        },
        "required": [
          "error",
          "code",
          "hint"
        ],
        "additionalProperties": false
      },
      "CreateFromTemplate": {
        "type": "object",
        "properties": {
          "template": {
            "type": "string",
            "description": "A template name from GET /api/v1/templates.",
            "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$",
            "examples": [
              "uptime-kuma"
            ]
          }
        },
        "required": [
          "template"
        ],
        "additionalProperties": false
      },
      "ImportRepo": {
        "type": "object",
        "properties": {
          "repo": {
            "type": "string",
            "description": "GitHub owner/name, one of GET /api/v1/repos.",
            "pattern": "^[A-Za-z0-9_.-]{1,100}\\/[A-Za-z0-9_.-]{1,100}$",
            "examples": [
              "acme/shop"
            ]
          },
          "branch": {
            "type": "string",
            "description": "The branch that deploys. Default: the repo's default branch.",
            "maxLength": 200
          },
          "dir": {
            "type": "string",
            "description": "Folder to build, for a monorepo. Default: the repo root.",
            "maxLength": 300
          },
          "name": {
            "type": "string",
            "description": "Name of the new service. Default: made from the repo name.",
            "pattern": "^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$"
          },
          "env": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "maxLength": 32768
            },
            "propertyNames": {
              "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,127}$"
            },
            "description": "Env vars, set as secrets before the first deploy."
          }
        },
        "required": [
          "repo"
        ],
        "additionalProperties": false
      },
      "Rollback": {
        "type": "object",
        "properties": {
          "to": {
            "type": "integer",
            "description": "Deployment ID to go back to. Default: the one before the live one.",
            "minimum": 1
          }
        },
        "required": [],
        "additionalProperties": false
      },
      "SetEnv": {
        "type": "object",
        "properties": {
          "value": {
            "type": "string",
            "maxLength": 32768
          },
          "secret": {
            "type": "boolean",
            "description": "Hide the value in lists and mask it in logs. Default: false."
          }
        },
        "required": [
          "value"
        ],
        "additionalProperties": false
      },
      "ConfirmDelete": {
        "type": "object",
        "properties": {
          "confirm": {
            "type": "string",
            "description": "The service name again, to confirm.",
            "examples": [
              "uptime-kuma"
            ]
          }
        },
        "required": [
          "confirm"
        ],
        "additionalProperties": false
      }
    }
  }
}
