HTTP API
The flypod REST API at https://flypod.dev deploys sites from a zip, redeploys new versions, rolls back, and manages accounts with bearer-token auth.
Base URL: https://flypod.dev. Authenticated requests use Authorization: Bearer <token>, where the token is a per-site manage_token or a Better Auth account session token. See Tokens & ownership.
Endpoints
| Method | Path | Auth | Body |
|---|---|---|---|
GET | /healthz | None | (none) |
POST | /sites | Optional account session | zip |
POST | /sites/:id/deploys | Required | zip |
POST | /sites/:id/rollback | Required | JSON |
GET | /sites/:id | Required | (none) |
POST | /sites/:id/claim | Identity + claim_token | JSON |
POST | /account/bulk-attach | Identity | JSON |
GET | /account/sites | Identity | (none) |
PATCH | /account/sites/:id | Account session (dashboard) | JSON |
GET /healthz
Liveness check.
curl https://flypod.dev/healthz{ "ok": true }POST /sites
Deploy a new site. Send a zip of the site as the raw request body with content-type: application/zip. Anonymous unless you send an account session.
curl -X POST https://flypod.dev/sites \
-H "content-type: application/zip" \
--data-binary @site.zipSend Authorization: Bearer <session_token> to make the deploy owned and permanent.
Returns the deploy JSON: url, site_id, version_id, expires_at, manage_token, optional claim_token, optional owner_account_id, next_actions, healed, warnings, and render.
expires_at is epoch milliseconds on an anonymous deploy and null when the site is owned. The optional keys are omitted when they don't apply, not returned as null: claim_token appears only on anonymous deploys, owner_account_id only on owned ones.
healed lists what flypod fixed on your behalf (e.g. spa-fallback). warnings lists what it could not, each with a code and a fix_recipe naming the change to make — check warnings.length before treating the URL as good, because a deploy can return 200 and still serve a broken site.
render is a headless-browser receipt — screenshot, console output, failed requests, DOM summary. It requires a render engine the server operator opts into, and flypod.dev does not run one: on this host render.status is always "pending", the arrays are always empty, and no screenshot key is returned. Treat it as reserved. It populates only on a self-hosted deployment started with RENDER=playwright.
Save the manage_token from the response. Calling the API directly, you are the only thing holding it — the server keeps just a hash, and there's no registry doing it for you the way the CLI does. It's the bearer token for redeploys, rollback, and reads on this site. It does not authorize deletion; that's account-only.
POST /sites/:id/deploys
Redeploy. Uploads a new version that becomes live. Requires a bearer token (manage_token or account session). Body is a zip.
curl -X POST https://flypod.dev/sites/<id>/deploys \
-H "authorization: Bearer <manage_token>" \
-H "content-type: application/zip" \
--data-binary @site.zipPOST /sites/:id/rollback
Repoint the live site to a prior version. Requires a bearer token. JSON body with the target version_id.
curl -X POST https://flypod.dev/sites/<id>/rollback \
-H "authorization: Bearer <manage_token>" -H "content-type: application/json" \
-d '{"version_id":"<version_id>"}'An unknown version returns 404 Unknown version.
GET /sites/:id
Returns the site record and its version list. Requires a bearer token.
curl https://flypod.dev/sites/<id> -H "authorization: Bearer <manage_token>"label is the display name set from the dashboard (null when unset — clients fall back to slug). Each version carries the title of that deploy's index.html, or null when it has none.
POST /sites/:id/claim
Attach a single anonymous deploy to an account using its claim_token. Requires an authenticated identity and a valid claim_token (from the original anonymous deploy response).
curl -X POST https://flypod.dev/sites/<id>/claim \
-H "authorization: Bearer <session_token>" -H "content-type: application/json" \
-d '{"claim_token":"<claim_token>"}'Returns 409 if the site already belongs to a different account: a claim_token proves you deployed the site, not that you still hold it, so it cannot re-point a site away from its current owner. 401 Invalid claim covers an unknown site or a bad token. Rate-limited per IP.
Most clients use POST /account/bulk-attach instead — see below. claim_token is the older, weaker path and is not the recommended one.
POST /account/bulk-attach
Attach multiple anonymous sites to the caller's account in one request, using each site's manage_token as proof of ownership. This is the endpoint flypod login calls to auto-claim every anonymous site the local CLI remembers.
curl -X POST https://flypod.dev/account/bulk-attach \
-H "authorization: Bearer <session_token>" -H "content-type: application/json" \
-d '{"sites":[{"site_id":"abc","manage_token":"fk_..."},{"site_id":"def","manage_token":"fk_..."}]}'Body: { sites: [{ site_id, manage_token }, …] }, 1 to 100 items. Returns:
{
"attached": ["abc"],
"skipped": [
{ "site_id": "def", "reason": "invalid_token" }
]
}reason is one of not_found, already_owned, already_yours, invalid_token, or invalid_input. Rate-limited per IP.
Account
| Endpoint | Description |
|---|---|
GET /account/sites | List your sites (site_id, slug, label, url). Identity-gated. |
PATCH /account/sites/:id | Rename a site: {"label":"Client pitch"}. An empty label clears it. Owning account session only. |
DELETE /account/sites/:id | Delete one site you own (blobs + metadata + versions). |
POST /account/sites/bulk-delete | Delete several sites you own. Ids you don't own are skipped. |
DELETE /account | Delete the account and every site it owns. |
All of these require an account session. Deletion is account-only — a manage_token does not authorize it, and there is no route that accepts one for delete.
Serving behavior
Served responses carry:
cache-control: public, max-age=60, must-revalidateThe ETag is "<live_version_id>:<path>".
| Status | Meaning |
|---|---|
404 | Unknown host. |
410 | Site expired. |
451 | Operator-disabled. |
TypeScript demo
A runnable TypeScript example that deploys a folder to a live URL with the flypod library. Download it and run, or check it inline. Zero auth.
How it works
flypod has two planes. A deploy pipeline ingests a zip into an immutable version, and a serve plane routes a subdomain to that version's files.