AGENTS / SCRIPTS / TERMINALS
Deploy without the dashboard.
One request. An HTML file or ZIP website. A public link back.
/api/public/deployBearer passwordQuickstart
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.
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.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.
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.
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>"}'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
{
"ok": true,
"slug": "my-site",
"url": "https://YOUR-PUBLISHED-DOMAIN/s/my-site/",
"path": "/s/my-site/"
}{
"ok": false,
"error": { "code": "slug_taken", "message": "That name is taken" }
}| Status | Meaning |
|---|---|
| 200 / 201 | Deployed with overwrite / new site created |
| 400 | Invalid slug, request or ZIP |
| 401 | Missing or incorrect password |
| 409 | Name taken; replacement not requested |
| 413 | Upload too large |
| 415 | Unsupported content type |
| 503 | Temporarily 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.