{
  "openapi": "3.1.0",
  "info": {
    "title": "Yeeet API",
    "version": "1.0.0",
    "description": "Atomic static-site deployments for humans, agents, and CI."
  },
  "servers": [{ "url": "/" }],
  "components": {
    "securitySchemes": {
      "bearer": { "type": "http", "scheme": "bearer" },
      "apiKey": { "type": "apiKey", "in": "header", "name": "X-API-Key" }
    },
    "schemas": {
      "ManifestFile": {
        "type": "object",
        "required": ["path", "size"],
        "properties": {
          "path": { "type": "string", "examples": ["index.html"] },
          "size": { "type": "integer", "minimum": 0 },
          "contentType": { "type": "string" },
          "checksum": { "type": "string" }
        }
      },
      "ApiError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": { "type": "string" },
              "message": { "type": "string" },
              "details": {}
            }
          }
        }
      }
    }
  },
  "security": [{ "bearer": [] }, { "apiKey": [] }],
  "paths": {
    "/api/v1/me": {
      "get": {
        "operationId": "getCurrentUser",
        "responses": {
          "200": { "description": "Current user" },
          "401": { "description": "Unauthorized" }
        }
      }
    },
    "/api/v1/sites": {
      "get": {
        "operationId": "listSites",
        "responses": {
          "200": { "description": "Live sites" },
          "401": { "description": "Unauthorized" }
        }
      }
    },
    "/api/v1/sites/{slug}": {
      "delete": {
        "operationId": "deleteOwnedSite",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Site, versions, domains, and objects deleted"
          },
          "404": { "description": "Site not found or not owned by the actor" }
        }
      }
    },
    "/api/v1/sites/{slug}/versions": {
      "get": {
        "operationId": "listSiteVersions",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Immutable version history and preview URLs"
          },
          "404": { "description": "Site not found" }
        }
      }
    },
    "/api/v1/sites/{slug}/versions/{deploymentId}/activate": {
      "post": {
        "operationId": "activateSiteVersion",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          },
          {
            "name": "deploymentId",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "minLength": 8 }
          }
        ],
        "responses": {
          "200": { "description": "Version atomically made live" },
          "409": { "description": "Version is ambiguous or not ready" }
        }
      }
    },
    "/api/v1/sites/{slug}/versions/{deploymentId}": {
      "delete": {
        "operationId": "deleteSiteVersion",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          },
          {
            "name": "deploymentId",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "minLength": 8 }
          }
        ],
        "responses": {
          "200": {
            "description": "Version and stored objects deleted; replacement live version returned"
          },
          "404": { "description": "Version not found" }
        }
      }
    },
    "/api/v1/sites/{slug}/versions/{deploymentId}/access": {
      "patch": {
        "operationId": "updateSiteVersionAccess",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          },
          {
            "name": "deploymentId",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "minLength": 8 }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "password": {
                    "oneOf": [
                      { "type": "string", "minLength": 8, "maxLength": 128 },
                      { "type": "null" }
                    ],
                    "description": "Set/change a password, or null to make public."
                  },
                  "rotateShareLink": {
                    "type": "boolean",
                    "description": "Invalidate the previous link and viewer cookies."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Updated privacy state and share URL" },
          "400": { "description": "Invalid password or empty access update" }
        }
      }
    },
    "/api/v1/sites/{slug}/domains": {
      "get": {
        "operationId": "listCustomDomains",
        "responses": {
          "200": {
            "description": "Custom domains, DNS records, and TLS status"
          }
        }
      },
      "post": {
        "operationId": "createCustomDomain",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["domain"],
                "properties": {
                  "domain": {
                    "type": "string",
                    "examples": ["docs.example.com"]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Domain attached; required DNS records returned"
          },
          "409": { "description": "Domain unavailable or already attached" },
          "503": {
            "description": "Railway domain automation is not configured"
          }
        }
      }
    },
    "/api/v1/sites/{slug}/domains/{domainId}/refresh": {
      "post": {
        "operationId": "refreshCustomDomain",
        "responses": {
          "200": { "description": "Current Railway DNS and certificate status" }
        }
      }
    },
    "/api/v1/sites/{slug}/domains/{domainId}": {
      "delete": {
        "operationId": "deleteCustomDomain",
        "responses": {
          "200": { "description": "Domain detached from Railway and Yeeet" }
        }
      }
    },
    "/api/v1/deployments": {
      "get": {
        "operationId": "listDeployments",
        "responses": { "200": { "description": "Recent deployments" } }
      },
      "post": {
        "operationId": "createDeployment",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["files"],
                "properties": {
                  "slug": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
                    "description": "Optional. A readable random subdomain is generated when omitted."
                  },
                  "spaFallback": {
                    "type": "boolean",
                    "default": true,
                    "description": "Serve index.html for unmatched browser navigation paths."
                  },
                  "password": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 128,
                    "description": "Optional password protection. Completion returns a signed one-click share URL."
                  },
                  "source": { "enum": ["web", "cli", "api"] },
                  "files": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 5000,
                    "items": { "$ref": "#/components/schemas/ManifestFile" }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "description": "Deployment and signed upload URLs created" },
          "400": { "description": "Invalid manifest" },
          "409": { "description": "Site name is owned by another user" }
        }
      }
    },
    "/api/v1/deployments/{deploymentId}/complete": {
      "post": {
        "operationId": "completeDeployment",
        "parameters": [
          {
            "name": "deploymentId",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "responses": {
          "200": {
            "description": "Deployment verified and atomically published"
          },
          "422": {
            "description": "One or more objects are missing or incomplete"
          }
        }
      }
    },
    "/api/v1/admin": {
      "get": {
        "operationId": "getAdminOverview",
        "responses": {
          "200": {
            "description": "Users, sites, invitations, counts, and audit events"
          },
          "403": { "description": "Administrator access required" }
        }
      }
    },
    "/api/v1/admin/invitations": {
      "post": {
        "operationId": "createInvitation",
        "responses": {
          "201": {
            "description": "Invitation created; plaintext code is returned once"
          }
        }
      }
    },
    "/api/v1/admin/invitations/{invitationId}/revoke": {
      "post": {
        "operationId": "revokeInvitation",
        "responses": { "200": { "description": "Invitation revoked" } }
      }
    },
    "/api/v1/admin/users/{userId}/ban": {
      "post": {
        "operationId": "suspendUser",
        "responses": {
          "200": { "description": "User suspended and active sessions revoked" }
        }
      }
    },
    "/api/v1/admin/users/{userId}/unban": {
      "post": {
        "operationId": "reinstateUser",
        "responses": { "200": { "description": "User reinstated" } }
      }
    },
    "/api/v1/admin/users/{userId}/role": {
      "post": {
        "operationId": "setUserRole",
        "responses": { "200": { "description": "User role changed" } }
      }
    },
    "/api/v1/admin/sites/{siteId}": {
      "delete": {
        "operationId": "deleteSite",
        "responses": {
          "200": { "description": "Site, versions, and stored objects deleted" }
        }
      }
    }
  }
}
