API Documentation

Overview

Core concepts — base URLs, authentication, error format, and site identifiers.

Base URL

The full management API lives on a dedicated subdomain and is versioned:

https://api.static.app/v1

Postman collection

Download the ready-to-import collection with every endpoint pre-configured (auth, variables, sample bodies):

Download Postman collection (v2.1)

After import, set the apiKey variable to your key and pid to a site's public ID.

Authentication

Endpoints marked Auth require a personal API key. Create one in Account → API. Keys start with sk_. Pass the key in the Authorization header:

Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Endpoints marked Public work without a key but are scoped to a domain or request context.

Errors

The API uses standard HTTP status codes and returns JSON with status/message on errors.

CodeMeaning
200Success.
201Created.
204No content.
400Bad request — malformed input, plan limit, invalid domain.
401Missing, invalid, or expired credentials, or wrong page password.
403No active team, invalid form token, plan limit exceeded.
404Site, file, form, page, or plan not found.
422Validation failed.
429Rate limited.
500Server error.

Example error response

{
    "status": "error",
    "message": "Site not found"
}

Response fields

FieldTypeDescription
statusstringAlways "error" on failure.
messagestringHuman-readable explanation.

Site identifier (pid)

Every site has a public 10-character alphanumeric identifier (e.g. 0aw4jtby1z). Use it everywhere a {pid} appears in the URL or a pid field appears in the body.

Workspaces

Workspaces (teams) group sites and members. Every request runs against your active workspace. List, inspect the current one, switch, or override per request.

List workspaces Auth

Returns every workspace you can access — ones you own and ones where you are a member.

GET /v1/workspaces

Example request

curl https://api.static.app/v1/workspaces \
    -H "Authorization: Bearer sk_xxxx"

Example response

{
    "data": [
        {
            "pid": "xxxxxxxxxx",
            "name": "My workspace",
            "is_owner": true,
            "is_current": true,
            "is_default": true
        },
        {
            "pid": "yyyyyyyyyy",
            "name": "Side project",
            "is_owner": false,
            "is_current": false,
            "is_default": false
        }
    ]
}

Response fields

FieldTypeDescription
data[].pidstringPublic workspace identifier.
data[].namestringWorkspace display name.
data[].is_ownerbooleantrue if the authenticated user owns the workspace.
data[].is_currentbooleantrue if this is the active workspace for the authenticated user.
data[].is_defaultbooleantrue if this is the user's default workspace.

Current workspace Auth

Returns the workspace that requests run against by default.

GET /v1/workspaces/current

Example request

curl https://api.static.app/v1/workspaces/current \
    -H "Authorization: Bearer sk_xxxx"

Example response

{
    "pid": "xxxxxxxxxx",
    "name": "My workspace",
    "is_owner": true
}

Response fields

FieldTypeDescription
pidstringActive workspace public identifier.
namestringWorkspace display name.
is_ownerbooleantrue if the user owns it.

Switch workspace Auth

Persists a new active workspace for your account. Subsequent requests use it until you switch again.

POST /v1/workspaces/switch

Body

NameTypeRequiredDescription
pidstringrequiredPublic ID of the workspace to switch to.

Example request

curl -X POST https://api.static.app/v1/workspaces/switch \
    -H "Authorization: Bearer sk_xxxx" \
    -H "Content-Type: application/json" \
    -d '{"pid":"yyyyyyyyyy"}'

Example response

{
    "status": "success",
    "workspace": {
        "pid": "yyyyyyyyyy",
        "name": "Side project"
    }
}

Response fields

FieldTypeDescription
statusstring"success" on success.
workspace.pidstringPublic ID of the now-active workspace.
workspace.namestringDisplay name.

Override per request

Without switching globally, run a single request against another workspace by passing X-Team-Pid header (or ?team_pid= query string):

curl https://api.static.app/v1/sites \
    -H "Authorization: Bearer sk_xxxx" \
    -H "X-Team-Pid: 9af1zr5w0e"

If the header is omitted, the active workspace (current_team_id, falling back to default_team_id if missing) is used.

