wp_insert_post() is returning the correct post ID, no failure, but the post content does not get updated

Don’t bother fixing your AJAX callback, there’s already a REST API endpoint that’s well tested and does all of this out of the box:

example.com/wp-json/wp/v2/zen_page

So lets enqueue a helper script to give us the URL and a security token/nonce:

wp_enqueue_script( 'wp-api' );

You will must enable the REST API for your custom post type by adding this option when registering your post type. If you don’t do this then the zen_page rest endpoints will not be created by core:

'show_in_rest' => true

Then adjust the JS:

var post_id = 1;
jQuery.ajax({
    url: wpApiSettings.root + 'wp/v2/zen_page/'+post_id,
    method: 'POST',
    beforeSend: function ( xhr ) {
        xhr.setRequestHeader( 'X-WP-Nonce', wpApiSettings.nonce );
    },
    data : {
        content: 'post content goes here',
        title:   'test post',
        status:  'publish'
    }
}).fail( function( response ) {
    // failure
    console.log( 'failure' );
    console.log( response );
}).done( function( response ) {
    // success!
    console.log( 'success' );
    console.log( response );
}).;

WP automatically includes the wpApiSettings object when you enqueue wp-api, and data takes these values. You’ll need an active login cookie to do this. If that’s not an option, you can create new rest endpoints

If you want to create a new one, send a POST request here:

example.com/wp-json/wp/v2/zen_page

If you want to update one, send a POST request here:

example.com/wp-json/wp/v2/zen_page/2

Where 2 is the post ID you want to update. Send a DELETE request to delete it. In a REST API, you don’t pass post IDs or actions, instead you use URLs, the same way different urls load different pages, different endpoints do different things. You can GET/POST/DELETE/PUT, and these are all the standard HTTP request types, not a parameter passed in the request.

Follow Up Notes

rest_base

This will let you modify what core uses to create the endpoint, e.g.

'rest_base' => 'bananas'

will change your endpoints URL to wp/v2/bananas. Note that you do not want to name this pages as there is already a pages endpoint provided by core.

zen_page

This might actually be better off if you just used the page post type and used a custom taxonomy to differentiate the zen_page’s from normal pages.

It doesn’t work

The REST API is designed to indicate why it didn’t work, e.g. if I try to visit wp-json/wp/v2/skjlfnvlkdjfv I’ll get:

{"code":"rest_no_route","message":"No route was found matching the URL and request method","data":{"status":404}}

and if I try to use the wp-json/wp/v2/settings endpoint in incognito mode:

{"code":"rest_forbidden","message":"Sorry, you are not allowed to do that.","data":{"status":403}}

Admin AJAX will return 0 in all of these scenarios, so be thankful but pay attention to failure or error messages. Here we saw I tried to make a request to an endpoint that doesn’t exist, then again to an endpoint I didn’t have permission for as I was logged out.