{
  "openapi": "3.0.0",
  "paths": {
    "/health": {
      "get": {
        "operationId": "HealthController_getHealth",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Service is healthy",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "ok"
                      ]
                    },
                    "sites": {
                      "type": "integer",
                      "description": "Total active sites"
                    },
                    "uptime": {
                      "type": "integer",
                      "description": "Seconds since server start"
                    }
                  }
                }
              }
            }
          }
        },
        "summary": "Health check",
        "tags": [
          "Health"
        ]
      }
    },
    "/sites": {
      "get": {
        "operationId": "SitesController_listSites",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Site list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SiteListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List your sites",
        "tags": [
          "Sites"
        ]
      }
    },
    "/sites/{siteId}": {
      "get": {
        "operationId": "SitesController_getSite",
        "parameters": [
          {
            "name": "siteId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Site details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SiteDetailResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Site not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get site details with deploy history",
        "tags": [
          "Sites"
        ]
      },
      "delete": {
        "operationId": "SitesController_deleteSite",
        "parameters": [
          {
            "name": "siteId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Site deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SiteDeleteResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Site not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Delete a site",
        "tags": [
          "Sites"
        ]
      }
    },
    "/account": {
      "get": {
        "operationId": "AccountController_getAccount",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Account info",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountStatusDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "View account status",
        "tags": [
          "Account"
        ]
      }
    },
    "/account/email": {
      "post": {
        "operationId": "AccountController_attachEmail",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AttachEmailBodyDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Confirmation email sent",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Attach email to account (sends confirmation link)",
        "tags": [
          "Account"
        ]
      }
    },
    "/account/confirm": {
      "get": {
        "operationId": "AccountController_confirmEmail",
        "parameters": [
          {
            "name": "token",
            "required": true,
            "in": "query",
            "description": "Confirmation token from email",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirects to confirmation page"
          },
          "400": {
            "description": "Invalid or expired token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "summary": "Confirm email (from link in email)",
        "tags": [
          "Account"
        ]
      }
    },
    "/account/recover": {
      "post": {
        "operationId": "AccountController_recover",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RecoverBodyDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recovery email sent (if account exists)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponseDto"
                }
              }
            }
          }
        },
        "summary": "Recover API key via email",
        "tags": [
          "Account"
        ]
      }
    },
    "/deploy": {
      "post": {
        "operationId": "DeployController_deploy",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "site"
                ],
                "properties": {
                  "site": {
                    "type": "string",
                    "format": "binary",
                    "description": "Zip or tar.gz archive"
                  },
                  "key": {
                    "type": "string",
                    "description": "API key (alternative to Authorization header)"
                  },
                  "email": {
                    "type": "string",
                    "description": "Email to attach to the account"
                  },
                  "name": {
                    "type": "string",
                    "description": "Site ID to redeploy (alternative to POST /deploy/:siteId)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Site deployed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeployResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid archive",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "413": {
            "description": "Archive too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Deploy a new site",
        "tags": [
          "Deploy"
        ]
      }
    },
    "/deploy/{siteId}": {
      "post": {
        "operationId": "DeployController_redeploy",
        "parameters": [
          {
            "name": "siteId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "site"
                ],
                "properties": {
                  "site": {
                    "type": "string",
                    "format": "binary",
                    "description": "Zip or tar.gz archive"
                  },
                  "key": {
                    "type": "string",
                    "description": "API key (alternative to Authorization header)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Site redeployed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RedeployResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid archive",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Site not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Redeploy an existing site",
        "tags": [
          "Deploy"
        ]
      }
    }
  },
  "info": {
    "title": "DropWeb API",
    "description": "Static site hosting via API. Upload a zip or tar.gz, get a live URL. One curl, no signup.",
    "version": "1.0.0",
    "contact": {
      "name": "DropWeb",
      "url": "https://dropweb.app",
      "email": "abuse@dropweb.app"
    }
  },
  "tags": [],
  "servers": [
    {
      "url": "https://api.dropweb.app",
      "description": "Production"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearer": {
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "type": "http",
        "description": "API key starting with sk_"
      }
    },
    "schemas": {
      "SiteSummaryDto": {
        "type": "object",
        "properties": {
          "siteId": {
            "type": "string",
            "example": "gentle-amber-fox.dropweb.app"
          },
          "url": {
            "type": "string",
            "example": "https://gentle-amber-fox.dropweb.app"
          },
          "files": {
            "type": "number",
            "example": 12
          },
          "sizeBytes": {
            "type": "number",
            "example": 48210
          },
          "createdAt": {
            "type": "string",
            "example": "2026-02-12T12:00:00.000Z"
          },
          "expiresAt": {
            "type": "string",
            "example": "2026-02-19T12:00:00.000Z"
          }
        },
        "required": [
          "siteId",
          "url",
          "files",
          "sizeBytes",
          "createdAt",
          "expiresAt"
        ]
      },
      "SiteListResponseDto": {
        "type": "object",
        "properties": {
          "sites": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SiteSummaryDto"
            }
          }
        },
        "required": [
          "sites"
        ]
      },
      "ErrorBody": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "example": "UNAUTHORIZED"
          },
          "message": {
            "type": "string",
            "example": "Invalid or missing API key."
          },
          "retryAfter": {
            "type": "number",
            "example": 120,
            "description": "Seconds until next allowed request (rate-limit errors only)"
          }
        },
        "required": [
          "code",
          "message"
        ]
      },
      "ErrorResponseDto": {
        "type": "object",
        "properties": {
          "error": {
            "$ref": "#/components/schemas/ErrorBody"
          }
        },
        "required": [
          "error"
        ]
      },
      "DeployHistoryDto": {
        "type": "object",
        "properties": {
          "hash": {
            "type": "string",
            "example": "a1b2c3d4"
          },
          "files": {
            "type": "number",
            "example": 12
          },
          "sizeBytes": {
            "type": "number",
            "example": 48210
          },
          "createdAt": {
            "type": "string",
            "example": "2026-02-12T12:00:00.000Z"
          }
        },
        "required": [
          "hash",
          "files",
          "sizeBytes",
          "createdAt"
        ]
      },
      "SiteDetailResponseDto": {
        "type": "object",
        "properties": {
          "siteId": {
            "type": "string",
            "example": "gentle-amber-fox.dropweb.app"
          },
          "url": {
            "type": "string",
            "example": "https://gentle-amber-fox.dropweb.app"
          },
          "files": {
            "type": "number",
            "example": 12
          },
          "sizeBytes": {
            "type": "number",
            "example": 48210
          },
          "createdAt": {
            "type": "string",
            "example": "2026-02-12T12:00:00.000Z"
          },
          "expiresAt": {
            "type": "string",
            "example": "2026-02-19T12:00:00.000Z"
          },
          "deploys": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DeployHistoryDto"
            }
          }
        },
        "required": [
          "siteId",
          "url",
          "files",
          "sizeBytes",
          "createdAt",
          "expiresAt",
          "deploys"
        ]
      },
      "SiteDeleteResponseDto": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "example": "Site deleted"
          },
          "siteId": {
            "type": "string",
            "example": "gentle-amber-fox.dropweb.app"
          }
        },
        "required": [
          "message",
          "siteId"
        ]
      },
      "AccountStatusDto": {
        "type": "object",
        "properties": {
          "tier": {
            "type": "string",
            "enum": [
              "anonymous",
              "free"
            ],
            "example": "anonymous"
          },
          "email": {
            "type": "object",
            "nullable": true,
            "example": null
          },
          "sites": {
            "type": "number",
            "example": 1
          },
          "sitesLimit": {
            "type": "number",
            "example": 3
          },
          "expiryDays": {
            "type": "number",
            "example": 7
          },
          "createdAt": {
            "type": "string",
            "example": "2026-02-12T12:00:00.000Z"
          }
        },
        "required": [
          "tier",
          "email",
          "sites",
          "sitesLimit",
          "expiryDays",
          "createdAt"
        ]
      },
      "AttachEmailBodyDto": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "example": "you@example.com"
          },
          "key": {
            "type": "string",
            "description": "API key (alternative to Authorization header)"
          }
        },
        "required": [
          "email"
        ]
      },
      "MessageResponseDto": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "example": "Confirmation email sent. Check your inbox."
          }
        },
        "required": [
          "message"
        ]
      },
      "RecoverBodyDto": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "example": "you@example.com"
          }
        },
        "required": [
          "email"
        ]
      },
      "AccountBlockDto": {
        "type": "object",
        "properties": {
          "tier": {
            "type": "string",
            "enum": [
              "anonymous",
              "free"
            ],
            "example": "anonymous"
          },
          "email": {
            "type": "object",
            "nullable": true,
            "example": null
          },
          "sites": {
            "type": "number",
            "example": 1,
            "description": "Number of active sites"
          },
          "sitesLimit": {
            "type": "number",
            "example": 3,
            "description": "Max sites for this tier"
          },
          "expiryDays": {
            "type": "number",
            "example": 7,
            "description": "Days until new sites expire"
          }
        },
        "required": [
          "tier",
          "email",
          "sites",
          "sitesLimit",
          "expiryDays"
        ]
      },
      "DeployResponseDto": {
        "type": "object",
        "properties": {
          "siteId": {
            "type": "string",
            "example": "gentle-amber-fox.dropweb.app"
          },
          "url": {
            "type": "string",
            "example": "https://gentle-amber-fox.dropweb.app"
          },
          "screenshot": {
            "type": "object",
            "example": "https://preview.dropweb.app/ac654c827dff3918e33056002d757e2b.jpg",
            "nullable": true
          },
          "expiresAt": {
            "type": "string",
            "example": "2026-02-19T12:00:00.000Z"
          },
          "files": {
            "type": "number",
            "example": 12
          },
          "sizeBytes": {
            "type": "number",
            "example": 48210
          },
          "key": {
            "type": "string",
            "example": "sk_a1b2c3d4e5f6...",
            "description": "API key — only on anonymous deploy, shown once"
          },
          "account": {
            "$ref": "#/components/schemas/AccountBlockDto"
          },
          "hint": {
            "type": "string",
            "example": "Your site is live at https://gentle-amber-fox.dropweb.app — ..."
          }
        },
        "required": [
          "siteId",
          "url",
          "screenshot",
          "expiresAt",
          "files",
          "sizeBytes",
          "account",
          "hint"
        ]
      },
      "RedeployResponseDto": {
        "type": "object",
        "properties": {
          "siteId": {
            "type": "string",
            "example": "gentle-amber-fox.dropweb.app"
          },
          "url": {
            "type": "string",
            "example": "https://gentle-amber-fox.dropweb.app"
          },
          "screenshot": {
            "type": "object",
            "example": "https://preview.dropweb.app/ac654c827dff3918e33056002d757e2b.jpg",
            "nullable": true
          },
          "hash": {
            "type": "string",
            "example": "a1b2c3d4"
          },
          "expiresAt": {
            "type": "string",
            "example": "2026-02-19T12:00:00.000Z"
          },
          "files": {
            "type": "number",
            "example": 15
          },
          "sizeBytes": {
            "type": "number",
            "example": 61440
          },
          "account": {
            "$ref": "#/components/schemas/AccountBlockDto"
          },
          "hint": {
            "type": "string",
            "example": "Your site is live at https://gentle-amber-fox.dropweb.app — ..."
          }
        },
        "required": [
          "siteId",
          "url",
          "screenshot",
          "hash",
          "expiresAt",
          "files",
          "sizeBytes",
          "account",
          "hint"
        ]
      }
    }
  }
}