Sites

Sites are the deployed projects in your workspace. List, create, update, and delete.

List sites Auth

Returns all sites in the active workspace.

GET /v1/sites

Example request

curl https://api.static.app/v1/sites \
    -H "Authorization: Bearer sk_xxxx"

Example response

{
    "data": [
        {
            "pid": "xxxxxxxxxx",
            "slug": "example-site",
            "name": "Example Site",
            "domain": "example.static.app",
            "status": true,
            "full_url": "https://example.static.app",
            "created_at": "2025-01-15T10:00:00Z",
            "updated_at": "2025-02-01T12:30:00Z"
        }
    ]
}

Response fields

FieldTypeDescription
data[].pidstringPublic site identifier.
data[].slugstringURL slug of the site.
data[].namestringSite display name.
data[].domainstringPrimary domain attached to the site.
data[].statusbooleantrue if the site is active.
data[].full_urlstringCanonical URL of the site.
data[].created_atstring (ISO-8601)Creation timestamp.
data[].updated_atstring (ISO-8601)Last modification timestamp.

Get site Auth

GET /v1/sites/{pid}

Path

NameTypeRequiredDescription
pidstringrequiredPublic site identifier.

Example request

curl https://api.static.app/v1/sites/xxxxxxxxxx \
    -H "Authorization: Bearer sk_xxxx"

Example response

{
    "pid": "xxxxxxxxxx",
    "slug": "example-site",
    "name": "Example Site",
    "domain": "example.static.app",
    "status": true,
    "full_url": "https://example.static.app",
    "created_at": "2025-01-15T10:00:00Z",
    "updated_at": "2025-02-01T12:30:00Z"
}

Response fields

FieldTypeDescription
pidstringPublic site identifier.
slugstringURL slug of the site.
namestringSite display name.
domainstringPrimary domain attached to the site.
statusbooleantrue if active.
full_urlstringCanonical URL.
created_atstring (ISO-8601)Creation timestamp.
updated_atstring (ISO-8601)Last modification timestamp.

Create / update from URL Auth

Imports a site from a remote ZIP archive. Pass pid to update an existing site.

POST /v1/sites

Body

NameTypeRequiredDescription
urlstringrequiredHTTPS link to a ZIP archive.
domainstringoptionalCustom subdomain. Random if omitted.
pidstringoptionalUpdate an existing site instead of creating one.

Example request

curl -X POST https://api.static.app/v1/sites \
    -H "Authorization: Bearer sk_xxxx" \
    -H "Content-Type: application/json" \
    -d '{"url":"https://example.com/site.zip","domain":"example-site"}'

Example response

{
    "status": "success",
    "pid": "xxxxxxxxxx",
    "slug": "example-site",
    "url": "https://example-site.static.app"
}

Response fields

FieldTypeDescription
statusstring"success" on success.
pidstringPublic identifier of the created (or updated) site.
slugstringURL slug.
urlstringCanonical URL of the site.

Create / update from ZIP upload Auth

Uploads a ZIP archive directly.

POST /v1/sites/zip

Body (multipart/form-data)

NameTypeRequiredDescription
archivefilerequiredThe ZIP archive.
domainstringoptionalCustom subdomain.
pidstringoptionalUpdate an existing site.

Example request

curl -X POST https://api.static.app/v1/sites/zip \
    -H "Authorization: Bearer sk_xxxx" \
    -F "[email protected]" \
    -F "pid=xxxxxxxxxx"

Example response

{
    "status": "success",
    "pid": "xxxxxxxxxx",
    "slug": "example-site",
    "url": "https://example-site.static.app"
}

Response fields

FieldTypeDescription
statusstring"success" on success.
pidstringPublic identifier of the created or updated site.
slugstringURL slug.
urlstringCanonical URL.

Delete site Auth

DELETE /v1/sites/{pid}

Returns 204 No Content.

Example request

curl -X DELETE https://api.static.app/v1/sites/xxxxxxxxxx \
    -H "Authorization: Bearer sk_xxxx"

