423 Locked: Publishing and deleting co-authored SharePoint pages programmatically

423 Locked: Publishing and deleting co-authored SharePoint pages programmatically

Last updated on September 27, 2026

I’ve been working on a SharePoint Framework (SPFx) solution that allows users to publish and delete groups of connected pages. These actions can be triggered by the user when they open the “main page” of the group in edit mode.

The initial logic implemented by another developer worked fine if the user had first checked out the page. However, if the user simply took the page to edit mode and then decided to publish or delete the group of pages, the page that was currently being edited never got published or deleted. Instead, the API would respond with the HTTP status 423 Locked.

The culprit turned out to be the co-authoring feature for SharePoint pages and news. In this article, I’ll introduce you to a couple of undocumented API operations that allow you to programmatically publish and delete pages, even when they are locked out for editing by the current user.

The problem: the co-authoring lock

Before co-authoring, opening a page in edit mode checked out the file to you. Other operations performed by the same user, such as publishing through the REST API, worked fine because the file was checked out to that user anyway.

With co-authoring, this changes. When you open a page in edit mode, the page editor starts a co-authoring session and places a shared lock on the file. Your changes don’t get written to the file as you type. Instead, they live in a Fluid Framework container that is tied to the session, and the editor periodically extends the session with a heartbeat call. Anything that tries to modify the file outside of that session gets rejected.

My first thought was to simply redirect the user to read mode and perform the publish and delete operations from there. But that didn’t work either. The session lingers on the server for a good while after the browser has left edit mode. Waiting for the session to time out would not have resulted in a good user experience, and so I kept on looking.

Investigating what the page editor does

Whenever an API refuses to do something the UI can clearly do, my go-to move is to open the browser developer tools, switch to the Network tab and do the thing in the UI. Filtering the requests with “sitepages” revealed a whole family of operations that you won’t find in the API documentation:

  • PublishCoAuth is called when you click Publish or Republish.
  • SavePageCoAuth is called when you click Save and exit. It is also called periodically to auto-save as you make changes to the page.
  • discardCoAuth is called when you click Discard changes. It ends the session and throws away the working copy. Any changes that were not manually saved by the user are lost.
  • startCoAuth starts the co-authoring session.
  • ExtendCoAuthSession is the heartbeat that keeps the session alive.

Also, if we GET the current page data via the pages endpoint, we can find some interesting co-authoring-related properties like AuthoringMetadata.SessionId and CoAuthState.SharedLockId, which are the key to everything that follows.

Deleting a page that is being co-authored

Deleting turned out to be the easier of the two scenarios. Because we don’t care about any unsaved changes of a page we are about to delete, we can simply end the session with discardCoAuth, which requires us to specify the CoAuthState.SharedLockId in the request body.

The values of AuthoringMetadata.SessionId and CoAuthState.SharedLockId are the same if there is only a single active co-authoring session, so you might think it doesn’t matter which one you use. However, if there are multiple active co-authoring locks, those property values can actually be different, and in that situation you need to use the CoAuthState.SharedLockId, otherwise the endpoint will give you HTTP 500 Internal Server Error.

After the discard, the DELETE request that had been returning 423 Locked went through without a problem, and I redirected the user to the site front page.

Publishing a page that is being co-authored

Executing the PublishCoAuth operation is slightly more complex because it expects to get the actual page JSON in the request body, including the entire page layout in the CanvasContent1 property. Luckily, we can GET the values for all of these properties from the pages endpoint, including the page layout:

GET https://{orgname}.sharepoint.com/sites/{sitename}/_api/sitepages/pages({pageid})

The values in the request body that should differ from the ones present in the GET response are the following:

  • ClientOperation should be set to 4 (publish)
  • Action should be set to 1
  • LockAction should be set to 2

The operation saves the session’s pending changes, publishes the page and ends the session in one go; exactly what I was looking for!

If you want to save the page (not publish), you can use the SavePageCoAuth operation. You use it otherwise exactly the same way as the PublishCoAuth operation, except the ClientOperation value needs to be set to 3.

Recognising whether a page is being co-authored

Not every page in edit mode is being co-authored. A page can still be checked out the old-fashioned way before it is opened for editing. In those cases, the PublishCoAuth operation has no session to work with, and the regular publish operation is the one to use.

Luckily, we can check if the page is being co-authored by simply checking if the CoAuthState.SharedLockId property has a value in the page data. If it is, we go through the co-authoring operation; otherwise, we fall back to the regular publish.

With this kind of check in place, we are able to publish the page regardless of whether the user has first checked it out for editing or not. And the same check can also be used when deleting a file to avoid calling the discardCoAuth operation unnecessarily.

Afterword

None of the operations in this article are documented by Microsoft. Everything in this article is based on observing what API endpoints the page editor calls and confirming that the same requests work when sent from my own code. Microsoft may change any of this without notice, potentially breaking your custom code.

I think that’s all the disclaimers! I hope you enjoyed reading this article and found it helpful. If you’d like to read more articles from me, you can either subscribe to my blog or follow me on LinkedIn to get notified when I publish something new.

Happy coding, and until next time!

Laura



Leave a Reply

Your email address will not be published. Required fields are marked *