Update Serving and Build Config

PUT /v1/applications/{uuid}

Update how an application's files are served and how it is built. This is a partial update — only the fields you send change. The source and type cannot be changed (create a new app to switch source).

Request body

name string
Optional. Rename the application (still unique within the account).
publish_dir string
Optional. The folder your site is served from: for an upload of a site that is already built, the folder inside the archive (e.g. dist). When Ngris builds the site, leave it empty and set output_dir instead; setting both to dist makes the site be looked for in dist/dist. Leading and trailing slashes are trimmed; a value containing .. is read as the archive root. Empty = archive root.
index_document string
Optional. Document served for directory/root requests. Default index.html.
spa_fallback boolean
Optional. When true, unmatched paths serve the index document — enable for React/Vue/Svelte client-side routing.
not_found_path string
Optional. A document served for missing paths when spa_fallback is off (a custom 404 page).
root_directory string
Optional. The repository subfolder builds run in (monorepos); "" = the repository root. Repo-relative: no leading /, no ... Takes effect on the next deploy.
require_manual_promotion boolean
Optional, default false. When true, every new deploy for this app — including git production-branch pushes — builds as a not-live candidate; you then promote it to production by hand. This is the whole-app version of the per-deploy activate=false flag: leave it false to keep the default auto-go-live behaviour.
skew_protection_enabled boolean
Optional, default false. Turns on Skew Protection — the edge pins each served HTML document to its deploy (a 24h ngris_dpl cookie) and, on an asset the newer deploy no longer ships, retries it against the pinned deploy, so a mid-session redeploy doesn't 404 stale hashed chunks for visitors already on the old page. Enabling requires the paid skew_protection entitlement — without it the request returns 403. Disabling (false) is always allowed.
speed_insights_enabled boolean
Optional, default false. Turns on real-user Speed Insights (Core Web Vitals) collection for the app. Enabling requires the paid speed_insights entitlement — without it the request returns 403. Disabling (false) is always allowed. When on, the edge serves the collector script on the app's own domain and auto-injects it into served HTML for static and fullstack static front-ends.
install_command string
Optional. Build setting: the app's own install command, run instead of the detected one. static and fullstack (the front end), and managed (Node apps only: a Python or Go app's build fails with one). "" = detected.
build_command string
Optional. The app's own build command, run after the install. static and fullstack (the front end), and managed (Python: after the dependencies; Go: must write /app/server). "" = detected.
output_dir string
Optional. static and fullstack (the front end): the folder the build writes the site to, inside the project (e.g. dist); everything in it is published. Leading and trailing slashes are trimmed; no ..; . = the project root; at most 512 bytes. Used only when a build runs. "" = detected.
start_command string
Optional. managed, and fullstack (the backend): how the server starts, run with a shell; the server must listen on $PORT. npm start, npm run <name>, yarn <name> and pnpm <name> run that package.json script. See Backend Apps. "" = detected.

The four build settings are trimmed, and each command is one line of at most 1024 bytes (chain steps with &&; control and invisible characters are refused). A function or wordpress app takes none of them. A value the app's type can't take is refused; "" (back to detection) is always accepted. They apply from the next build: saving doesn't rebuild.

Response (200 OK)

The updated application, the same object as Get Application (not wrapped). Serving config changes apply to the next request served (no rebuild needed); root_directory and the build settings apply to the next deploy.

Deprecated: until the sunset the application object also carries the legacy integer id and *_id fields. Don't use them; read uuid and the *_uuid fields.

Errors

400
Invalid root_directory, a refused build setting — install_command must be a single line (chain commands with &&) of at most 1024 bytes (the same for build_command and start_command), invalid output_dir: must be a folder inside the project, like dist or build/site (no '..'), or a build setting the app's type can't take, e.g. start_command can only be set on a managed or full-stack app, output_dir can only be set on a static or full-stack app, install_command can only be set on a static, managed or full-stack app — or a malformed body. Nothing is saved.
403
Enabling skew_protection_enabled or speed_insights_enabled without the matching entitlement.
404
Unknown application.
409
The app is paused over a plan limit (quota_paused); delete something or upgrade first.
Request
curl -X PUT "https://api.ngris.com/v1/applications/019be838-d261-7d9b-9499-71995a3a91c6" \ -H "X-API-KEY: <your_api_key>" \ -H "Content-Type: application/json" \ -d '{ "publish_dir": "dist", "spa_fallback": true }'
Response — 200 OK
{ "uuid": "019be838-d261-7d9b-9499-71995a3a91c6", "account_uuid": "019a68b9-1a79-7820-a884-41f554aba6bd", "name": "my-site", "type": "static", "source": "upload", "current_deploy_uuid": "019bf3d2-0116-7612-9567-75a812bf45fc", "publish_dir": "dist", "index_document": "index.html", "spa_fallback": true, "prod_visibility": "public", "preview_visibility": "private", "require_manual_promotion": false, "root_directory": "", "skew_protection_enabled": false, "speed_insights_enabled": false, "quota_paused": false, "backend_paths": [], "port": 0, "cpu_millis": 0, "memory_mb": 0, "min_replicas": 0, "max_replicas": 0, "health_check_path": "", "install_command": "", "build_command": "", "output_dir": "", "start_command": "", "primary_endpoint_uuid": "019bb1e2-6f10-7c41-a1d3-2b8e4c9f0a17", "endpoints": [ { "uuid": "019bb1e2-6f10-7c41-a1d3-2b8e4c9f0a17", "hostname": "example.com", "url": "https://example.com", "primary": true, "serving_mode": "agentless", "status": "active", "tls_status": "issued", "tls_source": "domain", "tls_expires_at": "2027-01-05T10:00:00Z", "quota_paused": false, "auth_mode": "none", "auth_protected": false, "created_at": "2026-10-01T09:00:00Z" } ], "created_at": "2026-07-28T12:00:00Z", "updated_at": "2026-10-20T09:15:00Z" }
Was this helpful?
Was this helpful?