Rename free domain Auth

POST /v1/sites/{pid}/rename-free-domain

Body

NameTypeRequiredDescription
domainstringrequiredNew subdomain name (without TLD).

Example request

curl -X POST https://api.static.app/v1/sites/xxxxxxxxxx/rename-free-domain \
    -H "Authorization: Bearer sk_xxxx" \
    -H "Content-Type: application/json" \
    -d '{"domain":"new-example-name"}'

Example response

{
    "status": "success"
}

Response fields

FieldTypeDescription
statusstring"success" on success.

Purge CDN cache Auth

POST /v1/sites/{pid}/purge-cache

Example request

curl -X POST https://api.static.app/v1/sites/xxxxxxxxxx/purge-cache \
    -H "Authorization: Bearer sk_xxxx"

Example response

{
    "status": "success",
    "message": "Cache purged successfully"
}

Response fields

FieldTypeDescription
statusstring"success" on success.
messagestringHuman-readable confirmation.

Hold / unhold sites Auth

Temporarily disables (hold) or re-enables (unhold) one or more sites.

POST /v1/sites/hold
POST /v1/sites/unhold

Body

NameTypeRequiredDescription
idsint | int[]requiredSite ID or array of site IDs.

Example request

curl -X POST https://api.static.app/v1/sites/hold \
    -H "Authorization: Bearer sk_xxxx" \
    -H "Content-Type: application/json" \
    -d '{"ids":[101, 102, 103]}'

Example response

{
    "status": "success",
    "processed": 3
}

Response fields

FieldTypeDescription
statusstring"success" on success.
processedintegerNumber of sites affected by the operation.

Files

Files live inside a site. List, read, write, upload, download, and delete.

List files in a site Auth

GET /v1/sites/files/{pid}

Query

NameTypeRequiredDescription
pathstringoptionalSubdirectory. Defaults to site root.

Example request

curl "https://api.static.app/v1/sites/files/xxxxxxxxxx?path=images" \
    -H "Authorization: Bearer sk_xxxx"

Example response

{
    "status": "success",
    "current_path": "",
    "files": [
        {
            "name": "index.html",
            "path": "index.html",
            "is_dir": false,
            "size": 1234,
            "modified": 1715600000,
            "type": "html",
            "hash": "0123456789abcdef0123456789abcdef"
        },
        {
            "name": "images",
            "path": "images",
            "is_dir": true,
            "size": null,
            "modified": 1715600000,
            "type": "directory",
            "hash": null
        }
    ]
}

Response fields

FieldTypeDescription
statusstring"success" on success.
current_pathstringDirectory being listed (empty string = site root).
files[].namestringFile or directory name.
files[].pathstringPath relative to site root.
files[].is_dirbooleantrue for directories.
files[].sizeinteger | nullSize in bytes; null for directories.
files[].modifiedintegerLast-modified Unix timestamp.
files[].typestringFile extension or "directory".
files[].hashstring | nullMD5 of file contents; null for directories.

List files for multiple sites Auth

Batch version of the previous endpoint.

POST /v1/sites/files-by-pids

Body

NameTypeRequiredDescription
pidsstring[]requiredArray of site PIDs.

Example request

curl -X POST https://api.static.app/v1/sites/files-by-pids \
    -H "Authorization: Bearer sk_xxxx" \
    -H "Content-Type: application/json" \
    -d '{"pids":["xxxxxxxxxx","yyyyyyyyyy"]}'

Example response

{
    "status": "success",
    "data": {
        "xxxxxxxxxx": {
            "status": "success",
            "files": [
                {
                    "name": "index.html",
                    "path": "index.html",
                    "is_dir": false,
                    "size": 1234,
                    "modified": 1715600000,
                    "type": "html",
                    "hash": "0123456789abcdef0123456789abcdef"
                }
            ]
        },
        "yyyyyyyyyy": {
            "status": "error",
            "message": "Site not found"
        }
    }
}

Response fields

