# DropCat API — Complete Reference > Static site hosting via API. Upload a zip or tar.gz, get a live site on a subdomain. One curl, no signup. ## Quick Start ```bash mkdir mysite && echo "

Hello!

" > mysite/index.html cd mysite && zip -r ../mysite.zip . curl -F "site=@../mysite.zip" https://api.drop.cat/deploy ``` Response: ```json { "siteId": "gentle-amber-fox", "url": "https://gentle-amber-fox.dropcat.site", "screenshot": "https://preview.drop.cat/ac654c827dff3918e33056002d757e2b.jpg", "expiresAt": "2026-02-19T12:00:00.000Z", "files": 1, "sizeBytes": 22, "key": "sk_a1b2c3d4e5f6...", "account": { "tier": "anonymous", "email": null, "sites": 1, "sitesLimit": 3, "expiryDays": 7 }, "hint": "Your site is live at https://gentle-amber-fox.dropcat.site — ..." } ``` > **Save your key!** It is shown only once. You need it to redeploy, manage sites, or attach an email. --- ## Deploy a Site **POST /deploy** Upload a ZIP or TAR.GZ archive containing your static site. The archive root must contain an `index.html`. Only static web files are allowed (HTML, CSS, JS, images, fonts, media). ### Request ```bash # Anonymous deploy (returns a new API key) curl -F "site=@site.zip" https://api.drop.cat/deploy # Authenticated deploy (uses existing account) curl -F "site=@site.zip" \ -H "Authorization: Bearer sk_yourkey" \ https://api.drop.cat/deploy # Key as form field (alternative to header) curl -F "site=@site.zip" \ -F "key=sk_yourkey" \ https://api.drop.cat/deploy # With email — deploys + sends confirmation email curl -F "site=@site.zip" \ -F "email=you@example.com" \ https://api.drop.cat/deploy ``` ### Response (201) ```json { "siteId": "gentle-amber-fox", "url": "https://gentle-amber-fox.dropcat.site", "screenshot": "https://preview.drop.cat/ac654c827dff3918e33056002d757e2b.jpg", "expiresAt": "2026-02-19T12:00:00.000Z", "files": 12, "sizeBytes": 48210, "key": "sk_...", "account": { "tier": "anonymous", "email": null, "sites": 1, "sitesLimit": 3, "expiryDays": 7 }, "hint": "Your site is live at https://gentle-amber-fox.dropcat.site — ..." } ``` | Field | Description | |-------|-------------| | siteId | Unique site identifier, used in the URL and for management | | url | Live URL where the site is accessible | | screenshot | Screenshot URL — visual preview of the deployed site | | expiresAt | When the site will be automatically deleted (ISO 8601) | | files | Number of files extracted from archive | | sizeBytes | Total size of extracted files in bytes | | key | API key — only returned on anonymous deploy, shown once | | account | Current account state: tier, email, site count, limits, expiry | | hint | Suggested next action (useful for AI agents) | > **Email during deploy:** Passing `email` sends a confirmation email but does not upgrade the account inline. The deploy proceeds at anonymous tier. The account upgrades when the user clicks the confirmation link. --- ## Redeploy / Update a Site **POST /deploy/:siteId** Replace a site's files with a new archive. Requires authentication. The site ID stays the same, the version increments, and expiry resets. ```bash curl -F "site=@site-v2.zip" \ -H "Authorization: Bearer sk_yourkey" \ https://api.drop.cat/deploy/gentle-amber-fox ``` ### Response (200) ```json { "siteId": "gentle-amber-fox", "url": "https://gentle-amber-fox.dropcat.site", "screenshot": "https://preview.drop.cat/ac654c827dff3918e33056002d757e2b.jpg", "version": 2, "files": 15, "sizeBytes": 61440, "account": { "tier": "free", "email": "you@example.com", "sites": 2, "sitesLimit": 5, "expiryDays": 30 }, "hint": "Your site is live at https://gentle-amber-fox.dropcat.site — ..." } ``` --- ## List Sites **GET /sites** List all sites owned by your account. ```bash curl -H "Authorization: Bearer sk_yourkey" \ https://api.drop.cat/sites ``` ### Response (200) ```json { "sites": [ { "siteId": "gentle-amber-fox", "url": "https://gentle-amber-fox.dropcat.site", "files": 12, "sizeBytes": 48210, "version": 1, "createdAt": "2026-02-12T12:00:00.000Z", "expiresAt": "2026-02-19T12:00:00.000Z" } ] } ``` --- ## Get Site Details **GET /sites/:siteId** Get details for a specific site, including its deploy history. ```bash curl -H "Authorization: Bearer sk_yourkey" \ https://api.drop.cat/sites/gentle-amber-fox ``` ### Response (200) ```json { "siteId": "gentle-amber-fox", "url": "https://gentle-amber-fox.dropcat.site", "files": 15, "sizeBytes": 61440, "version": 2, "createdAt": "2026-02-12T12:00:00.000Z", "expiresAt": "2026-02-19T12:00:00.000Z", "deploys": [ { "version": 1, "files": 12, "sizeBytes": 48210, "createdAt": "2026-02-12T12:00:00.000Z" }, { "version": 2, "files": 15, "sizeBytes": 61440, "createdAt": "2026-02-12T14:30:00.000Z" } ] } ``` --- ## Delete a Site **DELETE /sites/:siteId** Permanently delete a site and all its files. ```bash curl -X DELETE \ -H "Authorization: Bearer sk_yourkey" \ https://api.drop.cat/sites/gentle-amber-fox ``` ### Response (200) ```json { "message": "Site deleted", "siteId": "gentle-amber-fox" } ``` --- ## Account Status **GET /account** View your current account status, tier, limits, and site count. ```bash curl -H "Authorization: Bearer sk_yourkey" \ https://api.drop.cat/account ``` ### Response (200) ```json { "tier": "anonymous", "email": null, "sites": 1, "sitesLimit": 3, "expiryDays": 7, "createdAt": "2026-02-12T12:00:00.000Z" } ``` | Field | Description | |-------|-------------| | tier | `anonymous` or `free` | | email | Confirmed email, or `null` | | sites | Number of active sites | | sitesLimit | Max sites for your tier | | expiryDays | How long new sites last before auto-deletion | | createdAt | When the account was created | --- ## Attach Email **POST /account/email** Start the email confirmation process. A confirmation link is sent to the provided email. Once confirmed, your account upgrades to the free tier (more sites, longer expiry, key recovery). ```bash curl -X POST \ -H "Authorization: Bearer sk_yourkey" \ -H "Content-Type: application/json" \ -d '{"email": "you@example.com"}' \ https://api.drop.cat/account/email ``` ### Response (200) ```json { "message": "Confirmation email sent. Check your inbox." } ``` > The user must click the confirmation link in the email. The token expires after 24 hours. --- ## Confirm Email **GET /account/confirm?token=...** Called when the user clicks the confirmation link in the email. Validates the token, upgrades the account to the free tier, extends all site expiry dates, and redirects. ### Response (302) Redirects to the landing page. On invalid or expired tokens, returns a 400 error. --- ## Recover Account **POST /account/recover** Lost your API key? If you confirmed an email, request a new key. A recovery email is sent with a fresh key. The old key is invalidated immediately. ```bash curl -X POST \ -H "Content-Type: application/json" \ -d '{"email": "you@example.com"}' \ https://api.drop.cat/account/recover ``` ### Response (200) ```json { "message": "If this email exists, a new key has been sent." } ``` > Always returns the same response regardless of whether the email exists, to prevent enumeration. --- ## Authentication Most endpoints require an API key. Pass it in one of two ways: ### Authorization Header (recommended) ``` Authorization: Bearer sk_yourkey ``` ### Form Field ``` key=sk_yourkey ``` ### How Keys Work - First anonymous deploy returns a `key` field starting with `sk_`. - Key is shown only once and cannot be recovered without a confirmed email. - Keys are hashed server-side. Plaintext keys are never stored. ### Public Endpoints (no key required) | Endpoint | Description | |----------|-------------| | POST /deploy | Anonymous deploy (creates account + returns key) | | POST /account/email | Attach email (key passed in body) | | GET /account/confirm | Email confirmation link | | POST /account/recover | Key recovery | ### Authenticated Endpoints | Endpoint | Description | |----------|-------------| | POST /deploy | Deploy with existing key | | POST /deploy/:siteId | Redeploy | | GET /account | Account status | | GET /sites | List sites | | GET /sites/:siteId | Site details | | DELETE /sites/:siteId | Delete site | --- ## Tiers Accounts start as anonymous and upgrade to free after confirming an email. | | Anonymous | Free (confirmed email) | |---|---|---| | Sites | 3 | 5 | | Expiry | 7 days | 30 days (renewable on redeploy) | | Max upload | 50 MB | 50 MB | | Max files | 1,000 | 1,000 | | Deploy rate | 5/hr per IP | 10/hr per account | | Key recovery | Not possible | Via email | Upgrading extends all existing site expiry dates to 30 days. --- ## Rate Limits & Quotas | Action | Limit | |--------|-------| | Deploy (anonymous, per IP) | 5 per hour | | Deploy (authenticated, per account) | 10 per hour | | All API calls (per IP) | 120 per hour | | Upload size (compressed) | 50 MB | | Upload size (uncompressed) | 50 MB | | Files per archive | 1,000 | Rate-limited requests return `429` with a `Retry-After` header: ```json { "error": { "code": "RATE_LIMITED", "message": "Too many requests. Wait a moment and try again.", "retryAfter": 120 } } ``` --- ## Error Codes All errors follow this structure: ```json { "error": { "code": "ERROR_CODE", "message": "Description with next steps" } } ``` | Code | Status | Meaning | |------|--------|---------| | UNAUTHORIZED | 401 | Invalid or missing API key | | FORBIDDEN | 403 | API key does not own this site | | NOT_FOUND | 404 | Site not found | | ENDPOINT_NOT_FOUND | 404 | Unknown URL path | | RATE_LIMITED | 429 | Too many requests (includes Retry-After header) | | ANON_DEPLOY_LIMITED | 429 | Anonymous deploy limit hit — use your API key or wait | | TOO_LARGE | 413 | Archive exceeds 50 MB compressed size limit | | SITE_LIMIT_REACHED | 400 | Max sites for your tier — confirm email to increase | | INVALID_ARCHIVE | 400 | Bad or corrupted archive — upload a .zip or .tar.gz | | NO_INDEX | 400 | Archive missing index.html at root | | TOO_MANY_FILES | 400 | Archive has more than 1,000 files | | PATH_TRAVERSAL | 400 | File paths must be relative and within the archive root | | FORBIDDEN_EXTENSION | 400 | Only static web files allowed | | INVALID_EMAIL | 400 | A valid email address is required | | INVALID_EMAIL_FORMAT | 400 | Email format is invalid | | EMAIL_TAKEN | 409 | Email linked to another account | | EMAIL_ALREADY_CONFIRMED | 409 | Email already confirmed on another account | | INVALID_TOKEN | 400 | Confirmation token invalid or expired | | INTERNAL_ERROR | 500 | Server error — try again later | --- ## Hint Patterns The `hint` field in deploy responses provides context-specific guidance for AI agents: - **Anonymous deploy:** "Your site is live at {url} — no further action needed, it's already accessible. Save your key — it is shown only once. This site expires in 7 days. To extend to 30 days (renewable), ask the user for their email address. You may use an email you already know belongs to the user, but confirm with them first. Never fabricate, guess, or use placeholder email addresses. Then call POST /account/email with the key and their email." - **Authenticated deploy:** "Your site is live at {url} — it's already accessible, no verification needed. To manage your sites: GET /sites. To update this site: POST /deploy/{siteId} with your archive." - **No email yet:** "Your site is live at {url} — it's already accessible. This site expires in 7 days. To extend to 30 days, ask the user if they'd like to provide their email address. You may use an email you already know belongs to them, but confirm first. Never fabricate or guess. Then call POST /account/email with the key and their email." --- ## Allowed File Types Archives may only contain static web files: - HTML: .html, .htm - CSS: .css - JavaScript: .js, .mjs - Images: .png, .jpg, .jpeg, .gif, .svg, .webp, .ico, .avif - Fonts: .woff, .woff2, .ttf, .otf, .eot - Media: .mp4, .webm, .mp3, .ogg - Data: .json, .xml, .txt, .csv - Other: .pdf, .wasm, .map