diff --git a/tests/api/nc_webdav_dead_properties.hurl b/tests/api/nc_webdav_dead_properties.hurl
new file mode 100644
index 00000000..e2923874
--- /dev/null
+++ b/tests/api/nc_webdav_dead_properties.hurl
@@ -0,0 +1,470 @@
+# =============================================================
+# OxiCloud — NextCloud WebDAV: dead-properties (RFC 4918 §4.2)
+# =============================================================
+# `tests/api/webdav_dead_properties.hurl` covers the native
+# `/webdav/` surface end-to-end. This file covers the same
+# PROPPATCH/PROPFIND contract on the NextCloud-compatible surface
+# (`/remote.php/dav/files/{user}/...`), which — until now — had NO
+# generic dead-property support: PROPPATCH only special-cased
+# `oc:favorite` via an ad hoc XML scan and silently discarded any
+# other property while still claiming `200 OK`; PROPFIND always
+# emitted a fixed hardcoded property set with no dead-property
+# lookup at all. A client (or litmus) PROPPATCHing a custom label
+# through the NextCloud mount got a false success and then never
+# saw the property again.
+#
+# Coverage:
+# 1. Setup: JWT login, mint an NC app password.
+# 2. PUT a probe file via the NC DAV surface.
+# 3. PROPPATCH set a custom property → 207.
+# 4. PROPFIND → value round-trips verbatim.
+# 5. PROPPATCH upsert (same name, new value) → PROPFIND confirms
+# overwrite, not a duplicate row.
+# 6. PROPPATCH remove → PROPFIND confirms absence.
+# 7. PROPPATCH on a nonexistent resource → 404 (the tightened
+# contract: PROPPATCH now does real work, so a previous
+# "always claim success" no-op on a missing resource would be
+# a foot-gun, not a feature).
+# 8. Re-set a property, MOVE the file → PROPFIND on the new path
+# still returns it (resource id is stable across MOVE).
+# 9. DELETE, then PUT a fresh file at the same path → PROPFIND
+# does NOT see the old marker (new resource, no leaked state).
+# 10. Regression guard: `oc:favorite` PROPPATCH/PROPFIND still
+# works, unaffected by the refactor from the ad hoc favorite
+# scanner to generic `WebDavAdapter::parse_proppatch`.
+# 11. Folder coverage: MKCOL, PROPPATCH a dead property on the
+# folder, PROPFIND confirms it, cleanup.
+#
+# XPath assertions use `local-name()` so the test is robust against
+# the server's chosen namespace prefix for dead properties (`X:`).
+#
+# NOTE: in Hurl, [BasicAuth] must be the LAST section before the
+# blank-line/body — any request headers go above it, not below.
+# =============================================================
+
+
+# ─────────────────────────────────────────────────────────────
+# Step 1 — JWT login, then mint an NC app password (NC DAV uses
+# Basic Auth, not the JWT bearer token).
+# ─────────────────────────────────────────────────────────────
+POST {{base_url}}/api/auth/login
+Content-Type: application/json
+{ "username": "{{username}}", "password": "{{password}}" }
+
+HTTP 200
+[Captures]
+jwt: jsonpath "$.access_token"
+
+
+POST {{base_url}}/api/auth/app-passwords
+Authorization: Bearer {{jwt}}
+Content-Type: application/json
+{ "label": "nc_webdav_dead_properties" }
+
+HTTP 200
+[Captures]
+nc_username: jsonpath "$.username"
+nc_password: jsonpath "$.password"
+ap_id: jsonpath "$.id"
+
+
+# ─────────────────────────────────────────────────────────────
+# Step 2 — PUT a probe file through the NC DAV surface.
+# ─────────────────────────────────────────────────────────────
+PUT {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-probe.txt
+Content-Type: text/plain
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+```
+hello nc dead properties
+```
+
+HTTP 201
+
+
+# ─────────────────────────────────────────────────────────────
+# Step 3 — PROPPATCH set a custom (dead) property.
+# ─────────────────────────────────────────────────────────────
+PROPPATCH {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-probe.txt
+Content-Type: application/xml; charset=utf-8
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+```
+
+
+
+
+ hello-nc-dead-property
+
+
+
+```
+
+HTTP 207
+[Asserts]
+xpath "string(//*[local-name()='propstat']/*[local-name()='status'])" contains "200 OK"
+
+
+# ─────────────────────────────────────────────────────────────
+# Step 4 — PROPFIND confirms the round-trip.
+# ─────────────────────────────────────────────────────────────
+PROPFIND {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-probe.txt
+Depth: 0
+Content-Type: application/xml; charset=utf-8
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+```
+
+
+
+
+```
+
+HTTP 207
+[Asserts]
+xpath "string(//*[local-name()='testlabel'])" == "hello-nc-dead-property"
+
+
+# ─────────────────────────────────────────────────────────────
+# Step 5 — Upsert: setting the same name again overwrites rather
+# than duplicating (ON CONFLICT DO UPDATE at the store).
+# ─────────────────────────────────────────────────────────────
+PROPPATCH {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-probe.txt
+Content-Type: application/xml; charset=utf-8
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+```
+
+
+
+
+ updated-nc-value
+
+
+
+```
+
+HTTP 207
+
+
+PROPFIND {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-probe.txt
+Depth: 0
+Content-Type: application/xml; charset=utf-8
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+```
+
+
+
+
+```
+
+HTTP 207
+[Asserts]
+xpath "string(//*[local-name()='testlabel'])" == "updated-nc-value"
+xpath "count(//*[local-name()='testlabel'])" == 1
+
+
+# ─────────────────────────────────────────────────────────────
+# Step 6 — Remove the property; PROPFIND confirms absence.
+# ─────────────────────────────────────────────────────────────
+PROPPATCH {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-probe.txt
+Content-Type: application/xml; charset=utf-8
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+```
+
+
+
+
+
+
+
+
+```
+
+HTTP 207
+
+
+PROPFIND {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-probe.txt
+Depth: 0
+Content-Type: application/xml; charset=utf-8
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+```
+
+
+
+
+```
+
+HTTP 207
+[Asserts]
+xpath "count(//*[local-name()='testlabel'])" == 0
+
+
+# ─────────────────────────────────────────────────────────────
+# Step 7 — PROPPATCH against a nonexistent resource → 404.
+# Prior behaviour on this handler silently no-opped
+# (and still claimed success) when the body carried no
+# `oc:favorite` directive; now that PROPPATCH performs
+# real dead-property writes, a missing resource must be
+# a hard failure, matching the native `/webdav/` handler.
+# ─────────────────────────────────────────────────────────────
+PROPPATCH {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-does-not-exist.txt
+Content-Type: application/xml; charset=utf-8
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+```
+
+
+
+
+ should-not-be-stored
+
+
+
+```
+
+HTTP 404
+
+
+# ─────────────────────────────────────────────────────────────
+# Step 8 — Re-set a marker, MOVE the file, confirm the property
+# followed the resource (id-stable across MOVE — no
+# store-side rename bookkeeping needed).
+# ─────────────────────────────────────────────────────────────
+PROPPATCH {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-probe.txt
+Content-Type: application/xml; charset=utf-8
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+```
+
+
+
+
+ survives-nc-move
+
+
+
+```
+
+HTTP 207
+
+
+MOVE {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-probe.txt
+Destination: {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-moved.txt
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+
+# Fresh destination → 201 (RFC 4918 §9.9.4).
+HTTP 201
+
+
+PROPFIND {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-moved.txt
+Depth: 0
+Content-Type: application/xml; charset=utf-8
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+```
+
+
+
+
+```
+
+HTTP 207
+[Asserts]
+xpath "string(//*[local-name()='testlabel'])" == "survives-nc-move"
+
+
+# ─────────────────────────────────────────────────────────────
+# Step 9 — DELETE, then PUT a fresh file at the same path: the
+# old marker must NOT resurface (new resource, no leaked
+# dead-property state). Whether DELETE soft-deletes to
+# trash or hard-deletes, the recreated path resolves to
+# a brand-new resource id with no dead-property rows of
+# its own.
+# ─────────────────────────────────────────────────────────────
+DELETE {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-moved.txt
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+
+HTTP 204
+
+
+PUT {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-moved.txt
+Content-Type: text/plain
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+```
+fresh file at the same nc path
+```
+
+HTTP 201
+
+
+PROPFIND {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-moved.txt
+Depth: 0
+Content-Type: application/xml; charset=utf-8
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+```
+
+
+
+
+```
+
+HTTP 207
+[Asserts]
+xpath "count(//*[local-name()='testlabel'])" == 0
+
+
+# ─────────────────────────────────────────────────────────────
+# Step 10 — Regression guard: `oc:favorite` still works after the
+# PROPPATCH handler was rewritten from an ad hoc
+# favorite-only scanner to generic dead-property
+# handling with an `oc:favorite` special case.
+# ─────────────────────────────────────────────────────────────
+PROPPATCH {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-moved.txt
+Content-Type: application/xml; charset=utf-8
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+```
+
+
+
+
+ 1
+
+
+
+```
+
+HTTP 207
+[Asserts]
+xpath "string(//*[local-name()='propstat']/*[local-name()='status'])" contains "200 OK"
+
+
+PROPFIND {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-moved.txt
+Depth: 0
+Content-Type: application/xml; charset=utf-8
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+```
+
+
+
+
+```
+
+HTTP 207
+[Asserts]
+xpath "string(//*[local-name()='favorite'])" == "1"
+
+
+PROPPATCH {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-moved.txt
+Content-Type: application/xml; charset=utf-8
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+```
+
+
+
+
+ 0
+
+
+
+```
+
+HTTP 207
+
+
+PROPFIND {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-moved.txt
+Depth: 0
+Content-Type: application/xml; charset=utf-8
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+```
+
+
+
+
+```
+
+HTTP 207
+[Asserts]
+xpath "string(//*[local-name()='favorite'])" == "0"
+
+
+# ─────────────────────────────────────────────────────────────
+# Cleanup — probe file.
+# ─────────────────────────────────────────────────────────────
+DELETE {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-moved.txt
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+
+HTTP 204
+
+
+# ─────────────────────────────────────────────────────────────
+# Step 11 — Folder coverage: MKCOL, PROPPATCH, PROPFIND, cleanup.
+# ─────────────────────────────────────────────────────────────
+MKCOL {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-folder/
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+
+HTTP 201
+
+
+PROPPATCH {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-folder/
+Content-Type: application/xml; charset=utf-8
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+```
+
+
+
+
+ nc-folder-keeps-this
+
+
+
+```
+
+HTTP 207
+
+
+PROPFIND {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-folder/
+Depth: 0
+Content-Type: application/xml; charset=utf-8
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+```
+
+
+
+
+```
+
+HTTP 207
+[Asserts]
+xpath "string(//*[local-name()='foldermark'])" == "nc-folder-keeps-this"
+
+
+DELETE {{base_url}}/remote.php/dav/files/{{username}}/nc-dead-props-folder/
+[BasicAuth]
+{{nc_username}}: {{nc_password}}
+
+HTTP 204
+
+
+# ─────────────────────────────────────────────────────────────
+# Teardown — revoke the app password minted in Step 1.
+# ─────────────────────────────────────────────────────────────
+DELETE {{base_url}}/api/auth/app-passwords/{{ap_id}}
+Authorization: Bearer {{jwt}}
+
+HTTP 200
diff --git a/tests/api/run.sh b/tests/api/run.sh
index 6fa42e63..472272c5 100755
--- a/tests/api/run.sh
+++ b/tests/api/run.sh
@@ -168,6 +168,7 @@ hurl --variables-file "$API_DIR/test.env" --file-root "$REPO_ROOT/tests" --test
"$API_DIR/cross_drive_move.hurl" \
"$API_DIR/cross_drive_copy.hurl" \
"$API_DIR/webdav_dead_properties.hurl" \
+ "$API_DIR/nc_webdav_dead_properties.hurl" \
"$API_DIR/webdav_nested_move_cascade.hurl"
#bash "$API_DIR/dedup_bulk_upload.sh"