Is it possible to change an existing post status from ‘pending’ to ‘publish’ via email?

Step 1 – Register a Custom Query Var

By adding a custom query var, you make WP aware of it so it can be used in requests.

function wpse_add_query_vars( $vars ) {
    $vars[] = 'wpse_set_to_publish';
    return $vars;
}
add_filter( 'query_vars', 'wpse_add_query_vars' );

Step 2 – Sniff The Request

Now we hook into parse_request to look for our new custom query var. This is where you process it and it can get as complicated or simple as you want. For example, for security, you could check for the existence of a token that is stored as postmeta or a transient. But at it’s most basic level:

function wpse_sniff_request( $query ) {
    if ( ! is_admin() && isset( $query->query_vars['wpse_set_to_publish'] ) ) {
        $post_vars = array(
            'ID' => $query->query_vars['wpse_set_to_publish'],
            'post_status' => 'publish'
        );
        wp_update_post( $post_vars );
    }
}
add_action( 'parse_request', 'wpse_sniff_request' );

Note: you could also skip step one and do this step in an init hook and instead intercept $_GET['wpse_set_to_publish'] but it’s not exactly the “WordPress way”

Step 3 – Provide an Approval Link in Email

Finally, assuming you’re generating the email that goes out, you can include a link with something along these lines:

echo add_query_arg( array( 'wpse_set_to_publish' => $post_id ), site_url() );

Note: You’ll probably get the $post_id from the return value of wp_insert_post().

Then, once the person clicks on that link, it should take them to http://yourwebsite.com/?wpse_set_to_publish=123 and this will update the post with the ID of 123 to having a status of publish.

Unfortunately I don’t have time to fully test this right now, but this should get you steered in the right direction. Again, this is the most basic implementation and you should always consider security, permissions, etc. While this also might be more convenient, it would theoretically be a lot more secure if you just provide a link to get_edit_post_link() which would force the user to log in (if they weren’t already), then they could move right to publish from the admin.