FieldTypeDescription
statusstring"success" on the overall request.
data[pid]objectPer-site result, keyed by requested pid.
data[pid].statusstring"success" if the site was resolved.
data[pid].filesarrayFile entries (same shape as List files).
data[pid].messagestringError message when status is not "success".

Download site as ZIP Auth

GET /v1/sites/download/{pid}

Example request

curl https://api.static.app/v1/sites/download/xxxxxxxxxx \
    -H "Authorization: Bearer sk_xxxx"

Example response

{
    "status": "success",
    "url": "https://static.app/storage/zip/abcdef0123456789_download.zip"
}

Response fields

FieldTypeDescription
statusstring"success" on success.
urlstringTemporary signed URL to download the ZIP. Expires after a short window.

Download selected files Auth

POST /v1/sites/download-files/{pid}

Body

NameTypeRequiredDescription
filesstring[]requiredRelative file paths to bundle.

Example request

curl -X POST https://api.static.app/v1/sites/download-files/xxxxxxxxxx \
    -H "Authorization: Bearer sk_xxxx" \
    -H "Content-Type: application/json" \
    -d '{"files":["index.html","css/style.css"]}'

Example response

{
    "status": "success",
    "url": "https://static.app/storage/zip/abcdef0123456789_files.zip"
}

Response fields

FieldTypeDescription
statusstring"success" on success.
urlstringTemporary signed URL to download the bundle.

Upload files Auth

POST /v1/sites/upload-files/{pid}

Body (multipart/form-data)

NameTypeRequiredDescription
files[]file[]requiredOne or more files.
paths[]string[]optionalDestination path for each file. Defaults to site root.

Example request

curl -X POST https://api.static.app/v1/sites/upload-files/xxxxxxxxxx \
    -H "Authorization: Bearer sk_xxxx" \
    -F "files[][email protected]" \
    -F "files[][email protected]" \
    -F "paths[]=index.html" \
    -F "paths[]=css/style.css"

Example response

{
    "status": "success",
    "uploaded": 2,
    "files": [
        { "name": "index.html", "path": "index.html", "size": 1234 },
        { "name": "style.css",  "path": "css/style.css", "size": 567 }
    ],
    "errors": []
}

Response fields

FieldTypeDescription
statusstring"success" on success.
uploadedintegerNumber of files successfully written.
files[].namestringStored file name.
files[].pathstringFinal path relative to site root.
files[].sizeintegerBytes written.
errorsarrayPer-file error messages for entries that failed.

Delete files Auth

POST /v1/sites/delete-files/{pid}

Body

NameTypeRequiredDescription
pathsstring[]requiredRelative paths to delete.

Example request

curl -X POST https://api.static.app/v1/sites/delete-files/xxxxxxxxxx \
    -H "Authorization: Bearer sk_xxxx" \
    -H "Content-Type: application/json" \
    -d '{"paths":["old.html","images/legacy.png"]}'

Example response

{
    "status": "success",
    "deleted": 1,
    "paths": ["old.html"],
    "errors": []
}

Response fields

FieldTypeDescription
statusstring"success" on success.
deletedintegerNumber of files successfully deleted.
pathsstring[]Paths that were deleted.
errorsarrayPer-path error messages for entries that failed.

Read file Auth

Returns the full contents of a single file. Served from the main domain.

POST /api/files/{pid}/read

Body

NameTypeRequiredDescription
fileNamestringrequiredFile name, e.g. data.csv.
relativePathstringoptionalDirectory inside the site.

Example request

curl -X POST https://static.app/api/files/xxxxxxxxxx/read \
    -H "Authorization: Bearer sk_xxxx" \
    -H "Content-Type: application/json" \
    -d '{"fileName":"data.csv","relativePath":"public_html"}'

Example response

{
    "status": "success",
    "fileName": "data.csv",
    "content": "id,name\n1,foo\n2,bar\n"
}

Response fields

FieldTypeDescription
statusstring"success" on success.
fileNamestringThe file that was read.
contentstringFull file contents.

Append / prepend to file Auth

Writes content to the top or bottom of an existing file. Designed for incremental updates such as appending CSV rows.

