diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/Routing.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/Routing.kt
index 007ae9a7881cbd8119c3f07cdfc91f8e076e94da..1157de2d83417d1d2814c0fbec755814d43b854c 100644
--- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/Routing.kt
+++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/Routing.kt
@@ -7,7 +7,6 @@ import io.ktor.auth.authenticate
 import io.ktor.features.BadRequestException
 import io.ktor.features.MissingRequestParameterException
 import io.ktor.http.ContentType
-import io.ktor.http.HttpStatusCode
 import io.ktor.http.HttpStatusCode.Companion.BadRequest
 import io.ktor.http.HttpStatusCode.Companion.Unauthorized
 import io.ktor.response.respond
@@ -22,18 +21,24 @@ import io.ktor.util.getOrFail
 import org.briarproject.mailbox.core.contacts.ContactsManager
 import org.briarproject.mailbox.core.files.FileManager
 import org.briarproject.mailbox.core.setup.SetupManager
+import org.briarproject.mailbox.core.setup.WipeManager
 import org.briarproject.mailbox.core.system.InvalidIdException
 
 internal const val V = "/" // TODO set to "/v1" for release
 
-internal fun Application.configureBasicApi(setupManager: SetupManager) = routing {
+internal fun Application.configureBasicApi(
+    setupManager: SetupManager,
+    wipeManager: WipeManager,
+) = routing {
     route(V) {
         get {
             call.respondText("Hello world!", ContentType.Text.Plain)
         }
         authenticate {
             delete {
-                call.respond(HttpStatusCode.OK, "delete: Not yet implemented")
+                call.handle {
+                    wipeManager.onWipeRequest(call)
+                }
             }
             put("/setup") {
                 call.handle {
diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/WebServerManager.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/WebServerManager.kt
index 771167d3aa077a542167416c5dafd3cf7a329313..96889cbbb6a9871806910feeffa4602beb9cdf6e 100644
--- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/WebServerManager.kt
+++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/WebServerManager.kt
@@ -12,6 +12,7 @@ import org.briarproject.mailbox.core.files.FileManager
 import org.briarproject.mailbox.core.lifecycle.Service
 import org.briarproject.mailbox.core.server.WebServerManager.Companion.PORT
 import org.briarproject.mailbox.core.setup.SetupManager
+import org.briarproject.mailbox.core.setup.WipeManager
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory.getLogger
 import javax.inject.Inject
@@ -29,6 +30,7 @@ internal class WebServerManagerImpl @Inject constructor(
     private val setupManager: SetupManager,
     private val contactsManager: ContactsManager,
     private val fileManager: FileManager,
+    private val wipeManager: WipeManager,
 ) : WebServerManager {
 
     internal companion object {
@@ -50,7 +52,7 @@ internal class WebServerManagerImpl @Inject constructor(
             install(ContentNegotiation) {
                 jackson()
             }
-            configureBasicApi(setupManager)
+            configureBasicApi(setupManager, wipeManager)
             configureContactApi(contactsManager)
             configureFilesApi(fileManager)
         }
diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/setup/WipeManager.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/setup/WipeManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3da9b2e0cb39188ef58e20f1e2a6996db7210b3d
--- /dev/null
+++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/setup/WipeManager.kt
@@ -0,0 +1,37 @@
+package org.briarproject.mailbox.core.setup
+
+import io.ktor.application.ApplicationCall
+import io.ktor.auth.principal
+import io.ktor.http.HttpStatusCode
+import io.ktor.response.respond
+import org.briarproject.mailbox.core.db.Database
+import org.briarproject.mailbox.core.files.FileManager
+import org.briarproject.mailbox.core.server.AuthException
+import org.briarproject.mailbox.core.server.MailboxPrincipal
+import javax.inject.Inject
+
+class WipeManager @Inject constructor(
+    private val db: Database,
+    private val fileManager: FileManager,
+) {
+
+    /**
+     * Handler for `DELETE /` API endpoint.
+     *
+     * Wipes entire database as well as stored files
+     * and returns `204 No Content` response if successful
+     */
+    @Throws(AuthException::class)
+    suspend fun onWipeRequest(call: ApplicationCall) {
+        val principal = call.principal<MailboxPrincipal>()
+        if (principal !is MailboxPrincipal.OwnerPrincipal) throw AuthException()
+
+        db.transaction(false) { txn ->
+            db.clearDatabase(txn)
+        }
+        fileManager.deleteAllFiles()
+
+        call.respond(HttpStatusCode.NoContent)
+    }
+
+}
diff --git a/mailbox-core/src/test/java/org/briarproject/mailbox/core/TestComponent.kt b/mailbox-core/src/test/java/org/briarproject/mailbox/core/TestComponent.kt
index 564531b71e307b705f0864f5c9559cc752fc057a..6eeff9ca8f6c74f13c17479c5f3e0fc58c656c81 100644
--- a/mailbox-core/src/test/java/org/briarproject/mailbox/core/TestComponent.kt
+++ b/mailbox-core/src/test/java/org/briarproject/mailbox/core/TestComponent.kt
@@ -3,6 +3,7 @@ package org.briarproject.mailbox.core
 import dagger.Component
 import org.briarproject.mailbox.core.db.Database
 import org.briarproject.mailbox.core.files.FileManager
+import org.briarproject.mailbox.core.files.FileProvider
 import org.briarproject.mailbox.core.lifecycle.LifecycleManager
 import org.briarproject.mailbox.core.settings.SettingsManager
 import org.briarproject.mailbox.core.setup.SetupManager
@@ -23,4 +24,5 @@ interface TestComponent {
     fun getFileManager(): FileManager
     fun getDatabase(): Database
     fun getRandomIdManager(): RandomIdManager
+    fun getFileProvider(): FileProvider
 }
diff --git a/mailbox-core/src/test/java/org/briarproject/mailbox/core/setup/WipeManagerTest.kt b/mailbox-core/src/test/java/org/briarproject/mailbox/core/setup/WipeManagerTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..23fb16a71573106beaa0761d53702ba86e1d6ffc
--- /dev/null
+++ b/mailbox-core/src/test/java/org/briarproject/mailbox/core/setup/WipeManagerTest.kt
@@ -0,0 +1,71 @@
+package org.briarproject.mailbox.core.setup
+
+import io.ktor.client.request.delete
+import io.ktor.client.request.post
+import io.ktor.client.statement.HttpResponse
+import io.ktor.http.HttpStatusCode
+import kotlinx.coroutines.runBlocking
+import org.briarproject.mailbox.core.server.IntegrationTest
+import org.junit.jupiter.api.Test
+import kotlin.random.Random
+import kotlin.test.assertEquals
+import kotlin.test.assertNull
+import kotlin.test.assertTrue
+
+class WipeManagerTest : IntegrationTest() {
+
+    private val db by lazy { testComponent.getDatabase() }
+
+    @Test
+    fun `wipe request rejects non-owners`() = runBlocking {
+        addOwnerToken()
+        addContact(contact1)
+
+        // Unauthorized with random token
+        val response1 = httpClient.delete<HttpResponse>("$baseUrl/") {
+            authenticateWithToken(token)
+        }
+        assertEquals(HttpStatusCode.Unauthorized, response1.status)
+
+        // Unauthorized with contact's token
+        val response2 = httpClient.delete<HttpResponse>("$baseUrl/") {
+            authenticateWithToken(contact1.token)
+        }
+        assertEquals(HttpStatusCode.Unauthorized, response2.status)
+    }
+
+    @Test
+    fun `wipe request deletes files and db for owner`() = runBlocking {
+        addOwnerToken()
+        addContact(contact1)
+        addContact(contact2)
+
+        // owner uploads a file
+        val uploadResponse: HttpResponse = httpClient.post("$baseUrl/files/${contact1.inboxId}") {
+            authenticateWithToken(ownerToken)
+            body = Random.nextBytes(42)
+        }
+        assertEquals(HttpStatusCode.OK, uploadResponse.status)
+
+        // owner wipes mailbox
+        val response = httpClient.delete<HttpResponse>("$baseUrl/") {
+            authenticateWithToken(ownerToken)
+        }
+        assertEquals(HttpStatusCode.NoContent, response.status)
+
+        // no more contacts in DB
+        val contacts = db.transactionWithResult(true) { db.getContacts(it) }
+        assertEquals(0, contacts.size)
+
+        // owner token was cleared as well
+        val token = db.transactionWithResult(true) { txn ->
+            testComponent.getSetupManager().getOwnerToken(txn)
+        }
+        assertNull(token)
+
+        // no more files are stored
+        val folderRoot = testComponent.getFileProvider().folderRoot
+        assertTrue(folderRoot.listFiles()?.isEmpty() ?: false)
+    }
+
+}