diff --git a/api/config/urls.py b/api/config/urls.py
index 99fc32f1fce09e6a3b8c227e530102ba0e9abd17..aa38cc2b9029db131670de14edd838665db348d3 100644
--- a/api/config/urls.py
+++ b/api/config/urls.py
@@ -13,6 +13,7 @@ urlpatterns = [
     # Django Admin, use {% url 'admin:index' %}
     url(settings.ADMIN_URL, admin.site.urls),
     url(r"^api/", include(("config.api_urls", "api"), namespace="api")),
+    url(r"^dav/", include(("funkwhale_api.webdav.urls", "webdav"), namespace="webdav")),
     url(
         r"^",
         include(
diff --git a/api/funkwhale_api/common/middleware.py b/api/funkwhale_api/common/middleware.py
index 59d50b30ead3ea34f5219431475162889461b596..0502f960bbf1c97d7b29c63fff38b9b0e2a265ce 100644
--- a/api/funkwhale_api/common/middleware.py
+++ b/api/funkwhale_api/common/middleware.py
@@ -9,7 +9,7 @@ from django import urls
 from . import preferences
 from . import utils
 
-EXCLUDED_PATHS = ["/api", "/federation", "/.well-known"]
+EXCLUDED_PATHS = ["/api", "/federation", "/.well-known", "/dav", "/rest"]
 
 
 def should_fallback_to_spa(path):
diff --git a/api/funkwhale_api/webdav/__init__.py b/api/funkwhale_api/webdav/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/api/funkwhale_api/webdav/renderers.py b/api/funkwhale_api/webdav/renderers.py
new file mode 100644
index 0000000000000000000000000000000000000000..ac2fce675e91733feaf0773f3cd2fcb07fc715cd
--- /dev/null
+++ b/api/funkwhale_api/webdav/renderers.py
@@ -0,0 +1,258 @@
+from __future__ import unicode_literals
+
+
+from django.utils import six
+from django.utils.xmlutils import SimplerXMLGenerator
+from django.utils.six.moves import StringIO
+from django.utils.encoding import smart_text
+from rest_framework.renderers import BaseRenderer
+
+DATA = """<?xml version="1.0" encoding="utf-8"?>
+<d:multistatus xmlns:d="DAV:">
+    <d:response>
+        <d:href>https://www.ajaxbrowser.com:443/Userc97c746/Products/</d:href>
+        <d:propstat>
+            <d:status>HTTP/1.1 200 OK</d:status>
+            <d:prop>
+                <d:resourcetype>
+                    <d:collection />
+                </d:resourcetype>
+                <d:displayname>Products</d:displayname>
+                <d:creationdate>2019-02-22T14:36:36Z</d:creationdate>
+                <d:getlastmodified>Fri, 22 Feb 2019 14:36:36 GMT</d:getlastmodified>
+                <d:supportedlock>
+                    <d:lockentry>
+                        <d:lockscope>
+                            <d:exclusive />
+                        </d:lockscope>
+                        <d:locktype>
+                            <d:write />
+                        </d:locktype>
+                    </d:lockentry>
+                    <d:lockentry>
+                        <d:lockscope>
+                            <d:shared />
+                        </d:lockscope>
+                        <d:locktype>
+                            <d:write />
+                        </d:locktype>
+                    </d:lockentry>
+                </d:supportedlock>
+                <d:lockdiscovery />
+                <d:quota-available-bytes>102312153088</d:quota-available-bytes>
+                <d:quota-used-bytes>26520371200</d:quota-used-bytes>
+            </d:prop>
+        </d:propstat>
+        <d:propstat>
+            <d:status>HTTP/1.1 404 Not Found</d:status>
+            <d:prop>
+                <d:getcontenttype />
+                <d:getcontentlength />
+                <d:checked-in />
+                <d:checked-out />
+            </d:prop>
+            <d:responsedescription>Property was not found</d:responsedescription>
+        </d:propstat>
+    </d:response>
+    <d:response>
+        <d:href>https://www.ajaxbrowser.com:443/Userc97c746/Products/General.doc</d:href>
+        <d:propstat>
+            <d:status>HTTP/1.1 200 OK</d:status>
+            <d:prop>
+                <d:resourcetype />
+                <d:displayname>General.doc</d:displayname>
+                <d:creationdate>2019-02-22T14:36:36Z</d:creationdate>
+                <d:getlastmodified>Fri, 22 Feb 2019 14:36:36 GMT</d:getlastmodified>
+                <d:getcontenttype>application/msword</d:getcontenttype>
+                <d:getcontentlength>10752</d:getcontentlength>
+                <d:supportedlock>
+                    <d:lockentry>
+                        <d:lockscope>
+                            <d:exclusive />
+                        </d:lockscope>
+                        <d:locktype>
+                            <d:write />
+                        </d:locktype>
+                    </d:lockentry>
+                    <d:lockentry>
+                        <d:lockscope>
+                            <d:shared />
+                        </d:lockscope>
+                        <d:locktype>
+                            <d:write />
+                        </d:locktype>
+                    </d:lockentry>
+                </d:supportedlock>
+                <d:lockdiscovery />
+                <d:checked-in>
+                    <d:href>https://www.ajaxbrowser.com:443/Userc97c746/Products/General.doc?version=1</d:href>
+                </d:checked-in>
+            </d:prop>
+        </d:propstat>
+        <d:propstat>
+            <d:status>HTTP/1.1 404 Not Found</d:status>
+            <d:prop>
+                <d:quota-available-bytes />
+                <d:quota-used-bytes />
+                <d:checked-out />
+            </d:prop>
+            <d:responsedescription>Property was not found</d:responsedescription>
+        </d:propstat>
+    </d:response>
+    <d:response>
+        <d:href>https://www.ajaxbrowser.com:443/Userc97c746/Products/General.vsd</d:href>
+        <d:propstat>
+            <d:status>HTTP/1.1 200 OK</d:status>
+            <d:prop>
+                <d:resourcetype />
+                <d:displayname>General.vsd</d:displayname>
+                <d:creationdate>2019-02-22T14:36:36Z</d:creationdate>
+                <d:getlastmodified>Fri, 22 Feb 2019 14:36:36 GMT</d:getlastmodified>
+                <d:getcontenttype>application/x-visio</d:getcontenttype>
+                <d:getcontentlength>0</d:getcontentlength>
+                <d:supportedlock>
+                    <d:lockentry>
+                        <d:lockscope>
+                            <d:exclusive />
+                        </d:lockscope>
+                        <d:locktype>
+                            <d:write />
+                        </d:locktype>
+                    </d:lockentry>
+                    <d:lockentry>
+                        <d:lockscope>
+                            <d:shared />
+                        </d:lockscope>
+                        <d:locktype>
+                            <d:write />
+                        </d:locktype>
+                    </d:lockentry>
+                </d:supportedlock>
+                <d:lockdiscovery />
+                <d:checked-in>
+                    <d:href>https://www.ajaxbrowser.com:443/Userc97c746/Products/General.vsd?version=1</d:href>
+                </d:checked-in>
+            </d:prop>
+        </d:propstat>
+        <d:propstat>
+            <d:status>HTTP/1.1 404 Not Found</d:status>
+            <d:prop>
+                <d:quota-available-bytes />
+                <d:quota-used-bytes />
+                <d:checked-out />
+            </d:prop>
+            <d:responsedescription>Property was not found</d:responsedescription>
+        </d:propstat>
+    </d:response>
+    <d:response>
+        <d:href>https://www.ajaxbrowser.com:443/Userc97c746/Products/Product.mpp</d:href>
+        <d:propstat>
+            <d:status>HTTP/1.1 200 OK</d:status>
+            <d:prop>
+                <d:resourcetype />
+                <d:displayname>Product.mpp</d:displayname>
+                <d:creationdate>2019-02-22T14:36:36Z</d:creationdate>
+                <d:getlastmodified>Fri, 22 Feb 2019 14:36:36 GMT</d:getlastmodified>
+                <d:getcontenttype>application/vnd.ms-project</d:getcontenttype>
+                <d:getcontentlength>114176</d:getcontentlength>
+                <d:supportedlock>
+                    <d:lockentry>
+                        <d:lockscope>
+                            <d:exclusive />
+                        </d:lockscope>
+                        <d:locktype>
+                            <d:write />
+                        </d:locktype>
+                    </d:lockentry>
+                    <d:lockentry>
+                        <d:lockscope>
+                            <d:shared />
+                        </d:lockscope>
+                        <d:locktype>
+                            <d:write />
+                        </d:locktype>
+                    </d:lockentry>
+                </d:supportedlock>
+                <d:lockdiscovery />
+                <d:checked-in>
+                    <d:href>https://www.ajaxbrowser.com:443/Userc97c746/Products/Product.mpp?version=1</d:href>
+                </d:checked-in>
+            </d:prop>
+        </d:propstat>
+        <d:propstat>
+            <d:status>HTTP/1.1 404 Not Found</d:status>
+            <d:prop>
+                <d:quota-available-bytes />
+                <d:quota-used-bytes />
+                <d:checked-out />
+            </d:prop>
+            <d:responsedescription>Property was not found</d:responsedescription>
+        </d:propstat>
+    </d:response>
+</d:multistatus>
+"""
+
+
+class WebDAVXMLRenderer(BaseRenderer):
+    """
+    Renders WebDAV-compliant XML
+    """
+
+    media_type = "application/xml"
+    format = "xml"
+    charset = "utf-8"
+    item_tag_name = "list-item"
+    root_tag_name = "multistatus"
+
+    def render(self, data, accepted_media_type=None, renderer_context=None):
+        """
+        Renders `data` into serialized XML.
+        """
+        return DATA.encode("utf-8")
+        if data is None:
+            return ""
+
+        stream = StringIO()
+
+        xml = SimplerXMLGenerator(stream, self.charset)
+        xml.startDocument()
+        xml.startElement(self.root_tag_name, {})
+
+        self._to_xml(xml, data)
+
+        xml.endElement(self.root_tag_name)
+        xml.endDocument()
+        return stream.getvalue()
+
+    def _to_xml(self, xml, data):
+        if isinstance(data, (list, tuple)):
+            for item in data:
+                self._to_xml(xml, item)
+
+        elif isinstance(data, dict):
+            for key, value in six.iteritems(data):
+                if isinstance(value, ElementList):
+                    for innerval in value:
+                        xml.startElement(key, {})
+                        self._to_xml(xml, innerval)
+                        xml.endElement(key)
+                if isinstance(value, ElementGroup):
+                    for innerkey, innerval in value.items():
+                        xml.startElement(key, {})  # key=<propstat>
+                        self._to_xml(xml, innerval)
+                        xml.startElement(innerkey[0], {})
+                        self._to_xml(xml, innerkey[1])
+                        xml.endElement(innerkey[0])
+                        xml.endElement(key)
+
+                else:
+                    xml.startElement(key, {})
+                    self._to_xml(xml, value)
+                    xml.endElement(key)
+
+        elif data is None:
+            # Don't output any value
+            pass
+
+        else:
+            xml.characters(smart_text(data))
diff --git a/api/funkwhale_api/webdav/urls.py b/api/funkwhale_api/webdav/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..aaca41fc9bf2e135af611f3eedc94a4abef2b52c
--- /dev/null
+++ b/api/funkwhale_api/webdav/urls.py
@@ -0,0 +1,6 @@
+from django.conf.urls import url
+
+from . import views
+
+
+urlpatterns = [url(r"^libraries/(?P<path>.*)$", views.LibraryViewSet.as_view())]
diff --git a/api/funkwhale_api/webdav/views.py b/api/funkwhale_api/webdav/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..5a808f396339980d6ae0e218cdfd718c99ea80e7
--- /dev/null
+++ b/api/funkwhale_api/webdav/views.py
@@ -0,0 +1,58 @@
+from rest_framework import response, mixins, viewsets
+
+from . import renderers
+
+WEBDAV_METHODS = [
+    "get",
+    "put",
+    "lock",
+    "unlock",
+    "propfind",
+    "proppatch",
+    "delete",
+    "head",
+    "options",
+    "mkcol",
+    "copy",
+    "move",
+]
+ACTIONS = {m: "action_{}".format(m) for m in WEBDAV_METHODS}
+
+
+class WebDAVViewSet(viewsets.GenericViewSet):
+    http_method_names = WEBDAV_METHODS
+    renderer_classes = (renderers.WebDAVXMLRenderer,)
+
+    def dispatch(self, request, *args, **kwargs):
+        print("BODY", request.body)
+        response = super().dispatch(request, *args, **kwargs)
+        response["DAV"] = "1"
+        return response
+
+    @classmethod
+    def as_view(cls, *args, **kwargs):
+        actions = {}
+        for method in WEBDAV_METHODS:
+            handler_name = "action_{}".format(method)
+            if hasattr(cls, handler_name):
+                actions[method] = handler_name
+
+        kwargs["actions"] = actions
+        return super().as_view(*args, **kwargs)
+
+
+class LibraryViewSet(WebDAVViewSet):
+    authentication_classes = []
+    permission_classes = []
+
+    def action_propfind(self, request, *args, **kwargs):
+        return response.Response({})
+
+    def action_options(self, request, *args, **kwargs):
+        return response.Response({})
+
+    def action_get(self, request, *args, **kwargs):
+        return response.Response({})
+
+    def action_put(self, request, *args, **kwargs):
+        return response.Response({})