POST /api/files/{pid}/edit

Body

NameTypeRequiredDescription
fileNamestringrequiredFile to modify.
relativePathstringoptionalDirectory inside the site.
contentstringrequiredText to insert.
positionstringrequiredtop or bottom.
newlinebooleanoptionalInsert \n between old and new content.

Example request

curl -X POST https://static.app/api/files/xxxxxxxxxx/edit \
    -H "Authorization: Bearer sk_xxxx" \
    -H "Content-Type: application/json" \
    -d '{"fileName":"data.csv","content":"3,baz","position":"bottom","newline":true}'

Example response

{
    "status": "success",
    "fileName": "data.csv",
    "size": 28
}

Response fields

FieldTypeDescription
statusstring"success" on success.
fileNamestringThe file that was modified.
sizeintegerResulting file size in bytes.

Forms

Forms collect submissions on a site. List forms and inspect their entries.

List forms Auth

POST /v1/sites/{pid}/forms

Body

NameTypeRequiredDescription
sortstringoptionalasc or desc.

Example request

curl -X POST https://api.static.app/v1/sites/xxxxxxxxxx/forms \
    -H "Authorization: Bearer sk_xxxx" \
    -H "Content-Type: application/json" \
    -d '{"sort":"desc"}'

Example response

{
    "data": [
        {
            "pid": "zzzzzzzzzz",
            "name": "Contact form",
            "form_hash": "0123456789abcdef0123456789abcdef",
            "entries_count": 42,
            "created_at": "2025-01-15T10:00:00Z"
        }
    ]
}

Response fields

FieldTypeDescription
data[].pidstringPublic form identifier.
data[].namestringForm display name.
data[].form_hashstringHash embedded in the form template.
data[].entries_countintegerTotal submissions for this form.
data[].created_atstring (ISO-8601)Creation timestamp.

Get form Auth

POST /v1/sites/{pid}/forms/{form_id}

Example request

curl -X POST https://api.static.app/v1/sites/xxxxxxxxxx/forms/42 \
    -H "Authorization: Bearer sk_xxxx"

Example response

{
    "pid": "zzzzzzzzzz",
    "name": "Contact form",
    "form_hash": "0123456789abcdef0123456789abcdef",
    "notification_email": "[email protected]",
    "entries_count": 42,
    "created_at": "2025-01-15T10:00:00Z"
}

Response fields

FieldTypeDescription
pidstringPublic form identifier.
namestringForm display name.
form_hashstringHash embedded in the form template.
notification_emailstringEmail that receives submissions.
entries_countintegerTotal submissions.
created_atstring (ISO-8601)Creation timestamp.

Delete forms Auth

DELETE /v1/sites/{pid}/forms

Body

NameTypeRequiredDescription
form_idsint[]requiredForm IDs to delete.

Example request

curl -X DELETE https://api.static.app/v1/sites/xxxxxxxxxx/forms \
    -H "Authorization: Bearer sk_xxxx" \
    -H "Content-Type: application/json" \
    -d '{"form_ids":[42, 43]}'

Example response

{
    "status": "success",
    "message": "2 forms deleted."
}

Response fields

FieldTypeDescription
statusstring"success" on success.
messagestringHuman-readable confirmation.

Change notification email Auth

POST /v1/sites/{pid}/forms/change-email

Body

NameTypeRequiredDescription
emailstringrequiredEmail address to send submissions to.

Example request

curl -X POST https://api.static.app/v1/sites/xxxxxxxxxx/forms/change-email \
    -H "Authorization: Bearer sk_xxxx" \
    -H "Content-Type: application/json" \
    -d '{"email":"[email protected]"}'

Example response

{
    "status": "success"
}

Response fields

FieldTypeDescription
statusstring"success" on success.

List form entries Auth

POST /v1/sites/{pid}/forms/{form_id}/entries

Body

NameTypeRequiredDescription
per_pageintoptionalItems per page. Default 20.
sortstringoptionalasc or desc.

Example request

