drop.

AGENTS / SCRIPTS / TERMINALS

Deploy without the dashboard.

One request. An HTML file or ZIP website. A public link back.

POST/api/public/deployBearer password

Quickstart

Use your existing admin password in the Authorization header. No account, login request or cookie is needed. Use your published domain for external agents; this app must be published first.

Environment
export DROP_URL="https://YOUR-PUBLISHED-DOMAIN"
# Set DROP_PASSWORD securely to your existing admin password.
# Keep it out of source files, chat logs and deployed content.
Deploy HTML
curl --fail-with-body "$DROP_URL/api/public/deploy" \
  -H "Authorization: Bearer $DROP_PASSWORD" \
  -F "slug=my-site" \
  -F "file=@index.html"

The response includes the absolute url and relative path of the hosted site.

File uploads

Send multipart form fields slug and file. ZIPs must contain index.html at the root or inside one enclosing folder. Use relative links for CSS, scripts and images.

Deploy ZIP
curl --fail-with-body "$DROP_URL/api/public/deploy" \
  -H "Authorization: Bearer $DROP_PASSWORD" \
  -F "slug=my-site" \
  -F "file=@website.zip"

Inline HTML

Agents can send HTML directly as JSON. ZIP uploads use multipart instead.

JSON deployment
curl --fail-with-body "$DROP_URL/api/public/deploy" \
  -H "Authorization: Bearer $DROP_PASSWORD" \
  -H "Content-Type: application/json" \
  --data '{"slug":"my-site","content":"<!doctype html><h1>Hello from an agent</h1>"}'
Python
import os
from pathlib import Path
import requests

response = requests.post(
    os.environ["DROP_URL"] + "/api/public/deploy",
    headers={"Authorization": "Bearer " + os.environ["DROP_PASSWORD"]},
    json={"slug": "my-site", "content": Path("index.html").read_text()},
    timeout=120,
)
response.raise_for_status()
print(response.json()["url"])

Replace a site

An existing name returns 409 by default. To replace it deliberately, add overwrite: true to JSON or -F "overwrite=true" to multipart. The URL stays the same; the previous files are removed after the replacement is saved.

Responses

201 Created · ZIP example
{
  "ok": true,
  "slug": "my-site",
  "url": "https://YOUR-PUBLISHED-DOMAIN/s/my-site/",
  "path": "/s/my-site/"
}
409 Conflict
{
  "ok": false,
  "error": { "code": "slug_taken", "message": "That name is taken" }
}
StatusMeaning
200 / 201Deployed with overwrite / new site created
400Invalid slug, request or ZIP
401Missing or incorrect password
409Name taken; replacement not requested
413Upload too large
415Unsupported content type
503Temporarily unavailable; retry

Limits & security

  • HTML: 5MiB. ZIP: 20MiB compressed and extracted, up to 500 files. Hidden files are omitted; unsafe paths are rejected.
  • Slugs: 1–60 lowercase letters, numbers and hyphens; begin and end with a letter or number. Reserved: admin, api, s, login, assets.
  • Static sites only. ZIPs are not built on the server; upload the exported site, not its source project. No automatic SPA rewrite fallback.
  • Deployed pages are public. Never upload secrets or include the admin password in HTML, JavaScript or a ZIP.
  • The bearer password grants deployment and replacement access to all sites. Only share it with trusted agents. This endpoint does not accept the dashboard cookie as authorization.
  • Use HTTPS. Keep credentials in environment variables or an agent secret store, never in URLs. Server-to-server requests are supported; cross-origin browser calls are not enabled.
  • For an uncertain timeout, verify the returned site URL before retrying. A retry without overwrite may return 409 if the first deployment succeeded.