Static sites
A space holds one static website. Each deploy uploads the whole site as one archive and publishes it atomically.
Create a space
The slug is part of the permanent URL, so use the product's name: 1-30 characters, lowercase letters, digits and
single hyphens (my-app, not My_App or my--app).
curl -sS -X POST "$API/spaces" -H "$AUTH" -H "content-type: application/json" \
-d '{"slug":"my-app","name":"My App"}' -w '\nHTTP %{http_code}\n'{"id":"spc_7t1v...","slug":"my-app","name":"My App","status":"active","active_deployment_id":null,
"public_url":"https://heremyapp.com/peter/my-app","site_url":"https://peter--my-app.heremyapp.com/","created_at":"2026-10-08T06:15:13.492Z"}409 space_exists means the space is already there: use it. GET $API/spaces lists your spaces.
Share public_url; site_url is the origin it redirects to and may change.
Deploy
Put the built site in a folder with index.html at its root and send the folder as a tar.gz body:
tar -czf - -C dist . | curl -sS --fail-with-body -X POST "$API/spaces/my-app/deployments" \
-H "$AUTH" -H "content-type: application/gzip" --data-binary @-{"id":"dep_5adj...","space":"peter/my-app","status":"published","version":"1.0.0","build":1,"notes":null,
"file_count":12,"total_size":845120,
"public_url":"https://heremyapp.com/peter/my-app","site_url":"https://peter--my-app.heremyapp.com/",
"skipped":[".DS_Store"],"stripped_prefix":null,"created_at":"2026-10-08T06:15:15.476Z",
"advice":[{"code":"missing_terms","message":"No terms of use at /terms. ...","docs":"https://heremyapp.com/docs/legal"}]}- Every deploy gets the next build number and a version. Add
bump(major,minororpatch) andnotesto the deploy URL; see Releases and versioning. Withoutbump, the patch version goes up. - The response's
advicelist points out things people often miss (privacy policy, terms, link previews, page language, favicon). It never blocks a deploy; see Privacy policy and terms. - Publishing is atomic. A failed deploy leaves the live site untouched.
- Redeploying keeps the same URL, and the change is visible immediately.
- The server detects tar or tar.gz from the bytes; send
content-type: application/gziporapplication/x-tar. - If the archive wraps everything in a single folder (for example
dist/), that folder is unwrapped.
File rules
index.htmlmust exist at the site root.- Allowed extensions: html, htm, css, js, mjs, json, map, webmanifest, txt, md, xml, pdf, wasm, png, jpg, jpeg, gif, webp, avif, svg, ico, woff, woff2, ttf, otf, mp4, webm.
- If any file has another extension, or no extension, the whole deploy fails with
unsupported_file_type, anddetails.fileslists the offending files. Remove or rename them and deploy again. - Installers (.pkg, .dmg, .zip) are never site files. Publish them as installers.
- Hidden files and folders (
.DS_Store,._*,.git/) and__MACOSX/are skipped and listed inskipped. - Paths with
.., absolute paths, backslashes and duplicates are rejected (invalid_path,duplicate_path). Symlinks and other special files are rejected withunsupported_entry: copy the real files into the folder instead. - Limits: 20 MiB of files in total, 1,000 files, 25 MiB compressed upload.
How URLs map to files
The site is served at the root of its own origin, so relative (assets/app.css) and root-absolute
(/assets/app.css) paths both work.
| Request | File served |
|---|---|
/ |
index.html |
/style.css |
style.css (exact path first) |
/about |
about.html, otherwise about/index.html (redirects to /about/) |
/about/ |
about/index.html |
| anything missing | your 404.html with status 404, if it exists |
Verify
curl -sIL https://heremyapp.com/ACCOUNT/my-app | grep -iE "^HTTP|^location" # 302 to the site origin, then 200Stage, inspect and roll back
- Stage without publishing: add
?publish=falseto the deploy URL, then publish it withPOST $API/deployments/DEPLOYMENT_ID/publish. GET $API/spaces/my-app/deploymentslists the 50 most recent deployments, newest first. The live one has"status":"published".GET $API/deployments/DEPLOYMENT_IDshows one deployment with its file list.- Roll back by publishing an older deployment:
curl -sS --fail-with-body -X POST "$API/deployments/OLD_DEPLOYMENT_ID/publish" -H "$AUTH"