Ich habe dieses Script als Proof of Concept nach einer entsprechenden Anfrage geschrieben.

<?php
/**
 * @copyright Copyright (C) PMJ (https://pmj.rocks)
 * @author PMJ (https://pmj.rocks)
 * @license AGPL 3.0 (http://www.gnu.org/licenses/agpl-3.0.de.html)
 */

/*********
* CONFIG *
**********/
// this is the domain of your nextcloud instance
// if your nextcloud instance is installed in a subdirectory, add this as well without trailing slash
$nextcloud = '';
// this is your nextcloud username and password
$user = '';
$password = '';
// this is the URI of your calendar in which you want to post an event
// you can get the URI by copying the private link to the specific calendar
// e.g. https://your.domain/remote.php/dav/calendars/yourusername/public-calendar/
// the last part is the URI
$calendar = 'public-calendar';

/***************
* EVENT CONFIG *
****************/
// this is the actual config of your event
// you can set the variables with post variables from a form input
// timezone
$tzid = 'Europe/Zurich';
// event location
$location = 'Büro';
// event description
$description = 'Test Meeting';
// event summary
$summary = 'Hier wird hard getestet!';
// start- and enddate
// needs to be in the format yyyymmddThhmmss
$startdate = '20210210T231500';
$enddate = '20210210T234500';
// long before an event starts you should be notified
$alarmduration = '5M';


/*****************
* CHECK TIMESLOT *
******************/
// get the events list in json format so we can check if the timeslot is already occupied
// compile url for cURL request
$url = 'https://'.$nextcloud.'/remote.php/dav/calendars/'.$user.'/'.$calendar.'?export&accept=jcal&expand=1&start='.strtotime($startdate).'&end='.strtotime($enddate);
// set header for cURL request (we don't need any actually to request the eventslist)
$headers = array();

// init cURL handle
$ch = curl_init();
// set the cURL options
$coptions = array(
  CURLOPT_URL             => $url,
  CURLOPT_USERPWD         => $user.":".$password,
  CURLOPT_HTTPAUTH        => CURLAUTH_BASIC,
  CURLOPT_CUSTOMREQUEST   => "GET",
  CURLOPT_HTTPHEADER      => $headers,
  CURLOPT_RETURNTRANSFER  => TRUE,
);
curl_setopt_array($ch, $coptions);
// execute request
$result = curl_exec($ch);
// close connection
curl_close($ch);

// since this request returns jason it needs to be decoded
$result = json_decode($result);

// if the request returns events (which are in array positon 3) we can bail out
// you can set the return to whatever you need, I just exit the script with a message
if (!empty($result[2]))
{
  exit('This Timeslot is already occupied! Check the calendar for free timeslots!');
}

/*************
* POST EVENT *
**************/
// compile the event in ics format
// this is the full declaration, you can delete what you don't need
$ics = "BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//IceWarp//IceWarp Server 11.4.0.0 x64//EN
BEGIN:VTIMEZONE
TZID:". $tzid ."
BEGIN:STANDARD
TZOFFSETFROM:+0300
TZOFFSETTO:+0200
TZNAME:EET
DTSTART:19701025T000000
RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+0200
TZOFFSETTO:+0300
TZNAME:EEST
DTSTART:19700329T000000
RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
LOCATION:". $location."
DESCRIPTION:". $description."
SUMMARY:". $summary."
DTSTART;TZID=". $tzid .":".$startdate ."
DTEND;TZID=". $tzid .":".$enddate ."
CREATED:".$startdate ."Z
LAST-MODIFIED:".$startdate ."Z
DTSTAMP:".$startdate ."Z
PRIORITY:0
SEQUENCE:0
CLASS:PUBLIC
X-MICROSOFT-CDO-BUSYSTATUS:busy
TRANSP:OPAQUE
BEGIN:VALARM
ACTION:DISPLAY
TRIGGER;VALUE=DURATION:-PT". $alarmduration."
END:VALARM
END:VEVENT
END:VCALENDAR";

// nextcloud needs a filename for the event, easiest is to use the timestamp of the request
// if oyu intend to do updates on your events, you need to delete or comment the bailout code above and set a filename which you can remember
$event = time().'.ics';
// compile url for cURL request
$url = 'https://'.$nextcloud.'/remote.php/dav/calendars/'.$user.'/'.$calendar.'/'.$event;
// set headers, this time we need some
$headers = array(
    'Content-Type: text/calendar; charset=utf-8',
    'Expect: ',
    'Content-Length: '.strlen($ics),
);

// init cURL handle
$ch = curl_init();
// set the cURL options
$coptions = array(
  CURLOPT_URL             => $url,
  CURLOPT_USERPWD         => $user.":".$password,
  CURLOPT_HTTPAUTH        => CURLAUTH_BASIC,
  CURLOPT_CUSTOMREQUEST   => "PUT",
  CURLOPT_POSTFIELDS      => $ics,
  CURLOPT_HTTPHEADER      => $headers,
  CURLOPT_RETURNTRANSFER  => TRUE,
);
curl_setopt_array($ch, $coptions);
// execute request (post the event)
$result = curl_exec($ch);
// close connection
curl_close($ch);
?>