API Reference
Read one task with its per-URL indexing report.
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 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, source_url, or versioning_enabled=false. |
| GET | /projects/{project} |
Read one project, including workspace, host, status, and public URL. |
| PATCH | /projects/{project} |
Update project metadata. Body: name, description, versioning_enabled, analytics_enabled, ga_measurement_id (GA4 Measurement ID, e.g. G-XXXXXXXXXX; null/blank disables gtag.js). |
| DELETE | /projects/{project} |
Delete a project the token owner can write to. |
| POST | /projects/{project}/domain |
Start manual custom domain setup. Body: domain. Returns the one 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, queue a ZIP import, or queue URL import. Body depends on mode. |
| GET | /projects/{project}/imports/status |
Read the latest ZIP or URL import status before publishing. |
| DELETE | /projects/{project}/files |
Delete one or more workspace files or folders. Body: file_keys[]. |
Deployments
| GET | /projects/{project}/deployments |
List release history for the project. Sites with history disabled keep one baseline release. |
| POST | /projects/{project}/deployments |
Publish the current project state. Asynchronous: returns the deployment with status=running; poll the deployment detail endpoint until succeeded or failed. Body may include versioning_enabled=false to keep one analytics-ready baseline release. Requires a root index.html or index.htm and 404.html. |
| GET | /projects/{project}/deployments/{deployment} |
Read one published release or baseline release. |
| POST | /projects/{project}/deployments/{deployment}/rollback |
Activate a previous successful versioned 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. |
Indexing
| GET | /indexing/balance |
Read the account owner SpeedyIndex balance: tokens, indexer, checker. |
| GET | /indexing/pages |
List account-wide page inventory. Query: engine, status, site, workspace, page. Default engine=google, status=unchecked. status supports requested and not_requested. |
| POST | /projects/{project}/indexing/submit |
Submit URLs to SpeedyIndex. Body: engine=google, type=indexer, urls[]. Omit urls for the sitemap-diff set. Returns created, submitted, est_tokens. |
| POST | /projects/{project}/indexing/check |
Create a checker task. Body: engine=google, urls[]. Omit urls to check the whole site inventory. |
| GET | /projects/{project}/indexing/tasks |
List the site indexing and checker tasks. |
| GET | /projects/{project}/indexing/tasks/{task} |
Read one task with its per-URL indexing report. |
Indexing errors
SpeedyIndex error codes.
Indexing endpoints that call SpeedyIndex return a machine-readable error_code in the JSON body alongside the HTTP status, so agents can recover without parsing the message. The SpeedyIndex key is never included in any response.
| error_code | HTTP | Meaning | How to recover |
|---|---|---|---|
no_key |
401 | No SpeedyIndex key is connected for the account owner. | Connect a key in Settings โ Indexing. |
insufficient_balance |
402 | The owner SpeedyIndex balance is too low for the request. | Top up SpeedyIndex credits. |
engine_unsupported |
422 | Invalid engine/type, for example bing + indexer (bing has no indexer). | Use a supported engine and type. indexer = google or yandex; checker = google, yandex, or bing. |
rate_limited |
429 | SpeedyIndex rate limit reached. | Wait briefly, then retry. |
server |
502 | SpeedyIndex returned an upstream error. | Retry later. |
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. Add versioning_enabled=false when the site should keep one baseline release instead of growing history. |
staticx domain --site-id SITE_ID --domain app.example.com |
POST /projects/{project}/domain |
Start manual custom domain setup 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
API custom domains use the one-record DNS flow.
Send the domain you want, create the DNS record from the response, and StaticX monitors activation. Automatic DNS setup is dashboard-only because the domain owner must approve provider access in a browser.
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"
If an agent or CI job is asked to automate DNS, it should either use this manual DNS endpoint or ask the user to approve DNS Connect from the dashboard. Do not pretend a StaticX token can grant external DNS-provider access automatically.
Generated subdomain suffix changes are dashboard-only. When an account has an active publishing domain, Site โ Settings โ Domain can save a free subdomain on either the StaticX default domain or that active publishing domain. API clients should read the resulting host from GET /projects/{project} or GET /domains.
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 without release history
Use one baseline release from the start while keeping analytics, forms, and the live URL active.
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","versioning_enabled":false}'
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
Upload, import, and delete workspace files.
Plan limits are enforced here. Free accounts can upload up to 500 MB and store up to 500 MB total. Plus and Pro accounts can upload up to 50 GB per request with higher storage limits. Unlimited accounts use custom storage capacity.
| 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 |
Queue a built static site import. 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]"
# ZIP and URL imports are asynchronous. Poll until state is completed before publishing.
curl -X GET "https://staticx.site/api/v1/projects/{project}/imports/status" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
curl -X DELETE "https://staticx.site/api/v1/projects/{project}/files" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"file_keys":["assets/old.xml","assets/unused-folder"]}'
If a request exceeds storage or upload capacity, the API returns error_code=PLAN_QUOTA_EXCEEDED. Stop, show the exact message, then either remove storage, delete old form entries, or upgrade.
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"
# Disable deployment history for this site and update one baseline release.
curl -X POST "https://staticx.site/api/v1/projects/{project}/deployments" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"versioning_enabled":false}'
ZIP upload/import and deployment creation are both asynchronous. First poll GET /projects/{project}/imports/status until the import state is completed. Then create the deployment and poll GET /projects/{project}/deployments/{deployment} until status is succeeded or failed. If either step fails, stop and return the exact API message plus project logs.
Deployment versioning
Choose release history or one baseline release.
By default, every successful publish creates a rollback-ready release. If versioning_enabled is false, StaticX keeps one baseline release active so analytics, forms, and the live URL keep working without adding more versions. Rollback only applies when there is previous versioned history to activate.
# Persist the site setting without publishing.
curl -X PATCH "https://staticx.site/api/v1/projects/{project}" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"versioning_enabled":false}'
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,
"versioning_enabled": true,
"analytics_enabled": true,
"ga_measurement_id": "G-XXXXXXXXXX",
"deployment_mode": "versioned_history",
"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,
"versioning_enabled": true,
"deployment_mode": "versioned_history",
"file_count": 128,
"size_bytes": 934122,
"created_at": "2026-06-02T10:00:00+00:00",
"activated_at": "2026-06-02T10:00:01+00:00"
}
}
Write response
{
"message": "Deployment created.",
"data": {
"id": 245,
"kind": "deploy",
"status": "running"
}
}
Single reads return { data }; writes add a top-level message. Collections return { data: [...] } (plus pagination when server-paginated). GET /indexing/pages is a documented flat exception.
Validation error
{
"message": "Validation failed",
"errors": {
"source_url": [
"The source url field must be a valid URL."
]
}
}