I was missing the _acfnonce. It might be worth updating the development documents to specify this as it’s not anywhere on the net that I could find easily!
for reference my code was
<?php
$nonce = wp_create_nonce( 'acf_form' );
$args = wp_parse_args( $args, array(
'id' => 'acf-form',
'post_id' => false,
'new_post' => false,
'field_groups' => false,
'fields' => false,
'post_title' => false,
'post_content' => false,
'form' => true,
'form_attributes' => array(),
'return' => add_query_arg( 'updated', 'true', $url ),
'html_before_fields' => '',
'html_after_fields' => '',
'submit_value' => __("Update", 'acf'),
'updated_message' => __("Post updated", 'acf'),
'label_placement' => 'top',
'instruction_placement' => 'label',
'field_el' => 'div'
));
?>
<form id="post" class="acf-form" action="" method="post" autocomplete="off" enctype="">
<input type="hidden" name="_acfnonce" value="<?php echo $nonce; ?>">
<? acf_hidden_input(array( 'name' => '_acf_form', 'value' => base64_encode(json_encode('$args')) )); ?>
...rest of form ...
I don’t know if there is a shorter way, but this works for now!