Sheet 2 — specifications
Docs
Everything needed to ship: a token, seven endpoints, and nothing else. Every claim on this page is testable with curl.
Procedure 1
Get started
1 · Put your token in the environment
Your token comes with your Clave workspace and starts with
clave_. It is the whole auth story — there is
no login flow, here or anywhere.
export CLAVE_API_TOKEN=clave_…
2 · Check it works
Listing your sites doubles as the auth check. Every request carries the token as a bearer header.
curl https://clave-api.fly.dev/v1/sites \
-H "Authorization: Bearer $CLAVE_API_TOKEN"
{ "sites": [] } 3 · Ship a one-file site
Four requests: create the site, send the manifest, upload the one missing file, activate. The name you pick becomes the subdomain.
API=https://clave-api.fly.dev
AUTH="Authorization: Bearer $CLAVE_API_TOKEN"
# create the site — its name becomes s-my-site.heyclave.party
SITE=$(curl -s -X POST $API/v1/sites -H "$AUTH" \
-H "Content-Type: application/json" \
-d '{"name":"my-site"}' | jq -r .id)
# manifest: every file as path → sha-256 + size
HASH=$(sha256sum index.html | cut -c1-64)
SIZE=$(wc -c < index.html)
DEP=$(curl -s -X POST $API/v1/sites/$SITE/deployments -H "$AUTH" \
-H "Content-Type: application/json" \
-d "{\"manifest\":{\"/index.html\":{\"hash\":\"$HASH\",\"size\":$SIZE}}}" | jq -r .id)
# upload the missing file, then activate
curl -s -X PUT $API/v1/deployments/$DEP/files/$HASH -H "$AUTH" \
-H "Content-Type: application/octet-stream" --data-binary @index.html
curl -s -X POST $API/v1/deployments/$DEP/activate -H "$AUTH" | jq -r .url
https://s-my-site.heyclave.party 4 · Redeploy
Send a new manifest to the same site and upload only what the response asks for. Unchanged files never travel — a redeploy with nothing changed uploads zero bytes.
Prefer not to hand-roll the loop? The reference client is a single stdlib-only Node script (Node 18+) that ships with the Clave starter:
node scripts/deploy.mjs --name my-site --dir dist # first deploy — creates the site node scripts/deploy.mjs --site site_… --dir dist # every deploy after node scripts/deploy.mjs --check # verify token + reachability
Procedure 2
The deploy lifecycle
-
Manifest
POST /v1/sites/:id/deploymentswith every file aspath → { hash, size }(sha-256, hex). The response is the deployment id and the list of hashes the platform doesn't already have. -
Upload
PUT /v1/deployments/:id/files/:hashwith the raw bytes for each missing hash. Storage is content-addressed: identical files are stored once, across every deploy. -
Activate
POST /v1/deployments/:id/activateflips the deployment live atomically and returns the public URL. Visitors never see a half-deployed site.
Note — the sequence is idempotent. Re-run any step safely: the same manifest yields the same missing list, re-uploading a stored blob is a no-op, and re-activating an active deployment changes nothing.
Reference — /v1
API reference
Base URL https://clave-api.fly.dev. Every request:
Authorization: Bearer clave_…. JSON in, JSON out,
except file uploads (application/octet-stream).
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/sites | List your sites (also the auth check) |
| POST | /v1/sites | Create a site by name |
| GET | /v1/sites/:id | Inspect one site |
| DELETE | /v1/sites/:id | Take a site down |
| POST | /v1/sites/:id/deployments | Start a deploy: send the manifest |
| PUT | /v1/deployments/:id/files/:hash | Upload a missing file |
| POST | /v1/deployments/:id/activate | Flip the deployment live |
Sites
GET /v1/sites
→ { "sites": [ { "id": "site_…", "name": "my-site",
"url": "https://s-my-site.heyclave.party",
"status": "active", "created_at": "2026-07-25T00:56:11Z" } ] }
POST /v1/sites
{ "name": "my-site" } # lowercase letters, digits, hyphens
→ the site object above # name_taken if another account holds it
GET /v1/sites/:id → the site object
DELETE /v1/sites/:id → 204, the site is down Deployments
POST /v1/sites/:id/deployments
{ "manifest": {
"/index.html": { "hash": "9f8c…", "size": 5210 },
"/css/site.css": { "hash": "31ab…", "size": 894 } } }
→ { "id": "dep_…", "missing": ["31ab…"] } # only what's new uploads
PUT /v1/deployments/:id/files/:hash
Content-Type: application/octet-stream # body: the file bytes
POST /v1/deployments/:id/activate
→ { "id": "dep_…", "url": "https://s-my-site.heyclave.party",
"status": "active" } Reference — failure modes
Errors & retries
Every error has the same shape, and says whether retrying is safe — so an agent never has to guess.
{ "error": {
"code": "name_taken", # stable identifier — branch on this
"message": "…", # human-readable detail
"retryable": false } } # true → retry with backoff is safe - retryable: true — a transient fault. Retry with exponential backoff; the lifecycle is idempotent, so repeats are harmless.
- retryable: false — the request itself is wrong
(for example
name_takenwhen creating a site). Change the input, don't retry it.
Procedure 3
The no-code path: the Clave skill
You don't have to touch this API at all. The Clave skill teaches your agent the whole road — planning, design, build, and this deploy — and checks in with you at four milestones along the way.
npx skills add heyclave/clave --skill clave
Then ask for what you want — "build me a site for my bakery". The site's source and specs stay in your own repo; publishing lands here, on this API.