API Reference
Read recent project activity.
Base URL
https://staticx.site/api/v1
Every route requires Accept: application/json and a bearer token with the ability and scope needed for that route.
Authorization: Bearer STATICX_API_TOKEN
Accept: application/json
LLM-readable contract
Give agents the schema before they call the API.
StaticX publishes a concise LLM index, a full agent context file, and an OpenAPI 3.1 document that mirrors the current /api/v1 routes.
Use these files for AI agents, generated clients, internal tools, and CI automation.
curl https://staticx.site/openapi.json
curl https://staticx.site/llms-full.txt
CLI authentication
The npm CLI is a thin client over the same API.
staticx login stores the base URL and bearer token locally, then verifies access with GET /user. Every later command keeps using the same token scope and route contract shown on this page.
npm install -g staticx
staticx login --base-url "https://staticx.site/api/v1" --token "STATICX_API_TOKEN"
staticx guide
staticx whoami
Token scopes
Match the token to the job.
Global
Account-wide access for internal tools, dashboards, or broad admin automation.
Common access levels in the UI: Full access and Read only.
Site-scoped
Limited to one site. Best for CI jobs, deploy bots, and one-off agent runs.
Common access levels in the UI: Full access and Read only.
Workspace-scoped
Limited to one workspace. Best when one runner needs several sites but should stay inside one team or client boundary.
Common access levels in the UI: Full access and Read only.
The API still returns internal preset ids in current_token.preset for debugging. The dashboard shows the user-facing labels Full access and Read only.
Route index
Current versioned API contract.
Account and access
| GET | /user |
Return the authenticated user for the current token. |
| GET | /workspaces |
List workspaces visible to the token owner. |
| POST | /workspaces |
Create a workspace. Body: name. |
| GET | /workspaces/{workspace} |
Read a workspace with members and project count. |
| DELETE | /workspaces/{workspace} |
Delete a workspace. Body: site_action=move/delete, target_workspace_id when moving. |
| GET | /domains |
List visible custom domains and generated subdomains. |
Projects
| GET | /projects |
List projects visible to the token owner. Optional query: workspace_id. |
| POST | /projects |
Create a project. Body: workspace_id, name, description, archive, or source_url. |
| GET | /projects/{project} |
Read one project, including workspace, host, status, and public URL. |
| PATCH | /projects/{project} |
Update project metadata. Body: name, description. |
| DELETE | /projects/{project} |
Delete a project the token owner can write to. |
| POST | /projects/{project}/domain |
Move a project to a custom domain. Body: domain. Returns the DNS record to create. |
| GET | /projects/{project}/domain |
Read custom domain status, DNS instructions, and SSL activation state. |
Files and imports
| GET | /projects/{project}/files |
List workspace files. Optional query: path. |
| POST | /projects/{project}/files |
Upload files, upload a ZIP, or queue URL import. Body depends on mode. |
Deployments
| GET | /projects/{project}/deployments |
List published deployment versions for the project. |
| POST | /projects/{project}/deployments |
Publish the current project state as an immutable deployment. Requires a root index.html or index.htm and 404.html. |
| GET | /projects/{project}/deployments/{deployment} |
Read one published deployment version. |
| POST | /projects/{project}/deployments/{deployment}/rollback |
Activate a previous successful immutable deployment. |
| DELETE | /projects/{project}/deployments/{deployment} |
Delete one inactive deployment record. Active deployments are protected. |
| DELETE | /projects/{project}/deployments/delete-selected |
Delete multiple inactive deployments. Body: deployment_ids[]. |
Environment and logs
| GET | /projects/{project}/environment-variables |
List project variables. |
| POST | /projects/{project}/environment-variables |
Create one variable. Body: key, value, is_secret. |
| PUT | /projects/{project}/environment-variables |
Replace/sync variables. Body: variables[]. |
| DELETE | /projects/{project}/environment-variables/{environmentVariable} |
Delete one variable. |
| GET | /projects/{project}/logs |
Read recent project activity. |
Current token
Read the active token context with /user.
{
"data": {
"id": 18,
"name": "Amina Hart",
"email": "[email protected]",
"current_token": {
"id": 44,
"name": "Morning Peak deploy bot",
"kind": "project",
"preset": "project_editor",
"preset_label": "Full access",
"scope_type": "project",
"scope_id": 81,
"scope_label": "Site ยท Morning Peak",
"abilities": [
"user.read",
"projects.read",
"projects.write",
"files.read",
"files.write",
"deploy.read",
"deploy.run",
"logs.read"
],
"expires_at": "2026-05-22T10:00:00+00:00"
}
}
}
Command mapping
What the public CLI is expected to call.
| CLI command | API routes | Notes |
|---|---|---|
staticx login |
GET /user |
Stores the token client-side and confirms the current token metadata. |
staticx workspaces |
GET /workspaces |
Best with a Global token because it may cross workspace boundaries. |
staticx sites --workspace-id WORKSPACE_ID |
GET /projects?workspace_id=... |
Best with a Workspace token when the job should stay inside one workspace. |
staticx create --workspace-id ... --name "Site" |
POST /projects |
Creates a site inside the given workspace. |
staticx deploy --site-id SITE_ID --dir dist |
POST /projects/{project}/files, POST /projects/{project}/deployments |
Upload, then publish a new version. Best with a Site token. |
staticx domain --site-id SITE_ID --domain app.example.com |
POST /projects/{project}/domain |
Move a site to a custom domain and return the DNS record to create. |
staticx domain-status --site-id SITE_ID |
GET /projects/{project}/domain |
Read domain activation progress after the DNS record is created. |
staticx logs --site-id SITE_ID |
GET /projects/{project}/logs |
Use after uploads, builds, and deployments. |
Custom domain setup
One API call, one DNS record.
This is the API version of the dashboard domain setup. Send the domain you want, copy the DNS record from the response, and StaticX handles the rest automatically.
curl -X POST "https://staticx.site/api/v1/projects/81/domain" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"domain":"app.example.com"}'
curl "https://staticx.site/api/v1/projects/81/domain" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
Project creation
Create an empty site, a ZIP-backed site, or a URL import.
Empty project
Create a blank site first, then upload files or deploy content later.
curl -X POST "https://staticx.site/api/v1/projects" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"name":"Marketing Site"}'
Create from ZIP
Start the site with a ready-built archive when the project already exists locally.
curl -X POST "https://staticx.site/api/v1/projects" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json" \
-F "name=Marketing Site" \
-F "[email protected]"
Create from URL
Queue a remote import when STATICX should mirror a public site for you.
curl -X POST "https://staticx.site/api/v1/projects" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json" \
-F "source_url=https://example.com"
File upload modes
Use one endpoint for files, ZIP archives, and URL imports.
| Mode | Required fields | Use case |
|---|---|---|
files |
files[], optional path, optional overwrite_confirmed |
Upload one or more files into a project path. |
zip |
archive, optional path, optional overwrite_confirmed |
Upload a built static site. The ZIP root should contain index.html or index.htm plus 404.html. |
url |
source_url, optional path |
Queue an import from a public URL. |
curl -X POST "https://staticx.site/api/v1/projects/{project}/files" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json" \
-F "mode=zip" \
-F "overwrite_confirmed=1" \
-F "[email protected]"
Deploy
Upload first, publish second.
# The uploaded ZIP must include index.html or index.htm plus 404.html at the root.
curl -X POST "https://staticx.site/api/v1/projects/{project}/deployments" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
If the deployment fails, stop and return the API message plus project logs. Do not report success unless the deployment response has status=succeeded.
Deployment versioning
Rollback changes the active release pointer.
Successful deployments created by the current publisher include a readable release name, versioning state, active state, file count, and size. Rollback never copies files; it activates an existing deployment.
Rollback
curl -X POST "https://staticx.site/api/v1/projects/{project}/deployments/{deployment}/rollback" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
Delete one inactive deployment
curl -X DELETE "https://staticx.site/api/v1/projects/{project}/deployments/{deployment}" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
Delete selected deployments
curl -X DELETE "https://staticx.site/api/v1/projects/{project}/deployments/delete-selected" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"deployment_ids":[101,102]}'
The API rejects deletion of the active deployment. Publish another release or roll back first, then delete inactive versions.
Environment variables
Store per-project settings for automation.
Create one variable
curl -X POST "https://staticx.site/api/v1/projects/{project}/environment-variables" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"key":"PUBLIC_API_URL","value":"https://api.example.com","is_secret":false}'
Sync variables
curl -X PUT "https://staticx.site/api/v1/projects/{project}/environment-variables" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"variables":[{"key":"TOKEN","value":"secret","is_secret":true}]}'
Responses
Predictable JSON shapes.
Project response
{
"data": {
"id": 18,
"name": "Marketing Site",
"description": "Production landing pages",
"host": "marketing.example.test",
"public_url": "https://marketing.example.test",
"status": "active",
"workspace": {
"id": 3,
"name": "Personal Workspace",
"role": "owner"
},
"subdomain": "marketing",
"custom_domain": null,
"created_at": "2026-05-11T12:10:44+00:00",
"updated_at": "2026-05-11T12:10:45+00:00"
}
}
Deployment response
{
"data": {
"id": 245,
"version_name": "Release #245",
"version_summary": "Versioned release - live now",
"kind": "deploy",
"status": "succeeded",
"message": "Deployment published.",
"is_active": true,
"is_versioned": true,
"file_count": 128,
"size_bytes": 934122,
"created_at": "2026-06-02T10:00:00+00:00",
"activated_at": "2026-06-02T10:00:01+00:00"
}
}
Validation error
{
"message": "Validation failed",
"errors": {
"source_url": [
"The source url field must be a valid URL."
]
}
}