diff --git a/src/integration_tests/caldav/calendar.rs b/src/integration_tests/caldav/calendar.rs
new file mode 100644
index 0000000..d09d7eb
--- /dev/null
+++ b/src/integration_tests/caldav/calendar.rs
@@ -0,0 +1,105 @@
+use crate::integration_tests::{ResponseExtractString, get_app};
+use axum::body::{Body, Bytes};
+use axum::extract::Request;
+use headers::{Authorization, HeaderMapExt};
+use http::{HeaderValue, StatusCode};
+use rstest::rstest;
+use rustical_store::{Calendar, CalendarMetadata};
+use tower::ServiceExt;
+
+const MKCOL_REQUEST: &str = r#"
+
+
+
+
+
+
+
+
+ Amazing Calendar
+ Description
+ #FFF8DCFF
+ Europe/Berlin
+
+
+
+
+
+
+
+
+ "#;
+
+#[rstest]
+#[tokio::test]
+async fn test_caldav_calendar(
+ #[from(get_app)]
+ #[future]
+ app: axum::Router,
+) {
+ let app = app.await;
+
+ let request_template = || {
+ Request::builder()
+ .method("MKCALENDAR")
+ .uri("/caldav/principal/user/calendar")
+ .body(Body::from(MKCOL_REQUEST))
+ .unwrap()
+ };
+
+ // Try without authentication
+ let request = request_template();
+ let response = app.clone().oneshot(request).await.unwrap();
+ assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
+ let body = response.extract_string().await;
+ insta::assert_snapshot!(body);
+
+ // Try with correct credentials
+ let mut request = request_template();
+ request
+ .headers_mut()
+ .typed_insert(Authorization::basic("user", "pass"));
+ let response = app.clone().oneshot(request).await.unwrap();
+ assert_eq!(response.status(), StatusCode::CREATED);
+ let body = response.extract_string().await;
+ insta::assert_snapshot!(body);
+
+ let mut request = Request::builder()
+ .method("GET")
+ .uri("/caldav/principal/user/calendar")
+ .body(Body::empty())
+ .unwrap();
+ request
+ .headers_mut()
+ .typed_insert(Authorization::basic("user", "pass"));
+ let response = app.clone().oneshot(request).await.unwrap();
+ assert_eq!(response.status(), StatusCode::OK);
+ let body = response.extract_string().await;
+ insta::assert_snapshot!(body);
+
+ let mut request = Request::builder()
+ .method("PROPFIND")
+ .uri("/caldav/principal/user/calendar")
+ .body(Body::empty())
+ .unwrap();
+ request
+ .headers_mut()
+ .typed_insert(Authorization::basic("user", "pass"));
+ let response = app.clone().oneshot(request).await.unwrap();
+ assert_eq!(response.status(), StatusCode::MULTI_STATUS);
+ let body = response.extract_string().await;
+ insta::assert_snapshot!(body);
+
+ let mut request = Request::builder()
+ .method("DELETE")
+ .uri("/caldav/principal/user/calendar")
+ .body(Body::empty())
+ .unwrap();
+ request
+ .headers_mut()
+ .typed_insert(Authorization::basic("user", "pass"));
+ let response = app.clone().oneshot(request).await.unwrap();
+ assert_eq!(response.status(), StatusCode::OK);
+ let body = response.extract_string().await;
+ insta::assert_snapshot!(body);
+}
diff --git a/src/integration_tests/caldav/mod.rs b/src/integration_tests/caldav/mod.rs
index 72cd78d..85cffbc 100644
--- a/src/integration_tests/caldav/mod.rs
+++ b/src/integration_tests/caldav/mod.rs
@@ -6,6 +6,8 @@ use http::{HeaderValue, StatusCode};
use rstest::rstest;
use tower::ServiceExt;
+mod calendar;
+
#[rstest]
#[tokio::test]
async fn test_caldav_root(
diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal-4.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal-4.snap
index dfabdac..46158ec 100644
--- a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal-4.snap
+++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal-4.snap
@@ -50,4 +50,192 @@ expression: body
HTTP/1.1 200 OK
+
+ /caldav/principal/user/calendar/
+
+
+ #FFF8DCFF
+ Description
+ BEGIN:VCALENDAR
+PRODID:-//github.com/lennart-k/vzic-rs//RustiCal Calendar server//EN
+VERSION:2.0
+BEGIN:VTIMEZONE
+TZID:Europe/Berlin
+LAST-MODIFIED:20250723T190331Z
+X-LIC-LOCATION:Europe/Berlin
+X-PROLEPTIC-TZNAME:LMT
+BEGIN:STANDARD
+TZNAME:CET
+TZOFFSETFROM:+005328
+TZOFFSETTO:+0100
+DTSTART:18930401T000000
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:CEST
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+DTSTART:19160430T230000
+RDATE:19400401T020000
+RDATE:19430329T020000
+RDATE:19460414T020000
+RDATE:19470406T030000
+RDATE:19480418T020000
+RDATE:19490410T020000
+RDATE:19800406T020000
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:CET
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+DTSTART:19161001T010000
+RDATE:19421102T030000
+RDATE:19431004T030000
+RDATE:19441002T030000
+RDATE:19451118T030000
+RDATE:19461007T030000
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:CEST
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+DTSTART:19170416T020000
+RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3MO;UNTIL=19180415T010000Z
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:CET
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+DTSTART:19170917T030000
+RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=3MO;UNTIL=19180916T010000Z
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:CEST
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+DTSTART:19440403T020000
+RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1MO;UNTIL=19450402T010000Z
+END:DAYLIGHT
+BEGIN:DAYLIGHT
+TZNAME:CEMT
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0300
+DTSTART:19450524T020000
+RDATE:19470511T030000
+END:DAYLIGHT
+BEGIN:DAYLIGHT
+TZNAME:CEST
+TZOFFSETFROM:+0300
+TZOFFSETTO:+0200
+DTSTART:19450924T030000
+RDATE:19470629T030000
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:CET
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0100
+DTSTART:19460101T000000
+RDATE:19800101T000000
+END:STANDARD
+BEGIN:STANDARD
+TZNAME:CET
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+DTSTART:19471005T030000
+RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19491002T010000Z
+END:STANDARD
+BEGIN:STANDARD
+TZNAME:CET
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+DTSTART:19800928T030000
+RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1SU;UNTIL=19950924T010000Z
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:CEST
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+DTSTART:19810329T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:CET
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+DTSTART:19961027T030000
+RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
+END:STANDARD
+END:VTIMEZONE
+END:VCALENDAR
+
+
+ https://www.iana.org/time-zones
+
+ Europe/Berlin
+ 0
+
+
+
+
+
+
+
+
+
+ i;ascii-casemap
+ i;octet
+
+ 10000000
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -2621430101T000000Z
+ +2621421231T235959Z
+ github.com/lennart-k/rustical/ns/0
+ github.com/lennart-k/rustical/ns/0
+
+
+
+ 89828c25-cc5c-481a-bb3b-cde435cf9393
+
+
+ 1
+
+
+ 1
+
+
+
+
+
+
+ Amazing Calendar
+
+ /caldav/principal/user/
+
+
+
+
+
+
+
+ /caldav/principal/user/
+
+
+ HTTP/1.1 200 OK
+
+
diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__caldav_calendar-2.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__caldav_calendar-2.snap
new file mode 100644
index 0000000..ae58472
--- /dev/null
+++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__caldav_calendar-2.snap
@@ -0,0 +1,5 @@
+---
+source: src/integration_tests/caldav/calendar.rs
+expression: body
+---
+
diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__caldav_calendar-3.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__caldav_calendar-3.snap
new file mode 100644
index 0000000..5963b3c
--- /dev/null
+++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__caldav_calendar-3.snap
@@ -0,0 +1,12 @@
+---
+source: src/integration_tests/caldav/calendar.rs
+expression: body
+---
+BEGIN:VCALENDAR
+VERSION:4.0
+CALSCALE:GREGORIAN
+PRODID:RustiCal
+X-WR-CALNAME:Amazing Calendar
+X-WR-CALDESC:Description
+X-WR-TIMEZONE:Europe/Berlin
+END:VCALENDAR
diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__caldav_calendar-4.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__caldav_calendar-4.snap
new file mode 100644
index 0000000..9231300
--- /dev/null
+++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__caldav_calendar-4.snap
@@ -0,0 +1,195 @@
+---
+source: src/integration_tests/caldav/calendar.rs
+expression: body
+---
+
+
+
+ /caldav/principal/user/calendar/
+
+
+ #FFF8DCFF
+ Description
+ BEGIN:VCALENDAR
+PRODID:-//github.com/lennart-k/vzic-rs//RustiCal Calendar server//EN
+VERSION:2.0
+BEGIN:VTIMEZONE
+TZID:Europe/Berlin
+LAST-MODIFIED:20250723T190331Z
+X-LIC-LOCATION:Europe/Berlin
+X-PROLEPTIC-TZNAME:LMT
+BEGIN:STANDARD
+TZNAME:CET
+TZOFFSETFROM:+005328
+TZOFFSETTO:+0100
+DTSTART:18930401T000000
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:CEST
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+DTSTART:19160430T230000
+RDATE:19400401T020000
+RDATE:19430329T020000
+RDATE:19460414T020000
+RDATE:19470406T030000
+RDATE:19480418T020000
+RDATE:19490410T020000
+RDATE:19800406T020000
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:CET
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+DTSTART:19161001T010000
+RDATE:19421102T030000
+RDATE:19431004T030000
+RDATE:19441002T030000
+RDATE:19451118T030000
+RDATE:19461007T030000
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:CEST
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+DTSTART:19170416T020000
+RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3MO;UNTIL=19180415T010000Z
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:CET
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+DTSTART:19170917T030000
+RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=3MO;UNTIL=19180916T010000Z
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:CEST
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+DTSTART:19440403T020000
+RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1MO;UNTIL=19450402T010000Z
+END:DAYLIGHT
+BEGIN:DAYLIGHT
+TZNAME:CEMT
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0300
+DTSTART:19450524T020000
+RDATE:19470511T030000
+END:DAYLIGHT
+BEGIN:DAYLIGHT
+TZNAME:CEST
+TZOFFSETFROM:+0300
+TZOFFSETTO:+0200
+DTSTART:19450924T030000
+RDATE:19470629T030000
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:CET
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0100
+DTSTART:19460101T000000
+RDATE:19800101T000000
+END:STANDARD
+BEGIN:STANDARD
+TZNAME:CET
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+DTSTART:19471005T030000
+RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19491002T010000Z
+END:STANDARD
+BEGIN:STANDARD
+TZNAME:CET
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+DTSTART:19800928T030000
+RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1SU;UNTIL=19950924T010000Z
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:CEST
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+DTSTART:19810329T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:CET
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+DTSTART:19961027T030000
+RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
+END:STANDARD
+END:VTIMEZONE
+END:VCALENDAR
+
+
+ https://www.iana.org/time-zones
+
+ Europe/Berlin
+ 0
+
+
+
+
+
+
+
+
+
+ i;ascii-casemap
+ i;octet
+
+ 10000000
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -2621430101T000000Z
+ +2621421231T235959Z
+ github.com/lennart-k/rustical/ns/0
+ github.com/lennart-k/rustical/ns/0
+
+
+
+ 89828c25-cc5c-481a-bb3b-cde435cf9393
+
+
+ 1
+
+
+ 1
+
+
+
+
+
+
+ Amazing Calendar
+
+ /caldav/principal/user/
+
+
+
+
+
+
+
+ /caldav/principal/user/
+
+
+ HTTP/1.1 200 OK
+
+
+
diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__caldav_calendar-5.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__caldav_calendar-5.snap
new file mode 100644
index 0000000..ae58472
--- /dev/null
+++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__caldav_calendar-5.snap
@@ -0,0 +1,5 @@
+---
+source: src/integration_tests/caldav/calendar.rs
+expression: body
+---
+
diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__caldav_calendar.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__caldav_calendar.snap
new file mode 100644
index 0000000..ae58472
--- /dev/null
+++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__caldav_calendar.snap
@@ -0,0 +1,5 @@
+---
+source: src/integration_tests/caldav/calendar.rs
+expression: body
+---
+