CLAVE

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

  1. Manifest

    POST /v1/sites/:id/deployments with every file as path → { hash, size } (sha-256, hex). The response is the deployment id and the list of hashes the platform doesn't already have.

  2. Upload

    PUT /v1/deployments/:id/files/:hash with the raw bytes for each missing hash. Storage is content-addressed: identical files are stored once, across every deploy.

  3. Activate

    POST /v1/deployments/:id/activate flips 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_taken when 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.