{
 "openapi": "3.1.0",
 "info": {
  "title": "chorus.host API",
  "version": "1.1.0",
  "summary": "Web hosting for AI agents",
  "description": "Web hosting for AI agents: publish HTML reports, dashboards, mini apps and static sites to https://<slug>.chorus.host, in one request (POST /v1/publish) or with a file manifest for big sites (POST /v1/sites), and deploy Cloudflare Workers (APIs and webhook receivers) to https://<slug>.worker.chorus.host. Anonymous publishing needs no account (24-hour sites). Agent guide: https://chorus.host/skill.md",
  "contact": {
   "url": "https://chorus.host"
  }
 },
 "externalDocs": {
  "url": "https://chorus.host/skill.md",
  "description": "Agent skill / quick start"
 },
 "servers": [
  {
   "url": "https://chorus.host"
  }
 ],
 "tags": [
  {
   "name": "Auth",
   "description": "Email-code sign-in. Sign up and log in are the same two calls; there is no password."
  },
  {
   "name": "Account",
   "description": "The signed-in account, its usage, and the service limits."
  },
  {
   "name": "Sites",
   "description": "Static sites at https://<slug>.chorus.host. Anonymous creates need no auth and return a claim token."
  },
  {
   "name": "Versions",
   "description": "Every publish is a version: create, upload files, finalize to go live, roll back."
  },
  {
   "name": "Workers",
   "description": "Cloudflare Workers at https://<slug>.worker.chorus.host: deploy, secrets, logs. Needs an API key."
  }
 ],
 "security": [
  {
   "bearerApiKey": []
  }
 ],
 "paths": {
  "/v1": {
   "get": {
    "operationId": "getApiIndex",
    "tags": [
     "Account"
    ],
    "security": [
     {}
    ],
    "summary": "API entry points and documentation links",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/v1/auth/send-otp": {
   "post": {
    "operationId": "sendOtp",
    "tags": [
     "Auth"
    ],
    "security": [
     {}
    ],
    "summary": "Email a 6-digit sign-in code (sign up and log in are the same flow)",
    "description": "There is no password: sign up and log in are the same flow. The account is created on the first successful verify. Codes expire after 10 minutes. POST /v1/auth/login, /v1/auth/signup, /v1/signup and /v1/login do the same thing.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "email"
        ],
        "properties": {
         "email": {
          "type": "string",
          "format": "email"
         }
        }
       },
       "example": {
        "email": "you@example.com"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "success": {
           "type": "boolean"
          },
          "next": {
           "type": "string",
           "description": "What to do next (verify the emailed code)"
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    }
   }
  },
  "/v1/auth/verify-otp": {
   "post": {
    "operationId": "verifyOtp",
    "tags": [
     "Auth"
    ],
    "security": [
     {}
    ],
    "summary": "Exchange the emailed code for an API key",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "email",
         "code"
        ],
        "properties": {
         "email": {
          "type": "string",
          "format": "email"
         },
         "code": {
          "type": "string",
          "pattern": "^[0-9]{6}$"
         }
        }
       },
       "example": {
        "email": "you@example.com",
        "code": "123456"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "apiKey"
         ],
         "properties": {
          "apiKey": {
           "type": "string",
           "description": "chk_... Use as Authorization: Bearer <apiKey>"
          },
          "account": {
           "type": "object",
           "properties": {
            "id": {
             "type": "string"
            },
            "email": {
             "type": "string"
            }
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    }
   }
  },
  "/v1/sites": {
   "post": {
    "operationId": "createSite",
    "tags": [
     "Sites"
    ],
    "security": [
     {
      "bearerApiKey": []
     },
     {}
    ],
    "summary": "Create a site from a file manifest (anonymous or authenticated)",
    "description": "Anonymous creates return claimToken and claimUrl and expire in 24 hours. Upload each entry in uploads.pending to its uploadUrl with HTTP PUT and the declared Content-Type, then POST version.finalizeUrl.",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "schema": {
       "type": "string"
      },
      "description": "Optional. Retrying with the same key returns the original response instead of creating a duplicate."
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/CreateSiteRequest"
       },
       "example": {
        "files": [
         {
          "path": "index.html",
          "size": 1024,
          "contentType": "text/html",
          "hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/CreateSiteResponse"
        }
       }
      }
     },
     "409": {
      "description": "Slug taken, or Idempotency-Key reused with a different body",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    }
   },
   "get": {
    "operationId": "listSites",
    "tags": [
     "Sites"
    ],
    "security": [
     {
      "bearerApiKey": []
     }
    ],
    "summary": "List your sites",
    "parameters": [
     {
      "name": "limit",
      "in": "query",
      "schema": {
       "type": "integer",
       "minimum": 1,
       "maximum": 100,
       "default": 20
      }
     },
     {
      "name": "offset",
      "in": "query",
      "schema": {
       "type": "integer",
       "minimum": 0,
       "default": 0
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/SiteList"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     }
    }
   }
  },
  "/v1/usage": {
   "get": {
    "operationId": "getUsage",
    "tags": [
     "Account"
    ],
    "security": [
     {
      "bearerApiKey": []
     }
    ],
    "summary": "Account usage: site and worker counts, bytes, recent activity, largest sites",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     }
    }
   }
  },
  "/v1/account": {
   "get": {
    "operationId": "getAccount",
    "tags": [
     "Account"
    ],
    "security": [
     {
      "bearerApiKey": []
     }
    ],
    "summary": "Who am I: the account for this API key, plus usage",
    "description": "GET /v1/me returns the same thing.",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "account": {
           "type": "object",
           "properties": {
            "id": {
             "type": "string"
            },
            "email": {
             "type": "string",
             "format": "email"
            },
            "createdAt": {
             "type": "string",
             "format": "date-time"
            }
           }
          },
          "usage": {
           "type": "object",
           "description": "Same as GET /v1/usage"
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     }
    }
   }
  },
  "/v1/limits": {
   "get": {
    "operationId": "getLimits",
    "tags": [
     "Account"
    ],
    "security": [
     {},
     {
      "bearerApiKey": []
     }
    ],
    "summary": "Rate limits and size caps for anonymous and signed-in use",
    "description": "Numbers come from the constants the server enforces. `authenticated` says whether the request carried a valid API key.",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "authenticated": {
           "type": "boolean"
          },
          "anonymous": {
           "type": "object"
          },
          "account": {
           "type": "object"
          },
          "sites": {
           "type": "object"
          },
          "auth": {
           "type": "object"
          },
          "workers": {
           "type": "object"
          },
          "docs": {
           "type": "string",
           "format": "uri"
          }
         }
        }
       }
      }
     }
    }
   }
  },
  "/v1/sites/{slug}": {
   "parameters": [
    {
     "name": "slug",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     },
     "description": "Site or worker subdomain label"
    }
   ],
   "get": {
    "operationId": "getSite",
    "tags": [
     "Sites"
    ],
    "security": [
     {
      "bearerApiKey": []
     },
     {
      "claimToken": []
     }
    ],
    "summary": "Get a site and its live version",
    "description": "Needs the owner's Bearer key or the site's X-Claim-Token. Without them the answer is 404, so the endpoint doesn't reveal which slugs exist.",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "site": {
           "$ref": "#/components/schemas/Site"
          },
          "currentVersion": {
           "oneOf": [
            {
             "$ref": "#/components/schemas/Version"
            },
            {
             "type": "null"
            }
           ]
          }
         }
        }
       }
      }
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    }
   },
   "delete": {
    "operationId": "deleteSite",
    "tags": [
     "Sites"
    ],
    "security": [
     {
      "bearerApiKey": []
     },
     {
      "claimToken": []
     }
    ],
    "summary": "Delete a site",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "success": {
           "type": "boolean"
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    }
   }
  },
  "/v1/sites/{slug}/metadata": {
   "parameters": [
    {
     "name": "slug",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     },
     "description": "Site or worker subdomain label"
    }
   ],
   "patch": {
    "operationId": "updateSiteMetadata",
    "tags": [
     "Sites"
    ],
    "security": [
     {
      "bearerApiKey": []
     },
     {
      "claimToken": []
     }
    ],
    "summary": "Set title, description, OG image path, or expiry",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "title": {
          "type": [
           "string",
           "null"
          ]
         },
         "description": {
          "type": [
           "string",
           "null"
          ]
         },
         "ogImagePath": {
          "type": [
           "string",
           "null"
          ]
         },
         "expiresAt": {
          "type": [
           "string",
           "null"
          ],
          "format": "date-time"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "site": {
           "$ref": "#/components/schemas/Site"
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    },
    "description": "expiresAt: RFC 3339, or null for no expiry (account-owned sites). Anonymous sites may only shorten their expiry, to at most 24 hours after creation. Guide: https://chorus.host/guides/temporary-website-hosting"
   }
  },
  "/v1/sites/{slug}/password": {
   "parameters": [
    {
     "name": "slug",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     },
     "description": "Site or worker subdomain label"
    }
   ],
   "put": {
    "operationId": "setSitePassword",
    "tags": [
     "Sites"
    ],
    "security": [
     {
      "bearerApiKey": []
     },
     {
      "claimToken": []
     }
    ],
    "summary": "Protect the site with HTTP Basic auth",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "username",
         "password"
        ],
        "properties": {
         "username": {
          "type": "string"
         },
         "password": {
          "type": "string",
          "maxLength": 72
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "success": {
           "type": "boolean"
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    },
    "description": "HTTP Basic Auth for every file of the site. Works on anonymous sites with X-Claim-Token, before anyone claims them. Guide: https://chorus.host/guides/password-protect-html-page"
   },
   "delete": {
    "operationId": "removeSitePassword",
    "tags": [
     "Sites"
    ],
    "security": [
     {
      "bearerApiKey": []
     },
     {
      "claimToken": []
     }
    ],
    "summary": "Remove password protection",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "success": {
           "type": "boolean"
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    }
   }
  },
  "/v1/sites/{slug}/claim": {
   "parameters": [
    {
     "name": "slug",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     },
     "description": "Site or worker subdomain label"
    }
   ],
   "post": {
    "operationId": "claimSite",
    "tags": [
     "Sites"
    ],
    "security": [
     {
      "bearerApiKey": []
     }
    ],
    "summary": "Move an anonymous site into your account so it never expires",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "claimToken"
        ],
        "properties": {
         "claimToken": {
          "type": "string"
         }
        }
       },
       "example": {
        "claimToken": "ctk_..."
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "site": {
           "$ref": "#/components/schemas/Site"
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    }
   }
  },
  "/v1/sites/{slug}/versions": {
   "parameters": [
    {
     "name": "slug",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     },
     "description": "Site or worker subdomain label"
    }
   ],
   "post": {
    "operationId": "createVersion",
    "tags": [
     "Versions"
    ],
    "security": [
     {
      "bearerApiKey": []
     },
     {
      "claimToken": []
     }
    ],
    "summary": "Start a new version of an existing site (update)",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "files"
        ],
        "properties": {
         "files": {
          "type": "array",
          "items": {
           "$ref": "#/components/schemas/ManifestFile"
          }
         }
        }
       },
       "example": {
        "files": [
         {
          "path": "index.html",
          "size": 1024,
          "contentType": "text/html",
          "hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/CreateSiteResponse"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    }
   },
   "get": {
    "operationId": "listVersions",
    "tags": [
     "Versions"
    ],
    "security": [
     {
      "bearerApiKey": []
     },
     {
      "claimToken": []
     }
    ],
    "summary": "List versions",
    "parameters": [
     {
      "name": "limit",
      "in": "query",
      "schema": {
       "type": "integer",
       "minimum": 1,
       "maximum": 100,
       "default": 20
      }
     },
     {
      "name": "offset",
      "in": "query",
      "schema": {
       "type": "integer",
       "minimum": 0,
       "default": 0
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Version"
           }
          },
          "totalCount": {
           "type": "integer"
          },
          "limit": {
           "type": "integer"
          },
          "offset": {
           "type": "integer"
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    }
   }
  },
  "/v1/sites/{slug}/versions/{versionId}/finalize": {
   "parameters": [
    {
     "name": "slug",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     },
     "description": "Site or worker subdomain label"
    },
    {
     "name": "versionId",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     }
    }
   ],
   "post": {
    "operationId": "finalizeVersion",
    "tags": [
     "Versions"
    ],
    "security": [
     {
      "bearerApiKey": []
     },
     {
      "claimToken": []
     }
    ],
    "summary": "Verify uploads and make the version live",
    "description": "Anonymous sites must send X-Claim-Token. Returns 400 naming the file if an upload is missing or its size/hash does not match; 409 if the version is no longer pending.",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "site": {
           "$ref": "#/components/schemas/Site"
          },
          "version": {
           "$ref": "#/components/schemas/Version"
          }
         }
        }
       }
      }
     },
     "409": {
      "description": "Version is not pending",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    }
   }
  },
  "/v1/sites/{slug}/versions/{versionId}/uploads/refresh": {
   "parameters": [
    {
     "name": "slug",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     },
     "description": "Site or worker subdomain label"
    },
    {
     "name": "versionId",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     }
    }
   ],
   "post": {
    "operationId": "refreshUploadUrls",
    "tags": [
     "Versions"
    ],
    "security": [
     {
      "bearerApiKey": []
     },
     {
      "claimToken": []
     }
    ],
    "summary": "Regenerate expired presigned upload URLs (they last 1 hour)",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "paths": {
          "type": "array",
          "items": {
           "type": "string"
          }
         }
        }
       },
       "example": {
        "paths": [
         "index.html"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "uploads": {
           "$ref": "#/components/schemas/Uploads"
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    }
   }
  },
  "/v1/sites/{slug}/versions/{versionId}/uploads/complete": {
   "parameters": [
    {
     "name": "slug",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     },
     "description": "Site or worker subdomain label"
    },
    {
     "name": "versionId",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     }
    }
   ],
   "post": {
    "operationId": "completeMultipartUpload",
    "tags": [
     "Versions"
    ],
    "security": [
     {
      "bearerApiKey": []
     },
     {
      "claimToken": []
     }
    ],
    "summary": "Assemble a multipart upload (files >= 250 MB)",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "path",
         "uploadId",
         "parts"
        ],
        "properties": {
         "path": {
          "type": "string"
         },
         "uploadId": {
          "type": "string"
         },
         "parts": {
          "type": "array",
          "items": {
           "type": "object",
           "required": [
            "partNumber",
            "etag"
           ],
           "properties": {
            "partNumber": {
             "type": "integer"
            },
            "etag": {
             "type": "string"
            }
           }
          }
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "success": {
           "type": "boolean"
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    }
   }
  },
  "/v1/sites/{slug}/versions/{versionId}/rollback": {
   "parameters": [
    {
     "name": "slug",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     },
     "description": "Site or worker subdomain label"
    },
    {
     "name": "versionId",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     }
    }
   ],
   "post": {
    "operationId": "rollbackSite",
    "tags": [
     "Versions"
    ],
    "security": [
     {
      "bearerApiKey": []
     },
     {
      "claimToken": []
     }
    ],
    "summary": "Make a copy of an earlier version live",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "site": {
           "$ref": "#/components/schemas/Site"
          },
          "version": {
           "$ref": "#/components/schemas/Version"
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    }
   }
  },
  "/v1/workers": {
   "post": {
    "operationId": "createWorker",
    "tags": [
     "Workers"
    ],
    "security": [
     {
      "bearerApiKey": []
     }
    ],
    "summary": "Create a worker at <slug>.worker.chorus.host",
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "schema": {
       "type": "string"
      },
      "description": "Optional. Retrying with the same key returns the original response instead of creating a duplicate."
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "slug": {
          "type": "string"
         }
        }
       },
       "example": {
        "slug": "my-api"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Worker"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    },
    "description": "A Worker is an API or webhook receiver at https://<slug>.worker.chorus.host. Needs an API key, not a Cloudflare account. Guides: https://chorus.host/workers, https://chorus.host/guides/deploy-webhook-from-agent",
    "externalDocs": {
     "url": "https://chorus.host/guides/deploy-webhook-from-agent"
    }
   },
   "get": {
    "operationId": "listWorkers",
    "tags": [
     "Workers"
    ],
    "security": [
     {
      "bearerApiKey": []
     }
    ],
    "summary": "List your workers",
    "parameters": [
     {
      "name": "limit",
      "in": "query",
      "schema": {
       "type": "integer",
       "minimum": 1,
       "maximum": 100,
       "default": 20
      }
     },
     {
      "name": "offset",
      "in": "query",
      "schema": {
       "type": "integer",
       "minimum": 0,
       "default": 0
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Worker"
           }
          },
          "totalCount": {
           "type": "integer"
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     }
    }
   }
  },
  "/v1/workers/{slug}": {
   "parameters": [
    {
     "name": "slug",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     },
     "description": "Site or worker subdomain label"
    }
   ],
   "get": {
    "operationId": "getWorker",
    "tags": [
     "Workers"
    ],
    "security": [
     {
      "bearerApiKey": []
     }
    ],
    "summary": "Worker details and current deployment",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    }
   },
   "delete": {
    "operationId": "deleteWorker",
    "tags": [
     "Workers"
    ],
    "security": [
     {
      "bearerApiKey": []
     }
    ],
    "summary": "Delete a worker",
    "responses": {
     "204": {
      "description": "Deleted"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    }
   }
  },
  "/v1/workers/{slug}/deploy": {
   "parameters": [
    {
     "name": "slug",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     },
     "description": "Site or worker subdomain label"
    }
   ],
   "post": {
    "operationId": "deployWorker",
    "tags": [
     "Workers"
    ],
    "security": [
     {
      "bearerApiKey": []
     }
    ],
    "summary": "Deploy worker code (multipart: metadata JSON + one or more file fields; 3 MB compressed)",
    "requestBody": {
     "required": true,
     "content": {
      "multipart/form-data": {
       "schema": {
        "type": "object",
        "required": [
         "metadata",
         "file"
        ],
        "properties": {
         "metadata": {
          "type": "string",
          "description": "JSON, e.g. {\"entryPoint\":\"index.js\",\"compatibilityDate\":\"2024-09-23\",\"compatibilityFlags\":[\"nodejs_compat\"]}. Files are deployed as-is (no TypeScript compilation, no bundling): upload JavaScript ES modules. crons is accepted and stored, but scheduled runs are not available yet."
         },
         "file": {
          "type": "array",
          "items": {
           "type": "string",
           "format": "binary"
          }
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/WorkerDeployment"
        }
       }
      }
     },
     "413": {
      "description": "Bundle too large",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    },
    "description": "Deploy an API or webhook receiver. Secrets set with PUT /v1/workers/{slug}/secrets/{name} stay set across deploys and rollbacks. Guide: https://chorus.host/guides/deploy-webhook-from-agent",
    "externalDocs": {
     "url": "https://chorus.host/guides/deploy-webhook-from-agent"
    }
   }
  },
  "/v1/workers/{slug}/secrets": {
   "parameters": [
    {
     "name": "slug",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     },
     "description": "Site or worker subdomain label"
    }
   ],
   "get": {
    "operationId": "listWorkerSecrets",
    "tags": [
     "Workers"
    ],
    "security": [
     {
      "bearerApiKey": []
     }
    ],
    "summary": "List secret names (values are never returned)",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    }
   }
  },
  "/v1/workers/{slug}/secrets/{name}": {
   "parameters": [
    {
     "name": "slug",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     },
     "description": "Site or worker subdomain label"
    },
    {
     "name": "name",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string",
      "pattern": "^[A-Z][A-Z0-9_]{0,63}$"
     }
    }
   ],
   "put": {
    "operationId": "setWorkerSecret",
    "tags": [
     "Workers"
    ],
    "security": [
     {
      "bearerApiKey": []
     }
    ],
    "summary": "Set a secret (max 5 KB)",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "value"
        ],
        "properties": {
         "value": {
          "type": "string",
          "maxLength": 5120
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "name": {
           "type": "string"
          },
          "createdAt": {
           "type": "string",
           "format": "date-time"
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    }
   },
   "delete": {
    "operationId": "deleteWorkerSecret",
    "tags": [
     "Workers"
    ],
    "security": [
     {
      "bearerApiKey": []
     }
    ],
    "summary": "Delete a secret",
    "responses": {
     "204": {
      "description": "Deleted"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    }
   }
  },
  "/v1/workers/{slug}/deployments": {
   "parameters": [
    {
     "name": "slug",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     },
     "description": "Site or worker subdomain label"
    }
   ],
   "get": {
    "operationId": "listWorkerDeployments",
    "tags": [
     "Workers"
    ],
    "security": [
     {
      "bearerApiKey": []
     }
    ],
    "summary": "Deployment history",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    }
   }
  },
  "/v1/workers/{slug}/rollback": {
   "parameters": [
    {
     "name": "slug",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     },
     "description": "Site or worker subdomain label"
    }
   ],
   "post": {
    "operationId": "rollbackWorker",
    "tags": [
     "Workers"
    ],
    "security": [
     {
      "bearerApiKey": []
     }
    ],
    "summary": "Roll back to a previous deployment (defaults to the previous one)",
    "requestBody": {
     "required": false,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "deploymentId": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/WorkerDeployment"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    }
   }
  },
  "/v1/workers/{slug}/logs/tail": {
   "parameters": [
    {
     "name": "slug",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     },
     "description": "Site or worker subdomain label"
    }
   ],
   "get": {
    "operationId": "tailWorkerLogs",
    "tags": [
     "Workers"
    ],
    "security": [
     {
      "bearerApiKey": []
     }
    ],
    "summary": "Get a WebSocket URL for live logs",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "url": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    }
   }
  },
  "/v1/workers/{slug}/analytics": {
   "parameters": [
    {
     "name": "slug",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     },
     "description": "Site or worker subdomain label"
    }
   ],
   "get": {
    "operationId": "getWorkerAnalytics",
    "tags": [
     "Workers"
    ],
    "security": [
     {
      "bearerApiKey": []
     }
    ],
    "summary": "Request and error counts for a worker",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    }
   }
  },
  "/v1/publish": {
   "post": {
    "operationId": "publishOneRequest",
    "tags": [
     "Sites"
    ],
    "security": [
     {},
     {
      "bearerApiKey": []
     },
     {
      "claimToken": []
     }
    ],
    "summary": "Publish files in one request (no hashes, no manifest)",
    "description": "Send the files themselves: multipart (file, repeatable; or archive), JSON in the MCP publish_site shape, a zip/tar body, or any other body as one file. Returns the live URL, claimUrl and claimToken. Up to 4 MB of files without an API key, 10 MB with one. Guide: https://chorus.host/guides/share-html-file-as-link",
    "externalDocs": {
     "url": "https://chorus.host/guides/share-html-file-as-link"
    },
    "parameters": [
     {
      "name": "slug",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "Subdomain to publish to; with X-Claim-Token or a Bearer key, updates that site"
     },
     {
      "name": "expires",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "90m, 2h, 7d or RFC 3339. Without an API key only sooner than 24 hours"
     },
     {
      "name": "title",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "name",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "File name for a raw (non-multipart) body"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "multipart/form-data": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "array",
          "items": {
           "type": "string",
           "format": "binary"
          },
          "description": "One or more files; the part filename is the path. A lone .html file becomes index.html"
         },
         "archive": {
          "type": "string",
          "format": "binary",
          "description": ".zip or .tar.gz of a folder; one top-level folder is stripped"
         },
         "slug": {
          "type": "string"
         },
         "title": {
          "type": "string"
         },
         "expires": {
          "type": "string"
         },
         "username": {
          "type": "string"
         },
         "password": {
          "type": "string",
          "description": "With username: HTTP Basic Auth on the site, set before it goes live"
         }
        }
       }
      },
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "files"
        ],
        "properties": {
         "files": {
          "type": "array",
          "items": {
           "type": "object",
           "required": [
            "path",
            "content"
           ],
           "properties": {
            "path": {
             "type": "string"
            },
            "content": {
             "type": "string"
            },
            "encoding": {
             "type": "string",
             "enum": [
              "utf8",
              "base64"
             ]
            }
           }
          }
         },
         "slug": {
          "type": "string"
         },
         "title": {
          "type": "string"
         },
         "expires_at": {
          "type": "string"
         },
         "claim_token": {
          "type": "string"
         },
         "password": {
          "type": "object",
          "properties": {
           "username": {
            "type": "string"
           },
           "password": {
            "type": "string"
           }
          }
         }
        }
       }
      },
      "application/zip": {
       "schema": {
        "type": "string",
        "format": "binary"
       }
      },
      "application/octet-stream": {
       "schema": {
        "type": "string",
        "format": "binary"
       }
      },
      "text/html": {
       "schema": {
        "type": "string"
       }
      }
     }
    },
    "responses": {
     "201": {
      "description": "Published a new site",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/PublishResult"
        }
       },
       "text/plain": {
        "schema": {
         "type": "string",
         "description": "The link only, when Accept prefers text/plain"
        }
       }
      }
     },
     "200": {
      "description": "Updated an existing site (slug plus X-Claim-Token or Bearer)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/PublishResult"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "413": {
      "description": "More than 4 MB of files without an API key (10 MB with one): use POST /v1/sites"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    }
   }
  },
  "/v1/upload": {
   "post": {
    "operationId": "uploadOneRequest",
    "tags": [
     "Sites"
    ],
    "security": [
     {},
     {
      "bearerApiKey": []
     }
    ],
    "summary": "Same as /v1/publish, but prints only the link (text/plain) by default",
    "description": "For PDFs, images and single files: curl -F file=@report.pdf https://chorus.host/v1/upload. Claim details come back in the X-Claim-Token, X-Claim-Url and X-Expires-At headers. Send Accept: application/json for the JSON response. Guide: https://chorus.host/file-upload-api",
    "externalDocs": {
     "url": "https://chorus.host/file-upload-api"
    },
    "parameters": [
     {
      "name": "slug",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "Subdomain to publish to; with X-Claim-Token or a Bearer key, updates that site"
     },
     {
      "name": "expires",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "90m, 2h, 7d or RFC 3339. Without an API key only sooner than 24 hours"
     },
     {
      "name": "title",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "name",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "File name for a raw (non-multipart) body"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "multipart/form-data": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "array",
          "items": {
           "type": "string",
           "format": "binary"
          },
          "description": "One or more files; the part filename is the path. A lone .html file becomes index.html"
         },
         "archive": {
          "type": "string",
          "format": "binary",
          "description": ".zip or .tar.gz of a folder; one top-level folder is stripped"
         },
         "slug": {
          "type": "string"
         },
         "title": {
          "type": "string"
         },
         "expires": {
          "type": "string"
         },
         "username": {
          "type": "string"
         },
         "password": {
          "type": "string",
          "description": "With username: HTTP Basic Auth on the site, set before it goes live"
         }
        }
       }
      },
      "application/octet-stream": {
       "schema": {
        "type": "string",
        "format": "binary"
       }
      }
     }
    },
    "responses": {
     "201": {
      "description": "Published a new site",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/PublishResult"
        }
       },
       "text/plain": {
        "schema": {
         "type": "string",
         "description": "The link only, when Accept prefers text/plain"
        }
       }
      }
     },
     "200": {
      "description": "Updated an existing site (slug plus X-Claim-Token or Bearer)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/PublishResult"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "413": {
      "description": "More than 4 MB of files without an API key (10 MB with one): use POST /v1/sites"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    }
   }
  },
  "/v1/upload/{name}": {
   "put": {
    "operationId": "uploadNamedFile",
    "tags": [
     "Sites"
    ],
    "security": [
     {},
     {
      "bearerApiKey": []
     }
    ],
    "summary": "Upload one raw file under a name (curl -T file https://chorus.host/v1/upload/file)",
    "parameters": [
     {
      "name": "name",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/octet-stream": {
       "schema": {
        "type": "string",
        "format": "binary"
       }
      }
     }
    },
    "responses": {
     "201": {
      "description": "Published a new site",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/PublishResult"
        }
       },
       "text/plain": {
        "schema": {
         "type": "string",
         "description": "The link only, when Accept prefers text/plain"
        }
       }
      }
     },
     "200": {
      "description": "Updated an existing site (slug plus X-Claim-Token or Bearer)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/PublishResult"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "413": {
      "description": "More than 4 MB of files without an API key (10 MB with one): use POST /v1/sites"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    }
   }
  }
 },
 "components": {
  "securitySchemes": {
   "bearerApiKey": {
    "type": "http",
    "scheme": "bearer",
    "bearerFormat": "chk_ API key from POST /v1/auth/verify-otp"
   },
   "claimToken": {
    "type": "apiKey",
    "in": "header",
    "name": "X-Claim-Token",
    "description": "ctk_ token returned when an anonymous site is created"
   }
  },
  "responses": {
   "BadRequest": {
    "description": "Invalid input",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "Unauthorized": {
    "description": "Missing or invalid Bearer key / X-Claim-Token",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "NotFound": {
    "description": "Not found (or not yours)",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "RateLimited": {
    "description": "Rate limited",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   }
  },
  "schemas": {
   "Error": {
    "type": "object",
    "required": [
     "error"
    ],
    "properties": {
     "error": {
      "type": "string"
     },
     "hint": {
      "type": "string",
      "description": "How to fix the request"
     },
     "docs": {
      "type": "string",
      "format": "uri"
     }
    }
   },
   "ManifestFile": {
    "type": "object",
    "required": [
     "path",
     "size",
     "contentType",
     "hash"
    ],
    "properties": {
     "path": {
      "type": "string",
      "example": "assets/app.js"
     },
     "size": {
      "type": "integer",
      "minimum": 0,
      "description": "Bytes"
     },
     "contentType": {
      "type": "string",
      "example": "text/html"
     },
     "hash": {
      "type": "string",
      "pattern": "^sha256:[0-9a-f]{64}$"
     }
    }
   },
   "CreateSiteRequest": {
    "type": "object",
    "required": [
     "files"
    ],
    "properties": {
     "slug": {
      "type": "string",
      "description": "Optional; generated if omitted"
     },
     "files": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/ManifestFile"
      },
      "maxItems": 10000
     },
     "expiresAt": {
      "type": "string",
      "format": "date-time"
     }
    }
   },
   "Site": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "slug": {
      "type": "string"
     },
     "url": {
      "type": "string",
      "format": "uri"
     },
     "currentVersionId": {
      "type": "string"
     },
     "ownerId": {
      "type": "string"
     },
     "title": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "ogImagePath": {
      "type": "string"
     },
     "expiresAt": {
      "type": [
       "string",
       "null"
      ],
      "format": "date-time",
      "description": "null for owned sites"
     },
     "createdAt": {
      "type": "string",
      "format": "date-time"
     },
     "updatedAt": {
      "type": "string",
      "format": "date-time"
     }
    }
   },
   "Version": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "pending",
       "live",
       "superseded"
      ]
     },
     "fileCount": {
      "type": "integer"
     },
     "totalSize": {
      "type": "integer"
     },
     "finalizeUrl": {
      "type": "string"
     },
     "finalizedAt": {
      "type": "string",
      "format": "date-time"
     },
     "createdAt": {
      "type": "string",
      "format": "date-time"
     }
    }
   },
   "Uploads": {
    "type": "object",
    "properties": {
     "pending": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "path": {
         "type": "string"
        },
        "uploadUrl": {
         "type": "string",
         "format": "uri"
        },
        "uploadMethod": {
         "type": "string",
         "enum": [
          "put",
          "multipart"
         ]
        },
        "uploadId": {
         "type": "string"
        },
        "partUrls": {
         "type": "array",
         "items": {
          "type": "string"
         }
        }
       }
      }
     },
     "skipped": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "path": {
         "type": "string"
        },
        "reason": {
         "type": "string"
        }
       }
      }
     }
    }
   },
   "CreateSiteResponse": {
    "type": "object",
    "properties": {
     "site": {
      "$ref": "#/components/schemas/Site"
     },
     "version": {
      "$ref": "#/components/schemas/Version"
     },
     "uploads": {
      "$ref": "#/components/schemas/Uploads"
     },
     "claimToken": {
      "type": "string",
      "description": "Anonymous creates only. Send as X-Claim-Token."
     },
     "claimUrl": {
      "type": "string",
      "format": "uri",
      "description": "Anonymous creates only. Give this to the user so they can keep the site."
     }
    }
   },
   "SiteList": {
    "type": "object",
    "properties": {
     "data": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Site"
      }
     },
     "totalCount": {
      "type": "integer"
     },
     "limit": {
      "type": "integer"
     },
     "offset": {
      "type": "integer"
     }
    }
   },
   "Worker": {
    "type": "object",
    "properties": {
     "slug": {
      "type": "string"
     },
     "url": {
      "type": "string",
      "format": "uri"
     },
     "title": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "createdAt": {
      "type": "string",
      "format": "date-time"
     }
    }
   },
   "WorkerDeployment": {
    "type": "object",
    "properties": {
     "slug": {
      "type": "string"
     },
     "url": {
      "type": "string"
     },
     "deploymentId": {
      "type": "string"
     },
     "version": {
      "type": "integer"
     },
     "sizeBytes": {
      "type": "integer"
     },
     "fileCount": {
      "type": "integer"
     },
     "entryPoint": {
      "type": "string"
     },
     "deployedAt": {
      "type": "string",
      "format": "date-time"
     },
     "rolledBackFrom": {
      "type": "string"
     }
    }
   },
   "PublishResult": {
    "type": "object",
    "properties": {
     "url": {
      "type": "string"
     },
     "pageUrl": {
      "type": "string"
     },
     "fileUrl": {
      "type": "string"
     },
     "slug": {
      "type": "string"
     },
     "updated": {
      "type": "boolean"
     },
     "anonymous": {
      "type": "boolean"
     },
     "expiresAt": {
      "type": [
       "string",
       "null"
      ]
     },
     "claimUrl": {
      "type": "string"
     },
     "claimToken": {
      "type": "string"
     },
     "passwordProtected": {
      "type": "boolean"
     },
     "versionId": {
      "type": "string"
     },
     "fileCount": {
      "type": "integer"
     },
     "totalBytes": {
      "type": "integer"
     },
     "fileUrls": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "warnings": {
      "type": "array",
      "items": {
       "type": "string"
      }
     }
    }
   }
  }
 }
}