curl -X POST https://api.static.app/v1/sites/xxxxxxxxxx/forms/42/entries \
    -H "Authorization: Bearer sk_xxxx" \
    -H "Content-Type: application/json" \
    -d '{"per_page":20,"sort":"desc"}'

Example response

{
    "data": [
        {
            "pid": "qqqqqqqqqq",
            "page": "/contact",
            "fullurl": "https://example.static.app/contact",
            "ip": "203.0.113.42",
            "is_spam": false,
            "data": [
                { "name": "name",    "value": "Jane Doe" },
                { "name": "email",   "value": "[email protected]" },
                { "name": "message", "value": "Hello!" }
            ],
            "created_at": "2025-02-01T12:34:56Z"
        }
    ],
    "meta": {
        "current_page": 1,
        "per_page": 20,
        "total": 42
    }
}

Response fields

FieldTypeDescription
data[].pidstringPublic entry identifier.
data[].pagestringPage path where the form was submitted.
data[].fullurlstringFull submission URL.
data[].ipstringSubmitter IP (may be empty).
data[].is_spambooleantrue if marked as spam.
data[].data[].namestringField name.
data[].data[].valuestringField value.
data[].created_atstring (ISO-8601)Submission timestamp.
meta.current_pageintegerCurrent pagination page.
meta.per_pageintegerPage size.
meta.totalintegerTotal entries for the form.

Delete form entries Auth

DELETE /v1/sites/{pid}/forms/{form_id}/entries

Body

NameTypeRequiredDescription
entry_idsint[]requiredEntry IDs to delete.

Example request

curl -X DELETE https://api.static.app/v1/sites/xxxxxxxxxx/forms/42/entries \
    -H "Authorization: Bearer sk_xxxx" \
    -H "Content-Type: application/json" \
    -d '{"entry_ids":[101, 102]}'

Example response

{
    "status": "success",
    "message": "2 entries deleted."
}

Response fields

FieldTypeDescription
statusstring"success" on success.
messagestringHuman-readable confirmation.

Mark entries as spam Auth

POST /v1/sites/{pid}/forms/{form_id}/entries/mark-spam

Body

NameTypeRequiredDescription
entry_idsint[]requiredEntry IDs.

Example request

curl -X POST https://api.static.app/v1/sites/xxxxxxxxxx/forms/42/entries/mark-spam \
    -H "Authorization: Bearer sk_xxxx" \
    -H "Content-Type: application/json" \
    -d '{"entry_ids":[101]}'

Example response

{
    "status": "success",
    "message": "1 entries marked as spam."
}

Response fields

FieldTypeDescription
statusstring"success" on success.
messagestringHuman-readable confirmation.

Users

Your account profile. Read the signed-in user's details.

Get profile Auth

Returns the authenticated user's profile, current plan, and usage stats.

GET /v1/users/profile

Example request

curl https://api.static.app/v1/users/profile \
    -H "Authorization: Bearer sk_xxxx"

Example response

{
    "data": {
        "first_name": "Jane",
        "last_name": "Doe",
        "email": "[email protected]",
        "image": "",
        "plan": {
            "name": "large",
            "status": "active",
            "sites": 30,
            "storage": 10000
        },
        "usage": {
            "sites": 10,
            "storage": 262.15
        },
        "sites": 10,
        "limit": true,
        "created_at": "2021-03-19T20:38:55Z"
    }
}

Response fields

FieldTypeDescription
data.first_namestringUser's first name.
data.last_namestringUser's last name.
data.emailstringUser email.
data.imagestringAvatar URL, or empty string.
data.plan.namestringPlan slug.
data.plan.statusstringactive, pending, or incomplete.
data.plan.sitesintegerSite quota included in the plan.
data.plan.storageintegerStorage quota in megabytes.
data.usage.sitesintegerCurrent site count.
data.usage.storagenumberCurrent storage usage in megabytes.
data.sitesintegerConvenience copy of usage.sites.
data.limitbooleantrue if usage is within plan limits.
data.created_atstring (ISO-8601)Account creation timestamp.
Hey there πŸ‘‹  Friends from designmodo are here to help!