// Apps Script

Keep a stable web app URL across deployments.

Every time you hit "New deployment" in Apps Script you get a fresh /exec URL and everything pointing to the old one breaks. Use a named deployment and update its version instead.

I want my Apps Script web app URL to stay the same after I push changes, because every new deployment hands me a different URL and breaks bookmarks and integrations that saved the old one.

The script

copy · paste · trigger
deployVersion.gs
Apps Script
// Update an existing named deployment to a new script version.
// Run once after each code change to keep the /exec URL stable.
function bumpDeployment() {
  var scriptId = ScriptApp.getScriptId();
  var deploymentId = 'AKfycb_YOUR_DEPLOYMENT_ID_HERE';

  var newVersion = ScriptApp.getProjectVersion();

  var resource = {
    description: 'Production',
    versionNumber: newVersion
  };

  var url = 'https://script.googleapis.com/v1/projects/'
    + scriptId + '/deployments/' + deploymentId;

  var options = {
    method: 'put',
    contentType: 'application/json',
    headers: { Authorization: 'Bearer ' + ScriptApp.getOAuthToken() },
    payload: JSON.stringify(resource)
  };

  UrlFetchApp.fetch(url, options);
  Logger.log('Deployment updated to version ' + newVersion);
}

Need a variant? Gnaw writes a custom version from one sentence — fields, triggers, edge cases handled.

Walkthrough

Why a new deployment always means a new URL

Apps Script ties each /exec URL to a specific deployment record, not to the project. When you click Deploy > New deployment, the editor creates a brand-new record with its own ID, and that ID is part of the URL path. The old record still exists and its URL still works, pointing at whatever version it was pinned to when you created it. Nothing migrates automatically. Anyone who bookmarked, hardcoded, or integrated the old URL is now pointing at stale code.

The first time I hit this I had a Sheets sidebar calling a doGet endpoint whose URL I had pasted into the client-side HTML. After a redeploy the sidebar kept returning the old response and I spent an hour convinced the cache was to blame. It was just the wrong URL.

Creating one named deployment and keeping it

The fix is to create exactly one deployment for production purposes, then update that same record every time you ship a change. In the Apps Script editor: Deploy > Manage deployments > New deployment, give it a description like 'Production', choose 'Web app', pick who can access it, and confirm. Copy the deployment ID from the URL in your browser's address bar — it starts with AKfycb and is about 70 characters long. That string is stable forever.

From this point on, never click New deployment again for this project (unless you intentionally want a separate staging endpoint). Instead, save a new version with File > Save (which creates a numbered version), then update the existing deployment record to point at that new version. The /exec URL does not change. Every integration keeps working.

Automating the version bump with the Apps Script REST API

The snippet above calls the Apps Script REST API's PUT /deployments/{deploymentId} endpoint from inside the script itself. ScriptApp.getProjectVersion() returns the latest saved version number, and ScriptApp.getOAuthToken() hands you a short-lived bearer token scoped to the project. Paste your real deployment ID, run bumpDeployment() from the editor after each code change, and the deployment record updates in place.

One gotcha: the Apps Script API must be enabled in the Cloud project linked to your script. Go to the Google Cloud Console, find the project (it matches the script project name), and enable 'Apps Script API' under APIs & Services. Without this the fetch returns a 403 with a message about the API not being enabled, which is easy to confuse with an OAuth scope error. The required OAuth scope is https://www.googleapis.com/auth/script.deployments — add it to your appsscript.json manifest if the authorization prompt does not appear automatically.

Want a custom version?

Describe your sheet and the rule you want. Gnaw writes the Apps Script — fields, triggers, edge cases — in one shot.

FAQ

4 questions
Is there a way to deploy without a version number — just use HEAD?
Yes. When you create a deployment in the editor you can set the version to 'Latest (Head)'. Every request then executes whatever is saved, with no version pinning. The URL stays stable and you skip the version-bump step entirely. The downside is zero rollback capability: a broken save goes live immediately. Use Head for internal tools where you control all users; pin to a version number for anything external.
What is the deployment ID and where do I find it after the fact?
It is the long AKfycb... string that appears in the Manage deployments dialog next to each entry. You can also retrieve all deployments programmatically by calling GET https://script.googleapis.com/v1/projects/{scriptId}/deployments with a bearer token — the response lists every deployment record with its ID, current version, and web app URL. Useful if you lost track of which ID you pasted somewhere.
Does updating a deployment cause any downtime for users already on the page?
No. The update is atomic from the runtime's perspective: in-flight requests to the old version finish against the old code, and new requests after the PUT completes hit the new version. There is no restart or cold-start gap. Users who already have the web app open in a browser and make a fetch call mid-session might hit either version depending on exact timing, but in practice the window is under a second.
Can I have one URL for staging and a different one for production?
Yes, and this is the right pattern. Create two deployments: one named 'Staging' pinned to Head, one named 'Production' pinned to a specific version. The two /exec URLs are different, which is what you want — staging tests code that hasn't been promoted yet. Only the production URL gets shared externally. When a staging build is validated, run bumpDeployment() pointing at the production deployment ID to promote it.
// one good script a week

Get a working Apps Script snippet in your inbox, weekly.