Clawsite.ai — Static Website Hosting for AI Agents
When to Use Clawsite
- Your agent generates static HTML / CSS / JS / images and wants to publish them as a public URL
- You want a zero-config hosting account with HTTPS, CloudFront CDN, and atomic deploys
- You want a memorable random URL like
happy-otter-42.clawsite.ai(the slug is auto-generated; you can't pick it)
Quick Start
Your sandbox already has an account provisioned. Check these env vars before doing anything else:
| Env var | Required? | Purpose |
|---|---|---|
CLAWSITE_API_KEY | required | Bearer token for all authenticated endpoints (e.g. csk_live_...) |
CLAWSITE_SITE_ID | required | Your assigned site identifier (e.g. site_01KQ...) |
CLAWSITE_URL | informational | Your live site URL, the one to share with the user (e.g. https://happy-otter-42.clawsite.ai). If unset, derive from GET /v1/sites. |
CLAWSITE_API_URL | optional | API base URL. Defaults to https://api.clawsite.ai if unset. Dev sandboxes override to https://api.dev.clawsite.ai. |
If CLAWSITE_API_KEY or CLAWSITE_SITE_ID is unset, see "Standalone Registration" at the bottom.
Examples below use
$CLAWSITE_API_URLliterally; if it's unset, fall back tohttps://api.clawsite.ai.
API base: $CLAWSITE_API_URL/v1
All authenticated endpoints require Authorization: Bearer $CLAWSITE_API_KEY.
1. Deploy a directory of static files
The deploy endpoint takes a .zip of your site contents (max 4 MB compressed; expanded contents must fit the per-site quota — see "Quotas" below).
POST $CLAWSITE_API_URL/v1/sites/$CLAWSITE_SITE_ID/deploy Authorization: Bearer $CLAWSITE_API_KEY Content-Type: application/zip
(body: raw bytes of the .zip)
Workflow:
- Create your files in a directory:
site/ index.html style.css app.js images/logo.png - Zip the contents of the directory (no parent directory inside the zip):
cd site/ && zip -r ../site.zip . - POST the zip:
curl -X POST "$CLAWSITE_API_URL/v1/sites/$CLAWSITE_SITE_ID/deploy" \ -H "Authorization: Bearer $CLAWSITE_API_KEY" \ -H "Content-Type: application/zip" \ --data-binary "@site.zip"
-> Returns: siteId, url, fileCount, sizeBytes, deployedAt (Unix seconds)
{
"siteId": "site_...",
"url": "https://happy-otter-42.clawsite.ai",
"fileCount": 12,
"sizeBytes": 458231,
"deployedAt": 1744732800
}
Atomic full-site replacement: any files from the previous deploy that are NOT in the new zip get deleted. Deploy = full snapshot, not incremental upload.
Cache: every deploy automatically invalidates CloudFront cache. Manual purge below is rarely needed.
Routing: the path inside the zip becomes the URL path. index.html at the zip root is served at /. Subdirectories work: images/logo.png is at /images/logo.png. For pretty URLs without .html, name files like about/index.html and link as /about/.
2. Show the user their site
The site is live at $CLAWSITE_URL immediately after a successful deploy. Tell the user:
"Your site is live at $CLAWSITE_URL"
3. Purge CloudFront cache (rarely needed)
POST $CLAWSITE_API_URL/v1/sites/$CLAWSITE_SITE_ID/purge-cache Authorization: Bearer $CLAWSITE_API_KEY
(no body)
-> Returns: siteId, purgedAt
{ "siteId": "site_...", "purgedAt": 1744732800 }
Use this only if the cache is serving stale content unrelated to a deploy. Normal deploys auto-invalidate.
4. List sites and check quota usage
GET $CLAWSITE_API_URL/v1/sites Authorization: Bearer $CLAWSITE_API_KEY
-> Returns: array of { siteId, slug, url, sizeBytes, fileCount, lastDeployAt }
{
"sites": [{
"siteId": "site_...",
"slug": "happy-otter-42",
"url": "https://happy-otter-42.clawsite.ai",
"sizeBytes": 458231,
"fileCount": 12,
"lastDeployAt": 1744732800
}]
}
Use this to verify your CLAWSITE_SITE_ID matches and to inspect current usage vs quotas.
Other Endpoints
Delete a site
DELETE $CLAWSITE_API_URL/v1/sites/{siteId} Authorization: Bearer $CLAWSITE_API_KEY
Permanently deletes the site and every file under it. The slug is tombstoned (kept reserved forever) so the URL can never be re-used by another account — this prevents URL takeover of a previously-shared link.
Standalone Registration (no sandbox env vars)
If you're running outside a ZenClaw sandbox and CLAWSITE_API_KEY isn't pre-set, register via email OTP. Verification is delegated to MBID (MixerBox ID).
Step 1 — request a 6-digit code via email:
POST $CLAWSITE_API_URL/v1/register Content-Type: application/json
{ "email": "your-email@example.com" }
-> Returns: { "challengeId": "<JWT>" }
Step 2 — verify (after the 6-digit code arrives in your inbox):
POST $CLAWSITE_API_URL/v1/register Content-Type: application/json
{ "challengeId": "<JWT from step 1>", "code": "123456" }
-> Returns: accountId, apiKey, and a default sites[] entry. Save the apiKey immediately — it cannot be recovered.
You can also create additional sites later via:
POST $CLAWSITE_API_URL/v1/sites Authorization: Bearer $apiKey
(no body needed; slug is auto-assigned)
Note: in v1 the per-account site quota is 1, so this returns 409
quota_exceededif you already have one site.
Quotas (v1)
| Item | Limit |
|---|---|
| Sites per account | 1 |
| Storage per site | 3 MB (uncompressed total) |
| Max single file | 1 MB |
| Max files per site | 100 |
| Max compressed zip body | 4 MB |
| Deploy frequency | 10 / hour per account |
| Purge frequency | 5 / hour per account |
| Bandwidth | unlimited |
Allowed file extensions: html, css, js, json, svg, png, jpg, jpeg, gif, webp, ico, woff2, txt, md.
Anything else (e.g. .php, .exe, .py) → 400 unsupported_extension.
Errors
All errors return JSON:
{ "error": { "code": "<machine-code>", "message": "<human-readable>" } }
| Code | HTTP | Cause |
|---|---|---|
unauthorized | 401 | Missing or invalid API key |
missing_fields | 400 | Required fields absent or malformed (e.g. invalid email shape on register) |
not_found | 404 | Site doesn't belong to your account, or doesn't exist |
quota_exceeded | 409 | Sites limit, storage limit, or rate limit hit |
unsupported_extension | 400 | File extension not in the whitelist above |
file_too_large | 400 | Single file > 1 MB, or zip body > 4 MB |
invalid_zip | 400 | Body not a valid zip, missing body, or contains paths with .. / absolute paths |
mbid_error | upstream | Forwarded from MBID's email-verify endpoints (mx_record_not_found, domain_typo, too_many_request, incorrect_verification_code) — only relevant during email registration |
Idempotency note: re-calling /v1/register with the same MBID-verified email returns the existing accountId, apiKey, and sites. The same apiKey is returned every time so a partner orchestrator (e.g. ZenClaw) can re-provision sandboxes for the same user without losing access. If you ever want a fresh key, DELETE the site and re-register.