Endpoints

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."
    ]
  }
}